Extending a type

In case you need to add fields to data types provided by another module (even OXID’s GraphQL modules) you can just use the Extending a type feature in GraphQLite.

Every data type can be extended via the @ExtendType annotation multiple times, even in the same module.

Important

Types, Controllers, Services, … can not be extended through OXID’s module chain!

In case you wonder, see description about why we made classes final.

Extend the product type

Given that you have

  • a custom module that

    • adds a custom subtitle field to the oxarticle database table

    • extends the OxidEsales\Eshop\Application\Model\Article model with a subtitle() method

  • want this field exposed via the GraphQL API

  • have installed the oxid-esales/graphql-storefront module

Create the extend type

The @ExtendType annotation has a class argument which has to be the data type you want to extend. That being the PHP class with the @Type annotation.

<?php

declare(strict_types=1);

namespace Full\Qualified\Namespace\Service;

use OxidEsales\GraphQL\Storefront\Product\DataType\Product;
use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\ExtendType;

/**
 * @ExtendType(class=Product::class)
 */
final class ProductExtendType
{
    /**
     * @Field()
     */
    public function subtitle(Product $product): string
    {
        return $product->getEshopModel()->subtitle();
    }
}

The first argument is always the class you are extending. When a client asks for your field, you’ll get the original data type and can then fetch the OXID eShop model from it in case needed.

This results in the following GraphQL schema

type Product {
    id: ID!
    # all other fields from data type
    # ...
    # plus now your field:
    subtitle: String!
}

Register type

For GraphQLite to find your newly created extend type

  • it must be registered via the namespace mappers getTypeNamespaceMapping()

  • needs to exist in the DI container and the container identifier MUST be the fully qualified class name

Namespace mapper

<?php

declare(strict_types=1);

namespace Full\Qualified\Namespace\Shared\Service;

use OxidEsales\GraphQL\Base\Framework\NamespaceMapperInterface;

final class NamespaceMapper implements NamespaceMapperInterface
{
    public function getTypeNamespaceMapping(): array
    {
        return [
            '\\Full\\Qualified\\Namespace\\Service' => __DIR__ . '/../../Service';
        ];
    }
}

services.yaml

services:

    Full\Qualified\Namespace\Service\ProductExtendType:
        class: Full\Qualified\Namespace\Service\ProductExtendType

    Full\Qualified\Namespace\Shared\Service\NamespaceMapper:
        class: Full\Qualified\Namespace\Shared\Service\NamespaceMapper
        tags: ['graphql_namespace_mapper']

Best practice

Try to withstand the urge to create another module for this case and integrate the GraphQL type mapping into your existing module. That way you do not end up with an invalid state when activating/deactivating a module. Otherwise you might deactivate the module that adds the subtitle() method to the OxidEsales\Eshop\Application\Model\Article class. The subtitle field is still in your GraphQL schema, but requesting that field will result in a PHP Fatal Error.