Create a Custom GraphQl in Magento 2 – Part 3: Simplify query and mutations

Create a Custom GraphQl in Magento 2 – Part 3: Simplify query and mutations

In recent posts we discussed about creating a simple query and mutation. We used query to read the posts from posts table and mutation to create or update our posts.

Today we will dig further and simplify both the query and mutations. The simplification process will involve separating the business logic in models and retrieving the response using WebApi service output processor.

The service contracts would be require to manage the entity models. So we will first create service contract for our Post models. To learn about service contracts see Magento devdocs

Let us begin with creating a PostInterface data interface: Know/Module/Api/Data/PostInterface.php

<?php
namespace Know\Module\Api\Data;

interface PostInterface
{
    /**
     * Returns Id.
     *
     * @return int|null
     */
    public function getId(): ?int;

    /**
     * @param int|null $id
     * @return mixed
     */
    public function setEntityId(?int $id);

    /**
     * @return String|null
     */
    public function getTitle(): ?String;

    /**
     * @param string $title
     * @return mixed
     */
    public function setTitle(string $title);

    /**
     * @return string|null
     */
    public function getDescription(): ?string;

    /**
     * @param string $description
     * @return mixed
     */
    public function setDescription(string $description);

    /**
     * @return string|null
     */
    public function getCategories(): ?string;

    /**
     * @param string|null $categories
     * @return mixed
     */
    public function setCategories(?string $categories = "default");

    /**
     * @return string|null
     */
    public function getCreated(): ?string;

    /**
     * @param string|null $created
     * @return mixed
     */
    public function setCreatedAt(?string $created);

    /**
     * @return string|null
     */
    public function getUpdated(): ?string;

    /**
     * @param string|null $updated
     * @return mixed
     */
    public function setUpdatedAt(?string $updated);
}

Make sure to create the DocBlock annotations before each method.

Next implement the PostInterface data interface in Post model and define its method.

Modify the Know\Module\Model\Post model

<?php
namespace Know\Module\Model;

use Know\Module\Api\Data\PostInterface;
use Magento\Framework\Model\AbstractModel;

class Post extends AbstractModel implements PostInterface
{
    public function _construct()
    {
        $this->_init(\Know\Module\Model\ResourceModel\Post::class);
    }

    /**
     * @inheritDoc
     */
    public function setId($value)
    {
        parent::setId($value);
        return $this;
    }

    /**
     * @inheritDoc
     */
    public function getId(): ?int
    {
        return $this->getData('entity_id');
    }

    /**
     * @inheritDoc
     */
    public function getTitle(): ?string
    {
        return $this->getData('title');
    }

    /**
     * @inheritDoc
     */
    public function setTitle(string $title)
    {
        return $this->setData('title', $title);
    }

    /**
     * @inheritDoc
     */
    public function getDescription(): ?string
    {
        return $this->getData('description');
    }

    /**
     * @inheritDoc
     */
    public function setDescription(string $description)
    {
        return $this->setData('description', $description);
    }

    /**
     * @inheritDoc
     */
    public function getCategories(): ?string
    {
        return $this->getData('categories');
    }

    /**
     * @inheritDoc
     */
    public function setCategories(?string $categories = "default")
    {
        return $this->setData('categories', $categories);
    }

    /**
     * @inheritDoc
     */
    public function getCreated(): ?string
    {
        return $this->getData('created_at');
    }

    /**
     * @inheritDoc
     */
    public function setCreatedAt(?string $created)
    {
        return $this->setData('created_at', $created);
    }

    /**
     * @inheritDoc
     */
    public function getUpdated(): ?string
    {
        return $this->getData('updated_at');
    }

    /**
     * @inheritDoc
     */
    public function setUpdatedAt(?string $updated)
    {
        return $this->setData('updated_at', $updated);
    }
}

Now create a PostRepositoryInterface interface: Know/Module/Api/PostRepositoryInterface.php

<?php
namespace Know\Module\Api;

