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

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