vendor/pimcore/pimcore/models/DataObject/Localizedfield.php line 467

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;
  15. use Pimcore\Localization\LocaleServiceInterface;
  16. use Pimcore\Model;
  17. use Pimcore\Model\DataObject;
  18. use Pimcore\Model\DataObject\ClassDefinition\Data\LazyLoadingSupportInterface;
  19. use Pimcore\Model\DataObject\ClassDefinition\Data\PreGetDataInterface;
  20. use Pimcore\Model\DataObject\ClassDefinition\Data\PreSetDataInterface;
  21. use Pimcore\Model\Element\DirtyIndicatorInterface;
  22. use Pimcore\Tool;
  23. /**
  24.  * @method Localizedfield\Dao getDao()*
  25.  * @method void delete($deleteQuery = true, $isUpdate = true)
  26.  * @method void load($object, $params = [])
  27.  * @method void save($params = [])
  28.  * @method void createUpdateTable($params = [])
  29.  */
  30. final class Localizedfield extends Model\AbstractModel implements
  31.     DirtyIndicatorInterface,
  32.     LazyLoadedFieldsInterface,
  33.     Model\Element\ElementDumpStateInterface,
  34.     OwnerAwareFieldInterface
  35. {
  36.     use Model\DataObject\Traits\OwnerAwareFieldTrait;
  37.     use Model\DataObject\Traits\LazyLoadedRelationTrait;
  38.     use Model\Element\Traits\DirtyIndicatorTrait;
  39.     use Model\Element\ElementDumpStateTrait;
  40.     /**
  41.      * @internal
  42.      */
  43.     const STRICT_DISABLED 0;
  44.     /**
  45.      * @internal
  46.      */
  47.     const STRICT_ENABLED 1;
  48.     /**
  49.      * @var bool
  50.      */
  51.     private static bool $getFallbackValues false;
  52.     /**
  53.      * @internal
  54.      *
  55.      * @var array
  56.      */
  57.     protected array $items = [];
  58.     /**
  59.      * @internal
  60.      *
  61.      * @var Concrete|Model\Element\ElementDescriptor|null
  62.      */
  63.     protected $object;
  64.     /**
  65.      * @internal
  66.      *
  67.      * @var ClassDefinition|null
  68.      */
  69.     protected ?ClassDefinition $class null;
  70.     /**
  71.      * @internal
  72.      *
  73.      * @var array|null
  74.      */
  75.     protected ?array $context = [];
  76.     /**
  77.      * @internal
  78.      *
  79.      * @var int|null
  80.      */
  81.     protected ?int $objectId null;
  82.     /**
  83.      * @var bool
  84.      */
  85.     private static bool $strictMode false;
  86.     /**
  87.      * list of dirty languages. if null then no language is dirty. if empty array then all languages are dirty
  88.      *
  89.      * @internal
  90.      *
  91.      * @var array|null
  92.      */
  93.     protected ?array $o_dirtyLanguages null;
  94.     /**
  95.      * @internal
  96.      *
  97.      * @var bool
  98.      */
  99.     protected bool $_loadedAllLazyData false;
  100.     /**
  101.      * @param bool $getFallbackValues
  102.      */
  103.     public static function setGetFallbackValues(bool $getFallbackValues): void
  104.     {
  105.         self::$getFallbackValues $getFallbackValues;
  106.     }
  107.     /**
  108.      * @return bool
  109.      */
  110.     public static function getGetFallbackValues(): bool
  111.     {
  112.         return self::$getFallbackValues;
  113.     }
  114.     /**
  115.      * @return bool
  116.      */
  117.     public static function isStrictMode(): bool
  118.     {
  119.         return self::$strictMode;
  120.     }
  121.     /**
  122.      * @param bool $strictMode
  123.      */
  124.     public static function setStrictMode(bool $strictMode): void
  125.     {
  126.         self::$strictMode $strictMode;
  127.     }
  128.     /**
  129.      * @return bool
  130.      */
  131.     public static function doGetFallbackValues(): bool
  132.     {
  133.         return self::$getFallbackValues;
  134.     }
  135.     /**
  136.      * @param array|null $items
  137.      */
  138.     public function __construct(array $items null)
  139.     {
  140.         if ($items) {
  141.             $this->setItems($items);
  142.         }
  143.         $this->markFieldDirty('_self');
  144.         $this->markAllLanguagesAsDirty();
  145.     }
  146.     /**
  147.      * @param mixed $item
  148.      */
  149.     public function addItem($item)
  150.     {
  151.         $this->items[] = $item;
  152.         $this->markFieldDirty('_self');
  153.         $this->markAllLanguagesAsDirty();
  154.     }
  155.     /**
  156.      * @param array $items
  157.      *
  158.      * @return $this
  159.      */
  160.     public function setItems(array $items)
  161.     {
  162.         $this->items $items;
  163.         $this->markFieldDirty('_self');
  164.         $this->markAllLanguagesAsDirty();
  165.         return $this;
  166.     }
  167.     /**
  168.      * @internal
  169.      */
  170.     public function loadLazyData(): void
  171.     {
  172.         $this->getInternalData(true);
  173.     }
  174.     /**
  175.      * @internal
  176.      *
  177.      * @param bool $mark
  178.      */
  179.     public function setLoadedAllLazyData($mark true)
  180.     {
  181.         $this->_loadedAllLazyData $mark;
  182.     }
  183.     /**
  184.      * Note: this is for pimcore/pimcore use only.
  185.      *
  186.      * @internal
  187.      *
  188.      * @param bool $loadLazyFields
  189.      *
  190.      * @return array
  191.      */
  192.     public function getInternalData(bool $loadLazyFields false): array
  193.     {
  194.         $loadLazyFieldNames $this->getLazyLoadedFieldNames();
  195.         if ($loadLazyFields && !empty($loadLazyFieldNames) && !$this->_loadedAllLazyData) {
  196.             $isDirtyDetectionDisabled DataObject::isDirtyDetectionDisabled();
  197.             DataObject::disableDirtyDetection();
  198.             foreach ($loadLazyFieldNames as $name) {
  199.                 foreach (Tool::getValidLanguages() as $language) {
  200.                     $fieldDefinition $this->getFieldDefinition($name$this->getContext());
  201.                     $this->loadLazyField($fieldDefinition$name$language);
  202.                 }
  203.             }
  204.             DataObject::setDisableDirtyDetection($isDirtyDetectionDisabled);
  205.             $this->setLoadedAllLazyData();
  206.         }
  207.         foreach ($this->getFieldDefinitions($this->getContext(), ['suppressEnrichment' => true]) as $fieldDefinition) {
  208.             if ($fieldDefinition instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  209.                 foreach (Tool::getValidLanguages() as $language) {
  210.                     $this->setLocalizedValue($fieldDefinition->getName(), null$languagefalse);
  211.                 }
  212.             }
  213.         }
  214.         return $this->items;
  215.     }
  216.     /**
  217.      * @param Concrete|Model\Element\ElementDescriptor|null $object
  218.      * @param bool $markAsDirty
  219.      *
  220.      * @return $this
  221.      *
  222.      * @throws \Exception
  223.      */
  224.     public function setObject($objectbool $markAsDirty true)
  225.     {
  226.         if ($object instanceof Model\Element\ElementDescriptor) {
  227.             $object Service::getElementById($object->getType(), $object->getId());
  228.         }
  229.         if (!is_null($object) && !$object instanceof Concrete) {
  230.             throw new \Exception('must be instance of object concrete');
  231.         }
  232.         if ($markAsDirty) {
  233.             $this->markAllLanguagesAsDirty();
  234.         }
  235.         $this->object $object;
  236.         $this->objectId $object $object->getId() : null;
  237.         $this->setClass($object $object->getClass() : null);
  238.         return $this;
  239.     }
  240.     /**
  241.      * @return Concrete|null
  242.      */
  243.     public function getObject(): ?Concrete
  244.     {
  245.         if ($this->objectId && !$this->object) {
  246.             $this->setObject(Concrete::getById($this->objectId));
  247.         }
  248.         return $this->object;
  249.     }
  250.     /**
  251.      * @param ClassDefinition|null $class
  252.      *
  253.      * @return $this
  254.      */
  255.     public function setClass(?ClassDefinition $class)
  256.     {
  257.         $this->class $class;
  258.         return $this;
  259.     }
  260.     /**
  261.      * @return ClassDefinition|null
  262.      */
  263.     public function getClass(): ?ClassDefinition
  264.     {
  265.         if (!$this->class && $this->getObject()) {
  266.             $this->class $this->getObject()->getClass();
  267.         }
  268.         return $this->class;
  269.     }
  270.     /**
  271.      * @throws \Exception
  272.      *
  273.      * @param string|null $language
  274.      *
  275.      * @return string
  276.      */
  277.     public function getLanguage(string $language null): string
  278.     {
  279.         if ($language) {
  280.             return $language;
  281.         }
  282.         // try to get the language from the service container
  283.         try {
  284.             $locale \Pimcore::getContainer()->get(LocaleServiceInterface::class)->getLocale();
  285.             if (Tool::isValidLanguage($locale)) {
  286.                 return $locale;
  287.             }
  288.             if (\Pimcore::inAdmin()) {
  289.                 foreach (Tool::getValidLanguages() as $validLocale) {
  290.                     if (str_starts_with($validLocale$locale.'_')) {
  291.                         return $validLocale;
  292.                     }
  293.                 }
  294.             }
  295.             throw new \Exception('Not supported language');
  296.         } catch (\Exception $e) {
  297.             return Tool::getDefaultLanguage();
  298.         }
  299.     }
  300.     /**
  301.      * @param string $language
  302.      *
  303.      * @return bool
  304.      */
  305.     public function languageExists(string $language): bool
  306.     {
  307.         return array_key_exists($language$this->items);
  308.     }
  309.     /**
  310.      * @param string $name
  311.      * @param array $context
  312.      *
  313.      * @return ClassDefinition\Data|null
  314.      */
  315.     public function getFieldDefinition(string $name$context = [])
  316.     {
  317.         if (isset($context['containerType']) && $context['containerType'] === 'fieldcollection') {
  318.             $containerKey $context['containerKey'];
  319.             $container Model\DataObject\Fieldcollection\Definition::getByKey($containerKey);
  320.         } elseif (isset($context['containerType']) && $context['containerType'] === 'objectbrick') {
  321.             $containerKey $context['containerKey'];
  322.             $container Model\DataObject\Objectbrick\Definition::getByKey($containerKey);
  323.         } elseif (isset($context['containerType']) && $context['containerType'] === 'block') {
  324.             $containerKey $context['containerKey'];
  325.             $object $this->getObject();
  326.             $blockDefinition $object->getClass()->getFieldDefinition($containerKey);
  327.             $container $blockDefinition;
  328.         } else {
  329.             $object $this->getObject();
  330.             $container $object->getClass();
  331.         }
  332.         /** @var Model\DataObject\ClassDefinition\Data\Localizedfields|null $localizedFields */
  333.         $localizedFields $container->getFieldDefinition('localizedfields');
  334.         if ($localizedFields) {
  335.             return $localizedFields->getFieldDefinition($name);
  336.         }
  337.         return null;
  338.     }
  339.     /**
  340.      * @param array $context
  341.      * @param array $params
  342.      *
  343.      * @return ClassDefinition\Data[]
  344.      *
  345.      * @throws \Exception
  346.      */
  347.     protected function getFieldDefinitions($context = [], $params = []): array
  348.     {
  349.         if (isset($context['containerType']) && $context['containerType'] === 'fieldcollection') {
  350.             $containerKey $context['containerKey'];
  351.             $fcDef Model\DataObject\Fieldcollection\Definition::getByKey($containerKey);
  352.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $container */
  353.             $container $fcDef->getFieldDefinition('localizedfields');
  354.         } elseif (isset($context['containerType']) && $context['containerType'] === 'objectbrick') {
  355.             $containerKey $context['containerKey'];
  356.             $brickDef Model\DataObject\Objectbrick\Definition::getByKey($containerKey);
  357.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $container */
  358.             $container $brickDef->getFieldDefinition('localizedfields');
  359.         } elseif (isset($context['containerType']) && $context['containerType'] === 'block') {
  360.             $containerKey $context['containerKey'];
  361.             $object $this->getObject();
  362.             /** @var Model\DataObject\ClassDefinition\Data\Block $block */
  363.             $block $object->getClass()->getFieldDefinition($containerKey);
  364.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $container */
  365.             $container $block->getFieldDefinition('localizedfields');
  366.         } else {
  367.             $class $this->getClass();
  368.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $container */
  369.             $container $class->getFieldDefinition('localizedfields');
  370.         }
  371.         return $container->getFieldDefinitions($params);
  372.     }
  373.     /**
  374.      * @param ClassDefinition\Data $fieldDefinition
  375.      * @param string $name
  376.      * @param string $language
  377.      *
  378.      * @internal
  379.      */
  380.     private function loadLazyField(Model\DataObject\ClassDefinition\Data $fieldDefinitionstring $namestring $language): void
  381.     {
  382.         $lazyKey $this->buildLazyKey($name$language);
  383.         if (!$this->isLazyKeyLoaded($lazyKey) && $fieldDefinition instanceof Model\DataObject\ClassDefinition\Data\CustomResourcePersistingInterface) {
  384.             $params['language'] = $language;
  385.             $params['object'] = $this->getObject();
  386.             $params['context'] = $this->getContext();
  387.             $params['owner'] = $this;
  388.             $params['fieldname'] = $name;
  389.             $isDirtyDetectionDisabled DataObject::isDirtyDetectionDisabled();
  390.             DataObject::disableDirtyDetection();
  391.             $data $fieldDefinition->load($this$params);
  392.             if ($data === || !empty($data)) {
  393.                 $this->setLocalizedValue($name$data$languagefalse);
  394.             }
  395.             DataObject::setDisableDirtyDetection($isDirtyDetectionDisabled);
  396.             $this->markLazyKeyAsLoaded($lazyKey);
  397.         }
  398.     }
  399.     /**
  400.      * @param string $name
  401.      * @param string|null $language
  402.      * @param bool $ignoreFallbackLanguage
  403.      *
  404.      * @return mixed
  405.      */
  406.     public function getLocalizedValue(string $namestring $language nullbool $ignoreFallbackLanguage false)
  407.     {
  408.         $data null;
  409.         $language $this->getLanguage($language);
  410.         $context $this->getContext();
  411.         $fieldDefinition $this->getFieldDefinition($name$context);
  412.         if ($fieldDefinition instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  413.             $valueData = new Model\DataObject\Data\CalculatedValue($fieldDefinition->getName());
  414.             $valueData->setContextualData('localizedfield''localizedfields'null$languagenullnull$fieldDefinition);
  415.             $data Service::getCalculatedFieldValue($this->getObject(), $valueData);
  416.             return $data;
  417.         }
  418.         if ($fieldDefinition instanceof LazyLoadingSupportInterface && $fieldDefinition->getLazyLoading() && !$this->_loadedAllLazyData) {
  419.             $this->loadLazyField($fieldDefinition$name$language);
  420.         }
  421.         if ($this->languageExists($language)) {
  422.             if (array_key_exists($name$this->items[$language])) {
  423.                 $data $this->items[$language][$name];
  424.             }
  425.         }
  426.         // check for inherited value
  427.         $doGetInheritedValues DataObject::doGetInheritedValues();
  428.         $allowInheritance $fieldDefinition->supportsInheritance();
  429.         if (isset($context['containerType']) && ($context['containerType'] === 'block' || $context['containerType'] === 'fieldcollection')) {
  430.             $allowInheritance false;
  431.         }
  432.         if ($fieldDefinition->isEmpty($data) && $doGetInheritedValues && $allowInheritance && $this->getObject() instanceof Concrete) {
  433.             $object $this->getObject();
  434.             $class $object->getClass();
  435.             $allowInherit $class->getAllowInherit();
  436.             if ($allowInherit) {
  437.                 if ($object->getParent() instanceof AbstractObject) {
  438.                     $parent $object->getParent();
  439.                     while ($parent && $parent->getType() == AbstractObject::OBJECT_TYPE_FOLDER) {
  440.                         $parent $parent->getParent();
  441.                     }
  442.                     if ($parent && ($parent->getType() == AbstractObject::OBJECT_TYPE_OBJECT || $parent->getType() == AbstractObject::OBJECT_TYPE_VARIANT)) {
  443.                         /** @var Concrete $parent */
  444.                         if ($parent->getClassId() == $object->getClassId()) {
  445.                             $method 'getLocalizedfields';
  446.                             $parentContainer $parent;
  447.                             if (isset($context['containerType']) && $context['containerType'] === 'objectbrick') {
  448.                                 if (!empty($context['fieldname'])) {
  449.                                     $brickContainerGetter 'get' ucfirst($context['fieldname']);
  450.                                     $brickContainer $parent->$brickContainerGetter();
  451.                                     $brickGetter 'get' $context['containerKey'];
  452.                                     $brickData $brickContainer->$brickGetter();
  453.                                     $parentContainer $brickData;
  454.                                 }
  455.                             }
  456.                             if ($parentContainer && method_exists($parentContainer$method)) {
  457.                                 $localizedFields $parentContainer->getLocalizedFields();
  458.                                 if ($localizedFields instanceof Localizedfield) {
  459.                                     if ($localizedFields->getObject()->getId() != $this->getObject()->getId()) {
  460.                                         $localizedFields->setContext($this->getContext());
  461.                                         $data $localizedFields->getLocalizedValue($name$languagetrue);
  462.                                     }
  463.                                 }
  464.                             }
  465.                         }
  466.                     }
  467.                 }
  468.             }
  469.         }
  470.         // check for fallback value
  471.         if ($fieldDefinition->isEmpty($data) && !$ignoreFallbackLanguage && self::doGetFallbackValues()) {
  472.             foreach (Tool::getFallbackLanguagesFor($language) as $l) {
  473.                 // fallback-language may not exist yet for lazy-loaded field (relation)
  474.                 if ($this->languageExists($l) || ($fieldDefinition instanceof LazyLoadingSupportInterface && $fieldDefinition->getLazyLoading())) {
  475.                     if ($data $this->getLocalizedValue($name$l)) {
  476.                         break;
  477.                     }
  478.                 }
  479.             }
  480.         }
  481.         //TODO Pimcore 11: remove method_exists BC layer
  482.         if ($fieldDefinition instanceof PreGetDataInterface || ($fieldDefinition && method_exists($fieldDefinition'preGetData'))) {
  483.             if (!$fieldDefinition instanceof PreGetDataInterface) {
  484.                 trigger_deprecation('pimcore/pimcore''10.1'sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  485.                     'Implement the %s interface instead.'PreGetDataInterface::class));
  486.             }
  487.             $data $fieldDefinition->preGetData(
  488.                 $this,
  489.                 [
  490.                     'data' => $data,
  491.                     'language' => $language,
  492.                     'name' => $name,
  493.                 ]
  494.             );
  495.         }
  496.         return $data;
  497.     }
  498.     /**
  499.      * @param string $name
  500.      * @param mixed $value
  501.      * @param string|null $language
  502.      * @param bool $markFieldAsDirty
  503.      *
  504.      * @return $this
  505.      *
  506.      * @throws \Exception
  507.      */
  508.     public function setLocalizedValue(string $name$valuestring $language nullbool $markFieldAsDirty true)
  509.     {
  510.         if ($markFieldAsDirty) {
  511.             $this->markFieldDirty('_self');
  512.         }
  513.         if (self::$strictMode) {
  514.             if (!$language || !in_array($languageTool::getValidLanguages())) {
  515.                 throw new \Exception('Language '.$language.' not accepted in strict mode');
  516.             }
  517.         }
  518.         $language $this->getLanguage($language);
  519.         if (!$this->languageExists($language)) {
  520.             $this->items[$language] = [];
  521.             $this->markLanguageAsDirty($language);
  522.         }
  523.         $contextInfo $this->getContext();
  524.         if (isset($contextInfo['containerType']) && $contextInfo['containerType'] === 'block') {
  525.             $classId $contextInfo['classId'];
  526.             $containerDefinition ClassDefinition::getById($classId);
  527.             /** @var Model\DataObject\ClassDefinition\Data\Block $blockDefinition */
  528.             $blockDefinition $containerDefinition->getFieldDefinition($contextInfo['fieldname']);
  529.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $fieldDefinition */
  530.             $fieldDefinition $blockDefinition->getFieldDefinition('localizedfields');
  531.         } else {
  532.             if (isset($contextInfo['containerType']) && $contextInfo['containerType'] === 'fieldcollection') {
  533.                 $containerKey $contextInfo['containerKey'];
  534.                 $containerDefinition Fieldcollection\Definition::getByKey($containerKey);
  535.             } elseif (isset($contextInfo['containerType']) && $contextInfo['containerType'] === 'objectbrick') {
  536.                 $containerKey $contextInfo['containerKey'];
  537.                 $containerDefinition Model\DataObject\Objectbrick\Definition::getByKey($containerKey);
  538.             } else {
  539.                 $containerDefinition $this->getObject()->getClass();
  540.             }
  541.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $localizedFieldDefinition */
  542.             $localizedFieldDefinition $containerDefinition->getFieldDefinition('localizedfields');
  543.             $fieldDefinition $localizedFieldDefinition->getFieldDefinition($name, ['object' => $this->getObject()]);
  544.         }
  545.         // if a lazy loaded field hasn't been loaded we cannot rely on the dirty check
  546.         // note that preSetData will just overwrite it with the new data and mark it as loaded
  547.         $forceLanguageDirty false;
  548.         $isLazyLoadedField $fieldDefinition instanceof LazyLoadingSupportInterface && $fieldDefinition->getLazyLoading();
  549.         $lazyKey $this->buildLazyKey($name$language);
  550.         if ($isLazyLoadedField) {
  551.             if (!$this->isLazyKeyLoaded($lazyKey)) {
  552.                 $forceLanguageDirty true;
  553.             }
  554.         }
  555.         //TODO Pimcore 11: remove method_exists BC layer
  556.         if ($fieldDefinition instanceof PreSetDataInterface || method_exists($fieldDefinition'preSetData')) {
  557.             if (!$fieldDefinition instanceof PreSetDataInterface) {
  558.                 trigger_deprecation('pimcore/pimcore''10.1',
  559.                     sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  560.                     'Implement the %s interface instead.'PreSetDataInterface::class));
  561.             }
  562.             $value $fieldDefinition->preSetData(
  563.                 $this,
  564.                 $value,
  565.                 [
  566.                     'language' => $language,
  567.                     'name' => $name,
  568.                 ]
  569.             );
  570.         }
  571.         $isEqual false;
  572.         if ($fieldDefinition instanceof Model\DataObject\ClassDefinition\Data\EqualComparisonInterface) {
  573.             $isEqual $fieldDefinition->isEqual($this->items[$language][$name] ?? null$value);
  574.         }
  575.         if ($markFieldAsDirty && ($forceLanguageDirty || !$isEqual)) {
  576.             $this->markLanguageAsDirty($language);
  577.         }
  578.         $this->items[$language][$name] = $value;
  579.         if ($isLazyLoadedField) {
  580.             $this->markLazyKeyAsLoaded($lazyKey);
  581.         }
  582.         return $this;
  583.     }
  584.     /**
  585.      * {@inheritdoc}
  586.      */
  587.     public function isAllLazyKeysMarkedAsLoaded(): bool
  588.     {
  589.         $object $this->getObject();
  590.         if ($object instanceof Concrete) {
  591.             return $this->getObject()->isAllLazyKeysMarkedAsLoaded();
  592.         }
  593.         return true;
  594.     }
  595.     /**
  596.      * @return array
  597.      */
  598.     public function __sleep(): array
  599.     {
  600.         if (!$this->isInDumpState()) {
  601.             /**
  602.              * Remove all lazy loaded fields if item gets serialized for the cache (not for versions)
  603.              * This is actually not perfect, but currently we don't have an alternative
  604.              */
  605.             $lazyLoadedFields $this->getLazyLoadedFieldNames();
  606.             foreach ($lazyLoadedFields as $fieldName) {
  607.                 foreach (Tool::getValidLanguages() as $language) {
  608.                     unset($this->items[$language][$fieldName]);
  609.                     $lazyKey $this->buildLazyKey($fieldName$language);
  610.                     $this->unmarkLazyKeyAsLoaded($lazyKey);
  611.                 }
  612.             }
  613.         }
  614.         return ['items''context''objectId'];
  615.     }
  616.     /**
  617.      * @return array
  618.      */
  619.     public function getContext(): array
  620.     {
  621.         return $this->context ?? [];
  622.     }
  623.     /**
  624.      * @param array|null $context
  625.      */
  626.     public function setContext(?array $context): void
  627.     {
  628.         $this->context $context ?? [];
  629.     }
  630.     /**
  631.      * @internal
  632.      *
  633.      * @return bool
  634.      */
  635.     public function hasDirtyLanguages(): bool
  636.     {
  637.         if (DataObject::isDirtyDetectionDisabled()) {
  638.             return true;
  639.         }
  640.         return is_array($this->o_dirtyLanguages) && count($this->o_dirtyLanguages) > 0;
  641.     }
  642.     /**
  643.      * @internal
  644.      *
  645.      * @param string $language
  646.      *
  647.      * @return bool
  648.      */
  649.     public function isLanguageDirty(string $language): bool
  650.     {
  651.         if (DataObject::isDirtyDetectionDisabled()) {
  652.             return true;
  653.         }
  654.         if (is_array($this->o_dirtyLanguages)) {
  655.             if (count($this->o_dirtyLanguages) == 0) {
  656.                 return true;
  657.             }
  658.             if (isset($this->o_dirtyLanguages[$language])) {
  659.                 return $this->o_dirtyLanguages[$language];
  660.             }
  661.         }
  662.         return false;
  663.     }
  664.     /**
  665.      * @internal
  666.      */
  667.     public function resetLanguageDirtyMap(): void
  668.     {
  669.         $this->o_dirtyLanguages null;
  670.     }
  671.     /**
  672.      * @internal
  673.      *
  674.      * @return array|null
  675.      */
  676.     public function getDirtyLanguages(): ?array
  677.     {
  678.         return $this->o_dirtyLanguages;
  679.     }
  680.     /**
  681.      * @internal
  682.      */
  683.     public function markAllLanguagesAsDirty(): void
  684.     {
  685.         $this->o_dirtyLanguages = [];
  686.     }
  687.     /**
  688.      * @internal
  689.      *
  690.      * @return bool
  691.      */
  692.     public function allLanguagesAreDirty(): bool
  693.     {
  694.         if (DataObject::isDirtyDetectionDisabled()) {
  695.             return true;
  696.         }
  697.         return is_array($this->o_dirtyLanguages) && count($this->o_dirtyLanguages) === 0;
  698.     }
  699.     /**
  700.      * @internal
  701.      *
  702.      * @param string $language
  703.      * @param bool $dirty
  704.      */
  705.     public function markLanguageAsDirty(string $languagebool $dirty true): void
  706.     {
  707.         if (DataObject::isDirtyDetectionDisabled()) {
  708.             return;
  709.         }
  710.         if (!is_array($this->o_dirtyLanguages) && $dirty) {
  711.             $this->o_dirtyLanguages = [];
  712.         }
  713.         if ($dirty) {
  714.             $this->o_dirtyLanguages[$language] = true;
  715.         }
  716.         if (!$this->o_dirtyLanguages) {
  717.             $this->o_dirtyLanguages null;
  718.         }
  719.     }
  720.     /**
  721.      * @internal
  722.      *
  723.      * @return array
  724.      *
  725.      * @throws \Exception
  726.      */
  727.     protected function getLazyLoadedFieldNames(): array
  728.     {
  729.         $lazyLoadedFieldNames = [];
  730.         if (isset($this->context['containerType']) && $this->context['containerType'] === 'block') {
  731.             // if localized field is embedded in a block element there is no lazy loading. Maybe we can
  732.             // prevent this already in the class definition editor
  733.             return $lazyLoadedFieldNames;
  734.         }
  735.         $fields $this->getFieldDefinitions($this->getContext(), ['suppressEnrichment' => true]);
  736.         foreach ($fields as $field) {
  737.             if ($field instanceof LazyLoadingSupportInterface && $field->getLazyLoading()) {
  738.                 $lazyLoadedFieldNames[] = $field->getName();
  739.             }
  740.         }
  741.         return $lazyLoadedFieldNames;
  742.     }
  743.     /**
  744.      * @return int|null
  745.      */
  746.     public function getObjectId(): ?int
  747.     {
  748.         return $this->objectId;
  749.     }
  750. }