use Know\Module\Api\Data\PostInterface;
use Magento\Framework\Exception\LocalizedException;

interface PostRepositoryInterface
{
    /**
     * Load Post Data
     *
     * @param int $id
     * @return PostInterface
     * @throws LocalizedException
     */
    public function get(int $id): PostInterface;

}

Create PostRepository model and implement the PostRepositoryInterface: Know/Module/Model/PostRepository.php

<?php
namespace Know\Module\Model;

use Know\Module\Api\Data\PostInterface;
use Know\Module\Api\PostRepositoryInterface;
use Magento\Framework\Exception\LocalizedException;

class PostRepository implements PostRepositoryInterface
{
    private PostFactory $postFactory;

    private ResourceModel\Post $resource;

    private ResourceModel\Post\CollectionFactory $collectionFactory;

    public function __construct(
        PostFactory $postFactory,
        ResourceModel\Post $resource
    ) {
        $this->postFactory = $postFactory;
        $this->resource = $resource;
    }

    public function get(int $id): PostInterface
    {
        // Create post instance.
        /** @var Post|PostInterface $post */
        $post = $this->postFactory->create();
        // Load the post by id.
        $this->resource->load($post, $id);
        if (!$post->getId()) {
            throw new LocalizedException(
                __('Post with ID "%1" not found.', $id)
            );
        }

        return $post;
    }
}

Create Know/Module/etc/di.xml and add preference for PostInterface data interface and PostRepositoryInterface

<?xml version="1.0" encoding="UTF-8" ?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Know\Module\Api\Data\PostInterface" type="Know\Module\Model\Post"/>
    <preference for="Know\Module\Api\PostRepositoryInterface" type="Know\Module\Model\PostRepository"/>
</config>

Create GetPost model to manage post data loading: Know/ModuleGraphql/Model/Post/GetPost.php

<?php
namespace Know\ModuleGraphql\Model\Post;

use Know\Module\Api\Data\PostInterface;
use Know\Module\Api\PostRepositoryInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;

class GetPost
{
    private PostRepositoryInterface $postRepository;

    public function __construct(PostRepositoryInterface $postRepository)
    {
        $this->postRepository = $postRepository;
    }

    public function execute(?int $id): PostInterface
    {
        try {
            $post = $this->postRepository->get($id);
        } catch (LocalizedException $e) {
            throw new GraphQlNoSuchEntityException(
                __('Post with id "%1" does not exist.', $id),
                $e
            );
        }
        return $post;
    }
}

To process the API data output, create ExtractPostData model: Know/ModuleGraphql/Model/Post/ExtractPostData.php

<?php
namespace Know\ModuleGraphql\Model\Post;

use Know\Module\Api\Data\PostInterface;
use Know\Module\Api\PostRepositoryInterface;
use Magento\Framework\Webapi\ServiceOutputProcessor;

class ExtractPostData
{
    private ServiceOutputProcessor $serviceOutputProcessor;

    public function __construct(ServiceOutputProcessor $serviceOutputProcessor)
    {
        $this->serviceOutputProcessor = $serviceOutputProcessor;
    }

    public function execute(PostInterface $post): array
    {
        return $this->serviceOutputProcessor->process(
            $post,
            PostRepositoryInterface::class,
            'get'
        );
    }
}

Modify the PostById resolver model and replace with the code below:

<?php
namespace Know\ModuleGraphql\Resolver;

use Know\ModuleGraphql\Model\Post\ExtractPostData;
use Know\ModuleGraphql\Model\Post\GetPost;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

class PostById implements ResolverInterface
{
    /**
     * @var GetPost
     */
    private GetPost $getPost;

    /**
     * @var ExtractPostData
     */
    private ExtractPostData $extractPostData;

    /**
     * @param GetPost $getPost
     * @param ExtractPostData $extractPostData
     */
    public function __construct(GetPost $getPost, ExtractPostData $extractPostData)
    {
        $this->getPost = $getPost;
        $this->extractPostData = $extractPostData;
    }

