Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create, Update & Delete Examples #77

Open
solocommerce opened this issue Nov 15, 2018 · 12 comments
Open

Create, Update & Delete Examples #77

solocommerce opened this issue Nov 15, 2018 · 12 comments

Comments

@solocommerce
Copy link

I have been successfully able to get a read query working, but unable to get create and update working. I've looked through community examples but I'm unable to find any examples of doing create, update and delete calls from the controller. Does anyone have an example of how they would look? My confusion is arising from normally using patchEntity then save, however, an entity is not applicable.

I'm trying to connect to a Magento 2 REST API
POST /V1/products
PUT /V1/products/:sku
DELETE /V1/products/:sku
GET /V1/products
GET /V1/products/:sku

@ADmad
Copy link
Member

ADmad commented Nov 15, 2018

Yes there aren't any implementations which do create/update/delete yet AFAIK.

Basically your webservice class will need to implement the _executeCreateQuery(), _executeUpdateQuery(), _executeDeleteQuery() methods along with the _executeReadQuery() method.

@ADmad ADmad added the question label Nov 15, 2018
@solocommerce
Copy link
Author

Thank you, I have those functions ready to go in my MagentoWebservice.php. The bit I'm struggling with is in my controller how do I initiate _executeCreateQuery() and _executeUpdateQuery() methods? Normally I would do something like

