vendor/pimcore/pimcore/models/DataObject/ClassDefinition/Data/Fieldcollections.php line 356

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\ClassDefinition\Data;
  15. use Pimcore\Logger;
  16. use Pimcore\Model;
  17. use Pimcore\Model\DataObject;
  18. use Pimcore\Model\DataObject\ClassDefinition\Data;
  19. use Pimcore\Normalizer\NormalizerInterface;
  20. class Fieldcollections extends Data implements CustomResourcePersistingInterfaceLazyLoadingSupportInterfaceTypeDeclarationSupportInterfaceNormalizerInterfaceDataContainerAwareInterfaceIdRewriterInterfacePreGetDataInterfacePreSetDataInterface
  21. {
  22.     use DataObject\Traits\ClassSavedTrait;
  23.     /**
  24.      * Static type of this element
  25.      *
  26.      * @internal
  27.      *
  28.      * @var string
  29.      */
  30.     public $fieldtype 'fieldcollections';
  31.     /**
  32.      * @internal
  33.      *
  34.      * @var array
  35.      */
  36.     public $allowedTypes = [];
  37.     /**
  38.      * @internal
  39.      *
  40.      * @var bool
  41.      */
  42.     public $lazyLoading;
  43.     /**
  44.      * @internal
  45.      *
  46.      * @var int|null
  47.      */
  48.     public $maxItems;
  49.     /**
  50.      * @internal
  51.      *
  52.      * @var bool
  53.      */
  54.     public $disallowAddRemove false;
  55.     /**
  56.      * @internal
  57.      *
  58.      * @var bool
  59.      */
  60.     public $disallowReorder false;
  61.     /**
  62.      * @internal
  63.      *
  64.      * @var bool
  65.      */
  66.     public $collapsed;
  67.     /**
  68.      * @internal
  69.      *
  70.      * @var bool
  71.      */
  72.     public $collapsible;
  73.     /**
  74.      * @internal
  75.      *
  76.      * @var bool
  77.      */
  78.     public $border false;
  79.     /**
  80.      * @return bool
  81.      */
  82.     public function getLazyLoading()
  83.     {
  84.         return $this->lazyLoading;
  85.     }
  86.     /**
  87.      * @param bool $lazyLoading
  88.      *
  89.      * @return $this
  90.      */
  91.     public function setLazyLoading($lazyLoading)
  92.     {
  93.         $this->lazyLoading = (bool) $lazyLoading;
  94.         return $this;
  95.     }
  96.     /**
  97.      * @see Data::getDataForEditmode
  98.      *
  99.      * @param DataObject\Fieldcollection|null $data
  100.      * @param null|DataObject\Concrete $object
  101.      * @param mixed $params
  102.      *
  103.      * @return array
  104.      */
  105.     public function getDataForEditmode($data$object null$params = [])
  106.     {
  107.         $editmodeData = [];
  108.         $idx = -1;
  109.         if ($data instanceof DataObject\Fieldcollection) {
  110.             foreach ($data as $item) {
  111.                 $idx++;
  112.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  113.                     $collectionData = [];
  114.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  115.                         if (!$fd instanceof CalculatedValue) {
  116.                             $value $item->{'get' $fd->getName()}();
  117.                             if (isset($params['context']['containerKey']) === false) {
  118.                                 $params['context']['containerKey'] = $idx;
  119.                             }
  120.                             $collectionData[$fd->getName()] = $fd->getDataForEditmode($value$object$params);
  121.                         }
  122.                     }
  123.                     $calculatedChilds = [];
  124.                     self::collectCalculatedValueItems($collectionDef->getFieldDefinitions(), $calculatedChilds);
  125.                     if ($calculatedChilds) {
  126.                         foreach ($calculatedChilds as $fd) {
  127.                             $data = new DataObject\Data\CalculatedValue($fd->getName());
  128.                             $data->setContextualData('fieldcollection'$this->getName(), $idxnullnullnull$fd);
  129.                             $data $fd->getDataForEditmode($data$object$params);
  130.                             $collectionData[$fd->getName()] = $data;
  131.                         }
  132.                     }
  133.                     $editmodeData[] = [
  134.                         'data' => $collectionData,
  135.                         'type' => $item->getType(),
  136.                         'oIndex' => $idx,
  137.                         'title' => $collectionDef->getTitle(),
  138.                     ];
  139.                 }
  140.             }
  141.         }
  142.         return $editmodeData;
  143.     }
  144.     /**
  145.      * @see Data::getDataFromEditmode
  146.      *
  147.      * @param array|null $data
  148.      * @param null|DataObject\Concrete $object
  149.      * @param mixed $params
  150.      *
  151.      * @return DataObject\Fieldcollection
  152.      */
  153.     public function getDataFromEditmode($data$object null$params = [])
  154.     {
  155.         $values = [];
  156.         $count 0;
  157.         if (is_array($data)) {
  158.             foreach ($data as $collectionRaw) {
  159.                 $collectionData = [];
  160.                 $collectionKey $collectionRaw['type'];
  161.                 $oIndex $collectionRaw['oIndex'] ?? null;
  162.                 $collectionDef DataObject\Fieldcollection\Definition::getByKey($collectionKey);
  163.                 $fieldname $this->getName();
  164.                 foreach ($collectionDef->getFieldDefinitions() as $fd) {
  165.                     $invisible $fd->getInvisible();
  166.                     if ($invisible && !is_null($oIndex)) {
  167.                         $containerGetter 'get' ucfirst($fieldname);
  168.                         $container $object->$containerGetter();
  169.                         if ($container) {
  170.                             $items $container->getItems();
  171.                             $invisibleData null;
  172.                             if ($items && count($items) > $oIndex) {
  173.                                 $item $items[$oIndex];
  174.                                 $getter 'get' ucfirst($fd->getName());
  175.                                 $invisibleData $item->$getter();
  176.                             }
  177.                             $collectionData[$fd->getName()] = $invisibleData;
  178.                         }
  179.                     } elseif (array_key_exists($fd->getName(), $collectionRaw['data'])) {
  180.                         $collectionParams = [
  181.                             'context' => [
  182.                                 'containerType' => 'fieldcollection',
  183.                                 'containerKey' => $collectionKey,
  184.                                 'fieldname' => $fieldname,
  185.                                 'index' => $count,
  186.                                 'oIndex' => $oIndex,
  187.                             ],
  188.                         ];
  189.                         $collectionData[$fd->getName()] = $fd->getDataFromEditmode(
  190.                             $collectionRaw['data'][$fd->getName()],
  191.                             $object,
  192.                             $collectionParams
  193.                         );
  194.                     }
  195.                 }
  196.                 $collectionClass '\\Pimcore\\Model\\DataObject\\Fieldcollection\\Data\\' ucfirst($collectionRaw['type']);
  197.                 /** @var DataObject\Fieldcollection\Data\AbstractData $collection */
  198.                 $collection \Pimcore::getContainer()->get('pimcore.model.factory')->build($collectionClass);
  199.                 $collection->setObject($object);
  200.                 $collection->setIndex($count);
  201.                 $collection->setFieldname($this->getName());
  202.                 $collection->setValues($collectionData);
  203.                 $values[] = $collection;
  204.                 $count++;
  205.             }
  206.         }
  207.         $container = new DataObject\Fieldcollection($values$this->getName());
  208.         return $container;
  209.     }
  210.     /**
  211.      * @see Data::getVersionPreview
  212.      *
  213.      * @param string $data
  214.      * @param DataObject\Concrete|null $object
  215.      * @param mixed $params
  216.      *
  217.      * @return string
  218.      */
  219.     public function getVersionPreview($data$object null$params = [])
  220.     {
  221.         return 'FIELDCOLLECTIONS';
  222.     }
  223.     /**
  224.      * {@inheritdoc}
  225.      */
  226.     public function getForCsvExport($object$params = [])
  227.     {
  228.         return 'NOT SUPPORTED';
  229.     }
  230.     /**
  231.      * {@inheritdoc}
  232.      */
  233.     public function getDataForSearchIndex($object$params = [])
  234.     {
  235.         $dataString '';
  236.         $fcData $this->getDataFromObjectParam($object);
  237.         if ($fcData instanceof DataObject\Fieldcollection) {
  238.             foreach ($fcData as $item) {
  239.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  240.                     continue;
  241.                 }
  242.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  243.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  244.                         $dataString .= $fd->getDataForSearchIndex($item$params) . ' ';
  245.                     }
  246.                 }
  247.             }
  248.         }
  249.         return $dataString;
  250.     }
  251.     /**
  252.      * {@inheritdoc}
  253.      */
  254.     public function save($object$params = [])
  255.     {
  256.         $container $this->getDataFromObjectParam($object);
  257.         if (is_null($container)) {
  258.             $container = new DataObject\Fieldcollection();
  259.             $container->setFieldname($this->getName());
  260.         }
  261.         if ($container instanceof DataObject\Fieldcollection) {
  262.             $params = [
  263.                 'context' => [
  264.                     'containerType' => 'fieldcollection',
  265.                     'fieldname' => $this->getName(),
  266.                 ],
  267.             ];
  268.             $container->save($object$params);
  269.         } else {
  270.             throw new \Exception('Invalid value for field "' $this->getName()."\" provided. You have to pass a DataObject\\Fieldcollection or 'null'");
  271.         }
  272.     }
  273.     /**
  274.      * {@inheritdoc}
  275.      */
  276.     public function load($object$params = [])
  277.     {
  278.         $container = new DataObject\Fieldcollection([], $this->getName());
  279.         $container->load($object);
  280.         if ($container->isEmpty()) {
  281.             return null;
  282.         }
  283.         return $container;
  284.     }
  285.     /**
  286.      * {@inheritdoc}
  287.      */
  288.     public function delete($object$params = [])
  289.     {
  290.         $container = new DataObject\Fieldcollection([], $this->getName());
  291.         $container->delete($object);
  292.     }
  293.     /**
  294.      * @return array
  295.      */
  296.     public function getAllowedTypes()
  297.     {
  298.         return $this->allowedTypes;
  299.     }
  300.     /**
  301.      * @param string|array|null $allowedTypes
  302.      *
  303.      * @return $this
  304.      */
  305.     public function setAllowedTypes($allowedTypes)
  306.     {
  307.         if (is_string($allowedTypes)) {
  308.             $allowedTypes explode(','$allowedTypes);
  309.         }
  310.         if (is_array($allowedTypes)) {
  311.             for ($i 0$i count($allowedTypes); $i++) {
  312.                 if (!DataObject\Fieldcollection\Definition::getByKey($allowedTypes[$i])) {
  313.                     Logger::warn("Removed unknown allowed type [ $allowedTypes[$i] ] from allowed types of field collection");
  314.                     unset($allowedTypes[$i]);
  315.                 }
  316.             }
  317.         }
  318.         $this->allowedTypes = (array)$allowedTypes;
  319.         $this->allowedTypes array_values($this->allowedTypes); // get rid of indexed array (.join() doesnt work in JS)
  320.         return $this;
  321.     }
  322.     /**
  323.      * @param DataObject\Fieldcollection|null $data
  324.      *
  325.      * @return array
  326.      */
  327.     public function resolveDependencies($data)
  328.     {
  329.         $dependencies = [];
  330.         if ($data instanceof DataObject\Fieldcollection) {
  331.             foreach ($data as $item) {
  332.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  333.                     continue;
  334.                 }
  335.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  336.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  337.                         $getter 'get' ucfirst($fd->getName());
  338.                         $dependencies array_merge($dependencies$fd->resolveDependencies($item->$getter()));
  339.                     }
  340.                 }
  341.             }
  342.         }
  343.         return $dependencies;
  344.     }
  345.     /**
  346.      * {@inheritdoc}
  347.      */
  348.     public function getCacheTags($data, array $tags = [])
  349.     {
  350.         if ($data instanceof DataObject\Fieldcollection) {
  351.             foreach ($data as $item) {
  352.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  353.                     continue;
  354.                 }
  355.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  356.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  357.                         $getter 'get' ucfirst($fd->getName());
  358.                         $tags $fd->getCacheTags($item->$getter(), $tags);
  359.                     }
  360.                 }
  361.             }
  362.         }
  363.         return $tags;
  364.     }
  365.     /**
  366.      * {@inheritdoc}
  367.      */
  368.     public function checkValidity($data$omitMandatoryCheck false$params = [])
  369.     {
  370.         if ($data instanceof DataObject\Fieldcollection) {
  371.             $validationExceptions = [];
  372.             $idx = -1;
  373.             foreach ($data as $item) {
  374.                 $idx++;
  375.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  376.                     continue;
  377.                 }
  378.                 //max limit check should be performed irrespective of omitMandatory check
  379.                 if (!empty($this->maxItems) && $idx $this->maxItems) {
  380.                     throw new Model\Element\ValidationException('Maximum limit reached for items in field collection: ' $this->getName());
  381.                 }
  382.                 if (!$omitMandatoryCheck) {
  383.                     if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  384.                         foreach ($collectionDef->getFieldDefinitions() as $fd) {
  385.                             try {
  386.                                 $getter 'get' ucfirst($fd->getName());
  387.                                 if (!$fd instanceof CalculatedValue) {
  388.                                     $fd->checkValidity($item->$getter(), false$params);
  389.                                 }
  390.                             } catch (Model\Element\ValidationException $ve) {
  391.                                 $ve->addContext($this->getName() . '-' $idx);
  392.                                 $validationExceptions[] = $ve;
  393.                             }
  394.                         }
  395.                     }
  396.                 }
  397.             }
  398.             if ($validationExceptions) {
  399.                 $errors = [];
  400.                 /** @var Model\Element\ValidationException $e */
  401.                 foreach ($validationExceptions as $e) {
  402.                     $errors[] = $e->getAggregatedMessage();
  403.                 }
  404.                 $message implode(' / '$errors);
  405.                 throw new Model\Element\ValidationException($message);
  406.             }
  407.         }
  408.     }
  409.     /**
  410.      * { @inheritdoc }
  411.      */
  412.     public function preGetData(/** mixed */ $container/** array */ $params = []) // : mixed
  413.     {
  414.         if (!$container instanceof DataObject\Concrete) {
  415.             throw new \Exception('Field Collections are only valid in Objects');
  416.         }
  417.         $data $container->getObjectVar($this->getName());
  418.         if ($this->getLazyLoading() && !$container->isLazyKeyLoaded($this->getName())) {
  419.             $data $this->load($container);
  420.             if ($data instanceof Model\Element\DirtyIndicatorInterface) {
  421.                 $data->resetDirtyMap();
  422.             }
  423.             $setter 'set' ucfirst($this->getName());
  424.             if (method_exists($container$setter)) {
  425.                 $container->$setter($data);
  426.                 $this->markLazyloadedFieldAsLoaded($container);
  427.             }
  428.         }
  429.         return $data;
  430.     }
  431.     /**
  432.      * { @inheritdoc }
  433.      */
  434.     public function preSetData(/** mixed */ $container/**  mixed */ $data/** array */ $params = []) // : mixed
  435.     {
  436.         $this->markLazyloadedFieldAsLoaded($container);
  437.         if ($data instanceof DataObject\Fieldcollection) {
  438.             $data->setFieldname($this->getName());
  439.         }
  440.         return $data;
  441.     }
  442.     /**
  443.      * @param DataObject\Fieldcollection|null $data
  444.      * @param DataObject\Concrete $object
  445.      * @param mixed $params
  446.      *
  447.      * @return string
  448.      */
  449.     public function getDataForGrid($data$object null$params = [])
  450.     {
  451.         return 'NOT SUPPORTED';
  452.     }
  453.     /**
  454.      * {@inheritdoc}
  455.      */
  456.     public function getGetterCode($class)
  457.     {
  458.         // getter, no inheritance here, that's the only difference
  459.         $key $this->getName();
  460.         if ($this instanceof DataObject\ClassDefinition\Data\TypeDeclarationSupportInterface && $this->getReturnTypeDeclaration()) {
  461.             $typeDeclaration ': ' $this->getReturnTypeDeclaration();
  462.         } else {
  463.             $typeDeclaration '';
  464.         }
  465.         $code '/**' "\n";
  466.         $code .= '* @return ' $this->getPhpdocReturnType() . "\n";
  467.         $code .= '*/' "\n";
  468.         $code .= 'public function get' ucfirst($key) . '()' $typeDeclaration "\n";
  469.         $code .= '{' "\n";
  470.         $code .= $this->getPreGetValueHookCode($key);
  471.         // TODO Pimcore 11: remove method_exists BC layer
  472.         // TODO else part should not be needed at all as preGetData is always there
  473.         // if ($this instanceof PreGetDataInterface || method_exists($this, 'preGetData')) {
  474.         $code .= "\t" '$data = $this->getClass()->getFieldDefinition("' $key '")->preGetData($this);' "\n";
  475.         //        } else {
  476.         //            $code .= "\t" . '$data = $this->' . $key . ";\n";
  477.         //        }
  478.         $code .= "\t" 'return $data;' "\n";
  479.         $code .= "}\n\n";
  480.         return $code;
  481.     }
  482.     /**
  483.      * @param int|null $maxItems
  484.      *
  485.      * @return $this
  486.      */
  487.     public function setMaxItems($maxItems)
  488.     {
  489.         $this->maxItems $this->getAsIntegerCast($maxItems);
  490.         return $this;
  491.     }
  492.     /**
  493.      * @return int|null
  494.      */
  495.     public function getMaxItems()
  496.     {
  497.         return $this->maxItems;
  498.     }
  499.     /**
  500.      * {@inheritdoc}
  501.      */
  502.     public function isDiffChangeAllowed($object$params = [])
  503.     {
  504.         return true;
  505.     }
  506.     /** Generates a pretty version preview (similar to getVersionPreview) can be either HTML or
  507.      * a image URL. See the https://github.com/pimcore/object-merger bundle documentation for details
  508.      *
  509.      * @param DataObject\Fieldcollection|null $data
  510.      * @param DataObject\Concrete|null $object
  511.      * @param mixed $params
  512.      *
  513.      * @return array
  514.      */
  515.     public function getDiffVersionPreview($data$object null$params = [])
  516.     {
  517.         $html '';
  518.         if ($data instanceof DataObject\Fieldcollection) {
  519.             $html '<table>';
  520.             foreach ($data as $item) {
  521.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  522.                     continue;
  523.                 }
  524.                 $type $item->getType();
  525.                 $html .= '<tr><th><b>' $type '</b></th><th>&nbsp;</th><th>&nbsp;</th></tr>';
  526.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  527.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  528.                         $title = !empty($fd->title) ? $fd->title $fd->getName();
  529.                         $html .= '<tr><td>&nbsp;</td><td>' $title '</td><td>';
  530.                         $html .= $fd->getVersionPreview($item->getObjectVar($fd->getName()), $object$params);
  531.                         $html .= '</td></tr>';
  532.                     }
  533.                 }
  534.             }
  535.             $html .= '</table>';
  536.         }
  537.         $value = [];
  538.         $value['html'] = $html;
  539.         $value['type'] = 'html';
  540.         return $value;
  541.     }
  542.     /**
  543.      * { @inheritdoc }
  544.      */
  545.     public function rewriteIds(/** mixed */ $container/** array */ $idMapping/** array */ $params = []) /** :mixed */
  546.     {
  547.         $data $this->getDataFromObjectParam($container$params);
  548.         if ($data instanceof DataObject\Fieldcollection) {
  549.             foreach ($data as $item) {
  550.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  551.                     continue;
  552.                 }
  553.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  554.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  555.                         //TODO Pimcore 11: remove method_exists BC layer
  556.                         if ($fd instanceof IdRewriterInterface || method_exists($fd'rewriteIds')) {
  557.                             $d $fd->rewriteIds($item$idMapping$params);
  558.                             $setter 'set' ucfirst($fd->getName());
  559.                             $item->$setter($d);
  560.                         }
  561.                     }
  562.                 }
  563.             }
  564.         }
  565.         return $data;
  566.     }
  567.     /**
  568.      * @param DataObject\ClassDefinition\Data\Fieldcollections $masterDefinition
  569.      */
  570.     public function synchronizeWithMasterDefinition(DataObject\ClassDefinition\Data $masterDefinition)
  571.     {
  572.         $this->allowedTypes $masterDefinition->allowedTypes;
  573.         $this->lazyLoading $masterDefinition->lazyLoading;
  574.         $this->maxItems $masterDefinition->maxItems;
  575.     }
  576.     /**
  577.      * This method is called in DataObject\ClassDefinition::save() and is used to create the database table for the localized data
  578.      *
  579.      * @param DataObject\ClassDefinition $class
  580.      * @param array $params
  581.      */
  582.     public function classSaved($class$params = [])
  583.     {
  584.         if (is_array($this->allowedTypes)) {
  585.             foreach ($this->allowedTypes as $i => $allowedType) {
  586.                 if ($definition DataObject\Fieldcollection\Definition::getByKey($allowedType)) {
  587.                     $definition->getDao()->createUpdateTable($class);
  588.                     $fieldDefinition $definition->getFieldDefinitions();
  589.                     foreach ($fieldDefinition as $fd) {
  590.                         //TODO Pimcore 11 remove method_exists call
  591.                         if (!$fd instanceof DataContainerAwareInterface && method_exists($fd'classSaved')) {
  592.                             // defer creation
  593.                             $fd->classSaved($class);
  594.                         }
  595.                     }
  596.                     $definition->getDao()->classSaved($class);
  597.                 } else {
  598.                     Logger::warn("Removed unknown allowed type [ $allowedType ] from allowed types of field collection");
  599.                     unset($this->allowedTypes[$i]);
  600.                 }
  601.             }
  602.         }
  603.     }
  604.     /**
  605.      * @param bool $disallowAddRemove
  606.      */
  607.     public function setDisallowAddRemove($disallowAddRemove)
  608.     {
  609.         $this->disallowAddRemove $disallowAddRemove;
  610.     }
  611.     /**
  612.      * @return bool
  613.      */
  614.     public function getDisallowAddRemove()
  615.     {
  616.         return $this->disallowAddRemove;
  617.     }
  618.     /**
  619.      * @param bool $disallowReorder
  620.      */
  621.     public function setDisallowReorder($disallowReorder)
  622.     {
  623.         $this->disallowReorder $disallowReorder;
  624.     }
  625.     /**
  626.      * @return bool
  627.      */
  628.     public function getDisallowReorder()
  629.     {
  630.         return $this->disallowReorder;
  631.     }
  632.     /**
  633.      * @return bool
  634.      */
  635.     public function getBorder(): bool
  636.     {
  637.         return $this->border;
  638.     }
  639.     /**
  640.      * @param bool $border
  641.      */
  642.     public function setBorder(bool $border): void
  643.     {
  644.         $this->border $border;
  645.     }
  646.     /**
  647.      * @return bool
  648.      */
  649.     public function isCollapsed()
  650.     {
  651.         return $this->collapsed;
  652.     }
  653.     /**
  654.      * @param bool $collapsed
  655.      */
  656.     public function setCollapsed($collapsed)
  657.     {
  658.         $this->collapsed = (bool) $collapsed;
  659.     }
  660.     /**
  661.      * @return bool
  662.      */
  663.     public function isCollapsible()
  664.     {
  665.         return $this->collapsible;
  666.     }
  667.     /**
  668.      * @param bool $collapsible
  669.      */
  670.     public function setCollapsible($collapsible)
  671.     {
  672.         $this->collapsible = (bool) $collapsible;
  673.     }
  674.     /**
  675.      * @param DataObject\ClassDefinition\Data[] $container
  676.      * @param DataObject\ClassDefinition\Data[] $list
  677.      */
  678.     public static function collectCalculatedValueItems($container, &$list = [])
  679.     {
  680.         if (is_array($container)) {
  681.             foreach ($container as $childDef) {
  682.                 if ($childDef instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  683.                     $list[] = $childDef;
  684.                 } else {
  685.                     if (method_exists($childDef'getFieldDefinitions')) {
  686.                         self::collectCalculatedValueItems($childDef->getFieldDefinitions(), $list);
  687.                     }
  688.                 }
  689.             }
  690.         }
  691.     }
  692.     /**
  693.      * {@inheritdoc}
  694.      */
  695.     public function supportsInheritance()
  696.     {
  697.         return false;
  698.     }
  699.     /**
  700.      * {@inheritdoc}
  701.      */
  702.     public function getParameterTypeDeclaration(): ?string
  703.     {
  704.         return '?\\' DataObject\Fieldcollection::class;
  705.     }
  706.     /**
  707.      * {@inheritdoc}
  708.      */
  709.     public function getReturnTypeDeclaration(): ?string
  710.     {
  711.         return '?\\' DataObject\Fieldcollection::class;
  712.     }
  713.     /**
  714.      * {@inheritdoc}
  715.      */
  716.     public function getPhpdocInputType(): ?string
  717.     {
  718.         return '\\' DataObject\Fieldcollection::class . '|null';
  719.     }
  720.     /**
  721.      * {@inheritdoc}
  722.      */
  723.     public function getPhpdocReturnType(): ?string
  724.     {
  725.         return '\\' DataObject\Fieldcollection::class . '|null';
  726.     }
  727.     /**
  728.      * {@inheritdoc}
  729.      */
  730.     public function normalize($value$params = [])
  731.     {
  732.         if ($value instanceof DataObject\Fieldcollection) {
  733.             $resultItems = [];
  734.             $items $value->getItems();
  735.             /** @var DataObject\Fieldcollection\Data\AbstractData $item */
  736.             foreach ($items as $item) {
  737.                 $type $item->getType();
  738.                 $resultItem = ['type' => $type];
  739.                 $fcDef DataObject\Fieldcollection\Definition::getByKey($type);
  740.                 $fcs $fcDef->getFieldDefinitions();
  741.                 foreach ($fcs as $fc) {
  742.                     $getter 'get' ucfirst($fc->getName());
  743.                     $value $item->$getter();
  744.                     if ($fc instanceof NormalizerInterface) {
  745.                         $value $fc->normalize($value$params);
  746.                     }
  747.                     $resultItem[$fc->getName()] = $value;
  748.                 }
  749.                 $resultItems[] = $resultItem;
  750.             }
  751.             return $resultItems;
  752.         }
  753.         return null;
  754.     }
  755.     /**
  756.      * {@inheritdoc}
  757.      */
  758.     public function denormalize($value$params = [])
  759.     {
  760.         if (is_array($value)) {
  761.             $resultItems = [];
  762.             foreach ($value as $idx => $itemData) {
  763.                 $type $itemData['type'];
  764.                 $fcDef DataObject\Fieldcollection\Definition::getByKey($type);
  765.                 $collectionClass '\\Pimcore\\Model\\DataObject\\Fieldcollection\\Data\\' ucfirst($type);
  766.                 /** @var DataObject\Fieldcollection\Data\AbstractData $collection */
  767.                 $collection \Pimcore::getContainer()->get('pimcore.model.factory')->build($collectionClass);
  768.                 $collection->setObject($params['object'] ?? null);
  769.                 $collection->setIndex($idx);
  770.                 $collection->setFieldname($params['fieldname'] ?? null);
  771.                 foreach ($itemData as $fieldKey => $fieldValue) {
  772.                     if ($fieldKey == 'type') {
  773.                         continue;
  774.                     }
  775.                     $fc $fcDef->getFieldDefinition($fieldKey);
  776.                     if ($fc instanceof NormalizerInterface) {
  777.                         $fieldValue $fc->denormalize($fieldValue$params);
  778.                     }
  779.                     $collection->set($fieldKey$fieldValue);
  780.                 }
  781.                 $resultItems[] = $collection;
  782.             }
  783.             $resultCollection = new DataObject\Fieldcollection();
  784.             $resultCollection->setItems($resultItems);
  785.             return $resultCollection;
  786.         }
  787.         return null;
  788.     }
  789. }