    /**
     * @inheritDoc
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        array $value = null,
        array $args = null
    ) {
        if (!isset($args['id'])) {
            throw new GraphQlInputException(__("Id is required field."));
        }

        $post = $this->getPost->execute($args['id']);
        return $this->extractPostData->execute($post);
    }
}

Remove the generated content:

rm -r generated/*

Flush the cache

php bin/magento c:c

Implement the query to see if changes are working fine:

query {
    getPostById(id: 1) {
      id
      title
      description
      categories
      updated
      created
    }
}

Sending request returns the output data:

{
  "data": {
    "post": {
      "id": 1,
      "title": "Changed title 2",
      "description": "changed description",
      "categories": "dev",
      "updated": "2022-08-03 12:34:19",
      "created": "2022-08-03 12:34:19"
    }
  }
}

List all posts

Modifying the Know/ModuleGraphql/etc/schema.graphqls schema and add posts query. The query will be used to list all posts.

type Query {
  ...

  posts(
        currentPage: Int
        pageSize: Int
    ): PostMetaData 
           @resolver(class: "Know\\ModuleGraphql\\Resolver\\Post") 
                @doc(description: "Retrieve all posts.")
    
  ...
}
...

According to the posts query, we need to create the Post resolver model. Go ahead and create the resolver model: Know/ModuleGraphql/Resolver/Post:

<?php
namespace Know\ModuleGraphql\Resolver;

class Post implements \Magento\Framework\GraphQl\Query\ResolverInterface
{
    public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
    {
        // @todo: implement the resolve method
    }
}

Before completing the Post resolver implementation we need to create getList method. Modify the Know/Module/Api/PostRepositoryInterface.php and add getList method to it:

<?php
namespace Know\Module\Api;

use Know\Module\Api\Data\PostInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Exception\LocalizedException;

interface PostRepositoryInterface
{
    /**
     * Load Post Data
     *
     * @param int $id
     * @return PostInterface
     * @throws LocalizedException
     */
    public function get(int $id): PostInterface;

    /**
     * @param SearchCriteriaInterface $criteria
     * @return SearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $criteria): SearchResultsInterface;
}

Implement the changes in PostRepository model: Know/Module/Model/PostRepository.php

<?php
namespace Know\Module\Model;

use Know\Module\Api\Data\PostInterface;
use Know\Module\Api\PostRepositoryInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessor;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Exception\LocalizedException;

class PostRepository implements PostRepositoryInterface
{
    /**
     * @var PostFactory
     */
    private PostFactory $postFactory;

    /**
     * @var ResourceModel\Post
     */
    private ResourceModel\Post $resource;

    /**
     * @var ResourceModel\Post\CollectionFactory
     */
    private ResourceModel\Post\CollectionFactory $collectionFactory;

    /**
     * @var CollectionProcessor
     */
    private CollectionProcessor $collectionProcessor;

    /**
     * @var SearchResultsInterface
     */
    private SearchResultsInterface $searchResults;

    /**
     * @param PostFactory $postFactory
     * @param ResourceModel\Post $resource
     * @param ResourceModel\Post\CollectionFactory $collectionFactory
     * @param CollectionProcessor $collectionProcessor
     * @param SearchResultsInterface $searchResults
     */
    public function __construct(
        PostFactory $postFactory,
        ResourceModel\Post $resource,
        ResourceModel\Post\CollectionFactory $collectionFactory,
        CollectionProcessor $collectionProcessor,
        SearchResultsInterface $searchResults
    ) {
        $this->postFactory = $postFactory;
        $this->resource = $resource;
        $this->collectionFactory = $collectionFactory;
        $this->collectionProcessor = $collectionProcessor;
        $this->searchResults = $searchResults;
    }

