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($preUpdateEvent, DataObjectEvents::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(false, false, 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($preUpdateEvent, DataObjectEvents::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($postUpdateEvent, DataObjectEvents::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($postUpdateFailureEvent, DataObjectEvents::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_OBJECT, self::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($arguments, 6, 0);
  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($arguments, 4, 0);
  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($this, false);
  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 $descriptor, string $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($descriptor, DataObject\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. }