Implement a mutation
Having the query for the products it is time to create a mutation to change products. Let’s keep it simple and just change the product title.
Anonymous users being able to change data is probably not a good idea. In fact we should make sure only a logged in user is able to update a product’s title.
Important
This is only an example. In production, only a logged in and authorized admin user should be able to mutate product data. Please see section Authorization for a PermissionProvider
example.
We start with the controller this time and (this is entirely done because of step by step tutorial reasons)
add the mutation to the Product
controller we build earlier.
use TheCodingMachine\GraphQLite\Annotations\Mutation;
use TheCodingMachine\GraphQLite\Annotations\Logged;
final class Product
{
// ...
/**
* @Mutation()
* @Logged()
*/
public function productTitleUpdate(ProductDataType $product): ProductDataType
{
$this->productService->store($product);
return $product;
}
}
In the product service, we need to add the method that takes care of storing the product
final class Product
{
// ...
/**
* @return true
*/
public function store(ProductDataType $product): bool
{
return $this->productRepository->saveProduct(
$product
);
}
}
As we can reuse the OXID eShop core functionality to save the application model, let’s add some functionality to
the infrastructure layers ProductRepository
from the query tutorial to deal with this.
use RuntimeException;
final class ProductRepository
{
// ..
/**
* @throws RuntimeException
* @return true
*/
public function saveProduct(ProductDataType $product): bool
{
if (!$product->getEshopModel()->save()) {
throw new RuntimeException('Object save failed');
}
return true;
}
}
We are not done yet. The request will probably contain the product id (we want to change exactly this product)
and the new title. If we look at the Controller\Product::productTitleUpdate()
method, it expects
a ProductDataType
as argument. Also we will need a check if the product in question even exists before it can be updated.
So we need some factory to create a ProductDataType
from the input:
<?php
declare(strict_types=1);
namespace MyVendor\GraphQL\MyGraph\Product\Service;
use MyVendor\GraphQL\MyGraph\Product\DataType\Product as ProductDataType;
use MyVendor\GraphQL\MyGraph\Product\Infrastructure\ProductRepository as ProductRepository;
use MyVendor\GraphQL\MyGraph\Product\Infrastructure\ProductMutation as ProductMutationService;
use MyVendor\GraphQL\MyGraph\Product\Exception\ProductNotFound;
use OxidEsales\GraphQL\Base\Exception\NotFound;
use TheCodingMachine\GraphQLite\Annotations\Factory;
final class ProductTitleInput
{
/** @var ProductRepository */
private $productRepository;
/** @var ProductMutationService */
private $productMutationService;
public function __construct(
ProductRepository $productRepository,
ProductMutationService $productMutationService
) {
$this->productRepository = $productRepository;
$this->productMutationService = $productMutationService;
}
/**
* @Factory(name="ProductInput")
*/
public function fromUserInput(string $productId, string $title): ProductDataType
{
try {
$product = $this->productRepository->product($productId);
} catch (NotFound $e) {
throw ProductNotFound::byId($productId);
}
return $this->productMutationService->assignTitle($product, $title);
}
}
This in turn needs some infrastructure layer part, because we need the OXID eShop model’s BaseModel::assign()
method.
<?php
declare(strict_types=1);
namespace MyVendor\GraphQL\MyGraph\Product\Infrastructure;
use MyVendor\GraphQL\MyGraph\Product\DataType\Product as ProductDataType;
final class ProductMutation
{
public function assignTitle(
ProductDataType $product,
string $title
): ProductDataType {
$product->getEshopModel()->assign([
'oxtitle' => $title
]);
return $product;
}
}
To get a clearer view on what we need for the mutation, here’s the relevant file structure after the changes from this tutorial applied.
├── ..
└── Product
├── Controller
│ └── Product.php
├── DataType
│ └── Product.php
├── Exception
│ └── ProductNotFound.php
├── Infrastructure
│ ├── ProductMutation.php
│ └── ProductRepository.php
└── Service
├── ProductTitleInput.php
└── Product.php
Important
As stated in section Authorization, when using the @Logged
annotation as done here, we need to send the token in the HTTP Authorization header in order to see the mutation in the schema.
Mutation
mutation {
productTitleUpdate(
product: {
productId: "dc5ffdf380e15674b56dd562a7cb6aec"
title: "my new product title"
}
){
id
title
}
}
Response
{
"data": {
"productTitleUpdate": {
"id": "dc5ffdf380e15674b56dd562a7cb6aec",
"title": "my new product title"
}
}
}