    /**
     * @inheritDoc
     */
    public function get(int $id): PostInterface
    {
        // Create post instance.
        /** @var Post|PostInterface $post */
        $post = $this->postFactory->create();
        // Load the post by id.
        $this->resource->load($post, $id);
        if (!$post->getId()) {
            throw new LocalizedException(
                __('Post with ID "%1" not found.', $id)
            );
        }

        return $post;
    }

    /**
     * @inheritDoc
     */
    public function getList(SearchCriteriaInterface $criteria): SearchResultsInterface
    {
        $collection = $this->collectionFactory->create();
        $this->collectionProcessor->process($criteria, $collection);

        $searchResults = $this->searchResults;
        $searchResults->setSearchCriteria($criteria);
        $searchResults->setItems($collection->getItems());
        $searchResults->setTotalCount($collection->getSize());
        return $searchResults;
    }
}

Now modify the Know/ModuleGraphql/Resolver/Post resolver model and add code to the resolve method.

<?php
namespace Know\ModuleGraphql\Resolver;

use Know\Module\Api\Data\PostInterface;
use Know\Module\Api\PostRepositoryInterface;
use Know\ModuleGraphql\Model\Post\ExtractPostData;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

class Post implements \Magento\Framework\GraphQl\Query\ResolverInterface
{
    /**
     * @var SearchCriteriaBuilder
     */
    private SearchCriteriaBuilder $searchCriteriaBuilder;

    /**
     * @var PostRepositoryInterface
     */
    private PostRepositoryInterface $postRepository;

    /**
     * @var ExtractPostData
     */
    private ExtractPostData $extractPostData;

    /**
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param PostRepositoryInterface $postRepository
     * @param ExtractPostData $extractPostData
     */
    public function __construct(
        SearchCriteriaBuilder $searchCriteriaBuilder,
        PostRepositoryInterface $postRepository,
        ExtractPostData $extractPostData
    ) {
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->postRepository = $postRepository;
        $this->extractPostData = $extractPostData;
    }

    /**
     * @inheritDoc
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        array $value = null,
        array $args = null
    ) {
        $searchCriteriaBuilder = $this->searchCriteriaBuilder;
        $criteria = $searchCriteriaBuilder->create();
        $criteria->setPageSize($args['pageSize'] ?? 4)
            ->setCurrentPage($args['currentPage'] ?? 1);
        $posts = $this->postRepository->getList($criteria);
        if (!$posts->getTotalCount()) {
            return [];
        }

        $totalPages = $posts->getSearchCriteria()->getPageSize() == 0 ?
            1 : (int) ceil($posts->getTotalCount() / $posts->getSearchCriteria()->getPageSize());

        $postData = [
            "items" =>  [],
            "currentPage" => $posts->getSearchCriteria()->getCurrentPage(),
            "pageSize" => $posts->getSearchCriteria()->getPageSize(),
            "totalPages" => $totalPages
        ];

        /** @var PostInterface $post */
        foreach ($posts->getItems() as $post) {
            $postData["items"][] = $this->extractPostData->execute($post);
        }
        return $postData;
    }
}

Flush the cache

php bin/magento c:c

Implement the posts query:

query {
  posts(
    currentPage: 2
    pageSize: 3
  ) {
    currentPage
    totalPages
    items {
      id
      title
      description
      categories
      updated
      created
    }
  }
}

Sending request will return the posts result similar like below:

{
  "data": {
    "posts": {
      "currentPage": 2,
      "totalPages": 3,
      "items": [
        {
          "id": 4,
          "title": "Modified title",
          "description": "Modified description",
          "categories": "",
          "updated": "2022-08-08 05:20:47",
          "created": "2022-08-08 05:20:47"
        },
        {
          "id": 5,
          "title": "A new record inserted",
          "description": "Some description about the record",
          "categories": "lorem ipsum, dev",
          "updated": "2022-08-08 05:34:16",
          "created": "2022-08-08 05:34:16"
        },
        {
          "id": 11,
          "title": "Things you should know about GraphQl (Edited)",
          "description": "We will discuss 10 points related to the GraphQl.",
          "categories": "GraphQl, Discussion, API, Blog",
          "updated": "2022-08-27 14:10:10",
          "created": "2022-08-27 14:09:51"
        }
      ]
    }
  }
}

