Skip to content

how do I create a custom GraphQL query that fetches a list of products in Magento 2.4.1

In my schema.graphqls file I’ve created this Query

type Query {
    getLinkedProductsByGroups(groups: [String]): [LinkedProductsByGroup] @resolver(class: "Vendor\ProductDisplay\Model\Resolver\LinkedProductsByGroups")
}

type LinkedProductsByGroup {
    group: String
    products: [ProductInterface]
}

I’ve created my resolver

<?php
/*
 * Copyright (c) 2023. Vendor TECHNOLOGIES
 */

namespace VendorProductDisplayModelResolver;

use VendorProductDisplayServiceLinkedProductsLocator;
use MagentoCatalogApiProductRepositoryInterface;
use MagentoCatalogGraphQlModelResolverProductProductFieldsSelector;
use MagentoCatalogGraphQlModelResolverProductsDataProviderDeferredProduct as ProductDataProvider;
use MagentoCatalogGraphQlModelResolverProductsDataProviderProductSearch;
use MagentoFrameworkApiSearchCriteriaBuilderFactory;
use MagentoFrameworkGraphQlConfigElementField;
use MagentoFrameworkGraphQlQueryResolverBatchResolverInterface;
use MagentoFrameworkGraphQlQueryResolverBatchResponse;
use MagentoFrameworkGraphQlQueryResolverContextInterface;
use MagentoFrameworkGraphQlQueryResolverValueFactory;
use MagentoFrameworkGraphQlQueryResolverInterface;
use MagentoFrameworkGraphQlSchemaTypeResolveInfo;

class LinkedProductsByGroups implements ResolverInterface
{
    protected LinkedProductsLocator $linkedProductsLocator;
    protected ProductRepositoryInterface $productRepository;
    protected SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory;

    public function __construct(
        SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory,
        ProductRepositoryInterface   $productRepository,
        LinkedProductsLocator        $linkedProductsLocator
    )
    {
        $this->linkedProductsLocator = $linkedProductsLocator;
        $this->searchCriteriaBuilderFactory = $searchCriteriaBuilderFactory;
        $this->productRepository = $productRepository;
    }

    public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
    {

        $result = [];
        $groups = $args['groups'];
        foreach ($groups as $group) {
            $resultItem = [];
            $resultItem['group'] = $group;
            $resultItem['products'] = [];

            //fetch related products
            $linkedProductIds = $this->linkedProductsLocator->getLinkedProductIdsByGroup($group);
            $scb = $this->searchCriteriaBuilderFactory->create();
            $scb->addFilter('entity_id', $linkedProductIds, 'in');
            $searchResults = $this->productRepository->getList($scb->create());
            if ($searchResults->getTotalCount() > 0) {
                foreach ($searchResults->getItems() as $item) {
                    $data = $item->toArray();
                    $data['model'] = 'product';
                    $resultItem['products'][] = $data;
                }
            }

            $result[] = $resultItem;
        }

        return $result;
    }
}

This pretty much works but I have a problem fetching composite fields like price or small_image.

When I run my query without them everything is fine.

But with them I get this error

query {
  getLinkedProductsByGroups(groups: ["001021", "001107"]) {
    group
    products {    
      name
      sku
      small_image {
          url
      }    
    }
  }
}

This error comes up

{
    "errors": [
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                0,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                1,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                2,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                3,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                4,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                5,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                6,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                7,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                8,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                9,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                10,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                0,
                "products",
                11,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                0,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                1,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                2,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                3,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                4,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                5,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                6,
                "small_image",
                "url"
            ]
        },
        {
            "debugMessage": "Call to a member function getData() on string",
            "message": "Internal server error",
            "extensions": {
                "category": "internal"
            },
            "locations": [
                {
                    "line": 8,
                    "column": 11
                }
            ],
            "path": [
                "getLinkedProductsByGroups",
                1,
                "products",
                7,
                "small_image",
                "url"
            ]
        }
    ],
    "data": {
        "getLinkedProductsByGroups": [
            {
                "group": "001021",
                "products": [
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-PKIB",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-PKYW",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-PKDBL",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "BIDI BADU Bella Tech Neck Women's T-shirt-XS",
                        "sku": "001021-PKDBL-XS",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-BKDBL",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-DBPK",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-CON",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-GRCO",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-PKGR",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Tech Neck Women's T-shirt-XS",
                        "sku": "001021-PKGR-XS",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-RDPK",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Bella Women's Tech Neck T-shirt",
                        "sku": "001021-ANDB",
                        "small_image": {
                            "url": null
                        }
                    }
                ]
            },
            {
                "group": "001107",
                "products": [
                    {
                        "name": "Bidi Badu Magu Crew Tech Sport Socks x 3",
                        "sku": "001107-WH",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Magu Crew Tech Socks - set of 3-39-42",
                        "sku": "001107-WH-39-42",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Magu Crew Tech Socks - set of 3-43-46",
                        "sku": "001107-WH-43-46",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Magu Crew Tech Socks - set of 3-46-50",
                        "sku": "001107-WH-46-50",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Magu Crew Tech Sport Socks x 3",
                        "sku": "001107-BK",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Magu Crew Tech Socks - set of 3-39-42",
                        "sku": "001107-BK-39-42",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Magu Crew Tech Socks - set of 3-43-46",
                        "sku": "001107-BK-43-46",
                        "small_image": {
                            "url": null
                        }
                    },
                    {
                        "name": "Bidi Badu Magu Crew Tech Socks - set of 3-46-50",
                        "sku": "001107-BK-46-50",
                        "small_image": {
                            "url": null
                        }
                    }
                ]
            }
        ]
    }
}

Of course I can’t use the price / image fields directly because the ProductInterface type must have a sub-selection for them. But the toArray() and the getData() functions don’t give the result as expected. So how do I make it convert the fields properly from the ProductInterface defined in the PHP classes, to the ProductInterface defined in GraphQl so that the result is correct?