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

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