Save/Update posts using mutations

Now we will simplify the post save process by creating the save method inside a PostRepository model and requesting it from CreatePost resolver model.

Modify the Know/Module/Api/PostRepositoryInterface.php interface and create save method:

<?php
namespace Know\Module\Api;

use Know\Module\Api\Data\PostInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Exception\LocalizedException;

interface PostRepositoryInterface
{
    /**
     * Load Post Data
     *
     * @param int $id
     * @return PostInterface
     * @throws LocalizedException
     */
    public function get(int $id): PostInterface;

    /**
     * @param SearchCriteriaInterface $criteria
     * @return SearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $criteria): SearchResultsInterface;

    /**
     * @param PostInterface $post
     * @return PostInterface
     */
    public function save(PostInterface $post): PostInterface;
}

Implement the save method inside the PostRepository: Know/Module/Model/PostRepository.php

<?php
namespace Know\Module\Model;

use Know\Module\Api\Data\PostInterface;
use Know\Module\Api\PostRepositoryInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessor;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\LocalizedException;


class PostRepository implements PostRepositoryInterface
{
    /**
     * @var PostFactory
     */
    private PostFactory $postFactory;

    /**
     * @var ResourceModel\Post
     */
    private ResourceModel\Post $resource;

    /**
     * @var ResourceModel\Post\CollectionFactory
     */
    private ResourceModel\Post\CollectionFactory $collectionFactory;

    /**
     * @var CollectionProcessor
     */
    private CollectionProcessor $collectionProcessor;

    /**
     * @var SearchResultsInterface
     */
    private SearchResultsInterface $searchResults;

    /**
     * @param PostFactory $postFactory
     * @param ResourceModel\Post $resource
     * @param ResourceModel\Post\CollectionFactory $collectionFactory
     * @param CollectionProcessor $collectionProcessor
     * @param SearchResultsInterface $searchResults
     */
    public function __construct(
        PostFactory $postFactory,
        ResourceModel\Post $resource,
        ResourceModel\Post\CollectionFactory $collectionFactory,
        CollectionProcessor $collectionProcessor,
        SearchResultsInterface $searchResults
    ) {
        $this->postFactory = $postFactory;
        $this->resource = $resource;
        $this->collectionFactory = $collectionFactory;
        $this->collectionProcessor = $collectionProcessor;
        $this->searchResults = $searchResults;
    }

    /**
     * @inheritDoc
     */
    public function get(int $id): PostInterface
    {
        // Create post instance.
        /** @var Post|PostInterface $post */
        $post = $this->postFactory->create();
        // Load the post by id.
        $this->resource->load($post, $id);
        if (!$post->getId()) {
            throw new LocalizedException(
                __('Post with ID "%1" not found.', $id)
            );
        }

        return $post;
    }

    /**
     * @inheritDoc
     */
    public function getList(SearchCriteriaInterface $criteria): SearchResultsInterface
    {
        $collection = $this->collectionFactory->create();
        $this->collectionProcessor->process($criteria, $collection);

        $searchResults = $this->searchResults;
        $searchResults->setSearchCriteria($criteria);
        $searchResults->setItems($collection->getItems());
        $searchResults->setTotalCount($collection->getSize());
        return $searchResults;
    }

    /**
     * @inheritDoc
     */
    public function save(PostInterface $post): PostInterface
    {
        try {
            $this->resource->save($post);
        } catch (\Exception $e) {
            throw new CouldNotSaveException(
                __("Could not save the post."),
                $e
            );
        }

        return $post;
    }
}

Modify the Know/ModuleGraphql/Model/CreatePost.php model and replace with following code:

<?php
namespace Know\ModuleGraphql\Model;

