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

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
  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  int|bool|null $lazyLoading
  88.      *
  89.      * @return $this
  90.      */
  91.     public function setLazyLoading($lazyLoading)
  92.     {
  93.         $this->lazyLoading $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(null$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(null$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.         $code '/**' "\n";
  461.         $code .= '* @return ' $this->getPhpdocReturnType() . "\n";
  462.         $code .= '*/' "\n";
  463.         $code .= 'public function get' ucfirst($key) . '()' "\n";
  464.         $code .= '{' "\n";
  465.         $code .= $this->getPreGetValueHookCode($key);
  466.         // TODO Pimcore 11: remove method_exists BC layer
  467.         // TODO else part should not be needed at all as preGetData is always there
  468.         // if ($this instanceof PreGetDataInterface || method_exists($this, 'preGetData')) {
  469.         $code .= "\t" '$data = $this->getClass()->getFieldDefinition("' $key '")->preGetData($this);' "\n";
  470. //        } else {
  471. //            $code .= "\t" . '$data = $this->' . $key . ";\n";
  472. //        }
  473.         $code .= "\t" 'return $data;' "\n";
  474.         $code .= "}\n\n";
  475.         return $code;
  476.     }
  477.     /**
  478.      * @param int|string|null $maxItems
  479.      *
  480.      * @return $this
  481.      */
  482.     public function setMaxItems($maxItems)
  483.     {
  484.         $this->maxItems $this->getAsIntegerCast($maxItems);
  485.         return $this;
  486.     }
  487.     /**
  488.      * @return int
  489.      */
  490.     public function getMaxItems()
  491.     {
  492.         return $this->maxItems;
  493.     }
  494.     /**
  495.      * {@inheritdoc}
  496.      */
  497.     public function isDiffChangeAllowed($object$params = [])
  498.     {
  499.         return true;
  500.     }
  501.     /** Generates a pretty version preview (similar to getVersionPreview) can be either HTML or
  502.      * a image URL. See the https://github.com/pimcore/object-merger bundle documentation for details
  503.      *
  504.      * @param DataObject\Fieldcollection|null $data
  505.      * @param DataObject\Concrete|null $object
  506.      * @param mixed $params
  507.      *
  508.      * @return array
  509.      */
  510.     public function getDiffVersionPreview($data$object null$params = [])
  511.     {
  512.         $html '';
  513.         if ($data instanceof DataObject\Fieldcollection) {
  514.             $html '<table>';
  515.             foreach ($data as $item) {
  516.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  517.                     continue;
  518.                 }
  519.                 $type $item->getType();
  520.                 $html .= '<tr><th><b>' $type '</b></th><th>&nbsp;</th><th>&nbsp;</th></tr>';
  521.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  522.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  523.                         $title = !empty($fd->title) ? $fd->title $fd->getName();
  524.                         $html .= '<tr><td>&nbsp;</td><td>' $title '</td><td>';
  525.                         $html .= $fd->getVersionPreview($item->getObjectVar($fd->getName()), $object$params);
  526.                         $html .= '</td></tr>';
  527.                     }
  528.                 }
  529.             }
  530.             $html .= '</table>';
  531.         }
  532.         $value = [];
  533.         $value['html'] = $html;
  534.         $value['type'] = 'html';
  535.         return $value;
  536.     }
  537.     /**
  538.      * { @inheritdoc }
  539.      */
  540.     public function rewriteIds(/** mixed */ $container/** array */ $idMapping/** array */ $params = []) /** :mixed */
  541.     {
  542.         $data $this->getDataFromObjectParam($container$params);
  543.         if ($data instanceof DataObject\Fieldcollection) {
  544.             foreach ($data as $item) {
  545.                 if (!$item instanceof DataObject\Fieldcollection\Data\AbstractData) {
  546.                     continue;
  547.                 }
  548.                 if ($collectionDef DataObject\Fieldcollection\Definition::getByKey($item->getType())) {
  549.                     foreach ($collectionDef->getFieldDefinitions() as $fd) {
  550.                         //TODO Pimcore 11: remove method_exists BC layer
  551.                         if ($fd instanceof IdRewriterInterface || method_exists($fd'rewriteIds')) {
  552.                             $d $fd->rewriteIds($item$idMapping$params);
  553.                             $setter 'set' ucfirst($fd->getName());
  554.                             $item->$setter($d);
  555.                         }
  556.                     }
  557.                 }
  558.             }
  559.         }
  560.         return $data;
  561.     }
  562.     /**
  563.      * @param DataObject\ClassDefinition\Data\Fieldcollections $masterDefinition
  564.      */
  565.     public function synchronizeWithMasterDefinition(DataObject\ClassDefinition\Data $masterDefinition)
  566.     {
  567.         $this->allowedTypes $masterDefinition->allowedTypes;
  568.         $this->lazyLoading $masterDefinition->lazyLoading;
  569.         $this->maxItems $masterDefinition->maxItems;
  570.     }
  571.     /**
  572.      * This method is called in DataObject\ClassDefinition::save() and is used to create the database table for the localized data
  573.      *
  574.      * @param DataObject\ClassDefinition $class
  575.      * @param array $params
  576.      */
  577.     public function classSaved($class$params = [])
  578.     {
  579.         if (is_array($this->allowedTypes)) {
  580.             foreach ($this->allowedTypes as $i => $allowedType) {
  581.                 if ($definition DataObject\Fieldcollection\Definition::getByKey($allowedType)) {
  582.                     $definition->getDao()->createUpdateTable($class);
  583.                     $fieldDefinition $definition->getFieldDefinitions();
  584.                     foreach ($fieldDefinition as $fd) {
  585.                         //TODO Pimcore 11 remove method_exists call
  586.                         if (!$fd instanceof DataContainerAwareInterface && method_exists($fd'classSaved')) {
  587.                             // defer creation
  588.                             $fd->classSaved($class);
  589.                         }
  590.                     }
  591.                     $definition->getDao()->classSaved($class);
  592.                 } else {
  593.                     Logger::warn("Removed unknown allowed type [ $allowedType ] from allowed types of field collection");
  594.                     unset($this->allowedTypes[$i]);
  595.                 }
  596.             }
  597.         }
  598.     }
  599.     /**
  600.      * @param bool $disallowAddRemove
  601.      */
  602.     public function setDisallowAddRemove($disallowAddRemove)
  603.     {
  604.         $this->disallowAddRemove $disallowAddRemove;
  605.     }
  606.     /**
  607.      * @return bool
  608.      */
  609.     public function getDisallowAddRemove()
  610.     {
  611.         return $this->disallowAddRemove;
  612.     }
  613.     /**
  614.      * @param bool $disallowReorder
  615.      */
  616.     public function setDisallowReorder($disallowReorder)
  617.     {
  618.         $this->disallowReorder $disallowReorder;
  619.     }
  620.     /**
  621.      * @return bool
  622.      */
  623.     public function getDisallowReorder()
  624.     {
  625.         return $this->disallowReorder;
  626.     }
  627.     /**
  628.      * @return bool
  629.      */
  630.     public function getBorder(): bool
  631.     {
  632.         return $this->border;
  633.     }
  634.     /**
  635.      * @param bool $border
  636.      */
  637.     public function setBorder(bool $border): void
  638.     {
  639.         $this->border $border;
  640.     }
  641.     /**
  642.      * @return bool
  643.      */
  644.     public function isCollapsed()
  645.     {
  646.         return $this->collapsed;
  647.     }
  648.     /**
  649.      * @param bool $collapsed
  650.      */
  651.     public function setCollapsed($collapsed)
  652.     {
  653.         $this->collapsed $collapsed;
  654.     }
  655.     /**
  656.      * @return bool
  657.      */
  658.     public function isCollapsible()
  659.     {
  660.         return $this->collapsible;
  661.     }
  662.     /**
  663.      * @param bool $collapsible
  664.      */
  665.     public function setCollapsible($collapsible)
  666.     {
  667.         $this->collapsible $collapsible;
  668.     }
  669.     /**
  670.      * @param DataObject\ClassDefinition\Data[] $container
  671.      * @param DataObject\ClassDefinition\Data[] $list
  672.      */
  673.     public static function collectCalculatedValueItems($container, &$list = [])
  674.     {
  675.         if (is_array($container)) {
  676.             foreach ($container as $childDef) {
  677.                 if ($childDef instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  678.                     $list[] = $childDef;
  679.                 } else {
  680.                     if (method_exists($childDef'getFieldDefinitions')) {
  681.                         self::collectCalculatedValueItems($childDef->getFieldDefinitions(), $list);
  682.                     }
  683.                 }
  684.             }
  685.         }
  686.     }
  687.     /**
  688.      * {@inheritdoc}
  689.      */
  690.     public function supportsInheritance()
  691.     {
  692.         return false;
  693.     }
  694.     /**
  695.      * {@inheritdoc}
  696.      */
  697.     public function getParameterTypeDeclaration(): ?string
  698.     {
  699.         return '?\\' DataObject\Fieldcollection::class;
  700.     }
  701.     /**
  702.      * {@inheritdoc}
  703.      */
  704.     public function getReturnTypeDeclaration(): ?string
  705.     {
  706.         return '?\\' DataObject\Fieldcollection::class;
  707.     }
  708.     /**
  709.      * {@inheritdoc}
  710.      */
  711.     public function getPhpdocInputType(): ?string
  712.     {
  713.         return '\\' DataObject\Fieldcollection::class . '|null';
  714.     }
  715.     /**
  716.      * {@inheritdoc}
  717.      */
  718.     public function getPhpdocReturnType(): ?string
  719.     {
  720.         return '\\' DataObject\Fieldcollection::class . '|null';
  721.     }
  722.     /**
  723.      * {@inheritdoc}
  724.      */
  725.     public function normalize($value$params = [])
  726.     {
  727.         if ($value instanceof DataObject\Fieldcollection) {
  728.             $resultItems = [];
  729.             $items $value->getItems();
  730.             /** @var DataObject\Fieldcollection\Data\AbstractData $item */
  731.             foreach ($items as $item) {
  732.                 $type $item->getType();
  733.                 $resultItem = ['type' => $type];
  734.                 $fcDef DataObject\Fieldcollection\Definition::getByKey($type);
  735.                 $fcs $fcDef->getFieldDefinitions();
  736.                 foreach ($fcs as $fc) {
  737.                     $getter 'get' ucfirst($fc->getName());
  738.                     $value $item->$getter();
  739.                     if ($fc instanceof NormalizerInterface) {
  740.                         $value $fc->normalize($value$params);
  741.                     }
  742.                     $resultItem[$fc->getName()] = $value;
  743.                 }
  744.                 $resultItems[] = $resultItem;
  745.             }
  746.             return $resultItems;
  747.         }
  748.         return null;
  749.     }
  750.     /**
  751.      * {@inheritdoc}
  752.      */
  753.     public function denormalize($value$params = [])
  754.     {
  755.         if (is_array($value)) {
  756.             $resultItems = [];
  757.             foreach ($value as $idx => $itemData) {
  758.                 $type $itemData['type'];
  759.                 $fcDef DataObject\Fieldcollection\Definition::getByKey($type);
  760.                 $collectionClass '\\Pimcore\\Model\\DataObject\\Fieldcollection\\Data\\' ucfirst($type);
  761.                 /** @var DataObject\Fieldcollection\Data\AbstractData $collection */
  762.                 $collection \Pimcore::getContainer()->get('pimcore.model.factory')->build($collectionClass);
  763.                 $collection->setObject($params['object'] ?? null);
  764.                 $collection->setIndex($idx);
  765.                 $collection->setFieldname($params['fieldname'] ?? null);
  766.                 foreach ($itemData as $fieldKey => $fieldValue) {
  767.                     if ($fieldKey == 'type') {
  768.                         continue;
  769.                     }
  770.                     $fc $fcDef->getFieldDefinition($fieldKey);
  771.                     if ($fc instanceof NormalizerInterface) {
  772.                         $fieldValue $fc->denormalize($fieldValue$params);
  773.                     }
  774.                     $collection->set($fieldKey$fieldValue);
  775.                 }
  776.                 $resultItems[] = $collection;
  777.             }
  778.             $resultCollection = new DataObject\Fieldcollection();
  779.             $resultCollection->setItems($resultItems);
  780.             return $resultCollection;
  781.         }
  782.         return null;
  783.     }
  784. }