Contents
What is Resolver?
A resolver performs GraphQL request processing. In general, it is responsible for constructing a query, fetching data and performing any calculations, then transforming the fetched and calculated data into a GraphQL array format. Finally, it returns the results wrapped by a callable function.
Batch Resolver
Batch resolvers gather GraphQL requests for the same field until there is no way to process the tree further without resolving previous requests. Magento recommends using batch resolvers for queries because they improve performance by fetching information required to resolve multiple GraphQL requests with a single operation.
It provides a way to resolve multiple branches/leaves at once (known as batching).
We are creating a batch resolver with an example module. Our module extends the default Product query with adding a new field named product_tag_label (Field).
product_tag_label field will give response Trending (string) or empty string identified by product attribute ‘is_trending’.
Follow the steps below to create a batch resolver with example module in Magento (Adobe Commerce)
- Create module
- Create new product attribute (Optional if your implementing your logic)
- Create schema graphql file
- Write batch resolver class
Note: You can skip step 2 (Create new product attribute) if you’re implementing your logic.
Step 1: Create Module
Create a Vendor/Module Folder. This is your root module folder. In our case the Vendor is DCKAP and the Module is TrendingProductGraphQl.
a) Create a module.xml file
path: Vendor/Module/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="DCKAP_TrendingProductGraphQl" setup_version="1.0.0">
<sequence>
<module name="Magento_GraphQl"/>
<module name="Magento_CatalogGraphQl"/>
</sequence>
</module>
</config>
b) Create a registration.php file
path: Vendor/Module/registration.php
<?php
use \Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'DCKAP_TrendingProductGraphQl',
__DIR__
);
c) Create a composer.json file
path: Vendor/Module/etc/composer.json
{
"name": "dckap/module-trendingproductgraphql",
"description": "",
"type": "magento2-module",
"license": "proprietary",
"authors": [
{
"email": "info@yourdomain.com",
"name": "DEV"
}
],
"minimum-stability": "dev",
"require": {},
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"DCKAP\\TrendingProductGraphQl\\": ""
}
}
}
Step 2: Create new product attribute
Create a new product attribute to identify whether the product is trending or not using Data patch.
path: Vendor/Module/Setup/Patch/Data/IsTrendingProductAttribute.php
<?php
namespace DCKAP\TrendingProductGraphQl\Setup\Patch\Data;
use Magento\Catalog\Model\Product;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Model\Entity\Attribute\Source\Boolean;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchRevertableInterface;
class IsTrendingProductAttribute implements DataPatchInterface, PatchRevertableInterface
{
const CODE = 'is_trending';
/**
* @var ModuleDataSetupInterface $moduleDataSetup
*/
private $moduleDataSetup;
/**
* @var EavSetupFactory
*/
private $eavSetupFactory;
/**
* @param ModuleDataSetupInterface $moduleDataSetup
* @param EavSetupFactory $eavSetupFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
EavSetupFactory $eavSetupFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->eavSetupFactory = $eavSetupFactory;
}
/**
* Do Upgrade
*
* @return void
*/
public function apply()
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->addAttribute(
Product::ENTITY,
self::CODE,
[
'type' => 'int',
'label' => 'Trending Product',
'input' => 'boolean',
'source' => Boolean::class,
'frontend' => '',
'required' => false,
'backend' => '',
'sort_order' => '100',
'global' => ScopedAttributeInterface::SCOPE_GLOBAL,
'default' => 0,
'visible' => true,
'user_defined' => true,
'searchable' => false,
'filterable' => false,
'comparable' => false,
'visible_on_front' => true,
'unique' => false,
'apply_to' => '',
'group' => 'General',
'used_in_product_listing' => true,
'is_used_in_grid' => true,
'is_visible_in_grid' => false,
'is_filterable_in_grid' => true,
]
);
$this->moduleDataSetup->getConnection()->endSetup();
}
public function revert()
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->removeAttribute(Product::ENTITY, self::CODE);
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* @inheritdoc
*/
public function getAliases()
{
return [];
}
/**
* @inheritdoc
*/
public static function getDependencies()
{
return [];
}
}