use Know\Module\Api\PostRepositoryInterface;
use Know\Module\Model\Post;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\Stdlib\DateTime\DateTime;

/**
 * CreatePost Handles business logic for the posts to be created.
 */
class CreatePost
{
    /**
     * @var Post
     */
    private Post $post;

    /**
     * @var PostRepositoryInterface
     */
    private PostRepositoryInterface $postRepository;

    /**
     * @var DateTime
     */
    private DateTime $dateTime;

    /**
     * @param Post $post
     * @param PostRepositoryInterface $postRepository
     * @param DateTime $dateTime
     */
    public function __construct(
        Post $post,
        PostRepositoryInterface $postRepository,
        DateTime $dateTime
    ) {
        $this->post = $post;
        $this->postRepository = $postRepository;
        $this->dateTime = $dateTime;
    }

    /**
     * @param int $postId
     * @param array $postData
     * @return Post
     * @throws GraphQlInputException
     * @throws LocalizedException
     */
    public function execute(int $postId, array $postData): Post
    {
        // Validate the data.
        $this->validateData($postData);
        // Entity model
        $post = $this->post;

        if ($postId) {
            $post = $this->postRepository->get($postId);
            $postData['created_at'] = $post->getCreated();
        }

        $post->setData($postData);
        $post->setUpdatedAt($this->dateTime->gmtDate("Y-m-d H:i:s"));

        if (!$postId) {
            $post->setCreatedAt($this->dateTime->gmtDate("Y-m-d H:i:s"));
        }

        // Save the post data.
        return $this->postRepository->save($post);
    }

    /**
     * Validates the Post Data.
     *
     * @param array $postData
     * @return void
     * @throws GraphQlInputException
     */
    private function validateData(array $postData): void
    {
        $errors = [];
        if (empty($postData['title'])) {
            $errors[] = "title";
        }

        if (empty($postData['description'])) {
            $errors[] = "description";
        }

        if (!empty($errors)) {
            throw new GraphQlInputException(
                __('Required parameters are missing: %1', implode(",", $errors))
            );
        }
    }
}

Now we need to simplify the CreatePost resolver model. Modify the Know/ModuleGraphql/Resolver/CreatePost.php resolver model and replace with the code below:

<?php
namespace Know\ModuleGraphql\Resolver;

use Know\ModuleGraphql\Model\Post\ExtractPostData;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

class CreatePost implements \Magento\Framework\GraphQl\Query\ResolverInterface
{
    /**
     * @var \Know\ModuleGraphql\Model\CreatePost
     */
    private \Know\ModuleGraphql\Model\CreatePost $createPost;

    /**
     * @var ExtractPostData
     */
    private ExtractPostData $extractPostData;

    /**
     * @param \Know\ModuleGraphql\Model\CreatePost $createPost
     * @param ExtractPostData $extractPostData
     */
    public function __construct(
        \Know\ModuleGraphql\Model\CreatePost $createPost,
        ExtractPostData $extractPostData
    ) {
        $this->createPost = $createPost;
        $this->extractPostData = $extractPostData;
    }

    /**
     * @inheritDoc
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        array $value = null,
        array $args = null
    ) {
        if (empty($args['input']) || !is_array($args['input'])) {
            throw new GraphQlInputException(
                __('"Input" value should be specified and in the form of array.')
            );
        }

        $data = $args['input'];
        $entity_id = isset($data['entity_id']) ? (int) $data['entity_id'] : 0;
        $post = $this->createPost->execute($entity_id, $data);
        return $this->extractPostData->execute($post);
    }
}

Remove the generated content:


rm -r generated/*

Flush the cache:


php bin/magento c:c

Impletement the creaePost mutation:

mutation {
  createPost(
    input: {
      title: "Binary tree"
      description: "A binary tree is a tree data structure where each node has up to two child nodes, creating the branches of the tree."
      categories: "Data Structures, Binary Tree, Algorithms"
    }
  ) {
    id
    title
    description
    categories
    created
    updated
  }
}

Send request and it will return the output below:

{
  "data": {
    "createPost": {
      "id": 13,
      "title": "Binary tree",
      "description": "A binary tree is a tree data structure where each node has up to two child nodes, creating the branches of the tree.",
      "categories": "Data Structures, Binary Tree, Algorithms",
      "created": "2022-09-06 04:34:23",
      "updated": "2022-09-06 04:34:23"
    }
  }
}

Delete a post

Lastly we will talk about deleting the posts. Before creating the delete request mutation we would have to create delete method inside the PostRepository.

Modify the Know/Module/Api/PostRepositoryInterface.php and add delete method to it.

   /**
     * @param PostInterface $post
     * @return bool
     */
    public function delete(PostInterface $post): bool;

Implement the delete inside Know/Module/Model/PostRepository.php

