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

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