Step 3: Create a schema graphql file
This is where we are going to add a new field for product query. In this schema file we need our new field with a resolver class path. We are adding a field in Product Interface so this field is accessible to all places that use Product Interface in GraphQL Response.
path: Vendor/Module/etc/schema.graphqls
interface ProductInterface {
product_tag_label: String @resolver(class: "DCKAP\\TrendingProductGraphQl\\Model\\Resolver\\Batch\\ProductTagLabel")
@doc(description: "product tag label")
}
Step 4: Write batch resolver class
Once scheme.graphqls file completed next step we need to write batch resolver class that implements BatchResoverInterface.
path: Vendor/Module/Model/Resolver/Batch/ProductTagLabel.php
<?php
namespace DCKAP\TrendingProductGraphQl\Model\Resolver\Batch;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\Resolver\BatchRequestItemInterface;
use Magento\Framework\GraphQl\Query\Resolver\BatchResolverInterface;
use Magento\Framework\GraphQl\Query\Resolver\BatchResponse;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
class ProductTagLabel implements BatchResolverInterface
{
/**
* @var ProductCollectionFactory
*/
private ProductCollectionFactory $productCollectionFactory;
public function __construct(
ProductCollectionFactory $productCollectionFactory
)
{
$this->productCollectionFactory = $productCollectionFactory;
}
public function resolve(ContextInterface $context, Field $field, array $requests): BatchResponse
{
$store=$context->getExtensionAttributes()->getStore();
$productIds=[];
foreach ($requests as $request) {
if (empty($request->getValue()['model'])) {
throw new LocalizedException(__('"model" value should be specified'));
}
$product=$request->getValue()['model'];
$productIds[] = $product->getId();
}
//preparing tag label for product ids
$tagLabelsList=$this->getTagLabelForProducts($productIds, $store);
//Matching requests with responses.
$response = new BatchResponse();
foreach ($requests as $request) {
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
$product = $request->getValue()['model'];
$result = [];
if (array_key_exists($product->getId(), $tagLabelsList)) {
$result =$tagLabelsList[$product->getId()];
}
$response->addResponse($request, $result);
}
return $response;
}
private function getTagLabelForProducts($productIds, $store){
$tagLabelsList=[];
$productCollection=$this->productCollectionFactory->create();
$productCollection->addFieldToSelect(
[
'entity_id',
'is_trending'
]
);
$productCollection->addIdFilter($productIds);
$items=$productCollection->getItems();
foreach ($items as $product) {
$label="";
$isTrending=$product->getIsTrending();
if($isTrending==1){
$label=__("Trending");
}
$tagLabelsList[$product->getId()] = $label;
}
return $tagLabelsList;
}
}
Here is the main function request processes the request public function resolve(ContextInterface $context, Field $field, array $requests): BatchResponse
resolve function has 3 params
- $context – we can get information about store and customer id (Magento\Framework\GraphQl\Query\Resolver\ContextInterface)
- $field – we can get information about fields requested
- $requests – Array that contains resolved request item data.
Here, we are gathering productIds for a single request to prepare a response from another function.
$product=$request->getValue()[‘model’];
$productIds[] = $product->getId();
Once productIds are collected, we are using ProductCollection with an IN filter to prepare results.
To prepare the results, we need to attach those results in batch resolver response $response->addResponse($request, $result); add Response function that accepts two params one is the request object second one is result value.
That it, We completed the Batch resolver.
After we need to run commands
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento module:status DCKAP_TrendingProductGraphQl
Result:
Request
{
products(
filter:{
category_id:{
eq:"21"
}
}
){
items{
sku
product_tag_label
}
}
}
Response:
{
"data": {
"products": {
"items": [
{
"sku": "WT09",
"product_tag_label": "Trending"
},
{
"sku": "WT08",
"product_tag_label": "Trending"
},
{
"sku": "WT07",
"product_tag_label": ""
},
{
"sku": "WS05",
"product_tag_label": "Trending"
},
{
"sku": "WJ12",
"product_tag_label": ""
}
Screen
Browser extension for GraphQL Testing
Link : Altair GraphQL Client
For further reading about Resolver