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

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 array $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 string $data
  171.      * @param DataObject\Concrete|null $object
  172.      * @param mixed $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 array|null $data
  245.      * @param null|DataObject\Concrete $object
  246.      * @param mixed $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 array $data
  287.      * @param null|DataObject\Concrete $object
  288.      * @param mixed $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 array|null $data
  404.      * @param DataObject\Concrete|null $object
  405.      * @param mixed $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 mixed $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 $masterDefinition
  445.      */
  446.     public function synchronizeWithMasterDefinition(Model\DataObject\ClassDefinition\Data $masterDefinition)
  447.     {
  448.         $this->disallowAddRemove $masterDefinition->disallowAddRemove;
  449.         $this->disallowReorder $masterDefinition->disallowReorder;
  450.         $this->collapsible $masterDefinition->collapsible;
  451.         $this->collapsed $masterDefinition->collapsed;
  452.     }
  453.     /**
  454.      * @param DataObject\Data\BlockElement[][]|null $data
  455.      *
  456.      * @return bool
  457.      */
  458.     public function isEmpty($data)
  459.     {
  460.         return is_null($data) || count($data) === 0;
  461.     }
  462.     /**
  463.      * @return array
  464.      */
  465.     public function getChildren()
  466.     {
  467.         return $this->children;
  468.     }
  469.     /**
  470.      * @param array $children
  471.      *
  472.      * @return $this
  473.      */
  474.     public function setChildren($children)
  475.     {
  476.         $this->children $children;
  477.         $this->fieldDefinitionsCache null;
  478.         return $this;
  479.     }
  480.     /**
  481.      * @return bool
  482.      */
  483.     public function hasChildren()
  484.     {
  485.         if (is_array($this->children) && count($this->children) > 0) {
  486.             return true;
  487.         }
  488.         return false;
  489.     }
  490.     /**
  491.      * @param Data|Layout $child
  492.      */
  493.     public function addChild($child)
  494.     {
  495.         $this->children[] = $child;
  496.         $this->fieldDefinitionsCache null;
  497.     }
  498.     /**
  499.      * @param array|null $layout
  500.      *
  501.      * @return $this
  502.      */
  503.     public function setLayout($layout)
  504.     {
  505.         $this->layout $layout;
  506.         return $this;
  507.     }
  508.     /**
  509.      * @return array|null
  510.      */
  511.     public function getLayout()
  512.     {
  513.         return $this->layout;
  514.     }
  515.     /**
  516.      * @param mixed $def
  517.      * @param array $fields
  518.      *
  519.      * @return array
  520.      */
  521.     public function doGetFieldDefinitions($def null$fields = [])
  522.     {
  523.         if ($def === null) {
  524.             $def $this->getChildren();
  525.         }
  526.         if (is_array($def)) {
  527.             foreach ($def as $child) {
  528.                 $fields array_merge($fields$this->doGetFieldDefinitions($child$fields));
  529.             }
  530.         }
  531.         if ($def instanceof DataObject\ClassDefinition\Layout) {
  532.             if ($def->hasChildren()) {
  533.                 foreach ($def->getChildren() as $child) {
  534.                     $fields array_merge($fields$this->doGetFieldDefinitions($child$fields));
  535.                 }
  536.             }
  537.         }
  538.         if ($def instanceof DataObject\ClassDefinition\Data) {
  539.             $existing $fields[$def->getName()] ?? false;
  540.             if ($existing && method_exists($existing'addReferencedField')) {
  541.                 // this is especially for localized fields which get aggregated here into one field definition
  542.                 // in the case that there are more than one localized fields in the class definition
  543.                 // see also pimcore.object.edit.addToDataFields();
  544.                 $existing->addReferencedField($def);
  545.             } else {
  546.                 $fields[$def->getName()] = $def;
  547.             }
  548.         }
  549.         return $fields;
  550.     }
  551.     /**
  552.      * @param array $context additional contextual data
  553.      *
  554.      * @return DataObject\ClassDefinition\Data[]
  555.      */
  556.     public function getFieldDefinitions($context = [])
  557.     {
  558.         if (empty($this->fieldDefinitionsCache)) {
  559.             $definitions $this->doGetFieldDefinitions();
  560.             foreach ($this->getReferencedFields() as $rf) {
  561.                 if ($rf instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  562.                     $definitions array_merge($definitions$this->doGetFieldDefinitions($rf->getChildren()));
  563.                 }
  564.             }
  565.             $this->fieldDefinitionsCache $definitions;
  566.         }
  567.         if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  568.             return $this->fieldDefinitionsCache;
  569.         }
  570.         $enrichedFieldDefinitions = [];
  571.         if (is_array($this->fieldDefinitionsCache)) {
  572.             foreach ($this->fieldDefinitionsCache as $key => $fieldDefinition) {
  573.                 $fieldDefinition $this->doEnrichFieldDefinition($fieldDefinition$context);
  574.                 $enrichedFieldDefinitions[$key] = $fieldDefinition;
  575.             }
  576.         }
  577.         return $enrichedFieldDefinitions;
  578.     }
  579.     /**
  580.      * @param string $name
  581.      * @param array $context additional contextual data
  582.      *
  583.      * @return DataObject\ClassDefinition\Data|null
  584.      */
  585.     public function getFieldDefinition($name$context = [])
  586.     {
  587.         $fds $this->getFieldDefinitions();
  588.         if (isset($fds[$name])) {
  589.             if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  590.                 return $fds[$name];
  591.             }
  592.             $fieldDefinition $this->doEnrichFieldDefinition($fds[$name], $context);
  593.             return $fieldDefinition;
  594.         }
  595.         return null;
  596.     }
  597.     protected function doEnrichFieldDefinition($fieldDefinition$context = [])
  598.     {
  599.         //TODO Pimcore 11: remove method_exists BC layer
  600.         if ($fieldDefinition instanceof FieldDefinitionEnrichmentInterface || method_exists($fieldDefinition'enrichFieldDefinition')) {
  601.             if (!$fieldDefinition instanceof FieldDefinitionEnrichmentInterface) {
  602.                 trigger_deprecation('pimcore/pimcore''10.1',
  603.                     sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  604.                     'Implement the %s interface instead.'FieldDefinitionEnrichmentInterface::class));
  605.             }
  606.             $context['containerType'] = 'block';
  607.             $context['containerKey'] = $this->getName();
  608.             $fieldDefinition $fieldDefinition->enrichFieldDefinition($context);
  609.         }
  610.         return $fieldDefinition;
  611.     }
  612.     /**
  613.      * @param array $referencedFields
  614.      */
  615.     public function setReferencedFields($referencedFields)
  616.     {
  617.         $this->referencedFields $referencedFields;
  618.         $this->fieldDefinitionsCache null;
  619.     }
  620.     /**
  621.      * @return Data[]
  622.      */
  623.     public function getReferencedFields()
  624.     {
  625.         return $this->referencedFields;
  626.     }
  627.     /**
  628.      * @param Data $field
  629.      */
  630.     public function addReferencedField($field)
  631.     {
  632.         $this->referencedFields[] = $field;
  633.         $this->fieldDefinitionsCache null;
  634.     }
  635.     /**
  636.      * @return array
  637.      */
  638.     public function getBlockedVarsForExport(): array
  639.     {
  640.         return [
  641.             'fieldDefinitionsCache',
  642.             'referencedFields',
  643.             'blockedVarsForExport',
  644.             'childs',
  645.         ];
  646.     }
  647.     /**
  648.      * @return array
  649.      */
  650.     public function __sleep()
  651.     {
  652.         $vars get_object_vars($this);
  653.         $blockedVars $this->getBlockedVarsForExport();
  654.         foreach ($blockedVars as $blockedVar) {
  655.             unset($vars[$blockedVar]);
  656.         }
  657.         return array_keys($vars);
  658.     }
  659.     /**
  660.      * @param array|null $data
  661.      *
  662.      * @return array
  663.      */
  664.     public function resolveDependencies($data)
  665.     {
  666.         $dependencies = [];
  667.         if (!is_array($data)) {
  668.             return [];
  669.         }
  670.         foreach ($data as $blockElements) {
  671.             foreach ($blockElements as $elementName => $blockElement) {
  672.                 $fd $this->getFieldDefinition($elementName);
  673.                 if (!$fd) {
  674.                     // class definition seems to have changed
  675.                     Logger::warn('class definition seems to have changed, element name: ' $elementName);
  676.                     continue;
  677.                 }
  678.                 $elementData $blockElement->getData();
  679.                 $dependencies array_merge($dependencies$fd->resolveDependencies($elementData));
  680.             }
  681.         }
  682.         return $dependencies;
  683.     }
  684.     /**
  685.      * {@inheritdoc}
  686.      */
  687.     public function getCacheTags($data, array $tags = [])
  688.     {
  689.         if ($this->getLazyLoading()) {
  690.             return $tags;
  691.         }
  692.         if (!is_array($data)) {
  693.             return $tags;
  694.         }
  695.         foreach ($data as $blockElements) {
  696.             foreach ($blockElements as $elementName => $blockElement) {
  697.                 $fd $this->getFieldDefinition($elementName);
  698.                 if (!$fd) {
  699.                     // class definition seems to have changed
  700.                     Logger::warn('class definition seems to have changed, element name: ' $elementName);
  701.                     continue;
  702.                 }
  703.                 $data $blockElement->getData();
  704.                 $tags $fd->getCacheTags($data$tags);
  705.             }
  706.         }
  707.         return $tags;
  708.     }
  709.     /**
  710.      * @return bool
  711.      */
  712.     public function isCollapsed()
  713.     {
  714.         return $this->collapsed;
  715.     }
  716.     /**
  717.      * @param bool $collapsed
  718.      */
  719.     public function setCollapsed($collapsed)
  720.     {
  721.         $this->collapsed = (bool) $collapsed;
  722.     }
  723.     /**
  724.      * @return bool
  725.      */
  726.     public function isCollapsible()
  727.     {
  728.         return $this->collapsible;
  729.     }
  730.     /**
  731.      * @param bool $collapsible
  732.      */
  733.     public function setCollapsible($collapsible)
  734.     {
  735.         $this->collapsible = (bool) $collapsible;
  736.     }
  737.     /**
  738.      * @return string
  739.      */
  740.     public function getStyleElement()
  741.     {
  742.         return $this->styleElement;
  743.     }
  744.     /**
  745.      * @param string $styleElement
  746.      *
  747.      * @return $this
  748.      */
  749.     public function setStyleElement($styleElement)
  750.     {
  751.         $this->styleElement $styleElement;
  752.         return $this;
  753.     }
  754.     /**
  755.      * @return bool
  756.      */
  757.     public function getLazyLoading()
  758.     {
  759.         return $this->lazyLoading;
  760.     }
  761.     /**
  762.      * @param bool $lazyLoading
  763.      *
  764.      * @return $this
  765.      */
  766.     public function setLazyLoading($lazyLoading)
  767.     {
  768.         $this->lazyLoading = (bool) $lazyLoading;
  769.         return $this;
  770.     }
  771.     /**
  772.      * { @inheritdoc }
  773.      */
  774.     public function preSetData(/** mixed */ $container/**  mixed */ $data/** array */ $params = []) // : mixed
  775.     {
  776.         $this->markLazyloadedFieldAsLoaded($container);
  777.         $lf $this->getFieldDefinition('localizedfields');
  778.         if ($lf && is_array($data)) {
  779.             foreach ($data as $item) {
  780.                 if (is_array($item)) {
  781.                     foreach ($item as $itemElement) {
  782.                         if ($itemElement->getType() === 'localizedfields') {
  783.                             /** @var DataObject\Localizedfield $itemElementData */
  784.                             $itemElementData $itemElement->getData();
  785.                             $itemElementData->setObject($container);
  786.                             // the localized field needs at least the containerType as this is important
  787.                             // for lazy loading
  788.                             $context $itemElementData->getContext() ? $itemElementData->getContext() : [];
  789.                             $context['containerType'] = 'block';
  790.                             $context['containerKey'] = $this->getName();
  791.                             $itemElementData->setContext($context);
  792.                         }
  793.                     }
  794.                 }
  795.             }
  796.         }
  797.         return $data;
  798.     }
  799.     /**
  800.      * {@inheritdoc}
  801.      */
  802.     public function save($object$params = [])
  803.     {
  804.     }
  805.     /**
  806.      * {@inheritdoc}
  807.      */
  808.     public function load($container$params = [])
  809.     {
  810.         $field $this->getName();
  811.         $db Db::get();
  812.         $data null;
  813.         if ($container instanceof DataObject\Concrete) {
  814.             $query 'select ' $db->quoteIdentifier($field) . ' from object_store_' $container->getClassId() . ' where oo_id  = ' $container->getId();
  815.             $data $db->fetchOne($query);
  816.             $data $this->getDataFromResource($data$container$params);
  817.         } elseif ($container instanceof DataObject\Localizedfield) {
  818.             $context $params['context'];
  819.             $object $context['object'];
  820.             $containerType $context['containerType'] ?? null;
  821.             if ($containerType === 'fieldcollection') {
  822.                 $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'];
  823.             } elseif ($containerType === 'objectbrick') {
  824.                 $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']);
  825.             } else {
  826.                 $query 'select ' $db->quoteIdentifier($field) . ' from object_localized_data_' $object->getClassId() . ' where language = ' $db->quote($params['language']) . ' and  ooo_id  = ' $object->getId();
  827.             }
  828.             $data $db->fetchOne($query);
  829.             $data $this->getDataFromResource($data$object$params);
  830.         } elseif ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  831.             $context $params['context'];
  832.             $object $context['object'];
  833.             $brickType $context['containerKey'];
  834.             $brickField $context['brickField'];
  835.             $fieldname $context['fieldname'];
  836.             $query 'select ' $db->quoteIdentifier($brickField) . ' from object_brick_store_' $brickType '_' $object->getClassId()
  837.                 . ' where  o_id  = ' $object->getId() . ' and fieldname = ' $db->quote($fieldname);
  838.             $data $db->fetchOne($query);
  839.             $data $this->getDataFromResource($data$object$params);
  840.         } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  841.             $context $params['context'];
  842.             $collectionType $context['containerKey'];
  843.             $object $context['object'];
  844.             $fcField $context['fieldname'];
  845.             //TODO index!!!!!!!!!!!!!!
  846.             $query 'select ' $db->quoteIdentifier($field) . ' from object_collection_' $collectionType '_' $object->getClassId()
  847.                 . ' where  o_id  = ' $object->getId() . ' and fieldname = ' $db->quote($fcField) . ' and `index` = ' $context['index'];
  848.             $data $db->fetchOne($query);
  849.             $data $this->getDataFromResource($data$object$params);
  850.         }
  851.         return $data;
  852.     }
  853.     /**
  854.      * {@inheritdoc}
  855.      */
  856.     public function delete($object$params = [])
  857.     {
  858.     }
  859.     /**
  860.      * {@inheritdoc}
  861.      */
  862.     public function preGetData(/** mixed */ $container/** array */ $params = []) // : mixed
  863.     {
  864.         $data null;
  865.         $params['owner'] = $container;
  866.         $params['fieldname'] = $this->getName();
  867.         if ($container instanceof DataObject\Concrete) {
  868.             $data $container->getObjectVar($this->getName());
  869.             if ($this->getLazyLoading() && !$container->isLazyKeyLoaded($this->getName())) {
  870.                 $data $this->load($container$params);
  871.                 $setter 'set' ucfirst($this->getName());
  872.                 if (method_exists($container$setter)) {
  873.                     $container->$setter($data);
  874.                     $this->markLazyloadedFieldAsLoaded($container);
  875.                 }
  876.             }
  877.         } elseif ($container instanceof DataObject\Localizedfield) {
  878.             $data $params['data'];
  879.         } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  880.             $data $container->getObjectVar($this->getName());
  881.         } elseif ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  882.             $data $container->getObjectVar($this->getName());
  883.         }
  884.         return is_array($data) ? $data : [];
  885.     }
  886.     /**
  887.      * @return int|null
  888.      */
  889.     public function getMaxItems()
  890.     {
  891.         return $this->maxItems;
  892.     }
  893.     /**
  894.      * @param int|null $maxItems
  895.      */
  896.     public function setMaxItems($maxItems)
  897.     {
  898.         $this->maxItems $this->getAsIntegerCast($maxItems);
  899.     }
  900.     /**
  901.      * @return bool
  902.      */
  903.     public function isDisallowAddRemove()
  904.     {
  905.         return $this->disallowAddRemove;
  906.     }
  907.     /**
  908.      * @param bool $disallowAddRemove
  909.      */
  910.     public function setDisallowAddRemove($disallowAddRemove)
  911.     {
  912.         $this->disallowAddRemove = (bool) $disallowAddRemove;
  913.     }
  914.     /**
  915.      * @return bool
  916.      */
  917.     public function isDisallowReorder()
  918.     {
  919.         return $this->disallowReorder;
  920.     }
  921.     /**
  922.      * @param bool $disallowReorder
  923.      */
  924.     public function setDisallowReorder($disallowReorder)
  925.     {
  926.         $this->disallowReorder = (bool) $disallowReorder;
  927.     }
  928.     /**
  929.      * {@inheritdoc}
  930.      */
  931.     public function checkValidity($data$omitMandatoryCheck false$params = [])
  932.     {
  933.         if (!$omitMandatoryCheck) {
  934.             if (is_array($data)) {
  935.                 $blockDefinitions $this->getFieldDefinitions();
  936.                 $validationExceptions = [];
  937.                 $idx = -1;
  938.                 foreach ($data as $item) {
  939.                     $idx++;
  940.                     if (!is_array($item)) {
  941.                         continue;
  942.                     }
  943.                     foreach ($blockDefinitions as $fd) {
  944.                         try {
  945.                             $blockElement $item[$fd->getName()] ?? null;
  946.                             if (!$blockElement) {
  947.                                 if ($fd->getMandatory()) {
  948.                                     throw new Element\ValidationException('Block element empty [ ' $fd->getName() . ' ]');
  949.                                 } else {
  950.                                     continue;
  951.                                 }
  952.                             }
  953.                             $data $blockElement->getData();
  954.                             if ($data instanceof DataObject\Localizedfield && $fd instanceof Localizedfields) {
  955.                                 foreach ($data->getInternalData() as $language => $fields) {
  956.                                     foreach ($fields as $fieldName => $values) {
  957.                                         $lfd $fd->getFieldDefinition($fieldName);
  958.                                         if ($lfd instanceof ManyToManyRelation || $lfd instanceof ManyToManyObjectRelation) {
  959.                                             if (!method_exists($lfd'getAllowMultipleAssignments') || !$lfd->getAllowMultipleAssignments()) {
  960.                                                 $lfd->performMultipleAssignmentCheck($values);
  961.                                             }
  962.                                         }
  963.                                     }
  964.                                 }
  965.                             } elseif ($fd instanceof ManyToManyRelation || $fd instanceof ManyToManyObjectRelation) {
  966.                                 $fd->performMultipleAssignmentCheck($data);
  967.                             }
  968.                             $fd->checkValidity($datafalse$params);
  969.                         } catch (Model\Element\ValidationException $ve) {
  970.                             $ve->addContext($this->getName() . '-' $idx);
  971.                             $validationExceptions[] = $ve;
  972.                         }
  973.                     }
  974.                 }
  975.                 if ($validationExceptions) {
  976.                     $errors = [];
  977.                     /** @var Element\ValidationException $e */
  978.                     foreach ($validationExceptions as $e) {
  979.                         $errors[] = $e->getAggregatedMessage();
  980.                     }
  981.                     $message implode(' / '$errors);
  982.                     throw new Model\Element\ValidationException($message);
  983.                 }
  984.             }
  985.         }
  986.     }
  987.     /**
  988.      * This method is called in DataObject\ClassDefinition::save()
  989.      *
  990.      * @param DataObject\ClassDefinition $class
  991.      * @param array $params
  992.      */
  993.     public function classSaved($class$params = [])
  994.     {
  995.         $blockDefinitions $this->getFieldDefinitions();
  996.         if (is_array($blockDefinitions)) {
  997.             foreach ($blockDefinitions as $field) {
  998.                 if ($field instanceof LazyLoadingSupportInterface && $field->getLazyLoading()) {
  999.                     // Lazy loading inside blocks isn't supported, turn it off if possible
  1000.                     if (method_exists($field'setLazyLoading')) {
  1001.                         $field->setLazyLoading(false);
  1002.                     }
  1003.                 }
  1004.             }
  1005.         }
  1006.     }
  1007.     /**
  1008.      * {@inheritdoc}
  1009.      */
  1010.     public function getParameterTypeDeclaration(): ?string
  1011.     {
  1012.         return '?array';
  1013.     }
  1014.     /**
  1015.      * {@inheritdoc}
  1016.      */
  1017.     public function getReturnTypeDeclaration(): ?string
  1018.     {
  1019.         return '?array';
  1020.     }
  1021.     private function setBlockElementOwner(DataObject\Data\BlockElement $blockElement$params = [])
  1022.     {
  1023.         if (!isset($params['owner'])) {
  1024.             throw new \Error('owner missing');
  1025.         } else {
  1026.             // addition check. if owner is passed but no fieldname then there is something wrong with the params.
  1027.             if (!array_key_exists('fieldname'$params)) {
  1028.                 // do not throw an exception because it is silently swallowed by the caller
  1029.                 throw new \Error('params contains owner but no fieldname');
  1030.             }
  1031.             if ($params['owner'] instanceof DataObject\Localizedfield) {
  1032.                 //make sure that for a localized field parent the language param is set and not empty
  1033.                 if (($params['language'] ?? null) === null) {
  1034.                     throw new \Error('language param missing');
  1035.                 }
  1036.             }
  1037.             $blockElement->_setOwner($params['owner']);
  1038.             $blockElement->_setOwnerFieldname($params['fieldname']);
  1039.             $blockElement->_setOwnerLanguage($params['language'] ?? null);
  1040.         }
  1041.     }
  1042.     /**
  1043.      * {@inheritdoc}
  1044.      */
  1045.     public function getPhpdocInputType(): ?string
  1046.     {
  1047.         return '\\' DataObject\Data\BlockElement::class . '[][]';
  1048.     }
  1049.     /**
  1050.      * {@inheritdoc}
  1051.      */
  1052.     public function getPhpdocReturnType(): ?string
  1053.     {
  1054.         return '\\' .DataObject\Data\BlockElement::class . '[][]';
  1055.     }
  1056.     /**
  1057.      * {@inheritdoc}
  1058.      */
  1059.     public function normalize($value$params = [])
  1060.     {
  1061.         $result null;
  1062.         if ($value) {
  1063.             $result = [];
  1064.             $fieldDefinitions $this->getFieldDefinitions();
  1065.             foreach ($value as $block) {
  1066.                 $resultItem = [];
  1067.                 /**
  1068.                  * @var  string $key
  1069.                  * @var  DataObject\Data\BlockElement $fieldValue
  1070.                  */
  1071.                 foreach ($block as $key => $fieldValue) {
  1072.                     $fd $fieldDefinitions[$key];
  1073.                     if ($fd instanceof NormalizerInterface) {
  1074.                         $normalizedData $fd->normalize($fieldValue->getData(), [
  1075.                             'object' => $params['object'] ?? null,
  1076.                             'fieldDefinition' => $fd,
  1077.                         ]);
  1078.                         $resultItem[$key] = $normalizedData;
  1079.                     } else {
  1080.                         throw new \Exception('data type ' $fd->getFieldtype() . ' does not implement normalizer interface');
  1081.                     }
  1082.                 }
  1083.                 $result[] = $resultItem;
  1084.             }
  1085.         }
  1086.         return $result;
  1087.     }
  1088.     /**
  1089.      * {@inheritdoc}
  1090.      */
  1091.     public function denormalize($value$params = [])
  1092.     {
  1093.         if (is_array($value)) {
  1094.             $result = [];
  1095.             $fieldDefinitions $this->getFieldDefinitions();
  1096.             foreach ($value as $idx => $blockItem) {
  1097.                 $resultItem = [];
  1098.                 /**
  1099.                  * @var  string $key
  1100.                  * @var  DataObject\Data\BlockElement $fieldValue
  1101.                  */
  1102.                 foreach ($blockItem as $key => $fieldValue) {
  1103.                     $fd $fieldDefinitions[$key];
  1104.                     if ($fd instanceof NormalizerInterface) {
  1105.                         $denormalizedData $fd->denormalize($fieldValue, [
  1106.                             'object' => $params['object'],
  1107.                             'fieldDefinition' => $fd,
  1108.                         ]);
  1109.                         $resultItem[$key] = $denormalizedData;
  1110.                     } else {
  1111.                         throw new \Exception('data type does not implement normalizer interface');
  1112.                     }
  1113.                 }
  1114.                 $result[] = $resultItem;
  1115.             }
  1116.             return $result;
  1117.         }
  1118.         return null;
  1119.     }
  1120.     public static function __set_state($data)
  1121.     {
  1122.         $obj = new static();
  1123.         $obj->setValues($data);
  1124.         $obj->childs $obj->children;  // @phpstan-ignore-line
  1125.         return $obj;
  1126.     }
  1127. }