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

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