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

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|null
  405.      *
  406.      * @throws \Exception
  407.      * @throws Model\Exception\NotFoundException
  408.      */
  409.     public function getLocalizedValue(string $namestring $language nullbool $ignoreFallbackLanguage false)
  410.     {
  411.         $data null;
  412.         $language $this->getLanguage($language);
  413.         $context $this->getContext();
  414.         $fieldDefinition $this->getFieldDefinition($name$context);
  415.         if (!$fieldDefinition instanceof ClassDefinition\Data) {
  416.             throw new Model\Exception\NotFoundException(sprintf('Field "%s" does not exist in localizedfields'$name));
  417.         }
  418.         if ($fieldDefinition instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  419.             $valueData = new Model\DataObject\Data\CalculatedValue($fieldDefinition->getName());
  420.             $valueData->setContextualData('localizedfield''localizedfields'null$languagenullnull$fieldDefinition);
  421.             $data Service::getCalculatedFieldValue($this->getObject(), $valueData);
  422.             return $data;
  423.         }
  424.         if ($fieldDefinition instanceof LazyLoadingSupportInterface && $fieldDefinition->getLazyLoading() && !$this->_loadedAllLazyData) {
  425.             $this->loadLazyField($fieldDefinition$name$language);
  426.         }
  427.         if ($this->languageExists($language)) {
  428.             if (array_key_exists($name$this->items[$language])) {
  429.                 $data $this->items[$language][$name];
  430.             }
  431.         }
  432.         // check for inherited value
  433.         $doGetInheritedValues DataObject::doGetInheritedValues();
  434.         $allowInheritance $fieldDefinition->supportsInheritance();
  435.         if (isset($context['containerType']) && ($context['containerType'] === 'block' || $context['containerType'] === 'fieldcollection')) {
  436.             $allowInheritance false;
  437.         }
  438.         if ($fieldDefinition->isEmpty($data) && $doGetInheritedValues && $allowInheritance && $this->getObject() instanceof Concrete) {
  439.             $object $this->getObject();
  440.             $class $object->getClass();
  441.             $allowInherit $class->getAllowInherit();
  442.             if ($allowInherit) {
  443.                 if ($object->getParent() instanceof AbstractObject) {
  444.                     $parent $object->getParent();
  445.                     while ($parent && $parent->getType() == AbstractObject::OBJECT_TYPE_FOLDER) {
  446.                         $parent $parent->getParent();
  447.                     }
  448.                     if ($parent && ($parent->getType() == AbstractObject::OBJECT_TYPE_OBJECT || $parent->getType() == AbstractObject::OBJECT_TYPE_VARIANT)) {
  449.                         /** @var Concrete $parent */
  450.                         if ($parent->getClassId() == $object->getClassId()) {
  451.                             $method 'getLocalizedfields';
  452.                             $parentContainer $parent;
  453.                             if (isset($context['containerType']) && $context['containerType'] === 'objectbrick') {
  454.                                 if (!empty($context['fieldname'])) {
  455.                                     $brickContainerGetter 'get' ucfirst($context['fieldname']);
  456.                                     $brickContainer $parent->$brickContainerGetter();
  457.                                     $brickGetter 'get' $context['containerKey'];
  458.                                     $brickData $brickContainer->$brickGetter();
  459.                                     $parentContainer $brickData;
  460.                                 }
  461.                             }
  462.                             if ($parentContainer && method_exists($parentContainer$method)) {
  463.                                 $localizedFields $parentContainer->getLocalizedFields();
  464.                                 if ($localizedFields instanceof Localizedfield) {
  465.                                     if ($localizedFields->getObject()->getId() != $this->getObject()->getId()) {
  466.                                         $localizedFields->setContext($this->getContext());
  467.                                         $data $localizedFields->getLocalizedValue($name$languagetrue);
  468.                                     }
  469.                                 }
  470.                             }
  471.                         }
  472.                     }
  473.                 }
  474.             }
  475.         }
  476.         // check for fallback value
  477.         if ($fieldDefinition->isEmpty($data) && !$ignoreFallbackLanguage && self::doGetFallbackValues()) {
  478.             foreach (Tool::getFallbackLanguagesFor($language) as $l) {
  479.                 // fallback-language may not exist yet for lazy-loaded field (relation)
  480.                 if ($this->languageExists($l) || ($fieldDefinition instanceof LazyLoadingSupportInterface && $fieldDefinition->getLazyLoading())) {
  481.                     if ($data $this->getLocalizedValue($name$l)) {
  482.                         break;
  483.                     }
  484.                 }
  485.             }
  486.         }
  487.         //TODO Pimcore 11: remove method_exists BC layer
  488.         if ($fieldDefinition instanceof PreGetDataInterface || method_exists($fieldDefinition'preGetData')) {
  489.             if (!$fieldDefinition instanceof PreGetDataInterface) {
  490.                 trigger_deprecation('pimcore/pimcore''10.1'sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  491.                     'Implement the %s interface instead.'PreGetDataInterface::class));
  492.             }
  493.             $data $fieldDefinition->preGetData(
  494.                 $this,
  495.                 [
  496.                     'data' => $data,
  497.                     'language' => $language,
  498.                     'name' => $name,
  499.                 ]
  500.             );
  501.         }
  502.         return $data;
  503.     }
  504.     /**
  505.      * @param string $name
  506.      * @param mixed $value
  507.      * @param string|null $language
  508.      * @param bool $markFieldAsDirty
  509.      *
  510.      * @return $this
  511.      *
  512.      * @throws \Exception
  513.      */
  514.     public function setLocalizedValue(string $name$valuestring $language nullbool $markFieldAsDirty true)
  515.     {
  516.         if ($markFieldAsDirty) {
  517.             $this->markFieldDirty('_self');
  518.         }
  519.         if (self::$strictMode) {
  520.             if (!$language || !in_array($languageTool::getValidLanguages())) {
  521.                 throw new \Exception('Language '.$language.' not accepted in strict mode');
  522.             }
  523.         }
  524.         $language $this->getLanguage($language);
  525.         if (!$this->languageExists($language)) {
  526.             $this->items[$language] = [];
  527.             $this->markLanguageAsDirty($language);
  528.         }
  529.         $contextInfo $this->getContext();
  530.         if (isset($contextInfo['containerType']) && $contextInfo['containerType'] === 'block') {
  531.             $classId $contextInfo['classId'];
  532.             $containerDefinition ClassDefinition::getById($classId);
  533.             /** @var Model\DataObject\ClassDefinition\Data\Block $blockDefinition */
  534.             $blockDefinition $containerDefinition->getFieldDefinition($contextInfo['fieldname']);
  535.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $fieldDefinition */
  536.             $fieldDefinition $blockDefinition->getFieldDefinition('localizedfields');
  537.         } else {
  538.             if (isset($contextInfo['containerType']) && $contextInfo['containerType'] === 'fieldcollection') {
  539.                 $containerKey $contextInfo['containerKey'];
  540.                 $containerDefinition Fieldcollection\Definition::getByKey($containerKey);
  541.             } elseif (isset($contextInfo['containerType']) && $contextInfo['containerType'] === 'objectbrick') {
  542.                 $containerKey $contextInfo['containerKey'];
  543.                 $containerDefinition Model\DataObject\Objectbrick\Definition::getByKey($containerKey);
  544.             } else {
  545.                 $containerDefinition $this->getObject()->getClass();
  546.             }
  547.             /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $localizedFieldDefinition */
  548.             $localizedFieldDefinition $containerDefinition->getFieldDefinition('localizedfields');
  549.             $fieldDefinition $localizedFieldDefinition->getFieldDefinition($name, ['object' => $this->getObject()]);
  550.         }
  551.         // if a lazy loaded field hasn't been loaded we cannot rely on the dirty check
  552.         // note that preSetData will just overwrite it with the new data and mark it as loaded
  553.         $forceLanguageDirty false;
  554.         $isLazyLoadedField $fieldDefinition instanceof LazyLoadingSupportInterface && $fieldDefinition->getLazyLoading();
  555.         $lazyKey $this->buildLazyKey($name$language);
  556.         if ($isLazyLoadedField) {
  557.             if (!$this->isLazyKeyLoaded($lazyKey)) {
  558.                 $forceLanguageDirty true;
  559.             }
  560.         }
  561.         //TODO Pimcore 11: remove method_exists BC layer
  562.         if ($fieldDefinition instanceof PreSetDataInterface || method_exists($fieldDefinition'preSetData')) {
  563.             if (!$fieldDefinition instanceof PreSetDataInterface) {
  564.                 trigger_deprecation('pimcore/pimcore''10.1',
  565.                     sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  566.                     'Implement the %s interface instead.'PreSetDataInterface::class));
  567.             }
  568.             $value $fieldDefinition->preSetData(
  569.                 $this,
  570.                 $value,
  571.                 [
  572.                     'language' => $language,
  573.                     'name' => $name,
  574.                 ]
  575.             );
  576.         }
  577.         $isEqual false;
  578.         if ($fieldDefinition instanceof Model\DataObject\ClassDefinition\Data\EqualComparisonInterface) {
  579.             $isEqual $fieldDefinition->isEqual($this->items[$language][$name] ?? null$value);
  580.         }
  581.         if ($markFieldAsDirty && ($forceLanguageDirty || !$isEqual)) {
  582.             $this->markLanguageAsDirty($language);
  583.         }
  584.         $this->items[$language][$name] = $value;
  585.         if ($isLazyLoadedField) {
  586.             $this->markLazyKeyAsLoaded($lazyKey);
  587.         }
  588.         return $this;
  589.     }
  590.     /**
  591.      * {@inheritdoc}
  592.      */
  593.     public function isAllLazyKeysMarkedAsLoaded(): bool
  594.     {
  595.         $object $this->getObject();
  596.         if ($object instanceof Concrete) {
  597.             return $this->getObject()->isAllLazyKeysMarkedAsLoaded();
  598.         }
  599.         return true;
  600.     }
  601.     /**
  602.      * @return array
  603.      */
  604.     public function __sleep(): array
  605.     {
  606.         if (!$this->isInDumpState()) {
  607.             /**
  608.              * Remove all lazy loaded fields if item gets serialized for the cache (not for versions)
  609.              * This is actually not perfect, but currently we don't have an alternative
  610.              */
  611.             $lazyLoadedFields $this->getLazyLoadedFieldNames();
  612.             foreach ($lazyLoadedFields as $fieldName) {
  613.                 foreach (Tool::getValidLanguages() as $language) {
  614.                     unset($this->items[$language][$fieldName]);
  615.                     $lazyKey $this->buildLazyKey($fieldName$language);
  616.                     $this->unmarkLazyKeyAsLoaded($lazyKey);
  617.                 }
  618.             }
  619.         }
  620.         return ['items''context''objectId'];
  621.     }
  622.     /**
  623.      * @return array
  624.      */
  625.     public function getContext(): array
  626.     {
  627.         return $this->context ?? [];
  628.     }
  629.     /**
  630.      * @param array|null $context
  631.      */
  632.     public function setContext(?array $context): void
  633.     {
  634.         $this->context $context ?? [];
  635.     }
  636.     /**
  637.      * @internal
  638.      *
  639.      * @return bool
  640.      */
  641.     public function hasDirtyLanguages(): bool
  642.     {
  643.         if (DataObject::isDirtyDetectionDisabled()) {
  644.             return true;
  645.         }
  646.         return is_array($this->o_dirtyLanguages) && count($this->o_dirtyLanguages) > 0;
  647.     }
  648.     /**
  649.      * @internal
  650.      *
  651.      * @param string $language
  652.      *
  653.      * @return bool
  654.      */
  655.     public function isLanguageDirty(string $language): bool
  656.     {
  657.         if (DataObject::isDirtyDetectionDisabled()) {
  658.             return true;
  659.         }
  660.         if (is_array($this->o_dirtyLanguages)) {
  661.             if (count($this->o_dirtyLanguages) == 0) {
  662.                 return true;
  663.             }
  664.             if (isset($this->o_dirtyLanguages[$language])) {
  665.                 return $this->o_dirtyLanguages[$language];
  666.             }
  667.         }
  668.         return false;
  669.     }
  670.     /**
  671.      * @internal
  672.      */
  673.     public function resetLanguageDirtyMap(): void
  674.     {
  675.         $this->o_dirtyLanguages null;
  676.     }
  677.     /**
  678.      * @internal
  679.      *
  680.      * @return array|null
  681.      */
  682.     public function getDirtyLanguages(): ?array
  683.     {
  684.         return $this->o_dirtyLanguages;
  685.     }
  686.     /**
  687.      * @internal
  688.      */
  689.     public function markAllLanguagesAsDirty(): void
  690.     {
  691.         $this->o_dirtyLanguages = [];
  692.     }
  693.     /**
  694.      * @internal
  695.      *
  696.      * @return bool
  697.      */
  698.     public function allLanguagesAreDirty(): bool
  699.     {
  700.         if (DataObject::isDirtyDetectionDisabled()) {
  701.             return true;
  702.         }
  703.         return is_array($this->o_dirtyLanguages) && count($this->o_dirtyLanguages) === 0;
  704.     }
  705.     /**
  706.      * @internal
  707.      *
  708.      * @param string $language
  709.      * @param bool $dirty
  710.      */
  711.     public function markLanguageAsDirty(string $languagebool $dirty true): void
  712.     {
  713.         if (DataObject::isDirtyDetectionDisabled()) {
  714.             return;
  715.         }
  716.         if (!is_array($this->o_dirtyLanguages) && $dirty) {
  717.             $this->o_dirtyLanguages = [];
  718.         }
  719.         if ($dirty) {
  720.             $this->o_dirtyLanguages[$language] = true;
  721.         }
  722.         if (!$this->o_dirtyLanguages) {
  723.             $this->o_dirtyLanguages null;
  724.         }
  725.     }
  726.     /**
  727.      * @internal
  728.      *
  729.      * @return array
  730.      *
  731.      * @throws \Exception
  732.      */
  733.     protected function getLazyLoadedFieldNames(): array
  734.     {
  735.         $lazyLoadedFieldNames = [];
  736.         if (isset($this->context['containerType']) && $this->context['containerType'] === 'block') {
  737.             // if localized field is embedded in a block element there is no lazy loading. Maybe we can
  738.             // prevent this already in the class definition editor
  739.             return $lazyLoadedFieldNames;
  740.         }
  741.         $fields $this->getFieldDefinitions($this->getContext(), ['suppressEnrichment' => true]);
  742.         foreach ($fields as $field) {
  743.             if ($field instanceof LazyLoadingSupportInterface && $field->getLazyLoading()) {
  744.                 $lazyLoadedFieldNames[] = $field->getName();
  745.             }
  746.         }
  747.         return $lazyLoadedFieldNames;
  748.     }
  749.     /**
  750.      * @return int|null
  751.      */
  752.     public function getObjectId(): ?int
  753.     {
  754.         return $this->objectId;
  755.     }
  756. }