vendor/pimcore/pimcore/models/DataObject/ClassDefinition/Data/Block.php line 1328

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\Db;
  16. use Pimcore\Element\MarshallerService;
  17. use Pimcore\Logger;
  18. use Pimcore\Model;
  19. use Pimcore\Model\DataObject;
  20. use Pimcore\Model\DataObject\ClassDefinition\Data;
  21. use Pimcore\Model\DataObject\ClassDefinition\Layout;
  22. use Pimcore\Model\Element;
  23. use Pimcore\Normalizer\NormalizerInterface;
  24. use Pimcore\Tool\Serialize;
  25. class Block extends Data implements CustomResourcePersistingInterfaceResourcePersistenceAwareInterfaceLazyLoadingSupportInterfaceTypeDeclarationSupportInterfaceVarExporterInterfaceNormalizerInterfaceDataContainerAwareInterfacePreGetDataInterfacePreSetDataInterface
  26. {
  27.     use Element\ChildsCompatibilityTrait;
  28.     use Extension\ColumnType;
  29.     use DataObject\Traits\ClassSavedTrait;
  30.     /**
  31.      * Static type of this element
  32.      *
  33.      * @internal
  34.      *
  35.      * @var string
  36.      */
  37.     public $fieldtype 'block';
  38.     /**
  39.      * @internal
  40.      *
  41.      * @var bool
  42.      */
  43.     public $lazyLoading;
  44.     /**
  45.      * @internal
  46.      *
  47.      * @var bool
  48.      */
  49.     public $disallowAddRemove;
  50.     /**
  51.      * @internal
  52.      *
  53.      * @var bool
  54.      */
  55.     public $disallowReorder;
  56.     /**
  57.      * @internal
  58.      *
  59.      * @var bool
  60.      */
  61.     public $collapsible;
  62.     /**
  63.      * @internal
  64.      *
  65.      * @var bool
  66.      */
  67.     public $collapsed;
  68.     /**
  69.      * @internal
  70.      *
  71.      * @var int|null
  72.      */
  73.     public $maxItems;
  74.     /**
  75.      * Type for the column
  76.      *
  77.      * @internal
  78.      *
  79.      * @var string
  80.      */
  81.     public $columnType 'longtext';
  82.     /**
  83.      * @internal
  84.      *
  85.      * @var string
  86.      */
  87.     public $styleElement '';
  88.     /**
  89.      * @internal
  90.      *
  91.      * @var array
  92.      */
  93.     public $children = [];
  94.     /**
  95.      * @internal
  96.      *
  97.      * @var array|null
  98.      */
  99.     public $layout;
  100.     /**
  101.      * contains further child field definitions if there are more than one localized fields in on class
  102.      *
  103.      * @internal
  104.      *
  105.      * @var array
  106.      */
  107.     protected $referencedFields = [];
  108.     /**
  109.      * @internal
  110.      *
  111.      * @var array|null
  112.      */
  113.     public $fieldDefinitionsCache;
  114.     /**
  115.      * @see ResourcePersistenceAwareInterface::getDataForResource
  116.      *
  117.      * @param mixed $data
  118.      * @param null|DataObject\Concrete $object
  119.      * @param array $params
  120.      *
  121.      * @return string
  122.      */
  123.     public function getDataForResource($data$object null$params = [])
  124.     {
  125.         $result = [];
  126.         if (is_array($data)) {
  127.             foreach ($data as $blockElements) {
  128.                 $resultElement = [];
  129.                 /** @var DataObject\Data\BlockElement $blockElement */
  130.                 foreach ($blockElements as $elementName => $blockElement) {
  131.                     $this->setBlockElementOwner($blockElement$params);
  132.                     $fd $this->getFieldDefinition($elementName);
  133.                     if (!$fd) {
  134.                         // class definition seems to have changed
  135.                         Logger::warn('class definition seems to have changed, element name: ' $elementName);
  136.                         continue;
  137.                     }
  138.                     $elementData $blockElement->getData();
  139.                     // $encodedDataBC = $fd->marshal($elementData, $object, ['raw' => true, 'blockmode' => true]);
  140.                     if ($fd instanceof NormalizerInterface) {
  141.                         $normalizedData $fd->normalize($elementData, [
  142.                             'object' => $object,
  143.                             'fieldDefinition' => $fd,
  144.                         ]);
  145.                         $encodedData $normalizedData;
  146.                         /** @var MarshallerService $marshallerService */
  147.                         $marshallerService \Pimcore::getContainer()->get(MarshallerService::class);
  148.                         if ($marshallerService->supportsFielddefinition('block'$fd->getFieldtype())) {
  149.                             $marshaller $marshallerService->buildFieldefinitionMarshaller('block'$fd->getFieldtype());
  150.                             // TODO format only passed in for BC reasons (localizedfields). remove it as soon as marshal is gone
  151.                             $encodedData $marshaller->marshal($normalizedData, ['object' => $object'fieldDefinition' => $fd'format' => 'block']);
  152.                         }
  153.                         // do not serialize the block element itself
  154.                         $resultElement[$elementName] = [
  155.                             'name' => $blockElement->getName(),
  156.                             'type' => $blockElement->getType(),
  157.                             'data' => $encodedData,
  158.                         ];
  159.                     }
  160.                 }
  161.                 $result[] = $resultElement;
  162.             }
  163.         }
  164.         $result Serialize::serialize($result);
  165.         return $result;
  166.     }
  167.     /**
  168.      * @see ResourcePersistenceAwareInterface::getDataFromResource
  169.      *
  170.      * @param mixed $data
  171.      * @param DataObject\Concrete|null $object
  172.      * @param array $params
  173.      *
  174.      * @return array|null
  175.      */
  176.     public function getDataFromResource($data$object null$params = [])
  177.     {
  178.         if ($data) {
  179.             $count 0;
  180.             //Fix old serialized data protected properties with \0*\0 prefix
  181.             //https://github.com/pimcore/pimcore/issues/9973
  182.             if (str_contains($data':" * ')) {
  183.                 $data preg_replace_callback('!s:(\d+):" \* (.*?)";!', function ($match) {
  184.                     return ($match[1] == strlen($match[2])) ? $match[0] : 's:' strlen($match[2]) .   ':"' $match[2] . '";';
  185.                 }, $data);
  186.             }
  187.             $unserializedData Serialize::unserialize($data);
  188.             $result = [];
  189.             foreach ($unserializedData as $blockElements) {
  190.                 $items = [];
  191.                 foreach ($blockElements as $elementName => $blockElementRaw) {
  192.                     $fd $this->getFieldDefinition($elementName);
  193.                     if (!$fd) {
  194.                         // class definition seems to have changed
  195.                         Logger::warn('class definition seems to have changed, element name: ' $elementName);
  196.                         continue;
  197.                     }
  198.                     // do not serialize the block element itself
  199.                     $elementData $blockElementRaw['data'];
  200.                     if ($fd instanceof NormalizerInterface) {
  201.                         /** @var MarshallerService $marshallerService */
  202.                         $marshallerService \Pimcore::getContainer()->get(MarshallerService::class);
  203.                         if ($marshallerService->supportsFielddefinition('block'$fd->getFieldtype())) {
  204.                             $unmarshaller $marshallerService->buildFieldefinitionMarshaller('block'$fd->getFieldtype());
  205.                             // TODO format only passed in for BC reasons (localizedfields). remove it as soon as marshal is gone
  206.                             $elementData $unmarshaller->unmarshal($elementData, ['object' => $object'fieldDefinition' => $fd'format' => 'block']);
  207.                         }
  208.                         $dataFromResource $fd->denormalize($elementData, [
  209.                             'object' => $object,
  210.                             'fieldDefinition' => $fd,
  211.                         ]);
  212.                         $blockElementRaw['data'] = $dataFromResource;
  213.                     }
  214.                     $blockElement = new DataObject\Data\BlockElement($blockElementRaw['name'], $blockElementRaw['type'], $blockElementRaw['data']);
  215.                     if ($blockElementRaw['type'] == 'localizedfields') {
  216.                         /** @var DataObject\Localizedfield|null $data */
  217.                         $data $blockElementRaw['data'];
  218.                         if ($data) {
  219.                             $data->setObject($object);
  220.                             $data->_setOwner($blockElement);
  221.                             $data->_setOwnerFieldname('localizedfields');
  222.                             $data->setContext(['containerType' => 'block',
  223.                                 'fieldname' => $this->getName(),
  224.                                 'index' => $count,
  225.                                 'containerKey' => $this->getName(),
  226.                                 'classId' => $object $object->getClassId() : null, ]);
  227.                             $blockElementRaw['data'] = $data;
  228.                         }
  229.                     }
  230.                     $blockElement->setNeedsRenewReferences(true);
  231.                     $this->setBlockElementOwner($blockElement$params);
  232.                     $items[$elementName] = $blockElement;
  233.                 }
  234.                 $result[] = $items;
  235.                 $count++;
  236.             }
  237.             return $result;
  238.         }
  239.         return null;
  240.     }
  241.     /**
  242.      * @see Data::getDataForEditmode
  243.      *
  244.      * @param mixed $data
  245.      * @param null|DataObject\Concrete $object
  246.      * @param array $params
  247.      *
  248.      * @return array
  249.      */
  250.     public function getDataForEditmode($data$object null$params = [])
  251.     {
  252.         $params = (array)$params;
  253.         $result = [];
  254.         $idx = -1;
  255.         if (is_array($data)) {
  256.             foreach ($data as $blockElements) {
  257.                 $resultElement = [];
  258.                 $idx++;
  259.                 /** @var DataObject\Data\BlockElement $blockElement */
  260.                 foreach ($blockElements as $elementName => $blockElement) {
  261.                     $fd $this->getFieldDefinition($elementName);
  262.                     if (!$fd) {
  263.                         // class definition seems to have changed
  264.                         Logger::warn('class definition seems to have changed, element name: ' $elementName);
  265.                         continue;
  266.                     }
  267.                     $elementData $blockElement->getData();
  268.                     $params['context']['containerType'] = 'block';
  269.                     $dataForEditMode $fd->getDataForEditmode($elementData$object$params);
  270.                     $resultElement[$elementName] = $dataForEditMode;
  271.                     if (isset($params['owner'])) {
  272.                         $this->setBlockElementOwner($blockElement$params);
  273.                     }
  274.                 }
  275.                 $result[] = [
  276.                     'oIndex' => $idx,
  277.                     'data' => $resultElement,
  278.                 ];
  279.             }
  280.         }
  281.         return $result;
  282.     }
  283.     /**
  284.      * @see Data::getDataFromEditmode
  285.      *
  286.      * @param mixed $data
  287.      * @param null|DataObject\Concrete $object
  288.      * @param array $params
  289.      *
  290.      * @return array
  291.      */
  292.     public function getDataFromEditmode($data$object null$params = [])
  293.     {
  294.         $result = [];
  295.         $count 0;
  296.         foreach ($data as $rawBlockElement) {
  297.             $resultElement = [];
  298.             $oIndex $rawBlockElement['oIndex'] ?? null;
  299.             $blockElement $rawBlockElement['data'] ?? null;
  300.             $blockElementDefinition $this->getFieldDefinitions();
  301.             foreach ($blockElementDefinition as $elementName => $fd) {
  302.                 $elementType $fd->getFieldtype();
  303.                 $invisible $fd->getInvisible();
  304.                 if ($invisible && !is_null($oIndex)) {
  305.                     $blockGetter 'get' ucfirst($this->getname());
  306.                     if (method_exists($object$blockGetter)) {
  307.                         $language $params['language'] ?? null;
  308.                         $items $object->$blockGetter($language);
  309.                         if (isset($items[$oIndex])) {
  310.                             $item $items[$oIndex][$elementName];
  311.                             $blockData $blockElement[$elementName] ?: $item->getData();
  312.                             $resultElement[$elementName] = new DataObject\Data\BlockElement($elementName$elementType$blockData);
  313.                         }
  314.                     } else {
  315.                         $params['blockGetter'] = $blockGetter;
  316.                         $blockData $this->getBlockDataFromContainer($object$params);
  317.                         if ($blockData) {
  318.                             $resultElement $blockData[$oIndex];
  319.                         }
  320.                     }
  321.                 } else {
  322.                     $elementData $blockElement[$elementName] ?? null;
  323.                     $blockData $fd->getDataFromEditmode(
  324.                         $elementData,
  325.                         $object,
  326.                         [
  327.                             'context' => [
  328.                                 'containerType' => 'block',
  329.                                 'fieldname' => $this->getName(),
  330.                                 'index' => $count,
  331.                                 'oIndex' => $oIndex,
  332.                                 'classId' => $object->getClassId(),
  333.                             ],
  334.                         ]
  335.                     );
  336.                     $resultElement[$elementName] = new DataObject\Data\BlockElement($elementName$elementType$blockData);
  337.                 }
  338.             }
  339.             $result[] = $resultElement;
  340.             $count++;
  341.         }
  342.         return $result;
  343.     }
  344.     /**
  345.      * @param DataObject\Concrete $object
  346.      * @param array $params
  347.      *
  348.      * @return mixed
  349.      *
  350.      * @throws \Exception
  351.      */
  352.     protected function getBlockDataFromContainer($object$params = [])
  353.     {
  354.         $data null;
  355.         $context $params['context'] ?? null;
  356.         if (isset($context['containerType'])) {
  357.             if ($context['containerType'] === 'fieldcollection') {
  358.                 $fieldname $context['fieldname'];
  359.                 if ($object instanceof DataObject\Concrete) {
  360.                     $containerGetter 'get' ucfirst($fieldname);
  361.                     $container $object->$containerGetter();
  362.                     if ($container) {
  363.                         $originalIndex $context['oIndex'];
  364.                         // field collection or block items
  365.                         if (!is_null($originalIndex)) {
  366.                             $items $container->getItems();
  367.                             if ($items && count($items) > $originalIndex) {
  368.                                 $item $items[$originalIndex];
  369.                                 $getter 'get' ucfirst($this->getName());
  370.                                 $data $item->$getter();
  371.                                 return $data;
  372.                             }
  373.                         } else {
  374.                             return null;
  375.                         }
  376.                     } else {
  377.                         return null;
  378.                     }
  379.                 }
  380.             } elseif ($context['containerType'] === 'objectbrick') {
  381.                 $fieldname $context['fieldname'];
  382.                 if ($object instanceof DataObject\Concrete) {
  383.                     $containerGetter 'get' ucfirst($fieldname);
  384.                     $container $object->$containerGetter();
  385.                     if ($container) {
  386.                         $brickGetter 'get' ucfirst($context['containerKey']);
  387.                         /** @var DataObject\Objectbrick\Data\AbstractData|null $brickData */
  388.                         $brickData $container->$brickGetter();
  389.                         if ($brickData) {
  390.                             $blockGetter $params['blockGetter'];
  391.                             $data $brickData->$blockGetter();
  392.                             return $data;
  393.                         }
  394.                     }
  395.                 }
  396.             }
  397.         }
  398.         return $data;
  399.     }
  400.     /**
  401.      * @see Data::getVersionPreview
  402.      *
  403.      * @param mixed $data
  404.      * @param DataObject\Concrete|null $object
  405.      * @param array $params
  406.      *
  407.      * @return string
  408.      */
  409.     public function getVersionPreview($data$object null$params = [])
  410.     {
  411.         return 'not supported';
  412.     }
  413.     /**
  414.      * {@inheritdoc}
  415.      */
  416.     public function getForCsvExport($object$params = [])
  417.     {
  418.         return '';
  419.     }
  420.     /**
  421.      * {@inheritdoc}
  422.      */
  423.     public function isDiffChangeAllowed($object$params = [])
  424.     {
  425.         return true;
  426.     }
  427.     /** Generates a pretty version preview (similar to getVersionPreview) can be either HTML or
  428.      * a image URL. See the https://github.com/pimcore/object-merger bundle documentation for details
  429.      *
  430.      * @param array|null $data
  431.      * @param DataObject\Concrete|null $object
  432.      * @param array $params
  433.      *
  434.      * @return string
  435.      */
  436.     public function getDiffVersionPreview($data$object null$params = [])
  437.     {
  438.         if ($data) {
  439.             return 'not supported';
  440.         }
  441.         return '';
  442.     }
  443.     /**
  444.      * @param Model\DataObject\ClassDefinition\Data\Block $mainDefinition
  445.      */
  446.     public function synchronizeWithMainDefinition(Model\DataObject\ClassDefinition\Data $mainDefinition)
  447.     {
  448.         $this->disallowAddRemove $mainDefinition->disallowAddRemove;
  449.         $this->disallowReorder $mainDefinition->disallowReorder;
  450.         $this->collapsible $mainDefinition->collapsible;
  451.         $this->collapsed $mainDefinition->collapsed;
  452.     }
  453.     /**
  454.      * @param Model\DataObject\ClassDefinition\Data\Block $masterDefinition
  455.      *
  456.      *@deprecated will be removed in Pimcore 11
  457.      */
  458.     public function synchronizeWithMasterDefinition(Model\DataObject\ClassDefinition\Data $masterDefinition)
  459.     {
  460.         trigger_deprecation(
  461.             'pimcore/pimcore',
  462.             '10.6.0',
  463.             sprintf('%s is deprecated and will be removed in Pimcore 11. Use %s instead.'__METHOD__str_replace('Master''Main'__METHOD__))
  464.         );
  465.         $this->synchronizeWithMainDefinition($masterDefinition);
  466.     }
  467.     /**
  468.      * @param mixed $data
  469.      *
  470.      * @return bool
  471.      */
  472.     public function isEmpty($data)
  473.     {
  474.         return is_null($data) || count($data) === 0;
  475.     }
  476.     /**
  477.      * @return array
  478.      */
  479.     public function getChildren()
  480.     {
  481.         return $this->children;
  482.     }
  483.     /**
  484.      * @param array $children
  485.      *
  486.      * @return $this
  487.      */
  488.     public function setChildren($children)
  489.     {
  490.         $this->children $children;
  491.         $this->fieldDefinitionsCache null;
  492.         return $this;
  493.     }
  494.     /**
  495.      * @return bool
  496.      */
  497.     public function hasChildren()
  498.     {
  499.         if (is_array($this->children) && count($this->children) > 0) {
  500.             return true;
  501.         }
  502.         return false;
  503.     }
  504.     /**
  505.      * @param Data|Layout $child
  506.      */
  507.     public function addChild($child)
  508.     {
  509.         $this->children[] = $child;
  510.         $this->fieldDefinitionsCache null;
  511.     }
  512.     /**
  513.      * @param array|null $layout
  514.      *
  515.      * @return $this
  516.      */
  517.     public function setLayout($layout)
  518.     {
  519.         $this->layout $layout;
  520.         return $this;
  521.     }
  522.     /**
  523.      * @return array|null
  524.      */
  525.     public function getLayout()
  526.     {
  527.         return $this->layout;
  528.     }
  529.     /**
  530.      * @param mixed $def
  531.      * @param array $fields
  532.      *
  533.      * @return array
  534.      */
  535.     public function doGetFieldDefinitions($def null$fields = [])
  536.     {
  537.         if ($def === null) {
  538.             $def $this->getChildren();
  539.         }
  540.         if (is_array($def)) {
  541.             foreach ($def as $child) {
  542.                 $fields array_merge($fields$this->doGetFieldDefinitions($child$fields));
  543.             }
  544.         }
  545.         if ($def instanceof DataObject\ClassDefinition\Layout) {
  546.             if ($def->hasChildren()) {
  547.                 foreach ($def->getChildren() as $child) {
  548.                     $fields array_merge($fields$this->doGetFieldDefinitions($child$fields));
  549.                 }
  550.             }
  551.         }
  552.         if ($def instanceof DataObject\ClassDefinition\Data) {
  553.             $existing $fields[$def->getName()] ?? false;
  554.             if ($existing && method_exists($existing'addReferencedField')) {
  555.                 // this is especially for localized fields which get aggregated here into one field definition
  556.                 // in the case that there are more than one localized fields in the class definition
  557.                 // see also pimcore.object.edit.addToDataFields();
  558.                 $existing->addReferencedField($def);
  559.             } else {
  560.                 $fields[$def->getName()] = $def;
  561.             }
  562.         }
  563.         return $fields;
  564.     }
  565.     /**
  566.      * @param array $context additional contextual data
  567.      *
  568.      * @return DataObject\ClassDefinition\Data[]
  569.      */
  570.     public function getFieldDefinitions($context = [])
  571.     {
  572.         if (empty($this->fieldDefinitionsCache)) {
  573.             $definitions $this->doGetFieldDefinitions();
  574.             foreach ($this->getReferencedFields() as $rf) {
  575.                 if ($rf instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  576.                     $definitions array_merge($definitions$this->doGetFieldDefinitions($rf->getChildren()));
  577.                 }
  578.             }
  579.             $this->fieldDefinitionsCache $definitions;
  580.         }
  581.         if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  582.             return $this->fieldDefinitionsCache;
  583.         }
  584.         $enrichedFieldDefinitions = [];
  585.         foreach ($this->fieldDefinitionsCache ?? [] as $key => $fieldDefinition) {
  586.             $fieldDefinition $this->doEnrichFieldDefinition($fieldDefinition$context);
  587.             $enrichedFieldDefinitions[$key] = $fieldDefinition;
  588.         }
  589.         return $enrichedFieldDefinitions;
  590.     }
  591.     /**
  592.      * @param string $name
  593.      * @param array $context additional contextual data
  594.      *
  595.      * @return DataObject\ClassDefinition\Data|null
  596.      */
  597.     public function getFieldDefinition($name$context = [])
  598.     {
  599.         $fds $this->getFieldDefinitions();
  600.         if (isset($fds[$name])) {
  601.             if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  602.                 return $fds[$name];
  603.             }
  604.             $fieldDefinition $this->doEnrichFieldDefinition($fds[$name], $context);
  605.             return $fieldDefinition;
  606.         }
  607.         return null;
  608.     }
  609.     protected function doEnrichFieldDefinition($fieldDefinition$context = [])
  610.     {
  611.         //TODO Pimcore 11: remove method_exists BC layer
  612.         if ($fieldDefinition instanceof FieldDefinitionEnrichmentInterface || method_exists($fieldDefinition'enrichFieldDefinition')) {
  613.             if (!$fieldDefinition instanceof FieldDefinitionEnrichmentInterface) {
  614.                 trigger_deprecation('pimcore/pimcore''10.1',
  615.                     sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  616.                     'Implement the %s interface instead.'FieldDefinitionEnrichmentInterface::class));
  617.             }
  618.             $context['containerType'] = 'block';
  619.             $context['containerKey'] = $this->getName();
  620.             $fieldDefinition $fieldDefinition->enrichFieldDefinition($context);
  621.         }
  622.         return $fieldDefinition;
  623.     }
  624.     /**
  625.      * @param array $referencedFields
  626.      */
  627.     public function setReferencedFields($referencedFields)
  628.     {
  629.         $this->referencedFields $referencedFields;
  630.         $this->fieldDefinitionsCache null;
  631.     }
  632.     /**
  633.      * @return Data[]
  634.      */
  635.     public function getReferencedFields()
  636.     {
  637.         return $this->referencedFields;
  638.     }
  639.     /**
  640.      * @param Data $field
  641.      */
  642.     public function addReferencedField($field)
  643.     {
  644.         $this->referencedFields[] = $field;
  645.         $this->fieldDefinitionsCache null;
  646.     }
  647.     /**
  648.      * @return array
  649.      */
  650.     public function getBlockedVarsForExport(): array
  651.     {
  652.         return [
  653.             'fieldDefinitionsCache',
  654.             'referencedFields',
  655.             'blockedVarsForExport',
  656.             'childs',
  657.         ];
  658.     }
  659.     /**
  660.      * @return array
  661.      */
  662.     public function __sleep()
  663.     {
  664.         $vars get_object_vars($this);
  665.         $blockedVars $this->getBlockedVarsForExport();
  666.         foreach ($blockedVars as $blockedVar) {
  667.             unset($vars[$blockedVar]);
  668.         }
  669.         return array_keys($vars);
  670.     }
  671.     /**
  672.      * @param mixed $data
  673.      *
  674.      * @return array
  675.      */
  676.     public function resolveDependencies($data)
  677.     {
  678.         $dependencies = [];
  679.         if (!is_array($data)) {
  680.             return [];
  681.         }
  682.         foreach ($data as $blockElements) {
  683.             foreach ($blockElements as $elementName => $blockElement) {
  684.                 $fd $this->getFieldDefinition($elementName);
  685.                 if (!$fd) {
  686.                     // class definition seems to have changed
  687.                     Logger::warn('class definition seems to have changed, element name: ' $elementName);
  688.                     continue;
  689.                 }
  690.                 $elementData $blockElement->getData();
  691.                 $dependencies array_merge($dependencies$fd->resolveDependencies($elementData));
  692.             }
  693.         }
  694.         return $dependencies;
  695.     }
  696.     /**
  697.      * {@inheritdoc}
  698.      */
  699.     public function getCacheTags($data, array $tags = [])
  700.     {
  701.         if ($this->getLazyLoading()) {
  702.             return $tags;
  703.         }
  704.         if (!is_array($data)) {
  705.             return $tags;
  706.         }
  707.         foreach ($data as $blockElements) {
  708.             foreach ($blockElements as $elementName => $blockElement) {
  709.                 $fd $this->getFieldDefinition($elementName);
  710.                 if (!$fd) {
  711.                     // class definition seems to have changed
  712.                     Logger::warn('class definition seems to have changed, element name: ' $elementName);
  713.                     continue;
  714.                 }
  715.                 $data $blockElement->getData();
  716.                 $tags $fd->getCacheTags($data$tags);
  717.             }
  718.         }
  719.         return $tags;
  720.     }
  721.     /**
  722.      * @return bool
  723.      */
  724.     public function isCollapsed()
  725.     {
  726.         return $this->collapsed;
  727.     }
  728.     /**
  729.      * @param bool $collapsed
  730.      */
  731.     public function setCollapsed($collapsed)
  732.     {
  733.         $this->collapsed = (bool) $collapsed;
  734.     }
  735.     /**
  736.      * @return bool
  737.      */
  738.     public function isCollapsible()
  739.     {
  740.         return $this->collapsible;
  741.     }
  742.     /**
  743.      * @param bool $collapsible
  744.      */
  745.     public function setCollapsible($collapsible)
  746.     {
  747.         $this->collapsible = (bool) $collapsible;
  748.     }
  749.     /**
  750.      * @return string
  751.      */
  752.     public function getStyleElement()
  753.     {
  754.         return $this->styleElement;
  755.     }
  756.     /**
  757.      * @param string $styleElement
  758.      *
  759.      * @return $this
  760.      */
  761.     public function setStyleElement($styleElement)
  762.     {
  763.         $this->styleElement $styleElement;
  764.         return $this;
  765.     }
  766.     /**
  767.      * @return bool
  768.      */
  769.     public function getLazyLoading()
  770.     {
  771.         return $this->lazyLoading;
  772.     }
  773.     /**
  774.      * @param bool $lazyLoading
  775.      *
  776.      * @return $this
  777.      */
  778.     public function setLazyLoading($lazyLoading)
  779.     {
  780.         $this->lazyLoading = (bool) $lazyLoading;
  781.         return $this;
  782.     }
  783.     /**
  784.      * {@inheritdoc}
  785.      */
  786.     public function preSetData(/** mixed */ $container/**  mixed */ $data/** array */ $params = []) // : mixed
  787.     {
  788.         $this->markLazyloadedFieldAsLoaded($container);
  789.         $lf $this->getFieldDefinition('localizedfields');
  790.         if ($lf && is_array($data)) {
  791.             foreach ($data as $item) {
  792.                 if (is_array($item)) {
  793.                     foreach ($item as $itemElement) {
  794.                         if ($itemElement->getType() === 'localizedfields') {
  795.                             /** @var DataObject\Localizedfield $itemElementData */
  796.                             $itemElementData $itemElement->getData();
  797.                             $itemElementData->setObject($container);
  798.                             // the localized field needs at least the containerType as this is important
  799.                             // for lazy loading
  800.                             $context $itemElementData->getContext() ? $itemElementData->getContext() : [];
  801.                             $context['containerType'] = 'block';
  802.                             $context['containerKey'] = $this->getName();
  803.                             $itemElementData->setContext($context);
  804.                         }
  805.                     }
  806.                 }
  807.             }
  808.         }
  809.         return $data;
  810.     }
  811.     /**
  812.      * {@inheritdoc}
  813.      */
  814.     public function save($object$params = [])
  815.     {
  816.     }
  817.     /**
  818.      * {@inheritdoc}
  819.      **/
  820.     public function load($container$params = [])
  821.     {
  822.         $field $this->getName();
  823.         $db Db::get();
  824.         $data null;
  825.         if ($container instanceof DataObject\Concrete) {
  826.             $query 'select ' $db->quoteIdentifier($field) . ' from object_store_' $container->getClassId() . ' where oo_id  = ' $container->getId();
  827.             $data $db->fetchOne($query);
  828.             $data $this->getDataFromResource($data$container$params);
  829.         } elseif ($container instanceof DataObject\Localizedfield) {
  830.             $context $params['context'];
  831.             $object $context['object'];
  832.             $containerType $context['containerType'] ?? null;
  833.             if ($containerType === 'fieldcollection') {
  834.                 $query 'select ' $db->quoteIdentifier($field) . ' from object_collection_' $context['containerKey'] . '_localized_' $object->getClassId() . ' where language = ' $db->quote($params['language']) . ' and  ooo_id  = ' $object->getId() . ' and fieldname = ' $db->quote($context['fieldname']) . ' and `index` =  ' $context['index'];
  835.             } elseif ($containerType === 'objectbrick') {
  836.                 $query 'select ' $db->quoteIdentifier($field) . ' from object_brick_localized_' $context['containerKey'] . '_' $object->getClassId() . ' where language = ' $db->quote($params['language']) . ' and  ooo_id  = ' $object->getId() . ' and fieldname = ' $db->quote($context['fieldname']);
  837.             } else {
  838.                 $query 'select ' $db->quoteIdentifier($field) . ' from object_localized_data_' $object->getClassId() . ' where language = ' $db->quote($params['language']) . ' and  ooo_id  = ' $object->getId();
  839.             }
  840.             $data $db->fetchOne($query);
  841.             $data $this->getDataFromResource($data$object$params);
  842.         } elseif ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  843.             $context $params['context'];
  844.             $object $context['object'];
  845.             $brickType $context['containerKey'];
  846.             $brickField $context['brickField'];
  847.             $fieldname $context['fieldname'];
  848.             $query 'select ' $db->quoteIdentifier($brickField) . ' from object_brick_store_' $brickType '_' $object->getClassId()
  849.                 . ' where  o_id  = ' $object->getId() . ' and fieldname = ' $db->quote($fieldname);
  850.             $data $db->fetchOne($query);
  851.             $data $this->getDataFromResource($data$object$params);
  852.         } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  853.             $context $params['context'];
  854.             $collectionType $context['containerKey'];
  855.             $object $context['object'];
  856.             $fcField $context['fieldname'];
  857.             //TODO index!!!!!!!!!!!!!!
  858.             $query 'select ' $db->quoteIdentifier($field) . ' from object_collection_' $collectionType '_' $object->getClassId()
  859.                 . ' where  o_id  = ' $object->getId() . ' and fieldname = ' $db->quote($fcField) . ' and `index` = ' $context['index'];
  860.             $data $db->fetchOne($query);
  861.             $data $this->getDataFromResource($data$object$params);
  862.         }
  863.         return $data;
  864.     }
  865.     /**
  866.      * {@inheritdoc}
  867.      */
  868.     public function delete($object$params = [])
  869.     {
  870.     }
  871.     /**
  872.      * {@inheritdoc}
  873.      */
  874.     public function preGetData(/** mixed */ $container/** array */ $params = []) // : mixed
  875.     {
  876.         $data null;
  877.         $params['owner'] = $container;
  878.         $params['fieldname'] = $this->getName();
  879.         if ($container instanceof DataObject\Concrete) {
  880.             $data $container->getObjectVar($this->getName());
  881.             if ($this->getLazyLoading() && !$container->isLazyKeyLoaded($this->getName())) {
  882.                 $data $this->load($container$params);
  883.                 $setter 'set' ucfirst($this->getName());
  884.                 if (method_exists($container$setter)) {
  885.                     $container->$setter($data);
  886.                     $this->markLazyloadedFieldAsLoaded($container);
  887.                 }
  888.             }
  889.         } elseif ($container instanceof DataObject\Localizedfield) {
  890.             $data $params['data'];
  891.         } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  892.             $data $container->getObjectVar($this->getName());
  893.         } elseif ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  894.             $data $container->getObjectVar($this->getName());
  895.         }
  896.         return is_array($data) ? $data : [];
  897.     }
  898.     /**
  899.      * @return int|null
  900.      */
  901.     public function getMaxItems()
  902.     {
  903.         return $this->maxItems;
  904.     }
  905.     /**
  906.      * @param int|null $maxItems
  907.      */
  908.     public function setMaxItems($maxItems)
  909.     {
  910.         $this->maxItems $this->getAsIntegerCast($maxItems);
  911.     }
  912.     /**
  913.      * @return bool
  914.      */
  915.     public function isDisallowAddRemove()
  916.     {
  917.         return $this->disallowAddRemove;
  918.     }
  919.     /**
  920.      * @param bool $disallowAddRemove
  921.      */
  922.     public function setDisallowAddRemove($disallowAddRemove)
  923.     {
  924.         $this->disallowAddRemove = (bool) $disallowAddRemove;
  925.     }
  926.     /**
  927.      * @return bool
  928.      */
  929.     public function isDisallowReorder()
  930.     {
  931.         return $this->disallowReorder;
  932.     }
  933.     /**
  934.      * @param bool $disallowReorder
  935.      */
  936.     public function setDisallowReorder($disallowReorder)
  937.     {
  938.         $this->disallowReorder = (bool) $disallowReorder;
  939.     }
  940.     /**
  941.      * {@inheritdoc}
  942.      */
  943.     public function checkValidity($data$omitMandatoryCheck false$params = [])
  944.     {
  945.         if (!$omitMandatoryCheck) {
  946.             if (is_array($data)) {
  947.                 $blockDefinitions $this->getFieldDefinitions();
  948.                 $validationExceptions = [];
  949.                 $idx = -1;
  950.                 foreach ($data as $item) {
  951.                     $idx++;
  952.                     if (!is_array($item)) {
  953.                         continue;
  954.                     }
  955.                     foreach ($blockDefinitions as $fd) {
  956.                         try {
  957.                             $blockElement $item[$fd->getName()] ?? null;
  958.                             if (!$blockElement) {
  959.                                 if ($fd->getMandatory()) {
  960.                                     throw new Element\ValidationException('Block element empty [ ' $fd->getName() . ' ]');
  961.                                 } else {
  962.                                     continue;
  963.                                 }
  964.                             }
  965.                             $data $blockElement->getData();
  966.                             if ($data instanceof DataObject\Localizedfield && $fd instanceof Localizedfields) {
  967.                                 foreach ($data->getInternalData() as $language => $fields) {
  968.                                     foreach ($fields as $fieldName => $values) {
  969.                                         $lfd $fd->getFieldDefinition($fieldName);
  970.                                         if ($lfd instanceof ManyToManyRelation || $lfd instanceof ManyToManyObjectRelation) {
  971.                                             if (!method_exists($lfd'getAllowMultipleAssignments') || !$lfd->getAllowMultipleAssignments()) {
  972.                                                 $lfd->performMultipleAssignmentCheck($values);
  973.                                             }
  974.                                         }
  975.                                     }
  976.                                 }
  977.                             } elseif ($fd instanceof ManyToManyRelation || $fd instanceof ManyToManyObjectRelation) {
  978.                                 $fd->performMultipleAssignmentCheck($data);
  979.                             }
  980.                             if ($fd instanceof Link) {
  981.                                 $params['resetInvalidFields'] = true;
  982.                             }
  983.                             $fd->checkValidity($datafalse$params);
  984.                         } catch (Model\Element\ValidationException $ve) {
  985.                             $ve->addContext($this->getName() . '-' $idx);
  986.                             $validationExceptions[] = $ve;
  987.                         }
  988.                     }
  989.                 }
  990.                 if ($validationExceptions) {
  991.                     $errors = [];
  992.                     /** @var Element\ValidationException $e */
  993.                     foreach ($validationExceptions as $e) {
  994.                         $errors[] = $e->getAggregatedMessage();
  995.                     }
  996.                     $message implode(' / '$errors);
  997.                     throw new Model\Element\ValidationException($message);
  998.                 }
  999.             }
  1000.         }
  1001.     }
  1002.     /**
  1003.      * This method is called in DataObject\ClassDefinition::save()
  1004.      *
  1005.      * @param DataObject\ClassDefinition $class
  1006.      * @param array $params
  1007.      */
  1008.     public function classSaved($class$params = [])
  1009.     {
  1010.         $blockDefinitions $this->getFieldDefinitions();
  1011.         if (is_array($blockDefinitions)) {
  1012.             foreach ($blockDefinitions as $field) {
  1013.                 if ($field instanceof LazyLoadingSupportInterface && $field->getLazyLoading()) {
  1014.                     // Lazy loading inside blocks isn't supported, turn it off if possible
  1015.                     if (method_exists($field'setLazyLoading')) {
  1016.                         $field->setLazyLoading(false);
  1017.                     }
  1018.                 }
  1019.             }
  1020.         }
  1021.     }
  1022.     /**
  1023.      * {@inheritdoc}
  1024.      */
  1025.     public function getParameterTypeDeclaration(): ?string
  1026.     {
  1027.         return '?array';
  1028.     }
  1029.     /**
  1030.      * {@inheritdoc}
  1031.      */
  1032.     public function getReturnTypeDeclaration(): ?string
  1033.     {
  1034.         return '?array';
  1035.     }
  1036.     private function setBlockElementOwner(DataObject\Data\BlockElement $blockElement$params = [])
  1037.     {
  1038.         if (!isset($params['owner'])) {
  1039.             throw new \Error('owner missing');
  1040.         } else {
  1041.             // addition check. if owner is passed but no fieldname then there is something wrong with the params.
  1042.             if (!array_key_exists('fieldname'$params)) {
  1043.                 // do not throw an exception because it is silently swallowed by the caller
  1044.                 throw new \Error('params contains owner but no fieldname');
  1045.             }
  1046.             if ($params['owner'] instanceof DataObject\Localizedfield) {
  1047.                 //make sure that for a localized field parent the language param is set and not empty
  1048.                 if (($params['language'] ?? null) === null) {
  1049.                     throw new \Error('language param missing');
  1050.                 }
  1051.             }
  1052.             $blockElement->_setOwner($params['owner']);
  1053.             $blockElement->_setOwnerFieldname($params['fieldname']);
  1054.             $blockElement->_setOwnerLanguage($params['language'] ?? null);
  1055.         }
  1056.     }
  1057.     /**
  1058.      * {@inheritdoc}
  1059.      */
  1060.     public function getPhpdocInputType(): ?string
  1061.     {
  1062.         return '\\' DataObject\Data\BlockElement::class . '[][]';
  1063.     }
  1064.     /**
  1065.      * {@inheritdoc}
  1066.      */
  1067.     public function getPhpdocReturnType(): ?string
  1068.     {
  1069.         return '\\' .DataObject\Data\BlockElement::class . '[][]';
  1070.     }
  1071.     /**
  1072.      * {@inheritdoc}
  1073.      */
  1074.     public function normalize($value$params = [])
  1075.     {
  1076.         $result null;
  1077.         if ($value) {
  1078.             $result = [];
  1079.             $fieldDefinitions $this->getFieldDefinitions();
  1080.             foreach ($value as $block) {
  1081.                 $resultItem = [];
  1082.                 /**
  1083.                  * @var  string $key
  1084.                  * @var  DataObject\Data\BlockElement $fieldValue
  1085.                  */
  1086.                 foreach ($block as $key => $fieldValue) {
  1087.                     $fd $fieldDefinitions[$key];
  1088.                     if ($fd instanceof NormalizerInterface) {
  1089.                         $normalizedData $fd->normalize($fieldValue->getData(), [
  1090.                             'object' => $params['object'] ?? null,
  1091.                             'fieldDefinition' => $fd,
  1092.                         ]);
  1093.                         $resultItem[$key] = $normalizedData;
  1094.                     } else {
  1095.                         throw new \Exception('data type ' $fd->getFieldtype() . ' does not implement normalizer interface');
  1096.                     }
  1097.                 }
  1098.                 $result[] = $resultItem;
  1099.             }
  1100.         }
  1101.         return $result;
  1102.     }
  1103.     /**
  1104.      * {@inheritdoc}
  1105.      */
  1106.     public function denormalize($value$params = [])
  1107.     {
  1108.         if (is_array($value)) {
  1109.             $result = [];
  1110.             $fieldDefinitions $this->getFieldDefinitions();
  1111.             foreach ($value as $idx => $blockItem) {
  1112.                 $resultItem = [];
  1113.                 /**
  1114.                  * @var  string $key
  1115.                  * @var  DataObject\Data\BlockElement $fieldValue
  1116.                  */
  1117.                 foreach ($blockItem as $key => $fieldValue) {
  1118.                     $fd $fieldDefinitions[$key];
  1119.                     if ($fd instanceof NormalizerInterface) {
  1120.                         $denormalizedData $fd->denormalize($fieldValue, [
  1121.                             'object' => $params['object'],
  1122.                             'fieldDefinition' => $fd,
  1123.                         ]);
  1124.                         $resultItem[$key] = $denormalizedData;
  1125.                     } else {
  1126.                         throw new \Exception('data type does not implement normalizer interface');
  1127.                     }
  1128.                 }
  1129.                 $result[] = $resultItem;
  1130.             }
  1131.             return $result;
  1132.         }
  1133.         return null;
  1134.     }
  1135.     /**
  1136.      * @param array $data
  1137.      *
  1138.      * @return static
  1139.      */
  1140.     public static function __set_state($data)
  1141.     {
  1142.         $obj = new static();
  1143.         $obj->setValues($data);
  1144.         $obj->childs $obj->children;  // @phpstan-ignore-line
  1145.         return $obj;
  1146.     }
  1147. }