Georgi Kolev
Georgi Kolev

Reputation: 41

Extending Shopware entity with foreign keys fails when merging version

I'm developing my first Shopware 6 admin plugin, for which is required to extend one of the existing Shopware plugins - Custom products.

I want to add a relation between already existing entities - TemplateExclusion and TemplateOptionDefinition. When I select from the UI my options, TemplateExclusion entity its getting saved in the database, without any problems.

When I save the Template entity (parent of TemplateExclusion), my "excluded_option_id" its getting overwritten with the 1st possible option from TemplateOptionDefinition entities.

I have notice that this is happening on "mergeVersion". Also when I try to save the Template entity with debug mode enabled and profiler, I'm getting an error during saving, that "excludedOptionId" is blank when merging, which is not true.

Error in profiler

Following the documentation I made first the migration:

class Migration1643023742TemplateExclusionRelation extends MigrationStep
{
    public function getCreationTimestamp(): int
    {
        return 1643023742;
    }

    public function update(Connection $connection): void
    {
        $connection->executeStatement('ALTER TABLE `swag_customized_products_template_exclusion` ADD COLUMN `excluded_option_id` BINARY(16) AFTER `template_version_id`;');
        $connection->executeStatement('ALTER TABLE `swag_customized_products_template_exclusion` ADD COLUMN `excluded_option_version_id` BINARY(16) AFTER `excluded_option_id`;');
        $connection->executeStatement('ALTER TABLE `swag_customized_products_template_exclusion` ADD CONSTRAINT `fk.swag_cupr_template_exclusion.excluded_option_id` FOREIGN KEY (`excluded_option_id`, `excluded_option_version_id`)
        REFERENCES `swag_customized_products_template_option` (`id`, `version_id`) ON DELETE CASCADE ON UPDATE CASCADE;');
    }

then I made an entity extension, where to define the new fields.

class TemplateExclusionExtension extends EntityExtension
{
    public function extendFields(FieldCollection $collection): void
    {
        $collection->add(
            (new FkField('excluded_option_id', 'excludedOptionId', TemplateOptionDefinition::class))
                ->addFlags(new Required(), new ApiAware())
        );

        $collection->add(
            (new ManyToOneAssociationField('excludedOption', 'excluded_option_id', TemplateOptionDefinition::class))
                ->addFlags(new ApiAware())
        );

        $collection->add(
            (new ReferenceVersionField(TemplateOptionDefinition::class, 'excluded_option_version_id'))
                ->addFlags(new Required(), new ApiAware()),
        );
    }

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

Solved:

It was wrong definition from my side:

public function extendFields(FieldCollection $collection): void
    {
        $collection->add(
            (new FkField('excluded_option_id', 'excludedOptionId', TemplateOptionDefinition::class))
                ->addFlags(new Required(), new ApiAware())
        );

        $collection->add(
            (new OneToOneAssociationField(
                EasyExtendCustomizedProducts::TEMPLATE_EXCLUSION_EXCLUDED_OPTION_EXTENSION,
                'excluded_option_id',
                'id',
                TemplateOptionDefinition::class,
                false
            ))->addFlags(new CascadeDelete(), new ApiAware())
        );
    }

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

Upvotes: 3

Views: 952

Answers (1)

dneustadt
dneustadt

Reputation: 13161

If I'm not mistaken the issue was the missing CascadeDelete delete flag.

To versionize the entity it is first fetched including its associated data and is then persisted with new primary keys, so basically it gets cloned. However not all associations are taken into account when fetching the data to be cloned. You can find the responsible code here, where the affected associations get filtered by the existence of the CascadeDelete flag. If they miss the flag they will be ignored for creating the cloned version. This behavior still needs to be documented more prominently.

Upvotes: 1

Related Questions