public function add()
{
    $article = $this->Articles->newEntity();
    if ($this->request->is('post')) {
        $article = $this->Articles->patchEntity($article, $this->request->getData());
        if ($this->Articles->save($article)) {
        ...

and

public function edit($id = null)
{
    $article = $this->Articles->get($id);
    if ($this->request->is(['patch', 'post', 'put'])) {
        $article = $this->Articles->patchEntity($article, $this->request->getData());
        if ($this->Articles->save($article)) {
        ...

and Cake handles the is new. In the case of a remote web service there is no Entity to patch.

I was hoping there might be a

$this->Articles->create($article)
or
$this->Articles->update($article)

@ADmad
Copy link
Member

ADmad commented Nov 15, 2018

The bit I'm struggling with is in my controller how do I initiate _executeCreateQuery() and _executeUpdateQuery() methods?

You don't have to access those methods directly. You have to use the newEntity(), patchEntity() and save() methods of your Endpoint class.

@solocommerce
Copy link
Author

I ended up figuring it out, I was over complicating the process. The process is exactly how I showed examples for in the controller. The missing piece for me was to set up the schema in the EndPoint. This is a great plugin, thank you.

@ADmad
Copy link
Member

ADmad commented Nov 25, 2018

It would be great if you could submit an update to the readme adding some info on how to handle create/update/delete.

@r0kawa
Copy link

r0kawa commented Dec 4, 2018

I ended up figuring it out, I was over complicating the process. The process is exactly how I showed examples for in the controller. The missing piece for me was to set up the schema in the EndPoint. This is a great plugin, thank you.

I would love an example that I can follow because I'm struggling to handle the edit , update , delete example.

@r0kawa
Copy link

r0kawa commented Dec 12, 2018

Hi @solocommerce , can you share with us some example for edit, add code. ?

@solocommerce
Copy link
Author

Sorry for the delay, happy to help. I'm not sure how to add to the documentation into the repo, so I'll type it here, give me an hour or so to type it out.

@solocommerce
Copy link
Author

I have done the best example I can, it is hard to give examples without knowing the actual webservice and vice versa it is hard to give example if it is specific to a webservice. It gets really confusing as well if you want to call your cake files articles and your webservice files articles as well, I would advise calling them different things. Which is why I've created a fake webserivce called Newspaper, whereby you post your articles to the newspaper webservice.

/plugins/Example/Newspaper/src/Model/Endpoint/NewspaperArticleEndpoint.php

<?php

// Think of the endpoint like a entity

namespace Example\Newspaper\Webservice;

use Muffin\Webservice\Model\Endpoint;
use Muffin\Webservice\Schema;

class NewspaperArticlesEndpoint extends Endpoint
{
    /**
     * Initialise Method
     *
     * @param array $config Array of config
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        // The primary key in which data is handled
        $this->primaryKey('id');
        $this->displayField('name');

        // Just like an entity this is the data the remote webservice expects.
        $schema = new Schema(null, [
            'id' => [
                'type' => 'integer',
            ],
            'name' => [
                'type' => 'string',
            ],
            'contents' => [
                'type' => 'string',
            ],
        ]);
        $this->schema($schema);
    }
}

/plugins/Example/Newspaper/src/Model/Resource/NewspaperArticle.php

<?php

// I have not implemented any resources, I believe it is the response
// the endpoint may provide back?

namespace Example\Newspaper\Model\Resource;

use Muffin\Webservice\Model\Resource;

/**
 * Class NewspaperArticle
 *
 * @package Example\Newspaper\Model\Resource
 */
class NewspaperArticle extends Resource
{

}

/plugins/Example/Newspaper/src/Webservice/NewspaperWebserivce.php

<?php

namespace Example\Newspaper\Webservice;

use Muffin\Webservice\Query;
use Muffin\Webservice\Webservice\Webservice;
use Psr\Http\Message\ResponseInterface;

/**
 * Class NewspaperWebservice
 *
 * @package Example\Newspaper\Webservice
 */
class NewspaperWebservice extends Webservice
{
    /**
     * Execute Create Query Method
     */
    protected function _executeCreateQuery(Query $query, array $options = [])
    {
        // This is the main webservice endpoint, in my example I'm assuming that 
        // there is something already operating here and it is different structure
        // to the articles webservice. In other words check NewspaperArticlesWebservice.php
    }

    /**
     * Execute Read Query Method
     */
    protected function _executeReadQuery(Query $query, array $options = [])
    {
        // As above check NewspaperArticlesWebservice.php
    }

    /**
     * Check Response Method
     */
    protected function _checkResponse(ResponseInterface $response)
    {
        // Globally handle 404s etc
    }

    /**
     * Execute Update Query Method
     */
    protected function _executeUpdateQuery(Query $query, array $options = [])
    {
        // As above check NewspaperArticlesWebservice.php
    }

    /**
     * Execute Delete Query Method
     */
    protected function _executeDeleteQuery(Query $query, array $options = [])
    {
        // As above check NewspaperArticlesWebservice.php
    }
}

/plugins/Example/Newspaper/src/Webservice/NewspaperArticlesWebservice.php

<?php

namespace Example\Newspaper\Webservice;

use Cake\Network\Http\Response;
use Muffin\Webservice\Model\Endpoint;
use Muffin\Webservice\Query;

/**
 * Class NewspaperArticlesWebservice
 *
 * @package Example\Newspaper\Webservice
 */
class NewspaperArticlesWebservice extends NewspaperWebservice
{
    /**
     * Initialise Method
     *
     * @return void
     */
    public function initialize()
    {
        parent::initialize();
    }

    /**
     * Execute Create Query Method
     */
    protected function _executeCreateQuery(Query $query, array $options = [])
    {
        // Query->set will initiate the schema created set out in the endpoint,
        // if missing wont map/create (just like entity)
        $parameters['article'] = $query->set();

        /* @var Response $response */
        $response = $this->driver()->client()->post($this->_baseUrl(), json_encode($parameters), ['type' => 'json']);

        $this->_checkResponse($response);

        return $this->_transformResource($query->endpoint(), $response->json);
    }

    /**
     * Base URL
     */
    protected function _baseUrl()
    {
        return '/articles';
    }

    /**
     * Transform Resource Method
     */
    protected function _transformResource(Endpoint $endpoint, array $result)
    {
        // Deal with the article data returned.
    }

    /**
     * Read Query Method
     */
    protected function _executeReadQuery(Query $query, array $options = [])
    {
        // ...
    }

    /**
     * Update Query Method
     */
    protected function _executeUpdateQuery(Query $query, array $options = [])
    {
        // Query->set will initiate the schema created set out in the endpoint,
        // if missing wont map/create (just like entity)
        $parameters['article'] = $query->set();
        $parameters['article'][$query->endpoint()->primaryKey()] = $query->where()['id'];

        /* @var Response $response */
        $response = $this->driver()->client()->post($this->_baseUrl(), json_encode($parameters), ['type' => 'json']);

        $this->_checkResponse($response);

        return $this->_transformResource($query->endpoint(), $response->json);
    }

    /**
     * Delete Query Method
     */
    protected function _executeDeleteQuery(Query $query, array $options = [])
    {
        // I have not tried a delete
    }
}

/src/Controller/NewspaperArticlesController.php

<?php

namespace App\Controller;

use App\Controller\AppController;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;

/**
 * Newspaper Articles Controller
 *
 * @property \Exammple\Newspaper\Model\Endpoint\NewspaperArticlesEndpoint $NewspaperArticles
 */
class NewspaperArticlesController extends AppController
{
    /**
     * @throws \Exception
     * @return void
     */
    public function initialize()
    {
        parent::initialize();

        $this->modelFactory('Endpoint', ['Muffin\Webservice\Model\EndpointRegistry', 'get']);
    }

    /**
     * Before Filter Method
     *
     * @param \Cake\Event\Event $event Event Object
     *
     * @return \Cake\Http\Response|null|void
     */
    public function beforeFilter(Event $event)
    {
        $this->loadModel('Example/Newspaper.NewspaperArticles', 'Endpoint');
    }

    /**
     * Add the article from Cake to Newspaper Article Web Service
     *
     * @param string|null $articleId The local article id
     *
     * @return \Cake\Http\Response|null
     */
    public function add($articleId = null)
    {
        // In my example, Im populating data from another table and saving (adding/pushing) 
        // that into a remote system using the webservice plugin. You may not wish to do 
        // that, in which case you could just post the data from your form to the remote 
        // webservice and skip loading data.

        // Pull in the data from the articles table (your local data in cake).
        $article = TableRegistry::getTableLocator()->get('Articles');
        $myArticle = $article->get($articleId);

        if (!$myArticle->id) {
            $this->Flash->error(__('This article doesn\'t exist'));

            return $this->redirect($this->referer());
        }

        // Initiate the new remote article (creates from endpoint schema)
        $newspaperArticle = $this->NewspaperArticles->newEntity();

        // The minimum to create an article on remote system.
        $articleData = [
            'name' => $myArticle->name,
            'contents' => $myArticle->contents,
        ];

        // Patch the endpoint with the data to be saved to remote system.
        $newspaperArticle = $this->NewspaperArticles->patchEntity($newspaperArticle, $articleData);

        $newspaperResponse = $this->NewspaperArticles->save($newspaperArticle);

        if ($newspaperResponse->id) {
            $this->Flash->success(__('The article has been created in the remote system.'));

            // here you may choose to save any of the data the remote system responded with
        } else {
            $this->Flash->error(__('The article has not been created in the remote system'));
        }

        return $this->redirect($this->referer());
    }

    public function edit($articleId = null)
    {
        // An edit would follow similar approach to above. Sorry once I found out that
        // magento didn't require me to separate and edit from an add, I just post everything 
        // to a create method and Magento handles if needs to create or update.
    }
}

I have done my best to provide as much information to help solve in an imaginary solution. The key things I found were the query->set and the schema. The guys have done a great job of keeping it very similar to the cake controllers. i.e. add > newEntity > patchEntity > save or edit > get > patchEntity > save. If the primary key exists then it is going to fire an _executeUpdateQuery, if it doesn't exist then going to fire _executeCreateQuery. Going through all of the example projects helped me gain insight into what I needed to do. I hope this

@r0kawa
Copy link

r0kawa commented Dec 13, 2018

Thank you very much @solocommerce . Have you solve the pagination for the listing ?

@solocommerce
Copy link
Author

@davidyell
Copy link
Collaborator

For pagination see #42 or my fork https://github.com/davidyell/Webservice/tree/develop/src

Once 2.0 is released we can start working on the pagination implementation. If you could try out the RC release, and report issues. That would be very helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants