DEV Community

Lars Roettig
Lars Roettig

Posted on • Originally published at larsroettig.dev on

2 2

How to create a GraphQL Mutation Endpoint for Magento 2.3

In my last Magento 2 tutorial, I showed how to build a basic GraphQL module with filtering.The primary purpose of GraphQL is to retrieve data (query), but every complete data exchange format needs a way to change server-based data (mutations).Therefore I want to show now how we can change data or create new pickup stores via the frontend.

Currently, the Magento 2 GraphQL Endpoint supports only access controls based on customers. This GraphQL will only allow a queryon the customer data if the customer’s token must proived in the header section.

Since I would like to show only exemplarily how such a thing could look, I did without access control!

Before we can start here, you can find the first module that we need ground for this tutorial.

How to create a GraphQL Endpoint for Magento 2.3

Github Repo

Create a new php file app/code/LarsRoettig/GraphQLStorePickup/Model/Resolver/CreatPickUpStore.php

This Class holds the logic for that endpoint is responsible for any GraphQL call that creates a new pickup store in the Database.Every Resolver Class is forced to implements Magento\Framework\GraphQl\Query\ResolverInterface to work correctly.

<?php

declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model\Resolver;

use LarsRoettig\GraphQLStorePickup\Model\CreatePickUpStore as CreatPickUpStoreService;
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 CreatPickUpStore implements ResolverInterface
{
    /**
     * @var CreatPickUpStoreService
     */
    private $creatPickUpStore;

    /**
     * @param CreatPickUpStore $creatPickUpStore
     */
    public function __construct(CreatPickUpStoreService $creatPickUpStore)
    {
        $this->creatPickUpStore = $creatPickUpStore;
    }

    /**
     * @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'));
        }

        return ['pick_up_store' => $this->creatPickUpStore->execute($args['input'])];
    }
}

File app/code/LarsRoettig/GraphQLStorePickup/Model/CreatePickUpStore.php:

The second step is to create a service class that will convert a given array to a pickup store and saves them in the database.A service class help us to have single small bussiness logic that can be used by differnt other php services.

<?php
declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model;

use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterfaceFactory;
use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;

class CreatePickUpStore
{

    /**
     * @var DataObjectHelper
     */
    private $dataObjectHelper;
    /**
     * @var StoreRepositoryInterface
     */
    private $storeRepository;
    /**
     * @var StoreInterfaceFactory
     */
    private $storeFactory;

    /**
     * @param DataObjectHelper $dataObjectHelper
     * @param StoreRepositoryInterface $storeRepository
     * @param StoreInterfaceFactory $storeInterfaceFactory
     */
    public function __construct(
        DataObjectHelper $dataObjectHelper,
        StoreRepositoryInterface $storeRepository,
        StoreInterfaceFactory $storeInterfaceFactory
    ) {
        $this->dataObjectHelper = $dataObjectHelper;
        $this->storeRepository = $storeRepository;
        $this->storeFactory = $storeInterfaceFactory;
    }

    /**
     * @param array $data
     * @return StoreInterface
     * @throws GraphQlInputException
     */
    public function execute(array $data): StoreInterface
    {
        try {
            $this->vaildateData($data);
            $store = $this->saveStore($this->createStore($data));
        } catch (\Exception $e) {
            throw new GraphQlInputException(__($e->getMessage()));
        }

        return $store;
    }

    /**
     * Guard function to handle bad request.
     * @param array $data
     * @throws LocalizedException
     */
    private function vaildateData(array $data)
    {
        if (!isset($data[StoreInterface::NAME])) {
            throw new LocalizedException(__('Name must be set'));
        }
    }

    /**
     * Persists the given store in the data base.
     * +
     * @param StoreInterface $store
     * @return StoreInterface
     * @throws CouldNotSaveException
     */
    private function saveStore(StoreInterface $store): StoreInterface
    {
        $this->storeRepository->save($store);

        return $store;
    }

    /**
     * Create a store dto by given data array.
     *
     * @param array $data
     * @return StoreInterface
     * @throws CouldNotSaveException
     */
    private function createStore(array $data): StoreInterface
    {
        /** @var StoreInterface $store */
        $store = $this->storeFactory->create();
        $this->dataObjectHelper->populateWithArray(
            $store,
            $data,
            StoreInterface::class
        );

        return $store;
    }
}

File app/code/LarsRoettig/GraphQLStorePickup/etc/schema.graphqls

This need to be added to the file:

type Mutation {
    createPickUpStores(input: PickUpStoreInput!): PickUpStoreOutput @resolver(class: "\\LarsRoettig\\GraphQLStorePickup\\Model\\Resolver\\CreatPickUpStore") @doc(description: "Create a new pickup store")
}

type PickUpStoreOutput {
    pick_up_store: PickUpStore!
}

input PickUpStoreInput {
    name: String @doc(description: "")
    street: String @doc(description: "")
    street_num: Int @doc(description: "")
    city: String @doc(description: "")
    postcode: String @doc(description: "")
    latitude:Float @doc(description: "")
    longitude: Float @doc(description: "")
}

Full File:

type Query {
    pickUpStores(
        filter: PickUpStoresFilterInput @doc(description: "")
        pageSize: Int = 5 @doc(description: "How many items should show on the page")
        currentPage: Int = 1 @doc(description: "Allows to ussing paging it start with 1")
    ):PickUpStoresOutput @resolver(class: "\\LarsRoettig\\GraphQLStorePickup\\Model\\Resolver\\PickUpStores") @doc(description: "Allow to query for a pickup store.")
}

type Mutation {
    createPickUpStores(input: PickUpStoreInput!): PickUpStoreOutput @resolver(class: "\\LarsRoettig\\GraphQLStorePickup\\Model\\Resolver\\CreatPickUpStore") @doc(description: "Create a new pickup store")
}

type PickUpStoreOutput {
    pick_up_store: PickUpStore!
}

input PickUpStoreInput {
    name: String @doc(description: "")
    street: String @doc(description: "")
    street_num: Int @doc(description: "")
    city: String @doc(description: "")
    postcode: String @doc(description: "")
    latitude:Float @doc(description: "")
    longitude: Float @doc(description: "")
}

input PickUpStoresFilterInput {
    name: FilterTypeInput @doc(description: "")
    postcode: FilterTypeInput @doc(description: ""),
    street: FilterTypeInput @doc(description: ""),
    street_num: FilterTypeInput @doc(description: ""),
    city: FilterTypeInput @doc(description: ""),
    latitude:FilterTypeInput @doc(description: ""),
    longitude: FilterTypeInput @doc(description: ""),
    or: PickUpStoresFilterInput
}

type PickUpStoresOutput {
    total_count: Int @doc(description: "")
    items: [PickUpStore] @doc(description: "")
}

type PickUpStore {
    entity_id: Int @doc(description: ""),
    name: String @doc(description: ""),
    street: String @doc(description: ""),
    street_num: Int @doc(description: ""),
    city: String @doc(description: ""),
    postcode: String @doc(description: ""),
    latitude:Float @doc(description: ""),
    longitude: Float @doc(description: ""),
}

3. Installation:

bin/magento module:enable LarsRoettig_GraphQLStorePickup
bin/magento setup:db-declaration:generate-whitelist --module-name=LarsRoettig_GraphQLStorePickup
bin/magento setup:upgrade

The repo bramch can founded at Github:

https://github.com/larsroettig/module-graphqlstorepickup/tree/2.0-develop

How to call your GraphQL Endpoint

Attention the Url https://your\_domain.test/graphql work only with an valid GraphQL-Request!




GrapQL Mutation

GraphQL-Mutation:

mutation {
  createPickUpStores(
    input: {
      name: "Mustation Store"
      street: "sweswq"
      street_num: 12
      postcode: "83059"
      latitude: 22.3
      longitude:22.3
    }
  )
  {
    pick_up_store {
      name
    }
  }
}

(This article was posted to my blog at https://larsroettig.dev. You can read it online by clicking here.)

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay