Search code examples
phpshopwareshopware6

EntityExtension ManyToMany via Store API


I wrote an EntityExtension for products:

class ProductExtension extends EntityExtension
{
    public function extendFields(FieldCollection $collection): void
    {
        $collection->add(
            (new ManyToManyAssociationField(
                'logos',
                LogosDefinition::class,
                LogosProductDefinition::class,
                'product_id',
                'logo_id'
            ))->addFlags(new ApiAware(), new Inherited())
        );
    }

    public function getDefinitionClass(): string
    {
        return ProductDefinition::class;
    }
}

The definition LogosProductDefinition.php is structured as follows:

class LogosProductDefinition extends MappingEntityDefinition
{
    public function getEntityName(): string
    {
        return 'logo_product';
    }

    protected function defineFields(): FieldCollection
    {
        return new FieldCollection([
            (new FkField('_logo_id', 'logoId', LogosDefinition::class))->addFlags(new PrimaryKey(), new Required()),
            (new FkField('product_id', 'productId', ProductDefinition::class))->addFlags(new PrimaryKey(), new Required()),
            (new ReferenceVersionField(ProductDefinition::class))->addFlags(new PrimaryKey(), new Required()),
            new ManyToOneAssociationField('logos', 'logo_id', LogosDefinition::class),
            new ManyToOneAssociationField('product', 'product_id', ProductDefinition::class),
            new CreatedAtField(),
        ]);
    }
}

LogosDefinition.php

class LogosDefinition extends EntityDefinition
{
    /* ......... */

    protected function defineFields(): FieldCollection
    {
        return new FieldCollection([
            (new IdField('id', 'id'))->addFlags(new Required(), new PrimaryKey()),
            (new StringField('url', 'url'))->addFlags(new Required()),
            new TranslatedField('title'),
            new FkField('media_id', 'mediaId', MediaDefinition::class),
            new ManyToOneAssociationField('media', 'media_id', MediaDefinition::class, 'id'),
            new TranslationsAssociationField(LogosTranslationDefinition::class, 'logos_id'),
            new ManyToManyAssociationField('products', ProductDefinition::class, LogosProductDefinition::class, 'logo_id', 'product_id'),
        ]);
    }
}

As a subscriber I registered the PRODUCT_LOADED_EVENT:

class ProductSubscriber implements EventSubscriberInterface
{    
    public static function getSubscribedEvents(): array
    {
        return [
            'sales_channel.' . ProductEvents::PRODUCT_LOADED_EVENT => 'salesChannelLoaded',
        ];
    }

    public function salesChannelLoaded(SalesChannelEntityLoadedEvent $event): void
    {
        /** @var SalesChannelProductEntity $product */
        $products = $event->getEntities();

        foreach ($products as $product) {
            $id = $product->getParentId() ? $product->getParentId() : $product->getId();
            $criteria = new Criteria([$id]);
            $criteria->addAssociation('logos');
                        
            $productEntity = $this->productRepository->search($criteria, \Shopware\Core\Framework\Context::createDefaultContext())->first();

            $prodExtensions = $productEntity->getExtensions();
            if (isset($prodExtensions['logos']) && !empty($prodExtensions['logos'])) {
                $logos = $prodExtensions['logos']->getElements();
                foreach ($logos as &$logo) {
                    if ($tLogo->mediaId) {
                        $mediaEntity = $this->mediaRepository->search((new Criteria([$logo->mediaId])), \Shopware\Core\Framework\Context::createDefaultContext())->first();
                        $logo->media= $mediaEntity;
                    }
                }
                $prodExtensions['logos']->setExtensions($logos);
                $product->setExtensions($prodExtensions);
            }
        }
    }
}

Now I have the problem when I access the store API via https://myshop.com/store-api/product/<uuid_product> then I see the extensions “logos” listed, but without any content (without properties montioned in LogosDefinition.php).

"extensions": {
   "foreignKeys": {
      "apiAlias": "array_struct"
    },
    "logos": [
      {
       "_uniqueIdentifier": "baf43ddc20174786bb86c929a3f3c450",
       "versionId": null,
       "translated": [],
        "createdAt": "2023-10-31T06:52:54.851+00:00",
        "updatedAt": "2023-11-02T15:36:13.339+00:00",
        "extensions": {
          "foreignKeys": {
            "apiAlias": "array_struct"
          }
        },
        "apiAlias": "logos"
     }
   ]
}

Solution

  • You need to define which fields are visible for the store API in your EntityDefinition, by adding the ApiAware flag.

    See: https://developer.shopware.com/docs/resources/references/core-reference/dal-reference/flags-reference.html

    For example:

    class LogosDefinition extends EntityDefinition
    {
        /* ......... */
    
        protected function defineFields(): FieldCollection
        {
            return new FieldCollection([
                (new IdField('id', 'id'))->addFlags(new Required(), new PrimaryKey(), new ApiAware()),
                (new StringField('url', 'url'))->addFlags(new Required(), new ApiAware()),
                (new TranslatedField('title'))->addFlags(new ApiAware()),
                (new FkField('media_id', 'mediaId', MediaDefinition::class))->addFlags(new ApiAware()),
                (new ManyToOneAssociationField('media', 'media_id', MediaDefinition::class, 'id'))->addFlags(new ApiAware()),
                new TranslationsAssociationField(LogosTranslationDefinition::class, 'logos_id'),
                new ManyToManyAssociationField('products', ProductDefinition::class, LogosProductDefinition::class, 'logo_id', 'product_id'),
            ]);
        }
    }