vendor/pimcore/pimcore/models/DataObject/Concrete.php line 403

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\Db;
  16. use Pimcore\Event\DataObjectEvents;
  17. use Pimcore\Event\Model\DataObjectEvent;
  18. use Pimcore\Logger;
  19. use Pimcore\Messenger\VersionDeleteMessage;
  20. use Pimcore\Model;
  21. use Pimcore\Model\DataObject;
  22. use Pimcore\Model\DataObject\ClassDefinition\Data\LazyLoadingSupportInterface;
  23. use Pimcore\Model\DataObject\ClassDefinition\Data\Relations\AbstractRelations;
  24. use Pimcore\Model\DataObject\Exception\InheritanceParentNotFoundException;
  25. use Pimcore\Model\Element\DirtyIndicatorInterface;
  26. /**
  27.  * @method \Pimcore\Model\DataObject\Concrete\Dao getDao()
  28.  * @method \Pimcore\Model\Version|null getLatestVersion($userId = null)
  29.  */
  30. class Concrete extends DataObject implements LazyLoadedFieldsInterface
  31. {
  32.     use Model\DataObject\Traits\LazyLoadedRelationTrait;
  33.     use Model\Element\Traits\ScheduledTasksTrait;
  34.     /**
  35.      * @internal
  36.      *
  37.      * @var array|null
  38.      */
  39.     protected $__rawRelationData null;
  40.     /**
  41.      * @internal
  42.      *
  43.      * @var array
  44.      */
  45.     public const SYSTEM_COLUMN_NAMES = ['id''fullpath''key''published''creationDate''modificationDate''filename''classname''index'];
  46.     /**
  47.      * @internal
  48.      *
  49.      * @var bool
  50.      */
  51.     protected $o_published;
  52.     /**
  53.      * @internal
  54.      *
  55.      * @var ClassDefinition|null
  56.      */
  57.     protected ?ClassDefinition $o_class null;
  58.     /**
  59.      * @internal
  60.      *
  61.      * @var string
  62.      */
  63.     protected $o_classId;
  64.     /**
  65.      * @internal
  66.      *
  67.      * @var string
  68.      */
  69.     protected $o_className;
  70.     /**
  71.      * @internal
  72.      *
  73.      * @var array|null
  74.      */
  75.     protected $o_versions null;
  76.     /**
  77.      * @internal
  78.      *
  79.      * @var bool|null
  80.      */
  81.     protected $omitMandatoryCheck;
  82.     /**
  83.      * @internal
  84.      *
  85.      * @var bool
  86.      */
  87.     protected $allLazyKeysMarkedAsLoaded false;
  88.     /**
  89.      * returns the class ID of the current object class
  90.      *
  91.      * @return string
  92.      */
  93.     public static function classId()
  94.     {
  95.         $v get_class_vars(get_called_class());
  96.         return $v['o_classId'];
  97.     }
  98.     /**
  99.      * {@inheritdoc}
  100.      */
  101.     protected function update($isUpdate null$params = [])
  102.     {
  103.         $fieldDefinitions $this->getClass()->getFieldDefinitions();
  104.         $validationExceptions = [];
  105.         foreach ($fieldDefinitions as $fd) {
  106.             try {
  107.                 $getter 'get' ucfirst($fd->getName());
  108.                 if (method_exists($this$getter)) {
  109.                     $value $this->$getter();
  110.                     $omitMandatoryCheck $this->getOmitMandatoryCheck();
  111.                     //check throws Exception
  112.                     try {
  113.                         $fd->checkValidity($value$omitMandatoryCheck$params);
  114.                     } catch (\Exception $e) {
  115.                         if ($this->getClass()->getAllowInherit() && $fd->supportsInheritance() && $fd->isEmpty($value)) {
  116.                             //try again with parent data when inheritance is activated
  117.                             try {
  118.                                 $getInheritedValues DataObject::doGetInheritedValues();
  119.                                 DataObject::setGetInheritedValues(true);
  120.                                 $value $this->$getter();
  121.                                 $fd->checkValidity($value$omitMandatoryCheck$params);
  122.                                 DataObject::setGetInheritedValues($getInheritedValues);
  123.                             } catch (\Exception $e) {
  124.                                 if (!$e instanceof Model\Element\ValidationException) {
  125.                                     throw $e;
  126.                                 }
  127.                                 $exceptionClass get_class($e);
  128.                                 $newException = new $exceptionClass($e->getMessage() . ' fieldname=' $fd->getName(), $e->getCode(), $e->getPrevious());
  129.                                 $newException->setSubItems($e->getSubItems());
  130.                                 throw $newException;
  131.                             }
  132.                         } else {
  133.                             if ($e instanceof Model\Element\ValidationException) {
  134.                                 throw $e;
  135.                             }
  136.                             $exceptionClass get_class($e);
  137.                             throw new $exceptionClass($e->getMessage() . ' fieldname=' $fd->getName(), $e->getCode(), $e);
  138.                         }
  139.                     }
  140.                 }
  141.             } catch (Model\Element\ValidationException $ve) {
  142.                 $validationExceptions[] = $ve;
  143.             }
  144.         }
  145.         $preUpdateEvent = new DataObjectEvent($this, [
  146.             'validationExceptions' => $validationExceptions,
  147.             'message' => 'Validation failed: ',
  148.             'separator' => ' / ',
  149.         ]);
  150.         \Pimcore::getEventDispatcher()->dispatch($preUpdateEventDataObjectEvents::PRE_UPDATE_VALIDATION_EXCEPTION);
  151.         $validationExceptions $preUpdateEvent->getArgument('validationExceptions');
  152.         if ($validationExceptions) {
  153.             $message $preUpdateEvent->getArgument('message');
  154.             $errors = [];
  155.             /** @var Model\Element\ValidationException $e */
  156.             foreach ($validationExceptions as $e) {
  157.                 $errors[] = $e->getAggregatedMessage();
  158.             }
  159.             $message .= implode($preUpdateEvent->getArgument('separator'), $errors);
  160.             throw new Model\Element\ValidationException($message);
  161.         }
  162.         $isDirtyDetectionDisabled self::isDirtyDetectionDisabled();
  163.         try {
  164.             $oldVersionCount $this->getVersionCount();
  165.             parent::update($isUpdate$params);
  166.             $newVersionCount $this->getVersionCount();
  167.             if (($newVersionCount != $oldVersionCount 1) || ($this instanceof DirtyIndicatorInterface && $this->isFieldDirty('o_parentId'))) {
  168.                 self::disableDirtyDetection();
  169.             }
  170.             $this->getDao()->update($isUpdate);
  171.             // scheduled tasks are saved in $this->saveVersion();
  172.             $this->saveVersion(falsefalse, isset($params['versionNote']) ? $params['versionNote'] : null);
  173.             $this->saveChildData();
  174.         } finally {
  175.             self::setDisableDirtyDetection($isDirtyDetectionDisabled);
  176.         }
  177.     }
  178.     private function saveChildData(): void
  179.     {
  180.         if ($this->getClass()->getAllowInherit()) {
  181.             $this->getDao()->saveChildData();
  182.         }
  183.     }
  184.     /**
  185.      * {@inheritdoc}
  186.      */
  187.     protected function doDelete()
  188.     {
  189.         // Dispatch Symfony Message Bus to delete versions
  190.         \Pimcore::getContainer()->get('messenger.bus.pimcore-core')->dispatch(
  191.             new VersionDeleteMessage(Model\Element\Service::getElementType($this), $this->getId())
  192.         );
  193.         $this->getDao()->deleteAllTasks();
  194.         parent::doDelete();
  195.     }
  196.     /**
  197.      * $callPluginHook is true when the method is called from outside (eg. directly in the controller "save only version")
  198.      * it is false when the method is called by $this->update()
  199.      *
  200.      * @param bool $setModificationDate
  201.      * @param bool $saveOnlyVersion
  202.      * @param string $versionNote version note
  203.      * @param bool $isAutoSave
  204.      *
  205.      * @return Model\Version
  206.      */
  207.     public function saveVersion($setModificationDate true$saveOnlyVersion true$versionNote null$isAutoSave false)
  208.     {
  209.         try {
  210.             if ($setModificationDate) {
  211.                 $this->setModificationDate(time());
  212.             }
  213.             // hook should be also called if "save only new version" is selected
  214.             if ($saveOnlyVersion) {
  215.                 $preUpdateEvent = new DataObjectEvent($this, [
  216.                     'saveVersionOnly' => true,
  217.                     'isAutoSave' => $isAutoSave,
  218.                 ]);
  219.                 \Pimcore::getEventDispatcher()->dispatch($preUpdateEventDataObjectEvents::PRE_UPDATE);
  220.             }
  221.             // scheduled tasks are saved always, they are not versioned!
  222.             $this->saveScheduledTasks();
  223.             $version null;
  224.             // only create a new version if there is at least 1 allowed
  225.             // or if saveVersion() was called directly (it's a newer version of the object)
  226.             $objectsConfig \Pimcore\Config::getSystemConfiguration('objects');
  227.             if ((is_null($objectsConfig['versions']['days'] ?? null) && is_null($objectsConfig['versions']['steps'] ?? null))
  228.                 || (!empty($objectsConfig['versions']['steps']))
  229.                 || !empty($objectsConfig['versions']['days'])
  230.                 || $setModificationDate) {
  231.                 $saveStackTrace = !($objectsConfig['versions']['disable_stack_trace'] ?? false);
  232.                 $version $this->doSaveVersion($versionNote$saveOnlyVersion$saveStackTrace$isAutoSave);
  233.             }
  234.             // hook should be also called if "save only new version" is selected
  235.             if ($saveOnlyVersion) {
  236.                 $postUpdateEvent = new DataObjectEvent($this, [
  237.                     'saveVersionOnly' => true,
  238.                     'isAutoSave' => $isAutoSave,
  239.                 ]);
  240.                 \Pimcore::getEventDispatcher()->dispatch($postUpdateEventDataObjectEvents::POST_UPDATE);
  241.             }
  242.             return $version;
  243.         } catch (\Exception $e) {
  244.             $postUpdateFailureEvent = new DataObjectEvent($this, [
  245.                 'saveVersionOnly' => true,
  246.                 'exception' => $e,
  247.                 'isAutoSave' => $isAutoSave,
  248.             ]);
  249.             \Pimcore::getEventDispatcher()->dispatch($postUpdateFailureEventDataObjectEvents::POST_UPDATE_FAILURE);
  250.             throw $e;
  251.         }
  252.     }
  253.     /**
  254.      * @return Model\Version[]
  255.      */
  256.     public function getVersions()
  257.     {
  258.         if ($this->o_versions === null) {
  259.             $this->setVersions($this->getDao()->getVersions());
  260.         }
  261.         return $this->o_versions;
  262.     }
  263.     /**
  264.      * @param Model\Version[] $o_versions
  265.      *
  266.      * @return $this
  267.      */
  268.     public function setVersions($o_versions)
  269.     {
  270.         $this->o_versions $o_versions;
  271.         return $this;
  272.     }
  273.     /**
  274.      * @param string $key
  275.      *
  276.      * @return mixed
  277.      */
  278.     public function getValueForFieldName($key)
  279.     {
  280.         if (isset($this->$key)) {
  281.             return $this->$key;
  282.         }
  283.         if ($this->getClass()->getFieldDefinition($key) instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  284.             $value = new Model\DataObject\Data\CalculatedValue($key);
  285.             $value Service::getCalculatedFieldValue($this$value);
  286.             return $value;
  287.         }
  288.         return null;
  289.     }
  290.     /**
  291.      * @param array $tags
  292.      *
  293.      * @return array
  294.      */
  295.     public function getCacheTags(array $tags = []): array
  296.     {
  297.         $tags parent::getCacheTags($tags);
  298.         $tags['class_' $this->getClassId()] = 'class_' $this->getClassId();
  299.         foreach ($this->getClass()->getFieldDefinitions() as $name => $def) {
  300.             // no need to add lazy-loading fields to the cache tags
  301.             if (!$def instanceof LazyLoadingSupportInterface || !$def->getLazyLoading()) {
  302.                 $tags $def->getCacheTags($this->getValueForFieldName($name), $tags);
  303.             }
  304.         }
  305.         return $tags;
  306.     }
  307.     /**
  308.      * {@inheritdoc}
  309.      */
  310.     protected function resolveDependencies(): array
  311.     {
  312.         $dependencies = [parent::resolveDependencies()];
  313.         // check in fields
  314.         if ($this->getClass() instanceof ClassDefinition) {
  315.             foreach ($this->getClass()->getFieldDefinitions() as $field) {
  316.                 $key $field->getName();
  317.                 $dependencies[] = $field->resolveDependencies($this->$key ?? null);
  318.             }
  319.         }
  320.         return array_merge(...$dependencies);
  321.     }
  322.     /**
  323.      * @param ClassDefinition|null $o_class
  324.      *
  325.      * @return self
  326.      */
  327.     public function setClass(?ClassDefinition $o_class)
  328.     {
  329.         $this->o_class $o_class;
  330.         return $this;
  331.     }
  332.     /**
  333.      * @return ClassDefinition|null
  334.      */
  335.     public function getClass(): ?ClassDefinition
  336.     {
  337.         if (!$this->o_class) {
  338.             $this->setClass(ClassDefinition::getById($this->getClassId()));
  339.         }
  340.         return $this->o_class;
  341.     }
  342.     /**
  343.      * @return string
  344.      */
  345.     public function getClassId()
  346.     {
  347.         return $this->o_classId;
  348.     }
  349.     /**
  350.      * @param string $o_classId
  351.      *
  352.      * @return $this
  353.      */
  354.     public function setClassId($o_classId)
  355.     {
  356.         $this->o_classId $o_classId;
  357.         return $this;
  358.     }
  359.     /**
  360.      * @return string
  361.      */
  362.     public function getClassName()
  363.     {
  364.         return $this->o_className;
  365.     }
  366.     /**
  367.      * @param string $o_className
  368.      *
  369.      * @return $this
  370.      */
  371.     public function setClassName($o_className)
  372.     {
  373.         $this->o_className $o_className;
  374.         return $this;
  375.     }
  376.     /**
  377.      * @return bool
  378.      */
  379.     public function getPublished()
  380.     {
  381.         return (bool) $this->o_published;
  382.     }
  383.     /**
  384.      * @return bool
  385.      */
  386.     public function isPublished()
  387.     {
  388.         return (bool) $this->getPublished();
  389.     }
  390.     /**
  391.      * @param bool $o_published
  392.      *
  393.      * @return $this
  394.      */
  395.     public function setPublished($o_published)
  396.     {
  397.         $this->o_published = (bool) $o_published;
  398.         return $this;
  399.     }
  400.     /**
  401.      * @param bool $omitMandatoryCheck
  402.      *
  403.      * @return self
  404.      */
  405.     public function setOmitMandatoryCheck($omitMandatoryCheck)
  406.     {
  407.         $this->omitMandatoryCheck $omitMandatoryCheck;
  408.         return $this;
  409.     }
  410.     /**
  411.      * @return bool
  412.      */
  413.     public function getOmitMandatoryCheck()
  414.     {
  415.         if ($this->omitMandatoryCheck === null) {
  416.             return !$this->isPublished();
  417.         }
  418.         return $this->omitMandatoryCheck;
  419.     }
  420.     /**
  421.      * @param string $key
  422.      * @param mixed $params
  423.      *
  424.      * @return mixed
  425.      *
  426.      * @throws InheritanceParentNotFoundException
  427.      */
  428.     public function getValueFromParent($key$params null)
  429.     {
  430.         $parent $this->getNextParentForInheritance();
  431.         if ($parent) {
  432.             $method 'get' $key;
  433.             if (method_exists($parent$method)) {
  434.                 return $parent->$method($params);
  435.             }
  436.             throw new InheritanceParentNotFoundException(sprintf('Parent object does not have a method called `%s()`, unable to retrieve value for key `%s`'$method$key));
  437.         }
  438.         throw new InheritanceParentNotFoundException('No parent object available to get a value from');
  439.     }
  440.     /**
  441.      * @internal
  442.      *
  443.      * @return AbstractObject|null
  444.      */
  445.     public function getNextParentForInheritance()
  446.     {
  447.         return $this->getClosestParentOfClass($this->getClassId());
  448.     }
  449.     /**
  450.      * @param string $classId
  451.      *
  452.      * @return self|null
  453.      */
  454.     private function getClosestParentOfClass(string $classId): ?self
  455.     {
  456.         $parent $this->getParent();
  457.         if ($parent instanceof AbstractObject) {
  458.             while ($parent && (!$parent instanceof Concrete || $parent->getClassId() !== $classId)) {
  459.                 $parent $parent->getParent();
  460.             }
  461.             if ($parent && in_array($parent->getType(), [self::OBJECT_TYPE_OBJECTself::OBJECT_TYPE_VARIANT], true)) {
  462.                 /** @var Concrete $parent */
  463.                 if ($parent->getClassId() === $classId) {
  464.                     return $parent;
  465.                 }
  466.             }
  467.         }
  468.         return null;
  469.     }
  470.     /**
  471.      * get object relation data as array for a specific field
  472.      *
  473.      * @param string $fieldName
  474.      * @param bool $forOwner
  475.      * @param string $remoteClassId
  476.      *
  477.      * @return array
  478.      */
  479.     public function getRelationData($fieldName$forOwner$remoteClassId)
  480.     {
  481.         $relationData $this->getDao()->getRelationData($fieldName$forOwner$remoteClassId);
  482.         return $relationData;
  483.     }
  484.     /**
  485.      * @param string $method
  486.      * @param array $arguments
  487.      *
  488.      * @return Model\Listing\AbstractListing|Concrete|null
  489.      *
  490.      * @throws \Exception
  491.      */
  492.     public static function __callStatic($method$arguments)
  493.     {
  494.         // check for custom static getters like DataObject::getByMyfield()
  495.         $propertyName lcfirst(preg_replace('/^getBy/i'''$method));
  496.         $classDefinition ClassDefinition::getById(self::classId());
  497.         // get real fieldname (case sensitive)
  498.         $fieldnames = [];
  499.         $defaultCondition '';
  500.         foreach ($classDefinition->getFieldDefinitions() as $fd) {
  501.             $fieldnames[] = $fd->getName();
  502.         }
  503.         $realPropertyName implode(''preg_grep('/^' preg_quote($propertyName'/') . '$/i'$fieldnames));
  504.         if (!$classDefinition->getFieldDefinition($realPropertyName) instanceof Model\DataObject\ClassDefinition\Data) {
  505.             $localizedField $classDefinition->getFieldDefinition('localizedfields');
  506.             if ($localizedField instanceof Model\DataObject\ClassDefinition\Data\Localizedfields) {
  507.                 $fieldnames = [];
  508.                 foreach ($localizedField->getFieldDefinitions() as $fd) {
  509.                     $fieldnames[] = $fd->getName();
  510.                 }
  511.                 $realPropertyName implode(''preg_grep('/^' preg_quote($propertyName'/') . '$/i'$fieldnames));
  512.                 $localizedFieldDefinition $localizedField->getFieldDefinition($realPropertyName);
  513.                 if ($localizedFieldDefinition instanceof Model\DataObject\ClassDefinition\Data) {
  514.                     $realPropertyName 'localizedfields';
  515.                     \array_unshift($arguments$localizedFieldDefinition->getName());
  516.                 }
  517.             }
  518.         }
  519.         if ($classDefinition->getFieldDefinition($realPropertyName) instanceof Model\DataObject\ClassDefinition\Data) {
  520.             $field $classDefinition->getFieldDefinition($realPropertyName);
  521.             if (!$field->isFilterable()) {
  522.                 throw new \Exception("Static getter '::getBy".ucfirst($realPropertyName)."' is not allowed for fieldtype '" $field->getFieldType() . "'");
  523.             }
  524.             if ($field instanceof Model\DataObject\ClassDefinition\Data\Localizedfields) {
  525.                 $arguments array_pad($arguments60);
  526.                 [$localizedPropertyName$value$locale$limit$offset$objectTypes] = $arguments;
  527.                 $localizedField $field->getFieldDefinition($localizedPropertyName);
  528.                 if (!$localizedField instanceof Model\DataObject\ClassDefinition\Data) {
  529.                     Logger::error('Class: DataObject\\Concrete => call to undefined static method ' $method);
  530.                     throw new \Exception('Call to undefined static method ' $method ' in class DataObject\\Concrete');
  531.                 }
  532.                 if (!$localizedField->isFilterable()) {
  533.                     throw new \Exception("Static getter '::getBy".ucfirst($realPropertyName)."' is not allowed for fieldtype '" $localizedField->getFieldType() . "'");
  534.                 }
  535.                 $defaultCondition $localizedPropertyName ' = ' Db::get()->quote($value) . ' ';
  536.                 $listConfig = [
  537.                     'condition' => $defaultCondition,
  538.                 ];
  539.                 if ($locale) {
  540.                     $listConfig['locale'] = $locale;
  541.                 }
  542.             } else {
  543.                 $arguments array_pad($arguments40);
  544.                 [$value$limit$offset$objectTypes] = $arguments;
  545.                 if (!$field instanceof AbstractRelations) {
  546.                     $defaultCondition $realPropertyName ' = ' Db::get()->quote($value) . ' ';
  547.                 }
  548.                 $listConfig = [
  549.                     'condition' => $defaultCondition,
  550.                 ];
  551.             }
  552.             if (!is_array($limit)) {
  553.                 if ($limit) {
  554.                     $listConfig['limit'] = $limit;
  555.                 }
  556.                 if ($offset) {
  557.                     $listConfig['offset'] = $offset;
  558.                 }
  559.             } else {
  560.                 $listConfig array_merge($listConfig$limit);
  561.                 $limitCondition $limit['condition'] ?? '';
  562.                 $listConfig['condition'] = $defaultCondition $limitCondition;
  563.             }
  564.             $list = static::makeList($listConfig$objectTypes);
  565.             if ($field instanceof AbstractRelations) {
  566.                 $list $field->addListingFilter($list$value);
  567.             }
  568.             if (isset($listConfig['limit']) && $listConfig['limit'] == 1) {
  569.                 $elements $list->getObjects();
  570.                 return isset($elements[0]) ? $elements[0] : null;
  571.             }
  572.             return $list;
  573.         }
  574.         try {
  575.             return call_user_func_array([parent::class, $method], $arguments);
  576.         } catch (\Exception $e) {
  577.             // there is no property for the called method, so throw an exception
  578.             Logger::error('Class: DataObject\\Concrete => call to undefined static method '.$method);
  579.             throw new \Exception('Call to undefined static method '.$method.' in class DataObject\\Concrete');
  580.         }
  581.     }
  582.     /**
  583.      * @return $this
  584.      *
  585.      * @throws \Exception
  586.      */
  587.     public function save()
  588.     {
  589.         $isDirtyDetectionDisabled DataObject::isDirtyDetectionDisabled();
  590.         // if the class is newer then better disable the dirty detection. This should fix issues with the query table if
  591.         // the inheritance enabled flag has been changed in the meantime
  592.         if ($this->getClass()->getModificationDate() >= $this->getModificationDate() && $this->getId()) {
  593.             DataObject::disableDirtyDetection();
  594.         }
  595.         try {
  596.             $params = [];
  597.             if (func_num_args() && is_array(func_get_arg(0))) {
  598.                 $params func_get_arg(0);
  599.             }
  600.             parent::save($params);
  601.             if ($this instanceof DirtyIndicatorInterface) {
  602.                 $this->resetDirtyMap();
  603.             }
  604.         } finally {
  605.             DataObject::setDisableDirtyDetection($isDirtyDetectionDisabled);
  606.         }
  607.         return $this;
  608.     }
  609.     /**
  610.      * @internal
  611.      *
  612.      * @return array
  613.      */
  614.     public function getLazyLoadedFieldNames(): array
  615.     {
  616.         $lazyLoadedFieldNames = [];
  617.         $fields $this->getClass()->getFieldDefinitions(['suppressEnrichment' => true]);
  618.         foreach ($fields as $field) {
  619.             if ($field instanceof LazyLoadingSupportInterface && $field->getLazyLoading()) {
  620.                 $lazyLoadedFieldNames[] = $field->getName();
  621.             }
  622.         }
  623.         return $lazyLoadedFieldNames;
  624.     }
  625.     /**
  626.      * {@inheritdoc}
  627.      */
  628.     public function isAllLazyKeysMarkedAsLoaded(): bool
  629.     {
  630.         if (!$this->getId()) {
  631.             return true;
  632.         }
  633.         return $this->allLazyKeysMarkedAsLoaded;
  634.     }
  635.     public function markAllLazyLoadedKeysAsLoaded()
  636.     {
  637.         $this->allLazyKeysMarkedAsLoaded true;
  638.     }
  639.     public function __sleep()
  640.     {
  641.         $parentVars parent::__sleep();
  642.         $finalVars = [];
  643.         $blockedVars = [];
  644.         if (!$this->isInDumpState()) {
  645.             $blockedVars = ['loadedLazyKeys''allLazyKeysMarkedAsLoaded'];
  646.             // do not dump lazy loaded fields for caching
  647.             $lazyLoadedFields $this->getLazyLoadedFieldNames();
  648.             $blockedVars array_merge($lazyLoadedFields$blockedVars);
  649.         }
  650.         foreach ($parentVars as $key) {
  651.             if (!in_array($key$blockedVars)) {
  652.                 $finalVars[] = $key;
  653.             }
  654.         }
  655.         return $finalVars;
  656.     }
  657.     public function __wakeup()
  658.     {
  659.         parent::__wakeup();
  660.         // renew localized fields
  661.         // do not use the getter ($this->getLocalizedfields()) as it somehow slows down the process around a sec
  662.         // no clue why this happens
  663.         if (property_exists($this'localizedfields') && $this->localizedfields instanceof Localizedfield) {
  664.             $this->localizedfields->setObject($thisfalse);
  665.         }
  666.     }
  667.     /**
  668.      * load lazy loaded fields before cloning
  669.      */
  670.     public function __clone()
  671.     {
  672.         parent::__clone();
  673.         $this->o_class null;
  674.         $this->o_versions null;
  675.         $this->scheduledTasks null;
  676.     }
  677.     /**
  678.      * @internal
  679.      *
  680.      * @param array $descriptor
  681.      * @param string $table
  682.      *
  683.      * @return array
  684.      */
  685.     protected function doRetrieveData(array $descriptorstring $table)
  686.     {
  687.         $db Db::get();
  688.         $conditionParts Service::buildConditionPartsFromDescriptor($descriptor);
  689.         $query 'SELECT * FROM ' $table ' WHERE ' implode(' AND '$conditionParts);
  690.         $result $db->fetchAll($query);
  691.         return $result;
  692.     }
  693.     /**
  694.      * @internal
  695.      *
  696.      * @param array $descriptor
  697.      *
  698.      * @return array
  699.      */
  700.     public function retrieveSlugData($descriptor)
  701.     {
  702.         $descriptor['objectId'] = $this->getId();
  703.         return $this->doRetrieveData($descriptorDataObject\Data\UrlSlug::TABLE_NAME);
  704.     }
  705.     /**
  706.      * @internal
  707.      *
  708.      * @param array $descriptor
  709.      *
  710.      * @return array
  711.      */
  712.     public function retrieveRelationData($descriptor)
  713.     {
  714.         $descriptor['src_id'] = $this->getId();
  715.         $unfilteredData $this->__getRawRelationData();
  716.         $likes = [];
  717.         foreach ($descriptor as $column => $expectedValue) {
  718.             if (is_string($expectedValue)) {
  719.                 $trimmed rtrim($expectedValue'%');
  720.                 if (strlen($trimmed) < strlen($expectedValue)) {
  721.                     $likes[$column] = $trimmed;
  722.                 }
  723.             }
  724.         }
  725.         $filterFn = static function ($row) use ($descriptor$likes) {
  726.             foreach ($descriptor as $column => $expectedValue) {
  727.                 $actualValue $row[$column];
  728.                 if (isset($likes[$column])) {
  729.                     $expectedValue $likes[$column];
  730.                     if (strpos($actualValue$expectedValue) !== 0) {
  731.                         return false;
  732.                     }
  733.                 } elseif ($actualValue != $expectedValue) {
  734.                     return false;
  735.                 }
  736.             }
  737.             return true;
  738.         };
  739.         $filteredData array_filter($unfilteredData$filterFn);
  740.         return $filteredData;
  741.     }
  742.     /**
  743.      * @internal
  744.      *
  745.      * @return array
  746.      */
  747.     public function __getRawRelationData(): array
  748.     {
  749.         if ($this->__rawRelationData === null) {
  750.             $db Db::get();
  751.             $relations $db->fetchAll('SELECT * FROM object_relations_' $this->getClassId() . ' WHERE src_id = ?', [$this->getId()]);
  752.             $this->__rawRelationData $relations ?? [];
  753.         }
  754.         return $this->__rawRelationData;
  755.     }
  756. }