vendor/pimcore/pimcore/models/DataObject/ClassDefinition.php line 305

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Model\DataObject;
  15. use Pimcore\Cache;
  16. use Pimcore\Cache\RuntimeCache;
  17. use Pimcore\DataObject\ClassBuilder\FieldDefinitionDocBlockBuilderInterface;
  18. use Pimcore\DataObject\ClassBuilder\PHPClassDumperInterface;
  19. use Pimcore\Db;
  20. use Pimcore\Event\DataObjectClassDefinitionEvents;
  21. use Pimcore\Event\Model\DataObject\ClassDefinitionEvent;
  22. use Pimcore\Event\Traits\RecursionBlockingEventDispatchHelperTrait;
  23. use Pimcore\Logger;
  24. use Pimcore\Model;
  25. use Pimcore\Model\DataObject;
  26. use Pimcore\Model\DataObject\ClassDefinition\Data\FieldDefinitionEnrichmentInterface;
  27. /**
  28.  * @method \Pimcore\Model\DataObject\ClassDefinition\Dao getDao()
  29.  */
  30. final class ClassDefinition extends Model\AbstractModel
  31. {
  32.     use DataObject\ClassDefinition\Helper\VarExport;
  33.     use DataObject\Traits\LocateFileTrait;
  34.     use RecursionBlockingEventDispatchHelperTrait;
  35.     /**
  36.      * @internal
  37.      *
  38.      * @var string|null
  39.      */
  40.     public $id;
  41.     /**
  42.      * @internal
  43.      *
  44.      * @var string|null
  45.      */
  46.     public $name;
  47.     /**
  48.      * @internal
  49.      *
  50.      * @var string
  51.      */
  52.     public $description '';
  53.     /**
  54.      * @internal
  55.      *
  56.      * @var int|null
  57.      */
  58.     public $creationDate;
  59.     /**
  60.      * @internal
  61.      *
  62.      * @var int|null
  63.      */
  64.     public $modificationDate;
  65.     /**
  66.      * @internal
  67.      *
  68.      * @var int|null
  69.      */
  70.     public $userOwner;
  71.     /**
  72.      * @internal
  73.      *
  74.      * @var int|null
  75.      */
  76.     public $userModification;
  77.     /**
  78.      * @internal
  79.      *
  80.      * @var string
  81.      */
  82.     public $parentClass '';
  83.     /**
  84.      * Comma separated list of interfaces
  85.      *
  86.      * @internal
  87.      *
  88.      * @var string|null
  89.      */
  90.     public $implementsInterfaces;
  91.     /**
  92.      * Name of the listing parent class if set
  93.      *
  94.      * @internal
  95.      *
  96.      * @var string
  97.      */
  98.     public $listingParentClass '';
  99.     /**
  100.      * @internal
  101.      *
  102.      * @var string
  103.      */
  104.     public $useTraits '';
  105.     /**
  106.      * @internal
  107.      *
  108.      * @var string
  109.      */
  110.     public $listingUseTraits '';
  111.     /**
  112.      * @internal
  113.      *
  114.      * @var bool
  115.      */
  116.     protected $encryption false;
  117.     /**
  118.      * @internal
  119.      *
  120.      * @var array
  121.      */
  122.     protected $encryptedTables = [];
  123.     /**
  124.      * @internal
  125.      *
  126.      * @var bool
  127.      */
  128.     public $allowInherit false;
  129.     /**
  130.      * @internal
  131.      *
  132.      * @var bool
  133.      */
  134.     public $allowVariants false;
  135.     /**
  136.      * @internal
  137.      *
  138.      * @var bool
  139.      */
  140.     public $showVariants false;
  141.     /**
  142.      * @internal
  143.      *
  144.      * @var DataObject\ClassDefinition\Data[]
  145.      */
  146.     public array $fieldDefinitions = [];
  147.     /**
  148.      * @internal
  149.      *
  150.      * @var DataObject\ClassDefinition\Layout|null
  151.      */
  152.     public $layoutDefinitions;
  153.     /**
  154.      * @internal
  155.      *
  156.      * @var string|null
  157.      */
  158.     public $icon;
  159.     /**
  160.      * @internal
  161.      *
  162.      * @var string|null
  163.      */
  164.     public $previewUrl;
  165.     /**
  166.      * @internal
  167.      *
  168.      * @var string|null
  169.      */
  170.     public $group;
  171.     /**
  172.      * @internal
  173.      *
  174.      * @var bool
  175.      */
  176.     public $showAppLoggerTab false;
  177.     /**
  178.      * @internal
  179.      *
  180.      * @var string
  181.      */
  182.     public $linkGeneratorReference;
  183.     /**
  184.      * @internal
  185.      *
  186.      * @var string|null
  187.      */
  188.     public $previewGeneratorReference;
  189.     /**
  190.      * @internal
  191.      *
  192.      * @var array
  193.      */
  194.     public $compositeIndices = [];
  195.     /**
  196.      * @internal
  197.      *
  198.      * @var bool
  199.      */
  200.     public $generateTypeDeclarations true;
  201.     /**
  202.      * @internal
  203.      *
  204.      * @var bool
  205.      */
  206.     public $showFieldLookup false;
  207.     /**
  208.      * @internal
  209.      *
  210.      * @var array
  211.      */
  212.     public $propertyVisibility = [
  213.         'grid' => [
  214.             'id' => true,
  215.             'path' => true,
  216.             'published' => true,
  217.             'modificationDate' => true,
  218.             'creationDate' => true,
  219.         ],
  220.         'search' => [
  221.             'id' => true,
  222.             'path' => true,
  223.             'published' => true,
  224.             'modificationDate' => true,
  225.             'creationDate' => true,
  226.         ],
  227.     ];
  228.     /**
  229.      * @internal
  230.      *
  231.      * @var bool
  232.      */
  233.     public $enableGridLocking false;
  234.     /**
  235.      * @internal
  236.      *
  237.      * @var ClassDefinition\Data[]
  238.      */
  239.     private array $deletedDataComponents = [];
  240.     /**
  241.      * @param string $id
  242.      * @param bool $force
  243.      *
  244.      * @return null|ClassDefinition
  245.      *
  246.      * @throws \Exception
  247.      */
  248.     public static function getById(string $id$force false)
  249.     {
  250.         $cacheKey 'class_' $id;
  251.         try {
  252.             if ($force) {
  253.                 throw new \Exception('Forced load');
  254.             }
  255.             $class RuntimeCache::get($cacheKey);
  256.             if (!$class) {
  257.                 throw new \Exception('Class in registry is null');
  258.             }
  259.         } catch (\Exception $e) {
  260.             try {
  261.                 $class = new self();
  262.                 $name $class->getDao()->getNameById($id);
  263.                 if (!$name) {
  264.                     throw new \Exception('Class definition with name ' $name ' or ID ' $id ' does not exist');
  265.                 }
  266.                 $definitionFile $class->getDefinitionFile($name);
  267.                 $class = @include $definitionFile;
  268.                 if (!$class instanceof self) {
  269.                     throw new \Exception('Class definition with name ' $name ' or ID ' $id ' does not exist');
  270.                 }
  271.                 $class->setId($id);
  272.                 RuntimeCache::set($cacheKey$class);
  273.             } catch (\Exception $e) {
  274.                 Logger::info($e->getMessage());
  275.                 return null;
  276.             }
  277.         }
  278.         return $class;
  279.     }
  280.     /**
  281.      * @param string $name
  282.      *
  283.      * @return self|null
  284.      *
  285.      * @throws \Exception
  286.      */
  287.     public static function getByName($name)
  288.     {
  289.         try {
  290.             $class = new self();
  291.             $id $class->getDao()->getIdByName($name);
  292.             return self::getById($id);
  293.         } catch (Model\Exception\NotFoundException $e) {
  294.             return null;
  295.         }
  296.     }
  297.     /**
  298.      * @param array $values
  299.      *
  300.      * @return self
  301.      */
  302.     public static function create($values = [])
  303.     {
  304.         $class = new self();
  305.         $class->setValues($values);
  306.         return $class;
  307.     }
  308.     /**
  309.      * @internal
  310.      *
  311.      * @param string $name
  312.      */
  313.     public function rename($name)
  314.     {
  315.         $this->deletePhpClasses();
  316.         $this->getDao()->updateClassNameInObjects($name);
  317.         $this->setName($name);
  318.         $this->save();
  319.     }
  320.     /**
  321.      * @param mixed $data
  322.      *
  323.      * @internal
  324.      */
  325.     public static function cleanupForExport(&$data)
  326.     {
  327.         if (!is_object($data)) {
  328.             return;
  329.         }
  330.         if ($data instanceof DataObject\ClassDefinition\Data\VarExporterInterface) {
  331.             $blockedVars $data->resolveBlockedVars();
  332.             foreach ($blockedVars as $blockedVar) {
  333.                 if (isset($data->{$blockedVar})) {
  334.                     unset($data->{$blockedVar});
  335.                 }
  336.             }
  337.             if (isset($data->blockedVarsForExport)) {
  338.                 unset($data->blockedVarsForExport);
  339.             }
  340.         }
  341.         if (method_exists($data'getChildren')) {
  342.             $children $data->getChildren();
  343.             if (is_array($children)) {
  344.                 foreach ($children as $child) {
  345.                     self::cleanupForExport($child);
  346.                 }
  347.             }
  348.         }
  349.     }
  350.     /**
  351.      * @return bool
  352.      */
  353.     private function exists()
  354.     {
  355.         $name $this->getDao()->getNameById($this->getId());
  356.         return is_string($name);
  357.     }
  358.     /**
  359.      * @param bool $saveDefinitionFile
  360.      *
  361.      * @throws \Exception
  362.      * @throws DataObject\Exception\DefinitionWriteException
  363.      */
  364.     public function save($saveDefinitionFile true)
  365.     {
  366.         if ($saveDefinitionFile && !$this->isWritable()) {
  367.             throw new DataObject\Exception\DefinitionWriteException();
  368.         }
  369.         $fieldDefinitions $this->getFieldDefinitions();
  370.         foreach ($fieldDefinitions as $fd) {
  371.             if ($fd->isForbiddenName()) {
  372.                 throw new \Exception(sprintf('Forbidden name used for field definition: %s'$fd->getName()));
  373.             }
  374.             if ($fd instanceof DataObject\ClassDefinition\Data\DataContainerAwareInterface) {
  375.                 $fd->preSave($this);
  376.             }
  377.         }
  378.         if (!$this->getId()) {
  379.             $db Db::get();
  380.             $maxId $db->fetchOne('SELECT MAX(CAST(id AS SIGNED)) FROM classes;');
  381.             $maxId $maxId $maxId 1;
  382.             $this->setId((string) $maxId);
  383.         }
  384.         if (!preg_match('/[a-zA-Z][a-zA-Z0-9_]+/'$this->getName())) {
  385.             throw new \Exception(sprintf('Invalid name for class definition: %s'$this->getName()));
  386.         }
  387.         if (!preg_match('/[a-zA-Z0-9]([a-zA-Z0-9_]+)?/'$this->getId())) {
  388.             throw new \Exception(sprintf('Invalid ID `%s` for class definition %s'$this->getId(), $this->getName()));
  389.         }
  390.         foreach (['parentClass''listingParentClass''useTraits''listingUseTraits'] as $propertyName) {
  391.             $propertyValue $this->{'get'.ucfirst($propertyName)}();
  392.             if ($propertyValue && !preg_match('/^[a-zA-Z_\x7f-\xff\\\][a-zA-Z0-9_\x7f-\xff\\\ ,]*$/'$propertyValue)) {
  393.                 throw new \Exception(sprintf('Invalid %s value for class definition: %s'$propertyName,
  394.                     $this->getParentClass()));
  395.             }
  396.         }
  397.         $isUpdate $this->exists();
  398.         if (!$isUpdate) {
  399.             $this->dispatchEvent(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::PRE_ADD);
  400.         } else {
  401.             $this->dispatchEvent(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::PRE_UPDATE);
  402.         }
  403.         $this->setModificationDate(time());
  404.         $this->getDao()->save($isUpdate);
  405.         $this->generateClassFiles($saveDefinitionFile);
  406.         foreach ($fieldDefinitions as $fd) {
  407.             // call the method "classSaved" if exists, this is used to create additional data tables or whatever which depends on the field definition, for example for localizedfields
  408.             //TODO Pimcore 11 remove method_exists call
  409.             if (!$fd instanceof ClassDefinition\Data\DataContainerAwareInterface && method_exists($fd'classSaved')) {
  410.                 $fd->classSaved($this);
  411.             }
  412.         }
  413.         // empty object cache
  414.         try {
  415.             Cache::clearTag('class_'.$this->getId());
  416.         } catch (\Exception $e) {
  417.         }
  418.         foreach ($fieldDefinitions as $fd) {
  419.             if ($fd instanceof DataObject\ClassDefinition\Data\DataContainerAwareInterface) {
  420.                 $fd->postSave($this);
  421.             }
  422.         }
  423.         if ($isUpdate) {
  424.             $this->dispatchEvent(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::POST_UPDATE);
  425.         } else {
  426.             $this->dispatchEvent(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::POST_ADD);
  427.         }
  428.         $this->deleteDeletedDataComponentsInCustomLayout();
  429.     }
  430.     /**
  431.      * @param bool $generateDefinitionFile
  432.      *
  433.      * @throws \Exception
  434.      *
  435.      * @internal
  436.      */
  437.     public function generateClassFiles($generateDefinitionFile true)
  438.     {
  439.         \Pimcore::getContainer()->get(PHPClassDumperInterface::class)->dumpPHPClasses($this);
  440.         if ($generateDefinitionFile) {
  441.             // save definition as a php file
  442.             $definitionFile $this->getDefinitionFile();
  443.             if (!is_writable(dirname($definitionFile)) || (is_file($definitionFile) && !is_writable($definitionFile))) {
  444.                 throw new \Exception(
  445.                     'Cannot write definition file in: '.$definitionFile.' please check write permission on this directory.'
  446.                 );
  447.             }
  448.             /** @var self $clone */
  449.             $clone DataObject\Service::cloneDefinition($this);
  450.             $clone->setDao(null);
  451.             $clone->fieldDefinitions = [];
  452.             self::cleanupForExport($clone->layoutDefinitions);
  453.             $exportedClass var_export($clonetrue);
  454.             $data '<?php';
  455.             $data .= "\n\n";
  456.             $data .= $this->getInfoDocBlock();
  457.             $data .= "\n\n";
  458.             $data .= 'return '.$exportedClass.";\n";
  459.             \Pimcore\File::putPhpFile($definitionFile$data);
  460.         }
  461.     }
  462.     /**
  463.      * @return string
  464.      *
  465.      * @internal
  466.      */
  467.     protected function getInfoDocBlock(): string
  468.     {
  469.         $cd '/**' "\n";
  470.         $cd .= ' * Inheritance: '.($this->getAllowInherit() ? 'yes' 'no')."\n";
  471.         $cd .= ' * Variants: '.($this->getAllowVariants() ? 'yes' 'no')."\n";
  472.         if ($description $this->getDescription()) {
  473.             $description str_replace(['/**''*/''//'], ''$description);
  474.             $description str_replace("\n""\n * "$description);
  475.             $cd .= ' * '.$description."\n";
  476.         }
  477.         $cd .= " *\n";
  478.         $cd .= " * Fields Summary:\n";
  479.         $fieldDefinitionDocBlockBuilder \Pimcore::getContainer()->get(FieldDefinitionDocBlockBuilderInterface::class);
  480.         foreach ($this->getFieldDefinitions() as $fieldDefinition) {
  481.             $cd .= ' * ' str_replace("\n""\n * "trim($fieldDefinitionDocBlockBuilder->buildFieldDefinitionDocBlock($fieldDefinition))) . "\n";
  482.         }
  483.         $cd .= ' */';
  484.         return $cd;
  485.     }
  486.     public function delete()
  487.     {
  488.         $this->dispatchEvent(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::PRE_DELETE);
  489.         // delete all objects using this class
  490.         $list = new Listing();
  491.         $list->setCondition('o_classId = ?'$this->getId());
  492.         $list->load();
  493.         foreach ($list->getObjects() as $o) {
  494.             $o->delete();
  495.         }
  496.         $this->deletePhpClasses();
  497.         // empty object cache
  498.         try {
  499.             Cache::clearTag('class_'.$this->getId());
  500.         } catch (\Exception $e) {
  501.         }
  502.         // empty output cache
  503.         try {
  504.             Cache::clearTag('output');
  505.         } catch (\Exception $e) {
  506.         }
  507.         $customLayouts = new ClassDefinition\CustomLayout\Listing();
  508.         $id $this->getId();
  509.         $customLayouts->setFilter(function (DataObject\ClassDefinition\CustomLayout $layout) use ($id) {
  510.             return $layout->getClassId() === $id;
  511.         });
  512.         $customLayouts $customLayouts->load();
  513.         foreach ($customLayouts as $customLayout) {
  514.             $customLayout->delete();
  515.         }
  516.         $brickListing = new DataObject\Objectbrick\Definition\Listing();
  517.         $brickListing $brickListing->load();
  518.         foreach ($brickListing as $brickDefinition) {
  519.             $modified false;
  520.             $classDefinitions $brickDefinition->getClassDefinitions();
  521.             if (is_array($classDefinitions)) {
  522.                 foreach ($classDefinitions as $key => $classDefinition) {
  523.                     if ($classDefinition['classname'] == $this->getId()) {
  524.                         unset($classDefinitions[$key]);
  525.                         $modified true;
  526.                     }
  527.                 }
  528.             }
  529.             if ($modified) {
  530.                 $brickDefinition->setClassDefinitions($classDefinitions);
  531.                 $brickDefinition->save();
  532.             }
  533.         }
  534.         $this->getDao()->delete();
  535.         $this->dispatchEvent(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::POST_DELETE);
  536.     }
  537.     private function deletePhpClasses()
  538.     {
  539.         // delete the class files
  540.         @unlink($this->getPhpClassFile());
  541.         @unlink($this->getPhpListingClassFile());
  542.         @rmdir(dirname($this->getPhpListingClassFile()));
  543.         @unlink($this->getDefinitionFile());
  544.     }
  545.     /**
  546.      * @internal
  547.      *
  548.      * with PIMCORE_CLASS_DEFINITION_WRITABLE set, it globally allow/disallow creation and change in classes
  549.      * when the ENV is not set, it allows modification and creation of new in classes in /var/classes but disables modification of classes in config/pimcore/classes
  550.      * more details in 05_Deployment_Tools.md
  551.      *
  552.      * @return bool
  553.      */
  554.     public function isWritable(): bool
  555.     {
  556.         return $_SERVER['PIMCORE_CLASS_DEFINITION_WRITABLE'] ?? !str_starts_with($this->getDefinitionFile(), PIMCORE_CUSTOM_CONFIGURATION_DIRECTORY);
  557.     }
  558.     /**
  559.      * @internal
  560.      *
  561.      * @param string|null $name
  562.      *
  563.      * @return string
  564.      */
  565.     public function getDefinitionFile($name null)
  566.     {
  567.         return $this->locateDefinitionFile($name ?? $this->getName(), 'definition_%s.php');
  568.     }
  569.     /**
  570.      * @internal
  571.      */
  572.     public function getPhpClassFile(): string
  573.     {
  574.         return $this->locateFile(ucfirst($this->getName()), 'DataObject/%s.php');
  575.     }
  576.     /**
  577.      * @internal
  578.      */
  579.     public function getPhpListingClassFile(): string
  580.     {
  581.         return $this->locateFile(ucfirst($this->getName()), 'DataObject/%s/Listing.php');
  582.     }
  583.     /**
  584.      * @return string|null
  585.      */
  586.     public function getId()
  587.     {
  588.         return $this->id;
  589.     }
  590.     /**
  591.      * @return string|null
  592.      */
  593.     public function getName()
  594.     {
  595.         return $this->name;
  596.     }
  597.     /**
  598.      * @return int|null
  599.      */
  600.     public function getCreationDate()
  601.     {
  602.         return $this->creationDate;
  603.     }
  604.     /**
  605.      * @return int|null
  606.      */
  607.     public function getModificationDate()
  608.     {
  609.         return $this->modificationDate;
  610.     }
  611.     /**
  612.      * @return int|null
  613.      */
  614.     public function getUserOwner()
  615.     {
  616.         return $this->userOwner;
  617.     }
  618.     /**
  619.      * @return int|null
  620.      */
  621.     public function getUserModification()
  622.     {
  623.         return $this->userModification;
  624.     }
  625.     /**
  626.      * @param string $id
  627.      *
  628.      * @return $this
  629.      */
  630.     public function setId($id)
  631.     {
  632.         $this->id $id;
  633.         return $this;
  634.     }
  635.     /**
  636.      * @param string $name
  637.      *
  638.      * @return $this
  639.      */
  640.     public function setName($name)
  641.     {
  642.         $this->name $name;
  643.         return $this;
  644.     }
  645.     /**
  646.      * @param int $creationDate
  647.      *
  648.      * @return $this
  649.      */
  650.     public function setCreationDate($creationDate)
  651.     {
  652.         $this->creationDate = (int)$creationDate;
  653.         return $this;
  654.     }
  655.     /**
  656.      * @param int $modificationDate
  657.      *
  658.      * @return $this
  659.      */
  660.     public function setModificationDate($modificationDate)
  661.     {
  662.         $this->modificationDate = (int)$modificationDate;
  663.         return $this;
  664.     }
  665.     /**
  666.      * @param int $userOwner
  667.      *
  668.      * @return $this
  669.      */
  670.     public function setUserOwner($userOwner)
  671.     {
  672.         $this->userOwner = (int)$userOwner;
  673.         return $this;
  674.     }
  675.     /**
  676.      * @param int $userModification
  677.      *
  678.      * @return $this
  679.      */
  680.     public function setUserModification($userModification)
  681.     {
  682.         $this->userModification = (int)$userModification;
  683.         return $this;
  684.     }
  685.     /**
  686.      * @param array $context
  687.      *
  688.      * @return DataObject\ClassDefinition\Data[]
  689.      */
  690.     public function getFieldDefinitions($context = [])
  691.     {
  692.         if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  693.             return $this->fieldDefinitions;
  694.         }
  695.         $enrichedFieldDefinitions = [];
  696.         foreach ($this->fieldDefinitions as $key => $fieldDefinition) {
  697.             $fieldDefinition $this->doEnrichFieldDefinition($fieldDefinition$context);
  698.             $enrichedFieldDefinitions[$key] = $fieldDefinition;
  699.         }
  700.         return $enrichedFieldDefinitions;
  701.     }
  702.     /**
  703.      * @internal
  704.      */
  705.     protected function doEnrichFieldDefinition($fieldDefinition$context = [])
  706.     {
  707.         //TODO Pimcore 11: remove method_exists BC layer
  708.         if ($fieldDefinition instanceof FieldDefinitionEnrichmentInterface || method_exists($fieldDefinition'enrichFieldDefinition')) {
  709.             if (!$fieldDefinition instanceof FieldDefinitionEnrichmentInterface) {
  710.                 trigger_deprecation('pimcore/pimcore''10.1',
  711.                     sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  712.                     'Implement the %s interface instead.'FieldDefinitionEnrichmentInterface::class));
  713.             }
  714.             $context['class'] = $this;
  715.             $fieldDefinition $fieldDefinition->enrichFieldDefinition($context);
  716.         }
  717.         return $fieldDefinition;
  718.     }
  719.     /**
  720.      * @return DataObject\ClassDefinition\Layout|null
  721.      */
  722.     public function getLayoutDefinitions()
  723.     {
  724.         return $this->layoutDefinitions;
  725.     }
  726.     /**
  727.      * @param DataObject\ClassDefinition\Data[] $fieldDefinitions
  728.      *
  729.      * @return $this
  730.      */
  731.     public function setFieldDefinitions(array $fieldDefinitions)
  732.     {
  733.         $this->fieldDefinitions $fieldDefinitions;
  734.         return $this;
  735.     }
  736.     /**
  737.      * @param string $key
  738.      * @param DataObject\ClassDefinition\Data $data
  739.      *
  740.      * @return $this
  741.      */
  742.     public function addFieldDefinition($key$data)
  743.     {
  744.         $this->fieldDefinitions[$key] = $data;
  745.         return $this;
  746.     }
  747.     /**
  748.      * @param string $key
  749.      * @param array $context
  750.      *
  751.      * @return DataObject\ClassDefinition\Data|null
  752.      */
  753.     public function getFieldDefinition($key$context = [])
  754.     {
  755.         if (array_key_exists($key$this->fieldDefinitions)) {
  756.             if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  757.                 return $this->fieldDefinitions[$key];
  758.             }
  759.             $fieldDefinition $this->doEnrichFieldDefinition($this->fieldDefinitions[$key], $context);
  760.             return $fieldDefinition;
  761.         }
  762.         return null;
  763.     }
  764.     /**
  765.      * @param DataObject\ClassDefinition\Layout|null $layoutDefinitions
  766.      *
  767.      * @return $this
  768.      */
  769.     public function setLayoutDefinitions($layoutDefinitions)
  770.     {
  771.         $oldFieldDefinitions null;
  772.         if ($this->layoutDefinitions !== null) {
  773.             $this->setDeletedDataComponents([]);
  774.             $oldFieldDefinitions $this->getFieldDefinitions();
  775.         }
  776.         $this->layoutDefinitions $layoutDefinitions;
  777.         $this->fieldDefinitions = [];
  778.         $this->extractDataDefinitions($this->layoutDefinitions);
  779.         if ($oldFieldDefinitions !== null) {
  780.             $newFieldDefinitions $this->getFieldDefinitions();
  781.             $deletedComponents = [];
  782.             foreach ($oldFieldDefinitions as $fieldDefinition) {
  783.                 if (!array_key_exists($fieldDefinition->getName(), $newFieldDefinitions)) {
  784.                     array_push($deletedComponents$fieldDefinition);
  785.                 }
  786.             }
  787.             $this->setDeletedDataComponents($deletedComponents);
  788.         }
  789.         return $this;
  790.     }
  791.     /**
  792.      * @param DataObject\ClassDefinition\Layout|DataObject\ClassDefinition\Data $def
  793.      */
  794.     private function extractDataDefinitions($def)
  795.     {
  796.         if ($def instanceof DataObject\ClassDefinition\Layout) {
  797.             if ($def->hasChildren()) {
  798.                 foreach ($def->getChildren() as $child) {
  799.                     $this->extractDataDefinitions($child);
  800.                 }
  801.             }
  802.         }
  803.         if ($def instanceof DataObject\ClassDefinition\Data) {
  804.             $existing $this->getFieldDefinition($def->getName());
  805.             if (!$existing && method_exists($def'addReferencedField') && method_exists($def'setReferencedFields')) {
  806.                 $def->setReferencedFields([]);
  807.             }
  808.             if ($existing && method_exists($existing'addReferencedField')) {
  809.                 // this is especially for localized fields which get aggregated here into one field definition
  810.                 // in the case that there are more than one localized fields in the class definition
  811.                 // see also pimcore.object.edit.addToDataFields();
  812.                 $existing->addReferencedField($def);
  813.             } else {
  814.                 $this->addFieldDefinition($def->getName(), $def);
  815.             }
  816.         }
  817.     }
  818.     /**
  819.      * @return string
  820.      */
  821.     public function getParentClass()
  822.     {
  823.         return $this->parentClass;
  824.     }
  825.     /**
  826.      * @return string
  827.      */
  828.     public function getListingParentClass()
  829.     {
  830.         return $this->listingParentClass;
  831.     }
  832.     /**
  833.      * @return string
  834.      */
  835.     public function getUseTraits()
  836.     {
  837.         return $this->useTraits;
  838.     }
  839.     /**
  840.      * @param string $useTraits
  841.      *
  842.      * @return ClassDefinition
  843.      */
  844.     public function setUseTraits($useTraits)
  845.     {
  846.         $this->useTraits = (string) $useTraits;
  847.         return $this;
  848.     }
  849.     /**
  850.      * @return string
  851.      */
  852.     public function getListingUseTraits()
  853.     {
  854.         return $this->listingUseTraits;
  855.     }
  856.     /**
  857.      * @param string $listingUseTraits
  858.      *
  859.      * @return ClassDefinition
  860.      */
  861.     public function setListingUseTraits($listingUseTraits)
  862.     {
  863.         $this->listingUseTraits = (string) $listingUseTraits;
  864.         return $this;
  865.     }
  866.     /**
  867.      * @return bool
  868.      */
  869.     public function getAllowInherit()
  870.     {
  871.         return $this->allowInherit;
  872.     }
  873.     /**
  874.      * @return bool
  875.      */
  876.     public function getAllowVariants()
  877.     {
  878.         return $this->allowVariants;
  879.     }
  880.     /**
  881.      * @param string $parentClass
  882.      *
  883.      * @return $this
  884.      */
  885.     public function setParentClass($parentClass)
  886.     {
  887.         $this->parentClass $parentClass;
  888.         return $this;
  889.     }
  890.     /**
  891.      * @param string $listingParentClass
  892.      *
  893.      * @return $this
  894.      */
  895.     public function setListingParentClass($listingParentClass)
  896.     {
  897.         $this->listingParentClass = (string) $listingParentClass;
  898.         return $this;
  899.     }
  900.     /**
  901.      * @return bool
  902.      */
  903.     public function getEncryption(): bool
  904.     {
  905.         return $this->encryption;
  906.     }
  907.     /**
  908.      * @param bool $encryption
  909.      *
  910.      * @return $this
  911.      */
  912.     public function setEncryption(bool $encryption)
  913.     {
  914.         $this->encryption $encryption;
  915.         return $this;
  916.     }
  917.     /**
  918.      * @internal
  919.      *
  920.      * @param array $tables
  921.      */
  922.     public function addEncryptedTables(array $tables)
  923.     {
  924.         $this->encryptedTables array_unique(array_merge($this->encryptedTables$tables));
  925.     }
  926.     /**
  927.      * @internal
  928.      *
  929.      * @param array $tables
  930.      */
  931.     public function removeEncryptedTables(array $tables)
  932.     {
  933.         foreach ($tables as $table) {
  934.             if (($key array_search($table$this->encryptedTables)) !== false) {
  935.                 unset($this->encryptedTables[$key]);
  936.             }
  937.         }
  938.     }
  939.     /**
  940.      * @internal
  941.      *
  942.      * @param string $table
  943.      *
  944.      * @return bool
  945.      */
  946.     public function isEncryptedTable(string $table): bool
  947.     {
  948.         return (array_search($table$this->encryptedTables) === false) ? false true;
  949.     }
  950.     /**
  951.      * @return bool
  952.      */
  953.     public function hasEncryptedTables(): bool
  954.     {
  955.         return (bool) count($this->encryptedTables);
  956.     }
  957.     /**
  958.      * @internal
  959.      *
  960.      * @param array $encryptedTables
  961.      *
  962.      * @return $this
  963.      */
  964.     public function setEncryptedTables(array $encryptedTables)
  965.     {
  966.         $this->encryptedTables $encryptedTables;
  967.         return $this;
  968.     }
  969.     /**
  970.      * @param bool $allowInherit
  971.      *
  972.      * @return $this
  973.      */
  974.     public function setAllowInherit($allowInherit)
  975.     {
  976.         $this->allowInherit = (bool)$allowInherit;
  977.         return $this;
  978.     }
  979.     /**
  980.      * @param bool $allowVariants
  981.      *
  982.      * @return $this
  983.      */
  984.     public function setAllowVariants($allowVariants)
  985.     {
  986.         $this->allowVariants = (bool)$allowVariants;
  987.         return $this;
  988.     }
  989.     /**
  990.      * @return string|null
  991.      */
  992.     public function getIcon()
  993.     {
  994.         return $this->icon;
  995.     }
  996.     /**
  997.      * @param string|null $icon
  998.      *
  999.      * @return $this
  1000.      */
  1001.     public function setIcon($icon)
  1002.     {
  1003.         $this->icon $icon;
  1004.         return $this;
  1005.     }
  1006.     /**
  1007.      * @return array
  1008.      */
  1009.     public function getPropertyVisibility()
  1010.     {
  1011.         return $this->propertyVisibility;
  1012.     }
  1013.     /**
  1014.      * @param array $propertyVisibility
  1015.      *
  1016.      * @return $this
  1017.      */
  1018.     public function setPropertyVisibility($propertyVisibility)
  1019.     {
  1020.         if (is_array($propertyVisibility)) {
  1021.             $this->propertyVisibility $propertyVisibility;
  1022.         }
  1023.         return $this;
  1024.     }
  1025.     /**
  1026.      * @param string|null $previewUrl
  1027.      *
  1028.      * @return $this
  1029.      */
  1030.     public function setPreviewUrl($previewUrl)
  1031.     {
  1032.         $this->previewUrl $previewUrl;
  1033.         return $this;
  1034.     }
  1035.     /**
  1036.      * @return string|null
  1037.      */
  1038.     public function getPreviewUrl()
  1039.     {
  1040.         return $this->previewUrl;
  1041.     }
  1042.     /**
  1043.      * @return string|null
  1044.      */
  1045.     public function getGroup()
  1046.     {
  1047.         return $this->group;
  1048.     }
  1049.     /**
  1050.      * @param string|null $group
  1051.      *
  1052.      * @return $this
  1053.      */
  1054.     public function setGroup($group)
  1055.     {
  1056.         $this->group $group;
  1057.         return $this;
  1058.     }
  1059.     /**
  1060.      * @param string $description
  1061.      *
  1062.      * @return $this
  1063.      */
  1064.     public function setDescription($description)
  1065.     {
  1066.         $this->description $description;
  1067.         return $this;
  1068.     }
  1069.     /**
  1070.      * @return string
  1071.      */
  1072.     public function getDescription()
  1073.     {
  1074.         return $this->description;
  1075.     }
  1076.     /**
  1077.      * @param bool $showVariants
  1078.      *
  1079.      * @return $this
  1080.      */
  1081.     public function setShowVariants($showVariants)
  1082.     {
  1083.         $this->showVariants = (bool)$showVariants;
  1084.         return $this;
  1085.     }
  1086.     /**
  1087.      * @return bool
  1088.      */
  1089.     public function getShowVariants()
  1090.     {
  1091.         return $this->showVariants;
  1092.     }
  1093.     /**
  1094.      * @return bool
  1095.      */
  1096.     public function getShowAppLoggerTab()
  1097.     {
  1098.         return $this->showAppLoggerTab;
  1099.     }
  1100.     /**
  1101.      * @param bool $showAppLoggerTab
  1102.      *
  1103.      * @return $this
  1104.      */
  1105.     public function setShowAppLoggerTab($showAppLoggerTab)
  1106.     {
  1107.         $this->showAppLoggerTab = (bool) $showAppLoggerTab;
  1108.         return $this;
  1109.     }
  1110.     /**
  1111.      * @return bool
  1112.      */
  1113.     public function getShowFieldLookup()
  1114.     {
  1115.         return $this->showFieldLookup;
  1116.     }
  1117.     /**
  1118.      * @param bool $showFieldLookup
  1119.      *
  1120.      * @return $this
  1121.      */
  1122.     public function setShowFieldLookup($showFieldLookup)
  1123.     {
  1124.         $this->showFieldLookup = (bool) $showFieldLookup;
  1125.         return $this;
  1126.     }
  1127.     /**
  1128.      * @return string
  1129.      */
  1130.     public function getLinkGeneratorReference()
  1131.     {
  1132.         return $this->linkGeneratorReference;
  1133.     }
  1134.     /**
  1135.      * @param string $linkGeneratorReference
  1136.      *
  1137.      * @return $this
  1138.      */
  1139.     public function setLinkGeneratorReference($linkGeneratorReference)
  1140.     {
  1141.         $this->linkGeneratorReference $linkGeneratorReference;
  1142.         return $this;
  1143.     }
  1144.     /**
  1145.      * @return DataObject\ClassDefinition\LinkGeneratorInterface|null
  1146.      */
  1147.     public function getLinkGenerator()
  1148.     {
  1149.         return DataObject\ClassDefinition\Helper\LinkGeneratorResolver::resolveGenerator($this->getLinkGeneratorReference());
  1150.     }
  1151.     /**
  1152.      * @return string|null
  1153.      */
  1154.     public function getPreviewGeneratorReference(): ?string
  1155.     {
  1156.         return $this->previewGeneratorReference;
  1157.     }
  1158.     /**
  1159.      * @param string|null $previewGeneratorReference
  1160.      */
  1161.     public function setPreviewGeneratorReference(?string $previewGeneratorReference): void
  1162.     {
  1163.         $this->previewGeneratorReference $previewGeneratorReference;
  1164.     }
  1165.     /**
  1166.      * @return DataObject\ClassDefinition\PreviewGeneratorInterface|null
  1167.      */
  1168.     public function getPreviewGenerator()
  1169.     {
  1170.         return DataObject\ClassDefinition\Helper\PreviewGeneratorResolver::resolveGenerator($this->getPreviewGeneratorReference());
  1171.     }
  1172.     /**
  1173.      * @return bool
  1174.      */
  1175.     public function isEnableGridLocking(): bool
  1176.     {
  1177.         return $this->enableGridLocking;
  1178.     }
  1179.     /**
  1180.      * @param bool $enableGridLocking
  1181.      */
  1182.     public function setEnableGridLocking(bool $enableGridLocking): void
  1183.     {
  1184.         $this->enableGridLocking $enableGridLocking;
  1185.     }
  1186.     /**
  1187.      * @return string|null
  1188.      */
  1189.     public function getImplementsInterfaces(): ?string
  1190.     {
  1191.         return $this->implementsInterfaces;
  1192.     }
  1193.     /**
  1194.      * @param string|null $implementsInterfaces
  1195.      *
  1196.      * @return $this
  1197.      */
  1198.     public function setImplementsInterfaces(?string $implementsInterfaces)
  1199.     {
  1200.         $this->implementsInterfaces $implementsInterfaces;
  1201.         return $this;
  1202.     }
  1203.     /**
  1204.      * @return array
  1205.      */
  1206.     public function getCompositeIndices(): array
  1207.     {
  1208.         return $this->compositeIndices;
  1209.     }
  1210.     /**
  1211.      * @param array|null $compositeIndices
  1212.      *
  1213.      * @return $this
  1214.      */
  1215.     public function setCompositeIndices($compositeIndices)
  1216.     {
  1217.         $this->compositeIndices $compositeIndices ?? [];
  1218.         return $this;
  1219.     }
  1220.     /**
  1221.      * @return bool
  1222.      */
  1223.     public function getGenerateTypeDeclarations()
  1224.     {
  1225.         return (bool) $this->generateTypeDeclarations;
  1226.     }
  1227.     /**
  1228.      * @param bool $generateTypeDeclarations
  1229.      *
  1230.      * @return $this
  1231.      */
  1232.     public function setGenerateTypeDeclarations($generateTypeDeclarations)
  1233.     {
  1234.         $this->generateTypeDeclarations = (bool) $generateTypeDeclarations;
  1235.         return $this;
  1236.     }
  1237.     /**
  1238.      * @return ClassDefinition\Data[]
  1239.      */
  1240.     public function getDeletedDataComponents()
  1241.     {
  1242.         return $this->deletedDataComponents;
  1243.     }
  1244.     /**
  1245.      * @param ClassDefinition\Data[] $deletedDataComponents
  1246.      *
  1247.      * @return $this
  1248.      */
  1249.     public function setDeletedDataComponents(array $deletedDataComponents): ClassDefinition
  1250.     {
  1251.         $this->deletedDataComponents $deletedDataComponents;
  1252.         return $this;
  1253.     }
  1254.     private function deleteDeletedDataComponentsInCustomLayout(): void
  1255.     {
  1256.         if (empty($this->getDeletedDataComponents())) {
  1257.             return;
  1258.         }
  1259.         $customLayouts = new ClassDefinition\CustomLayout\Listing();
  1260.         $id $this->getId();
  1261.         $customLayouts->setFilter(function (DataObject\ClassDefinition\CustomLayout $layout) use ($id) {
  1262.             return $layout->getClassId() === $id;
  1263.         });
  1264.         $customLayouts $customLayouts->load();
  1265.         foreach ($customLayouts as $customLayout) {
  1266.             $layoutDefinition $customLayout->getLayoutDefinitions();
  1267.             $this->deleteDeletedDataComponentsInLayoutDefinition($layoutDefinition);
  1268.             $customLayout->setLayoutDefinitions($layoutDefinition);
  1269.             $customLayout->save();
  1270.         }
  1271.     }
  1272.     private function deleteDeletedDataComponentsInLayoutDefinition(ClassDefinition\Layout $layoutDefinition): void
  1273.     {
  1274.         $componentsToDelete $this->getDeletedDataComponents();
  1275.         $componentDeleted false;
  1276.         $children = &$layoutDefinition->getChildrenByRef();
  1277.         $count count($children);
  1278.         for ($i 0$i $count$i++) {
  1279.             $component $children[$i];
  1280.             if (in_array($component$componentsToDelete)) {
  1281.                 unset($children[$i]);
  1282.                 $componentDeleted true;
  1283.             }
  1284.             if ($component instanceof ClassDefinition\Layout) {
  1285.                 $this->deleteDeletedDataComponentsInLayoutDefinition($component);
  1286.             }
  1287.         }
  1288.         if ($componentDeleted) {
  1289.             $children array_values($children);
  1290.         }
  1291.     }
  1292.     public static function getByIdIgnoreCase(string $id): ClassDefinition|null
  1293.     {
  1294.         try {
  1295.             $class = new self();
  1296.             $name $class->getDao()->getNameByIdIgnoreCase($id);
  1297.             $definitionFile $class->getDefinitionFile($name);
  1298.             $class = @include $definitionFile;
  1299.             if (!$class instanceof self) {
  1300.                 throw new \Exception('Class definition with name ' $name ' or ID ' $id ' does not exist');
  1301.             }
  1302.             $class->setId($id);
  1303.         } catch (\Exception $e) {
  1304.             Logger::info($e->getMessage());
  1305.             return null;
  1306.         }
  1307.         return $class;
  1308.     }
  1309. }