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

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