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

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