vendor/pimcore/pimcore/models/DataObject/ClassDefinition/Data/Localizedfields.php line 1451

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Model\DataObject\ClassDefinition\Data;
  15. use Pimcore\Logger;
  16. use Pimcore\Model;
  17. use Pimcore\Model\DataObject;
  18. use Pimcore\Model\DataObject\ClassDefinition\Data;
  19. use Pimcore\Model\DataObject\ClassDefinition\Layout;
  20. use Pimcore\Model\Element;
  21. use Pimcore\Normalizer\NormalizerInterface;
  22. use Pimcore\Tool;
  23. class Localizedfields extends Data implements CustomResourcePersistingInterfaceTypeDeclarationSupportInterfaceNormalizerInterfaceDataContainerAwareInterfaceIdRewriterInterfacePreGetDataInterfaceVarExporterInterface
  24. {
  25.     use Element\ChildsCompatibilityTrait;
  26.     use Layout\Traits\LabelTrait;
  27.     use DataObject\Traits\ClassSavedTrait;
  28.     /**
  29.      * Static type of this element
  30.      *
  31.      * @internal
  32.      *
  33.      * @var string
  34.      */
  35.     public $fieldtype 'localizedfields';
  36.     /**
  37.      * @internal
  38.      *
  39.      * @var array
  40.      */
  41.     public $children = [];
  42.     /**
  43.      * @internal
  44.      *
  45.      * @var string
  46.      */
  47.     public $name;
  48.     /**
  49.      * @internal
  50.      *
  51.      * @var string
  52.      */
  53.     public $region;
  54.     /**
  55.      * @internal
  56.      *
  57.      * @var string
  58.      */
  59.     public $layout;
  60.     /**
  61.      * @internal
  62.      *
  63.      * @var string
  64.      */
  65.     public $title;
  66.     /**
  67.      * @internal
  68.      *
  69.      * @var string|int
  70.      */
  71.     public $width 0;
  72.     /**
  73.      * @internal
  74.      *
  75.      * @var string|int
  76.      */
  77.     public $height 0;
  78.     /**
  79.      * @internal
  80.      *
  81.      * @var int
  82.      */
  83.     public $maxTabs;
  84.     /**
  85.      * @internal
  86.      *
  87.      * @var bool
  88.      */
  89.     public $border false;
  90.     /**
  91.      * @internal
  92.      *
  93.      * @var bool
  94.      */
  95.     public $provideSplitView;
  96.     /**
  97.      * @internal
  98.      *
  99.      * @var string|null
  100.      */
  101.     public $tabPosition 'top';
  102.     /**
  103.      * @internal
  104.      *
  105.      * @var int
  106.      */
  107.     public $hideLabelsWhenTabsReached;
  108.     /**
  109.      * contains further localized field definitions if there are more than one localized fields in on class
  110.      *
  111.      * @internal
  112.      *
  113.      * @var array
  114.      */
  115.     protected $referencedFields = [];
  116.     /**
  117.      * @internal
  118.      *
  119.      * @var array|null
  120.      */
  121.     public $fieldDefinitionsCache;
  122.     /**
  123.      * @internal
  124.      *
  125.      * @var array|null
  126.      */
  127.     public $permissionView;
  128.     /**
  129.      * @internal
  130.      *
  131.      * @var array|null
  132.      */
  133.     public $permissionEdit;
  134.     /**
  135.      * @see Data::getDataForEditmode
  136.      *
  137.      * @param DataObject\Localizedfield $localizedField
  138.      * @param null|DataObject\Concrete $object
  139.      * @param mixed $params
  140.      *
  141.      * @return array
  142.      */
  143.     public function getDataForEditmode($localizedField$object null$params = [])
  144.     {
  145.         $fieldData = [];
  146.         $metaData = [];
  147.         if (!$localizedField instanceof DataObject\Localizedfield) {
  148.             return [];
  149.         }
  150.         $result $this->doGetDataForEditMode($localizedField$object$fieldData$metaData1$params);
  151.         // replace the real data with the data for the editmode
  152.         foreach ($result['data'] as $language => &$data) {
  153.             foreach ($data as $key => &$value) {
  154.                 $fieldDefinition $this->getFieldDefinition($key);
  155.                 if ($fieldDefinition instanceof CalculatedValue) {
  156.                     $childData = new DataObject\Data\CalculatedValue($fieldDefinition->getName());
  157.                     $ownerType $params['context']['containerType'] ?? 'localizedfield';
  158.                     $ownerName $params['fieldname'] ?? $this->getName();
  159.                     $index $params['context']['containerKey'] ?? null;
  160.                     $childData->setContextualData($ownerType$ownerName$index$languagenullnull$fieldDefinition);
  161.                     $value $fieldDefinition->getDataForEditmode($childData$object$params);
  162.                 } else {
  163.                     $value $fieldDefinition->getDataForEditmode($value$objectarray_merge($params$localizedField->getDao()->getFieldDefinitionParams($fieldDefinition->getName(), $language)));
  164.                 }
  165.             }
  166.         }
  167.         return $result;
  168.     }
  169.     /**
  170.      * @param DataObject\Localizedfield $data
  171.      * @param DataObject\Concrete $object
  172.      * @param array $fieldData
  173.      * @param array $metaData
  174.      * @param int $level
  175.      * @param array $params
  176.      *
  177.      * @return array
  178.      */
  179.     private function doGetDataForEditMode($data$object, &$fieldData, &$metaData$level 1$params = [])
  180.     {
  181.         $class $object->getClass();
  182.         $inheritanceAllowed $class->getAllowInherit();
  183.         $inherited false;
  184.         $loadLazy = !($params['objectFromVersion'] ?? false);
  185.         $dataItems $data->getInternalData($loadLazy);
  186.         foreach ($dataItems as $language => $values) {
  187.             foreach ($this->getFieldDefinitions() as $fd) {
  188.                 if ($fd instanceof LazyLoadingSupportInterface && $fd->getLazyLoading() && $loadLazy) {
  189.                     $lazyKey $data->buildLazyKey($fd->getName(), $language);
  190.                     if (!$data->isLazyKeyLoaded($lazyKey) && $fd instanceof CustomResourcePersistingInterface) {
  191.                         $params['language'] = $language;
  192.                         $params['object'] = $object;
  193.                         if (!isset($params['context'])) {
  194.                             $params['context'] = [];
  195.                         }
  196.                         $params['context']['object'] = $object;
  197.                         $value $fd->load($data$params);
  198.                         if ($value === || !empty($value)) {
  199.                             $data->setLocalizedValue($fd->getName(), $value$languagefalse);
  200.                             $values[$fd->getName()] = $value;
  201.                         }
  202.                         $data->markLazyKeyAsLoaded($lazyKey);
  203.                     }
  204.                 }
  205.                 $key $fd->getName();
  206.                 $fdata = isset($values[$fd->getName()]) ? $values[$fd->getName()] : null;
  207.                 if (!isset($fieldData[$language][$key]) || $fd->isEmpty($fieldData[$language][$key])) {
  208.                     // never override existing data
  209.                     $fieldData[$language][$key] = $fdata;
  210.                     if (!$fd->isEmpty($fdata)) {
  211.                         $inherited $level 1;
  212.                         if (isset($params['context']['containerType']) && $params['context']['containerType'] === 'block') {
  213.                             $inherited false;
  214.                         }
  215.                         $metaData[$language][$key] = ['inherited' => $inherited'objectid' => $object->getId()];
  216.                     }
  217.                 }
  218.             }
  219.         }
  220.         if (isset($params['context']['containerType']) && $params['context']['containerType'] === 'block') {
  221.             $inheritanceAllowed false;
  222.         }
  223.         if ($inheritanceAllowed) {
  224.             // check if there is a parent with the same type
  225.             $parent DataObject\Service::hasInheritableParentObject($object);
  226.             if ($parent) {
  227.                 // same type, iterate over all language and all fields and check if there is something missing
  228.                 $validLanguages Tool::getValidLanguages();
  229.                 $foundEmptyValue false;
  230.                 foreach ($validLanguages as $language) {
  231.                     $fieldDefinitions $this->getFieldDefinitions();
  232.                     foreach ($fieldDefinitions as $fd) {
  233.                         $key $fd->getName();
  234.                         if ($fd->isEmpty($fieldData[$language][$key] ?? null)) {
  235.                             $foundEmptyValue true;
  236.                             $inherited true;
  237.                             $metaData[$language][$key] = ['inherited' => true'objectid' => $parent->getId()];
  238.                         }
  239.                     }
  240.                 }
  241.                 if ($foundEmptyValue) {
  242.                     $parentData null;
  243.                     // still some values are passing, ask the parent
  244.                     if (isset($params['context']['containerType']) && $params['context']['containerType'] === 'objectbrick') {
  245.                         $brickContainerGetter 'get' ucfirst($params['fieldname']);
  246.                         $brickContainer $parent->$brickContainerGetter();
  247.                         $brickGetter 'get' ucfirst($params['context']['containerKey']);
  248.                         $brickData $brickContainer->$brickGetter();
  249.                         if ($brickData) {
  250.                             $parentData $brickData->getLocalizedFields();
  251.                         }
  252.                     } else {
  253.                         if (method_exists($parent'getLocalizedFields')) {
  254.                             $parentData $parent->getLocalizedFields();
  255.                         }
  256.                     }
  257.                     if ($parentData) {
  258.                         $parentResult $this->doGetDataForEditMode(
  259.                             $parentData,
  260.                             $parent,
  261.                             $fieldData,
  262.                             $metaData,
  263.                             $level 1,
  264.                             $params
  265.                         );
  266.                     }
  267.                 }
  268.             }
  269.         }
  270.         $result = [
  271.             'data' => $fieldData,
  272.             'metaData' => $metaData,
  273.             'inherited' => $inherited,
  274.         ];
  275.         return $result;
  276.     }
  277.     /**
  278.      * @see Data::getDataFromEditmode
  279.      *
  280.      * @param array $data
  281.      * @param null|DataObject\Concrete $object
  282.      * @param mixed $params
  283.      *
  284.      * @return DataObject\Localizedfield
  285.      */
  286.     public function getDataFromEditmode($data$object null$params = [])
  287.     {
  288.         $localizedFields $this->getDataFromObjectParam($object$params);
  289.         if (!$localizedFields instanceof DataObject\Localizedfield) {
  290.             $localizedFields = new DataObject\Localizedfield();
  291.             $context = isset($params['context']) ? $params['context'] : null;
  292.             $localizedFields->setContext($context);
  293.         }
  294.         if ($object) {
  295.             $localizedFields->setObject($object);
  296.         }
  297.         if (is_array($data)) {
  298.             foreach ($data as $language => $fields) {
  299.                 foreach ($fields as $name => $fdata) {
  300.                     $fd $this->getFieldDefinition($name);
  301.                     $params['language'] = $language;
  302.                     $localizedFields->setLocalizedValue(
  303.                         $name,
  304.                         $fd->getDataFromEditmode($fdata$object$params),
  305.                         $language
  306.                     );
  307.                 }
  308.             }
  309.         }
  310.         return $localizedFields;
  311.     }
  312.     /**
  313.      * @param DataObject\Localizedfield|null $data
  314.      * @param DataObject\Concrete|null $object
  315.      * @param mixed $params
  316.      *
  317.      * @return \stdClass
  318.      */
  319.     public function getDataForGrid($data$object null$params = [])
  320.     {
  321.         $result = new \stdClass();
  322.         foreach ($this->getFieldDefinitions() as $fd) {
  323.             $key $fd->getName();
  324.             $context $params['context'] ?? null;
  325.             if (isset($context['containerType']) && $context['containerType'] === 'objectbrick') {
  326.                 $result->$key 'NOT SUPPORTED';
  327.             } else {
  328.                 $result->$key $object->{'get' ucfirst($fd->getName())}();
  329.             }
  330.             if (method_exists($fd'getDataForGrid')) {
  331.                 $result->$key $fd->getDataForGrid($result->$key$object$params);
  332.             }
  333.         }
  334.         return $result;
  335.     }
  336.     /**
  337.      * @see Data::getVersionPreview
  338.      *
  339.      * @param DataObject\Localizedfield|null $data
  340.      * @param null|DataObject\Concrete $object
  341.      * @param mixed $params
  342.      *
  343.      * @return string
  344.      */
  345.     public function getVersionPreview($data$object null$params = [])
  346.     {
  347.         // this is handled directly in the template
  348.         // /bundles/AdminBundle/Resources/views/Admin/DataObject/DataObject/previewVersion.html.twig
  349.         return 'LOCALIZED FIELDS';
  350.     }
  351.     /**
  352.      * {@inheritdoc}
  353.      */
  354.     public function getForCsvExport($object$params = [])
  355.     {
  356.         return 'NOT SUPPORTED';
  357.     }
  358.     /**
  359.      * {@inheritdoc}
  360.      */
  361.     public function getDataForSearchIndex($object$params = [])
  362.     {
  363.         $dataString '';
  364.         $lfData $this->getDataFromObjectParam($object);
  365.         if ($lfData instanceof DataObject\Localizedfield) {
  366.             foreach ($lfData->getInternalData(true) as $language => $values) {
  367.                 foreach ($values as $fieldname => $lData) {
  368.                     $fd $this->getFieldDefinition($fieldname);
  369.                     if ($fd) {
  370.                         $forSearchIndex $fd->getDataForSearchIndex($lfData, [
  371.                             'injectedData' => $lData,
  372.                         ]);
  373.                         if ($forSearchIndex) {
  374.                             $dataString .= $forSearchIndex ' ';
  375.                         }
  376.                     }
  377.                 }
  378.             }
  379.         }
  380.         return $dataString;
  381.     }
  382.     /**
  383.      * @return array
  384.      */
  385.     public function getChildren()
  386.     {
  387.         return $this->children;
  388.     }
  389.     /**
  390.      * @param array $children
  391.      *
  392.      * @return $this
  393.      */
  394.     public function setChildren($children)
  395.     {
  396.         $this->children $children;
  397.         $this->fieldDefinitionsCache null;
  398.         return $this;
  399.     }
  400.     /**
  401.      * @return bool
  402.      */
  403.     public function hasChildren()
  404.     {
  405.         return is_array($this->children) && count($this->children) > 0;
  406.     }
  407.     /**
  408.      * @param Data|Layout $child
  409.      */
  410.     public function addChild($child)
  411.     {
  412.         $this->children[] = $child;
  413.         $this->fieldDefinitionsCache null;
  414.     }
  415.     /**
  416.      * @param Data[] $referencedFields
  417.      */
  418.     public function setReferencedFields($referencedFields)
  419.     {
  420.         $this->referencedFields $referencedFields;
  421.         $this->fieldDefinitionsCache null;
  422.     }
  423.     /**
  424.      * @return Data[]
  425.      */
  426.     public function getReferencedFields()
  427.     {
  428.         return $this->referencedFields;
  429.     }
  430.     /**
  431.      * @param Data $field
  432.      */
  433.     public function addReferencedField($field)
  434.     {
  435.         $this->referencedFields[] = $field;
  436.         $this->fieldDefinitionsCache null;
  437.     }
  438.     /**
  439.      * {@inheritdoc}
  440.      */
  441.     public function save($object$params = [])
  442.     {
  443.         $localizedFields $this->getDataFromObjectParam($object$params);
  444.         if ($localizedFields instanceof DataObject\Localizedfield) {
  445.             if ((!isset($params['newParent']) || !$params['newParent']) && isset($params['isUpdate']) && $params['isUpdate'] && !$localizedFields->hasDirtyLanguages()) {
  446.                 return;
  447.             }
  448.             if ($object instanceof DataObject\Fieldcollection\Data\AbstractData || $object instanceof DataObject\Objectbrick\Data\AbstractData) {
  449.                 $object $object->getObject();
  450.             }
  451.             $localizedFields->setObject($objectfalse);
  452.             $context = isset($params['context']) ? $params['context'] : null;
  453.             $localizedFields->setContext($context);
  454.             $localizedFields->loadLazyData();
  455.             $localizedFields->save($params);
  456.         }
  457.     }
  458.     /**
  459.      * {@inheritdoc}
  460.      */
  461.     public function load($object$params = [])
  462.     {
  463.         if ($object instanceof DataObject\Fieldcollection\Data\AbstractData || $object instanceof DataObject\Objectbrick\Data\AbstractData) {
  464.             $object $object->getObject();
  465.         }
  466.         $localizedFields = new DataObject\Localizedfield();
  467.         $localizedFields->setObject($object);
  468.         $context = isset($params['context']) ? $params['context'] : null;
  469.         $localizedFields->setContext($context);
  470.         $localizedFields->load($object$params);
  471.         $localizedFields->resetDirtyMap();
  472.         $localizedFields->resetLanguageDirtyMap();
  473.         return $localizedFields;
  474.     }
  475.     /**
  476.      * {@inheritdoc}
  477.      */
  478.     public function delete($object$params = [])
  479.     {
  480.         $localizedFields $this->getDataFromObjectParam($object$params);
  481.         if ($localizedFields instanceof DataObject\Localizedfield) {
  482.             $localizedFields->setObject($object);
  483.             $context $params['context'] ?? [];
  484.             $localizedFields->setContext($context);
  485.             $localizedFields->delete(truefalse);
  486.         }
  487.     }
  488.     /**
  489.      * This method is called in DataObject\ClassDefinition::save() and is used to create the database table for the localized data
  490.      *
  491.      * @param DataObject\ClassDefinition $class
  492.      * @param array $params
  493.      */
  494.     public function classSaved($class$params = [])
  495.     {
  496.         // create a dummy instance just for updating the tables
  497.         $localizedFields = new DataObject\Localizedfield();
  498.         $localizedFields->setClass($class);
  499.         $context $params['context'] ?? [];
  500.         $localizedFields->setContext($context);
  501.         $localizedFields->createUpdateTable($params);
  502.         foreach ($this->getFieldDefinitions() as $fd) {
  503.             //TODO Pimcore 11 remove method_exists call
  504.             if (!$fd instanceof DataContainerAwareInterface && method_exists($fd'classSaved')) {
  505.                 $fd->classSaved($class$params);
  506.             }
  507.         }
  508.     }
  509.     /**
  510.      * { @inheritdoc }
  511.      */
  512.     public function preGetData(/** mixed */ $container/** array */ $params = []) // : mixed
  513.     {
  514.         if (
  515.             !$container instanceof DataObject\Concrete &&
  516.             !$container instanceof DataObject\Fieldcollection\Data\AbstractData &&
  517.             !$container instanceof DataObject\Objectbrick\Data\AbstractData
  518.         ) {
  519.             throw new \Exception('Localized Fields are only valid in Objects, Fieldcollections and Objectbricks');
  520.         }
  521.         $lf $container->getObjectVar('localizedfields');
  522.         if (!$lf instanceof DataObject\Localizedfield) {
  523.             $lf = new DataObject\Localizedfield();
  524.             $object $container;
  525.             if ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  526.                 $object $container->getObject();
  527.                 $context = [
  528.                     'containerType' => 'objectbrick',
  529.                     'containerKey' => $container->getType(),
  530.                     'fieldname' => $container->getFieldname(),
  531.                 ];
  532.                 $lf->setContext($context);
  533.             } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  534.                 $object $container->getObject();
  535.                 $context = [
  536.                     'containerType' => 'fieldcollection',
  537.                     'containerKey' => $container->getType(),
  538.                     'fieldname' => $container->getFieldname(),
  539.                 ];
  540.                 $lf->setContext($context);
  541.             } elseif ($container instanceof DataObject\Concrete) {
  542.                 $context = ['object' => $container];
  543.                 $lf->setContext($context);
  544.             }
  545.             $lf->setObject($object);
  546.             $container->setObjectVar('localizedfields'$lf);
  547.         }
  548.         return $container->getObjectVar('localizedfields');
  549.     }
  550.     /**
  551.      * {@inheritdoc}
  552.      */
  553.     public function getGetterCode($class)
  554.     {
  555.         $code '';
  556.         if (!$class instanceof DataObject\Fieldcollection\Definition) {
  557.             $code .= parent::getGetterCode($class);
  558.         }
  559.         $fieldDefinitions $this->getFieldDefinitions();
  560.         foreach ($fieldDefinitions as $fd) {
  561.             $code .= $fd->getGetterCodeLocalizedfields($class);
  562.         }
  563.         return $code;
  564.     }
  565.     /**
  566.      * {@inheritdoc}
  567.      */
  568.     public function getSetterCode($class)
  569.     {
  570.         $code '';
  571.         if (!$class instanceof DataObject\Fieldcollection\Definition) {
  572.             $code .= parent::getSetterCode($class);
  573.         }
  574.         foreach ($this->getFieldDefinitions() as $fd) {
  575.             $code .= $fd->getSetterCodeLocalizedfields($class);
  576.         }
  577.         return $code;
  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($context);
  588.         if (isset($fds[$name])) {
  589.             $fieldDefinition $fds[$name];
  590.             if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  591.                 return $fieldDefinition;
  592.             }
  593.             $fieldDefinition $this->doEnrichFieldDefinition($fieldDefinition$context);
  594.             return $fieldDefinition;
  595.         }
  596.         return null;
  597.     }
  598.     /**
  599.      * @param array $context additional contextual data
  600.      *
  601.      * @return Data[]
  602.      */
  603.     public function getFieldDefinitions($context = [])
  604.     {
  605.         if (empty($this->fieldDefinitionsCache)) {
  606.             $definitions $this->doGetFieldDefinitions();
  607.             foreach ($this->getReferencedFields() as $rf) {
  608.                 if ($rf instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  609.                     $definitions array_merge($definitions$this->doGetFieldDefinitions($rf->getChildren()));
  610.                 }
  611.             }
  612.             $this->fieldDefinitionsCache $definitions;
  613.         }
  614.         if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  615.             return $this->fieldDefinitionsCache;
  616.         }
  617.         $enrichedFieldDefinitions = [];
  618.         if (is_array($this->fieldDefinitionsCache)) {
  619.             foreach ($this->fieldDefinitionsCache as $key => $fieldDefinition) {
  620.                 $fieldDefinition $this->doEnrichFieldDefinition($fieldDefinition$context);
  621.                 $enrichedFieldDefinitions[$key] = $fieldDefinition;
  622.             }
  623.         }
  624.         return $enrichedFieldDefinitions;
  625.     }
  626.     private function doEnrichFieldDefinition($fieldDefinition$context = [])
  627.     {
  628.         //TODO Pimcore 11: remove method_exists BC layer
  629.         if ($fieldDefinition instanceof FieldDefinitionEnrichmentInterface || method_exists($fieldDefinition'enrichFieldDefinition')) {
  630.             if (!$fieldDefinition instanceof FieldDefinitionEnrichmentInterface) {
  631.                 trigger_deprecation('pimcore/pimcore''10.1',
  632.                     sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  633.                     'Implement the %s interface instead.'FieldDefinitionEnrichmentInterface::class));
  634.             }
  635.             $context['class'] = $this;
  636.             $fieldDefinition $fieldDefinition->enrichFieldDefinition($context);
  637.         }
  638.         return $fieldDefinition;
  639.     }
  640.     /**
  641.      * @param mixed $def
  642.      * @param array $fields
  643.      *
  644.      * @return array
  645.      */
  646.     private function doGetFieldDefinitions($def null$fields = [])
  647.     {
  648.         if ($def === null) {
  649.             $def $this->getChildren();
  650.         }
  651.         if (is_array($def)) {
  652.             foreach ($def as $child) {
  653.                 $fields array_merge($fields$this->doGetFieldDefinitions($child$fields));
  654.             }
  655.         }
  656.         if ($def instanceof DataObject\ClassDefinition\Layout) {
  657.             if ($def->hasChildren()) {
  658.                 foreach ($def->getChildren() as $child) {
  659.                     $fields array_merge($fields$this->doGetFieldDefinitions($child$fields));
  660.                 }
  661.             }
  662.         }
  663.         if ($def instanceof DataObject\ClassDefinition\Data) {
  664.             $fields[$def->getName()] = $def;
  665.         }
  666.         return $fields;
  667.     }
  668.     /**
  669.      * {@inheritdoc}
  670.      */
  671.     public function getCacheTags($data, array $tags = [])
  672.     {
  673.         if (!$data instanceof DataObject\Localizedfield) {
  674.             return $tags;
  675.         }
  676.         foreach ($data->getInternalData(true) as $language => $values) {
  677.             foreach ($this->getFieldDefinitions() as $fd) {
  678.                 if (isset($values[$fd->getName()])) {
  679.                     $tags $fd->getCacheTags($values[$fd->getName()], $tags);
  680.                 }
  681.             }
  682.         }
  683.         return $tags;
  684.     }
  685.     /**
  686.      * @param DataObject\Localizedfield|null $data
  687.      *
  688.      * @return array
  689.      */
  690.     public function resolveDependencies($data)
  691.     {
  692.         $dependencies = [];
  693.         if (!$data instanceof DataObject\Localizedfield) {
  694.             return [];
  695.         }
  696.         foreach ($data->getInternalData(true) as $language => $values) {
  697.             foreach ($this->getFieldDefinitions() as $fd) {
  698.                 if (isset($values[$fd->getName()])) {
  699.                     $dependencies array_merge($dependencies$fd->resolveDependencies($values[$fd->getName()]));
  700.                 }
  701.             }
  702.         }
  703.         return $dependencies;
  704.     }
  705.     /**
  706.      * @param string|int $height
  707.      *
  708.      * @return $this
  709.      */
  710.     public function setHeight($height)
  711.     {
  712.         if (is_numeric($height)) {
  713.             $height = (int)$height;
  714.         }
  715.         $this->height $height;
  716.         return $this;
  717.     }
  718.     /**
  719.      * @return string|int
  720.      */
  721.     public function getHeight()
  722.     {
  723.         return $this->height;
  724.     }
  725.     /**
  726.      * @param mixed $layout
  727.      *
  728.      * @return $this
  729.      */
  730.     public function setLayout($layout)
  731.     {
  732.         $this->layout $layout;
  733.         return $this;
  734.     }
  735.     /**
  736.      * @return string
  737.      */
  738.     public function getLayout()
  739.     {
  740.         return $this->layout;
  741.     }
  742.     /**
  743.      * @return bool
  744.      */
  745.     public function getBorder(): bool
  746.     {
  747.         return $this->border;
  748.     }
  749.     /**
  750.      * @param bool $border
  751.      */
  752.     public function setBorder(bool $border): void
  753.     {
  754.         $this->border $border;
  755.     }
  756.     /**
  757.      * @param string $name
  758.      *
  759.      * @return $this
  760.      *
  761.      * @throws \Exception
  762.      */
  763.     public function setName($name)
  764.     {
  765.         if ($name !== 'localizedfields') {
  766.             throw new \Exception('Localizedfields can only be named `localizedfields`, no other names are allowed');
  767.         }
  768.         $this->name $name;
  769.         return $this;
  770.     }
  771.     /**
  772.      * @param string|null $region
  773.      *
  774.      * @return $this
  775.      */
  776.     public function setRegion($region)
  777.     {
  778.         $this->region $region;
  779.         return $this;
  780.     }
  781.     /**
  782.      * @return string
  783.      */
  784.     public function getRegion()
  785.     {
  786.         return $this->region;
  787.     }
  788.     /**
  789.      * @param int|string $width
  790.      *
  791.      * @return $this
  792.      */
  793.     public function setWidth($width)
  794.     {
  795.         if (is_numeric($width)) {
  796.             $width = (int)$width;
  797.         }
  798.         $this->width $width;
  799.         return $this;
  800.     }
  801.     /**
  802.      * @return int|string
  803.      */
  804.     public function getWidth()
  805.     {
  806.         return $this->width;
  807.     }
  808.     /**
  809.      * {@inheritdoc}
  810.      */
  811.     public function checkValidity($data$omitMandatoryCheck false$params = [])
  812.     {
  813.         $config \Pimcore\Config::getSystemConfiguration('general');
  814.         $languages = [];
  815.         if (isset($config['valid_languages'])) {
  816.             $languages explode(','$config['valid_languages']);
  817.         }
  818.         $dataForValidityCheck $this->getDataForValidity($data$languages);
  819.         $validationExceptions = [];
  820.         if (!$omitMandatoryCheck) {
  821.             foreach ($languages as $language) {
  822.                 foreach ($this->getFieldDefinitions() as $fd) {
  823.                     try {
  824.                         try {
  825.                             if (!isset($dataForValidityCheck[$language][$fd->getName()])) {
  826.                                 $dataForValidityCheck[$language][$fd->getName()] = null;
  827.                             }
  828.                             $fd->checkValidity($dataForValidityCheck[$language][$fd->getName()], false$params);
  829.                         } catch (\Exception $e) {
  830.                             if ($data->getObject()->getClass()->getAllowInherit() && $fd->supportsInheritance() && $fd->isEmpty($dataForValidityCheck[$language][$fd->getName()])) {
  831.                                 //try again with parent data when inheritance is activated
  832.                                 try {
  833.                                     $getInheritedValues DataObject::doGetInheritedValues();
  834.                                     DataObject::setGetInheritedValues(true);
  835.                                     $value null;
  836.                                     $context $data->getContext();
  837.                                     $containerType $context['containerType'] ?? null;
  838.                                     if ($containerType === 'objectbrick') {
  839.                                         $brickContainer $data->getObject()->{'get' ucfirst($context['fieldname'])}();
  840.                                         $brick $brickContainer->{'get' ucfirst($context['containerKey'])}();
  841.                                         if ($brick) {
  842.                                             $value $brick->{'get' ucfirst($fd->getName())}($language);
  843.                                         }
  844.                                     } elseif ($containerType === null || $containerType === 'object') {
  845.                                         $getter 'get' ucfirst($fd->getName());
  846.                                         $value $data->getObject()->$getter($language);
  847.                                     }
  848.                                     $fd->checkValidity($value$omitMandatoryCheck$params);
  849.                                     DataObject::setGetInheritedValues($getInheritedValues);
  850.                                 } catch (\Exception $e) {
  851.                                     if (!$e instanceof Model\Element\ValidationException) {
  852.                                         throw $e;
  853.                                     }
  854.                                     $exceptionClass get_class($e);
  855.                                     throw new $exceptionClass($e->getMessage() . ' fieldname=' $fd->getName(), $e->getCode(), $e->getPrevious());
  856.                                 }
  857.                             } else {
  858.                                 if ($e instanceof Model\Element\ValidationException) {
  859.                                     throw $e;
  860.                                 }
  861.                                 $exceptionClass get_class($e);
  862.                                 throw new $exceptionClass($e->getMessage() . ' fieldname=' $fd->getName(), $e->getCode(), $e);
  863.                             }
  864.                         }
  865.                     } catch (Model\Element\ValidationException $ve) {
  866.                         $ve->addContext($this->getName() . '-' $language);
  867.                         $validationExceptions[] = $ve;
  868.                     }
  869.                 }
  870.             }
  871.         }
  872.         if (count($validationExceptions) > 0) {
  873.             $errors = [];
  874.             /** @var Element\ValidationException $e */
  875.             foreach ($validationExceptions as $e) {
  876.                 $errors[] = $e->getAggregatedMessage();
  877.             }
  878.             $message implode(' / '$errors);
  879.             throw new Model\Element\ValidationException($message);
  880.         }
  881.     }
  882.     /**
  883.      * @param DataObject\Localizedfield $localizedObject
  884.      * @param array $languages
  885.      *
  886.      * @return array
  887.      */
  888.     private function getDataForValidity($localizedObject, array $languages)
  889.     {
  890.         if (!$localizedObject->getObject()
  891.             || $localizedObject->getObject()->getType() != DataObject::OBJECT_TYPE_VARIANT
  892.             || !$localizedObject instanceof DataObject\Localizedfield) {
  893.             return $localizedObject->getInternalData(true);
  894.         }
  895.         //prepare data for variants
  896.         $data = [];
  897.         foreach ($languages as $language) {
  898.             $data[$language] = [];
  899.             foreach ($this->getFieldDefinitions() as $fd) {
  900.                 $data[$language][$fd->getName()] = $localizedObject->getLocalizedValue($fd->getName(), $language);
  901.             }
  902.         }
  903.         return $data;
  904.     }
  905.     /**
  906.      * @param mixed $data
  907.      * @param DataObject\Concrete|null $object
  908.      * @param mixed $params
  909.      *
  910.      * @return array|null
  911.      */
  912.     public function getDiffDataForEditmode($data$object null$params = [])
  913.     {
  914.         $return = [];
  915.         $myname $this->getName();
  916.         if (!$data instanceof DataObject\Localizedfield) {
  917.             return [];
  918.         }
  919.         foreach ($data->getInternalData(true) as $language => $values) {
  920.             foreach ($this->getFieldDefinitions() as $fd) {
  921.                 $fieldname $fd->getName();
  922.                 $subdata $fd->getDiffDataForEditmode($values[$fieldname], $object$params);
  923.                 foreach ($subdata as $item) {
  924.                     $diffdata['field'] = $this->getName();
  925.                     $diffdata['key'] = $this->getName().'~'.$fieldname.'~'.$item['key'].'~'.$language;
  926.                     $diffdata['type'] = $item['type'];
  927.                     $diffdata['value'] = $item['value'];
  928.                     // this is not needed anymoe
  929.                     unset($item['type']);
  930.                     unset($item['value']);
  931.                     $diffdata['title'] = $this->getName().' / '.$item['title'];
  932.                     $diffdata['lang'] = $language;
  933.                     $diffdata['data'] = $item;
  934.                     $diffdata['extData'] = [
  935.                         'fieldname' => $fieldname,
  936.                     ];
  937.                     $diffdata['disabled'] = $item['disabled'];
  938.                     $return[] = $diffdata;
  939.                 }
  940.             }
  941.         }
  942.         return $return;
  943.     }
  944.     /**
  945.      * @param array $data
  946.      * @param DataObject\Concrete|null $object
  947.      * @param mixed $params
  948.      *
  949.      * @return DataObject\Localizedfield
  950.      */
  951.     public function getDiffDataFromEditmode($data$object null$params = [])
  952.     {
  953.         $localFields $this->getDataFromObjectParam($object$params);
  954.         $localData = [];
  955.         // get existing data
  956.         if ($localFields instanceof DataObject\Localizedfield) {
  957.             $localData $localFields->getInternalData(true);
  958.         }
  959.         $mapping = [];
  960.         foreach ($data as $item) {
  961.             $extData $item['extData'];
  962.             $fieldname $extData['fieldname'];
  963.             $language $item['lang'];
  964.             $values $mapping[$fieldname] ?? [];
  965.             $itemdata $item['data'];
  966.             if ($itemdata) {
  967.                 $values[] = $itemdata;
  968.             }
  969.             $mapping[$language][$fieldname] = $values;
  970.         }
  971.         foreach ($mapping as $language => $fields) {
  972.             foreach ($fields as $key => $value) {
  973.                 $fd $this->getFieldDefinition($key);
  974.                 if ($fd && $fd->isDiffChangeAllowed($object)) {
  975.                     if ($value == null) {
  976.                         unset($localData[$language][$key]);
  977.                     } else {
  978.                         $localData[$language][$key] = $fd->getDiffDataFromEditmode($value);
  979.                     }
  980.                 }
  981.             }
  982.         }
  983.         $localizedFields = new DataObject\Localizedfield($localData);
  984.         $localizedFields->setObject($object);
  985.         return $localizedFields;
  986.     }
  987.     /**
  988.      * {@inheritdoc}
  989.      */
  990.     public function isDiffChangeAllowed($object$params = [])
  991.     {
  992.         return true;
  993.     }
  994.     /**
  995.      * @return array
  996.      */
  997.     public function getBlockedVarsForExport(): array
  998.     {
  999.         return [
  1000.             'fieldDefinitionsCache',
  1001.             'referencedFields',
  1002.             'blockedVarsForExport',
  1003.             'permissionView',
  1004.             'permissionEdit',
  1005.             'childs',
  1006.         ];
  1007.     }
  1008.     /**
  1009.      * @return array
  1010.      */
  1011.     public function __sleep()
  1012.     {
  1013.         $vars get_object_vars($this);
  1014.         $blockedVars $this->getBlockedVarsForExport();
  1015.         foreach ($blockedVars as $blockedVar) {
  1016.             unset($vars[$blockedVar]);
  1017.         }
  1018.         return array_keys($vars);
  1019.     }
  1020.     /**
  1021.      * { @inheritdoc }
  1022.      */
  1023.     public function rewriteIds(/** mixed */ $container/** array */ $idMapping/** array */ $params = []) /** :mixed */
  1024.     {
  1025.         $data $this->getDataFromObjectParam($container$params);
  1026.         $validLanguages Tool::getValidLanguages();
  1027.         foreach ($validLanguages as $language) {
  1028.             foreach ($this->getFieldDefinitions() as $fd) {
  1029.                 //TODO Pimcore 11: remove method_exists BC layer
  1030.                 if ($fd instanceof IdRewriterInterface || method_exists($fd'rewriteIds')) {
  1031.                     if (!$fd instanceof IdRewriterInterface) {
  1032.                         trigger_deprecation('pimcore/pimcore''10.1',
  1033.                             sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  1034.                             'Implement the %s interface instead.'IdRewriterInterface::class));
  1035.                     }
  1036.                     $d $fd->rewriteIds($data$idMapping, ['language' => $language]);
  1037.                     $data->setLocalizedValue($fd->getName(), $d$language);
  1038.                 }
  1039.             }
  1040.         }
  1041.         return $data;
  1042.     }
  1043.     /**
  1044.      * @return int
  1045.      */
  1046.     public function getHideLabelsWhenTabsReached()
  1047.     {
  1048.         return $this->hideLabelsWhenTabsReached;
  1049.     }
  1050.     /**
  1051.      * @param int $hideLabelsWhenTabsReached
  1052.      *
  1053.      * @return $this
  1054.      */
  1055.     public function setHideLabelsWhenTabsReached($hideLabelsWhenTabsReached)
  1056.     {
  1057.         $this->hideLabelsWhenTabsReached $hideLabelsWhenTabsReached;
  1058.         return $this;
  1059.     }
  1060.     /**
  1061.      * @param int $maxTabs
  1062.      */
  1063.     public function setMaxTabs($maxTabs)
  1064.     {
  1065.         $this->maxTabs $maxTabs;
  1066.     }
  1067.     /**
  1068.      * @return int
  1069.      */
  1070.     public function getMaxTabs()
  1071.     {
  1072.         return $this->maxTabs;
  1073.     }
  1074.     /**
  1075.      * @param int $labelWidth
  1076.      */
  1077.     public function setLabelWidth($labelWidth)
  1078.     {
  1079.         $this->labelWidth = (int)$labelWidth;
  1080.     }
  1081.     /**
  1082.      * @return int
  1083.      */
  1084.     public function getLabelWidth()
  1085.     {
  1086.         return $this->labelWidth;
  1087.     }
  1088.     /**
  1089.      * @return array|null
  1090.      */
  1091.     public function getPermissionView(): ?array
  1092.     {
  1093.         return $this->permissionView;
  1094.     }
  1095.     /**
  1096.      * @param array|null $permissionView
  1097.      */
  1098.     public function setPermissionView($permissionView): void
  1099.     {
  1100.         $this->permissionView $permissionView;
  1101.     }
  1102.     /**
  1103.      * @return array|null
  1104.      */
  1105.     public function getPermissionEdit(): ?array
  1106.     {
  1107.         return $this->permissionEdit;
  1108.     }
  1109.     /**
  1110.      * @param array|null $permissionEdit
  1111.      */
  1112.     public function setPermissionEdit($permissionEdit): void
  1113.     {
  1114.         $this->permissionEdit $permissionEdit;
  1115.     }
  1116.     /**
  1117.      * @return bool
  1118.      */
  1119.     public function getProvideSplitView()
  1120.     {
  1121.         return $this->provideSplitView;
  1122.     }
  1123.     /**
  1124.      * @param bool $provideSplitView
  1125.      */
  1126.     public function setProvideSplitView($provideSplitView): void
  1127.     {
  1128.         $this->provideSplitView $provideSplitView;
  1129.     }
  1130.     /**
  1131.      * {@inheritdoc}
  1132.      */
  1133.     public function supportsDirtyDetection()
  1134.     {
  1135.         return true;
  1136.     }
  1137.     /**
  1138.      * {@inheritdoc}
  1139.      */
  1140.     public function isFilterable(): bool
  1141.     {
  1142.         return true;
  1143.     }
  1144.     /**
  1145.      * @return string
  1146.      */
  1147.     public function getTabPosition(): string
  1148.     {
  1149.         return $this->tabPosition ?? 'top';
  1150.     }
  1151.     /**
  1152.      * @param string|null $tabPosition
  1153.      */
  1154.     public function setTabPosition($tabPosition): void
  1155.     {
  1156.         $this->tabPosition $tabPosition;
  1157.     }
  1158.     /**
  1159.      * {@inheritdoc}
  1160.      */
  1161.     public function getParameterTypeDeclaration(): ?string
  1162.     {
  1163.         return '?\\' DataObject\Localizedfield::class;
  1164.     }
  1165.     /**
  1166.      * {@inheritdoc}
  1167.      */
  1168.     public function getReturnTypeDeclaration(): ?string
  1169.     {
  1170.         return '?\\' DataObject\Localizedfield::class;
  1171.     }
  1172.     /**
  1173.      * {@inheritdoc}
  1174.      */
  1175.     public function getPhpdocInputType(): ?string
  1176.     {
  1177.         return '\\'DataObject\Localizedfield::class . '|null';
  1178.     }
  1179.     /**
  1180.      * {@inheritdoc}
  1181.      */
  1182.     public function getPhpdocReturnType(): ?string
  1183.     {
  1184.         return '\\' DataObject\Localizedfield::class . '|null';
  1185.     }
  1186.     /**
  1187.      * {@inheritdoc}
  1188.      */
  1189.     public function normalize($value$params = [])
  1190.     {
  1191.         if ($value instanceof DataObject\Localizedfield) {
  1192.             $items $value->getInternalData();
  1193.             if (is_array($items)) {
  1194.                 $result = [];
  1195.                 foreach ($items as $language => $languageData) {
  1196.                     $languageResult = [];
  1197.                     foreach ($languageData as $elementName => $elementData) {
  1198.                         $fd $this->getFieldDefinition($elementName);
  1199.                         if (!$fd) {
  1200.                             // class definition seems to have changed
  1201.                             Logger::warn('class definition seems to have changed, element name: '.$elementName);
  1202.                             continue;
  1203.                         }
  1204.                         if ($fd instanceof NormalizerInterface) {
  1205.                             $dataForResource $fd->normalize($elementData$params);
  1206.                             $languageResult[$elementName] = $dataForResource;
  1207.                         }
  1208.                     }
  1209.                     $result[$language] = $languageResult;
  1210.                 }
  1211.                 return $result;
  1212.             }
  1213.         }
  1214.         return null;
  1215.     }
  1216.     /**
  1217.      * {@inheritdoc}
  1218.      */
  1219.     public function denormalize($value$params = [])
  1220.     {
  1221.         if (is_array($value)) {
  1222.             $lf = new DataObject\Localizedfield();
  1223.             $lf->setObject($params['object']);
  1224.             $items = [];
  1225.             foreach ($value as $language => $languageData) {
  1226.                 $languageResult = [];
  1227.                 foreach ($languageData as $elementName => $elementData) {
  1228.                     $fd $this->getFieldDefinition($elementName);
  1229.                     if (!$fd) {
  1230.                         // class definition seems to have changed
  1231.                         Logger::warn('class definition seems to have changed, element name: '.$elementName);
  1232.                         continue;
  1233.                     }
  1234.                     if ($fd instanceof NormalizerInterface) {
  1235.                         $dataFromResource $fd->denormalize($elementData$params);
  1236.                         $languageResult[$elementName] = $dataFromResource;
  1237.                     }
  1238.                 }
  1239.                 $items[$language] = $languageResult;
  1240.             }
  1241.             $lf->setItems($items);
  1242.             return $lf;
  1243.         }
  1244.         return null;
  1245.     }
  1246.     public static function __set_state($data)
  1247.     {
  1248.         $obj = new static();
  1249.         $obj->setValues($data);
  1250.         $obj->childs $obj->children;  // @phpstan-ignore-line
  1251.         return $obj;
  1252.     }
  1253. }