   /**
     * @inheritDoc
     */
    public function delete(PostInterface $post): bool
    {
        try {
            $this->resource->delete($post);
        } catch (\Exception $e) {
            throw new CouldNotDeleteException(__("Could not delete the post.", $e));
        }

        return true;
    }

Modify the Know/ModuleGraphql/etc/schema.graphqls schema and deletePost mutation to it:

type Mutation {
    deletePost(
        entity_id: Int!
        @doc(description: "Delete Post using entity_id")
    ): Boolean
        @resolver(class: "Know\\ModuleGraphql\\Resolver\\DeletePost")
            @doc(description: "Delete a post.")
}

The deletePost mutation will return a boolean value.

Now go ahead and create DeletePost resolver model: Know/ModuleGraphql/Resolver/DeletePost.php

<?php
namespace Know\ModuleGraphql\Resolver;

use Know\ModuleGraphql\Model\Post\GetPost;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

/**
 * DeletePost resolver model
 */
class DeletePost implements ResolverInterface
{
    /**
     * @var GetPost
     */
    private GetPost $getPost;

    /**
     * @var \Know\ModuleGraphql\Model\DeletePost
     */
    private \Know\ModuleGraphql\Model\DeletePost $deletePost;

    /**
     * @param GetPost $getPost
     * @param \Know\ModuleGraphql\Model\DeletePost $deletePost
     */
    public function __construct(GetPost $getPost, \Know\ModuleGraphql\Model\DeletePost $deletePost)
    {
        $this->getPost = $getPost;
        $this->deletePost = $deletePost;
    }

    /**
     * @inheritDoc
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        array $value = null,
        array $args = null
    ) {
        if (!isset($args['entity_id'])) {
            throw new GraphQlInputException(__('"Input" value should be specified.'));
        }

        $post = $this->getPost->execute($args['entity_id']);
        $this->deletePost->execute($post);
        return true;
    }
}

Create the DeletePost model to handle the delete logic: Know/ModuleGraphql/Model/DeletePost.php

<?php
namespace Know\ModuleGraphql\Model;

use Know\Module\Api\Data\PostInterface;
use Know\Module\Api\PostRepositoryInterface;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;

class DeletePost
{
    /**
     * @var PostRepositoryInterface
     */
    private PostRepositoryInterface $postRepository;

    /**
     * @param PostRepositoryInterface $postRepository
     */
    public function __construct(PostRepositoryInterface $postRepository)
    {
        $this->postRepository = $postRepository;
    }

    /**
     * @param PostInterface $post
     * @return void
     * @throws GraphQlInputException
     */
    public function execute(PostInterface $post): void
    {
        try {
            $this->postRepository->delete($post);
        } catch (CouldNotDeleteException $exception) {
            throw new GraphQlInputException(
                __($exception->getMessage()),
                $exception
            );
        }
    }
}

Flush the cache:

php bin/magento c:c

Impletement the deletePost mutation:

mutation {
  deletePost(entity_id: 11)
}

Following expected output will be returned:

{
  "data": {
    "deletePost": true
  }
}

The post has been deleted successfully.

Leave a Reply

Your email address will not be published.