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

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|null
  62. */
  63. protected $object = null;
  64. /**
  65. * @internal
  66. *
  67. * @var ClassDefinition|null
  68. */
  69. protected ?ClassDefinition $class = null;
  70. /**
  71. * @internal
  72. *
  73. * @var array
  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, $language, false);
  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($object, bool $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 $fieldDefinition, string $name, string $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 === 0 || !empty($data)) {
  393. $this->setLocalizedValue($name, $data, $language, false);
  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 $name, string $language = null, bool $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, $language, null, null, $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 (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, $language, true);
  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, $value, string $language = null, bool $markFieldAsDirty = true)
  509. {
  510. if ($markFieldAsDirty) {
  511. $this->markFieldDirty('_self');
  512. }
  513. if (self::$strictMode) {
  514. if (!$language || !in_array($language, Tool::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 $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 $language, bool $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. }