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

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