vendor/shopware/core/Framework/DataAbstractionLayer/EntityDefinition.php line 354

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\DataAbstractionLayer;
  3. use Shopware\Core\Content\Seo\SeoUrl\SeoUrlDefinition;
  4. use Shopware\Core\Framework\DataAbstractionLayer\Dbal\EntityHydrator;
  5. use Shopware\Core\Framework\DataAbstractionLayer\EntityProtection\EntityProtectionCollection;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Field\AutoIncrementField;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Field\CreatedAtField;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Field\Field;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Field\FkField;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\ApiAware;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Computed;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Extension;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\PrimaryKey;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Runtime;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Field\JsonField;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Field\LockedField;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToOneAssociationField;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToManyAssociationField;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToOneAssociationField;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Field\ParentAssociationField;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Field\ReferenceVersionField;
  23. use Shopware\Core\Framework\DataAbstractionLayer\Field\TranslatedField;
  24. use Shopware\Core\Framework\DataAbstractionLayer\Field\TranslationsAssociationField;
  25. use Shopware\Core\Framework\DataAbstractionLayer\Field\UpdatedAtField;
  26. use Shopware\Core\Framework\Struct\ArrayEntity;
  27. abstract class EntityDefinition
  28. {
  29.     protected ?CompiledFieldCollection $fields null;
  30.     /**
  31.      * @var EntityExtension[]
  32.      */
  33.     protected array $extensions = [];
  34.     protected ?TranslationsAssociationField $translationField null;
  35.     protected ?CompiledFieldCollection $primaryKeys null;
  36.     protected DefinitionInstanceRegistry $registry;
  37.     /**
  38.      * @var TranslatedField[]
  39.      */
  40.     protected array $translatedFields = [];
  41.     /**
  42.      * @var Field[]
  43.      */
  44.     protected array $extensionFields = [];
  45.     /**
  46.      * @var EntityDefinition|false|null
  47.      */
  48.     private $parentDefinition false;
  49.     private string $className;
  50.     private ?FieldVisibility $fieldVisibility null;
  51.     final public function __construct()
  52.     {
  53.         $this->className = static::class;
  54.     }
  55.     final public function getClass(): string
  56.     {
  57.         return static::class;
  58.     }
  59.     final public function isInstanceOf(EntityDefinition $other): bool
  60.     {
  61.         // same reference or instance of the other class
  62.         return $this === $other
  63.             || ($other->getClass() !== EntityDefinition::class && $this instanceof $other);
  64.     }
  65.     public function compile(DefinitionInstanceRegistry $registry): void
  66.     {
  67.         $this->registry $registry;
  68.     }
  69.     final public function addExtension(EntityExtension $extension): void
  70.     {
  71.         $this->extensions[] = $extension;
  72.         $this->fields null;
  73.     }
  74.     /**
  75.      * @internal
  76.      * Use this only for test purposes
  77.      */
  78.     final public function removeExtension(EntityExtension $toDelete): void
  79.     {
  80.         foreach ($this->extensions as $key => $extension) {
  81.             if (\get_class($extension) === \get_class($toDelete)) {
  82.                 unset($this->extensions[$key]);
  83.                 $this->fields null;
  84.                 return;
  85.             }
  86.         }
  87.     }
  88.     abstract public function getEntityName(): string;
  89.     final public function getFields(): CompiledFieldCollection
  90.     {
  91.         if ($this->fields !== null) {
  92.             return $this->fields;
  93.         }
  94.         $fields $this->defineFields();
  95.         foreach ($this->defaultFields() as $field) {
  96.             $fields->add($field);
  97.         }
  98.         foreach ($this->extensions as $extension) {
  99.             $new = new FieldCollection();
  100.             $extension->extendFields($new);
  101.             foreach ($new as $field) {
  102.                 $field->addFlags(new Extension());
  103.                 if ($field instanceof AssociationField) {
  104.                     $fields->add($field);
  105.                     continue;
  106.                 }
  107.                 if ($field->is(Runtime::class)) {
  108.                     $fields->add($field);
  109.                     continue;
  110.                 }
  111.                 if ($field instanceof ReferenceVersionField) {
  112.                     $fields->add($field);
  113.                     continue;
  114.                 }
  115.                 if (!$field instanceof FkField) {
  116.                     throw new \Exception('Only AssociationFields, FkFields/ReferenceVersionFields for a ManyToOneAssociationField or fields flagged as Runtime can be added as Extension.');
  117.                 }
  118.                 if (!$this->hasAssociationWithStorageName($field->getStorageName(), $new)) {
  119.                     throw new \Exception(sprintf('FkField %s has no configured OneToOneAssociationField or ManyToOneAssociationField in entity %s'$field->getPropertyName(), $this->className));
  120.                 }
  121.                 $fields->add($field);
  122.             }
  123.         }
  124.         foreach ($this->getBaseFields() as $baseField) {
  125.             $fields->add($baseField);
  126.         }
  127.         foreach ($fields as $field) {
  128.             if ($field instanceof TranslationsAssociationField) {
  129.                 $this->translationField $field;
  130.                 $fields->add(
  131.                     (new JsonField('translated''translated'))->addFlags(new ApiAware(), new Computed(), new Runtime())
  132.                 );
  133.                 break;
  134.             }
  135.         }
  136.         $this->fields $fields->compile($this->registry);
  137.         return $this->fields;
  138.     }
  139.     final public function getProtections(): EntityProtectionCollection
  140.     {
  141.         $protections $this->defineProtections();
  142.         foreach ($this->extensions as $extension) {
  143.             if (!$extension instanceof EntityExtension) {
  144.                 continue;
  145.             }
  146.             $extension->extendProtections($protections);
  147.         }
  148.         return $protections;
  149.     }
  150.     final public function getField(string $propertyName): ?Field
  151.     {
  152.         return $this->getFields()->get($propertyName);
  153.     }
  154.     final public function getFieldVisibility(): FieldVisibility
  155.     {
  156.         if ($this->fieldVisibility) {
  157.             return $this->fieldVisibility;
  158.         }
  159.         /** @var string[] $internalProperties */
  160.         $internalProperties $this->getFields()
  161.             ->fmap(function (Field $field): ?string {
  162.                 if ($field->is(ApiAware::class)) {
  163.                     return null;
  164.                 }
  165.                 return $field->getPropertyName();
  166.             });
  167.         return $this->fieldVisibility = new FieldVisibility(array_values($internalProperties));
  168.     }
  169.     /**
  170.      * @return class-string<EntityCollection>
  171.      */
  172.     public function getCollectionClass(): string
  173.     {
  174.         return EntityCollection::class;
  175.     }
  176.     /**
  177.      * @return class-string<Entity>
  178.      */
  179.     public function getEntityClass(): string
  180.     {
  181.         return ArrayEntity::class;
  182.     }
  183.     public function getParentDefinition(): ?EntityDefinition
  184.     {
  185.         if ($this->parentDefinition !== false) {
  186.             return $this->parentDefinition;
  187.         }
  188.         $parentDefinitionClass $this->getParentDefinitionClass();
  189.         if ($parentDefinitionClass === null) {
  190.             return $this->parentDefinition null;
  191.         }
  192.         $this->parentDefinition $this->registry->getByClassOrEntityName($parentDefinitionClass);
  193.         return $this->parentDefinition;
  194.     }
  195.     final public function getTranslationDefinition(): ?EntityDefinition
  196.     {
  197.         // value is initialized from this method
  198.         $this->getFields();
  199.         if ($this->translationField === null) {
  200.             return null;
  201.         }
  202.         return $this->translationField->getReferenceDefinition();
  203.     }
  204.     final public function getTranslationField(): ?TranslationsAssociationField
  205.     {
  206.         // value is initialized from this method
  207.         $this->getFields();
  208.         return $this->translationField;
  209.     }
  210.     final public function hasAutoIncrement(): bool
  211.     {
  212.         return $this->getField('autoIncrement') instanceof AutoIncrementField;
  213.     }
  214.     final public function getPrimaryKeys(): CompiledFieldCollection
  215.     {
  216.         if ($this->primaryKeys !== null) {
  217.             return $this->primaryKeys;
  218.         }
  219.         $fields $this->getFields()->filter(function (Field $field): bool {
  220.             return $field->is(PrimaryKey::class);
  221.         });
  222.         $fields->sort(static function (Field $aField $b) {
  223.             return $b->getExtractPriority() <=> $a->getExtractPriority();
  224.         });
  225.         return $this->primaryKeys $fields;
  226.     }
  227.     public function getDefaults(): array
  228.     {
  229.         return [];
  230.     }
  231.     public function getChildDefaults(): array
  232.     {
  233.         return [];
  234.     }
  235.     public function isChildrenAware(): bool
  236.     {
  237.         //used in VersionManager
  238.         return $this->getFields()->getChildrenAssociationField() !== null;
  239.     }
  240.     public function isParentAware(): bool
  241.     {
  242.         return $this->getFields()->get('parent') instanceof ParentAssociationField;
  243.     }
  244.     public function isInheritanceAware(): bool
  245.     {
  246.         return false;
  247.     }
  248.     public function isVersionAware(): bool
  249.     {
  250.         return $this->getFields()->has('versionId');
  251.     }
  252.     public function isLockAware(): bool
  253.     {
  254.         $field $this->getFields()->get('locked');
  255.         return $field && $field instanceof LockedField;
  256.     }
  257.     public function isSeoAware(): bool
  258.     {
  259.         $field $this->getFields()->get('seoUrls');
  260.         return $field instanceof OneToManyAssociationField && $field->getReferenceDefinition() instanceof SeoUrlDefinition;
  261.     }
  262.     public function since(): ?string
  263.     {
  264.         return null;
  265.     }
  266.     public function getHydratorClass(): string
  267.     {
  268.         return EntityHydrator::class;
  269.     }
  270.     /**
  271.      * @internal
  272.      *
  273.      * @return mixed
  274.      */
  275.     public function decode(string $property, ?string $value)
  276.     {
  277.         $field $this->getField($property);
  278.         if ($field === null) {
  279.             throw new \RuntimeException(sprintf('Field %s not found'$property));
  280.         }
  281.         return $field->getSerializer()->decode($field$value);
  282.     }
  283.     public function getTranslatedFields(): array
  284.     {
  285.         return $this->getFields()->getTranslatedFields();
  286.     }
  287.     public function getExtensionFields(): array
  288.     {
  289.         return $this->getFields()->getExtensionFields();
  290.     }
  291.     protected function getParentDefinitionClass(): ?string
  292.     {
  293.         return null;
  294.     }
  295.     /**
  296.      * @return Field[]
  297.      */
  298.     protected function defaultFields(): array
  299.     {
  300.         return [
  301.             (new CreatedAtField())->addFlags(new ApiAware()),
  302.             (new UpdatedAtField())->addFlags(new ApiAware()),
  303.         ];
  304.     }
  305.     abstract protected function defineFields(): FieldCollection;
  306.     protected function defineProtections(): EntityProtectionCollection
  307.     {
  308.         return new EntityProtectionCollection();
  309.     }
  310.     protected function getBaseFields(): array
  311.     {
  312.         return [];
  313.     }
  314.     private function hasAssociationWithStorageName(string $storageNameFieldCollection $new): bool
  315.     {
  316.         foreach ($new as $association) {
  317.             if (!$association instanceof ManyToOneAssociationField && !$association instanceof OneToOneAssociationField) {
  318.                 continue;
  319.             }
  320.             if ($association->getStorageName() === $storageName) {
  321.                 return true;
  322.             }
  323.         }
  324.         return false;
  325.     }
  326. }