vendor/pimcore/pimcore/models/DataObject/Concrete/Dao.php line 148

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\Concrete;
  15. use Pimcore\Db;
  16. use Pimcore\Db\Helper;
  17. use Pimcore\Logger;
  18. use Pimcore\Model;
  19. use Pimcore\Model\DataObject;
  20. use Pimcore\Model\DataObject\ClassDefinition\Data\CustomResourcePersistingInterface;
  21. use Pimcore\Model\DataObject\ClassDefinition\Data\LazyLoadingSupportInterface;
  22. use Pimcore\Model\DataObject\ClassDefinition\Data\QueryResourcePersistenceAwareInterface;
  23. use Pimcore\Model\DataObject\ClassDefinition\Data\ResourcePersistenceAwareInterface;
  24. /**
  25.  * @internal
  26.  *
  27.  * @property \Pimcore\Model\DataObject\Concrete $model
  28.  */
  29. class Dao extends Model\DataObject\AbstractObject\Dao
  30. {
  31.     use Model\Element\Traits\ScheduledTasksDaoTrait;
  32.     use Model\Element\Traits\VersionDaoTrait;
  33.     /**
  34.      * @var DataObject\Concrete\Dao\InheritanceHelper
  35.      */
  36.     protected $inheritanceHelper null;
  37.     public function init()
  38.     {
  39.         $this->inheritanceHelper = new DataObject\Concrete\Dao\InheritanceHelper($this->model->getClassId());
  40.     }
  41.     /**
  42.      * Get the data for the object from database for the given id
  43.      *
  44.      * @param int $id
  45.      *
  46.      * @throws Model\Exception\NotFoundException
  47.      */
  48.     public function getById($id)
  49.     {
  50.         $data $this->db->fetchAssociative("SELECT objects.*, tree_locks.locked as o_locked FROM objects
  51.             LEFT JOIN tree_locks ON objects.o_id = tree_locks.id AND tree_locks.type = 'object'
  52.                 WHERE o_id = ?", [$id]);
  53.         if (!empty($data['o_id'])) {
  54.             $this->assignVariablesToModel($data);
  55.             $this->getData();
  56.         } else {
  57.             throw new Model\Exception\NotFoundException('Object with the ID ' $id " doesn't exists");
  58.         }
  59.     }
  60.     /**
  61.      * @param  string $fieldName
  62.      *
  63.      * @return array
  64.      */
  65.     public function getRelationIds($fieldName)
  66.     {
  67.         $relations = [];
  68.         $allRelations $this->db->fetchAllAssociative('SELECT * FROM object_relations_' $this->model->getClassId() . " WHERE fieldname = ? AND src_id = ? AND ownertype = 'object' ORDER BY `index` ASC", [$fieldName$this->model->getId()]);
  69.         foreach ($allRelations as $relation) {
  70.             $relations[] = $relation['dest_id'];
  71.         }
  72.         return $relations;
  73.     }
  74.     /**
  75.      * @param string $field
  76.      * @param bool $forOwner
  77.      * @param string $remoteClassId
  78.      *
  79.      * @return array
  80.      */
  81.     public function getRelationData($field$forOwner$remoteClassId)
  82.     {
  83.         $id $this->model->getId();
  84.         if ($remoteClassId) {
  85.             $classId $remoteClassId;
  86.         } else {
  87.             $classId $this->model->getClassId();
  88.         }
  89.         $params = [$field$id$field$id$field$id];
  90.         $dest 'dest_id';
  91.         $src 'src_id';
  92.         if (!$forOwner) {
  93.             $dest 'src_id';
  94.             $src 'dest_id';
  95.         }
  96.         $relations $this->db->fetchAllAssociative('SELECT r.' $dest ' as dest_id, r.' $dest ' as id, r.type, o.o_className as subtype, o.o_published as published, concat(o.o_path ,o.o_key) as path , r.index
  97.             FROM objects o, object_relations_' $classId " r
  98.             WHERE r.fieldname= ?
  99.             AND r.ownertype = 'object'
  100.             AND r." $src ' = ?
  101.             AND o.o_id = r.' $dest "
  102.             AND r.type='object'
  103.             UNION SELECT r." $dest ' as dest_id, r.' $dest ' as id, r.type,  a.type as subtype, "null" as published, concat(a.path,a.filename) as path, r.index
  104.             FROM assets a, object_relations_' $classId " r
  105.             WHERE r.fieldname= ?
  106.             AND r.ownertype = 'object'
  107.             AND r." $src ' = ?
  108.             AND a.id = r.' $dest "
  109.             AND r.type='asset'
  110.             UNION SELECT r." $dest ' as dest_id, r.' $dest ' as id, r.type, d.type as subtype, d.published as published, concat(d.path,d.key) as path, r.index
  111.             FROM documents d, object_relations_' $classId " r
  112.             WHERE r.fieldname= ?
  113.             AND r.ownertype = 'object'
  114.             AND r." $src ' = ?
  115.             AND d.id = r.' $dest "
  116.             AND r.type='document'
  117.             ORDER BY `index` ASC"$params);
  118.         if (is_array($relations) && count($relations) > 0) {
  119.             return $relations;
  120.         } else {
  121.             return [];
  122.         }
  123.     }
  124.     /**
  125.      * Get all data-elements for all fields that are not lazy-loaded.
  126.      */
  127.     public function getData()
  128.     {
  129.         if (empty($this->model->getClass())) {
  130.             return;
  131.         }
  132.         if (!$data $this->db->fetchAssociative('SELECT * FROM object_store_' $this->model->getClassId() . ' WHERE oo_id = ?', [$this->model->getId()])) {
  133.             return;
  134.         }
  135.         $fieldDefinitions $this->model->getClass()->getFieldDefinitions(['object' => $this->model]);
  136.         foreach ($fieldDefinitions as $key => $value) {
  137.             if ($value instanceof CustomResourcePersistingInterface) {
  138.                 if (!$value instanceof LazyLoadingSupportInterface || !$value->getLazyLoading()) {
  139.                     // datafield has it's own loader
  140.                     $params = [
  141.                         'context' => [
  142.                             'object' => $this->model,
  143.                         ],
  144.                         'owner' => $this->model,
  145.                         'fieldname' => $key,
  146.                     ];
  147.                     $value $value->load($this->model$params);
  148.                     if ($value === || !empty($value)) {
  149.                         $this->model->setValue($key$value);
  150.                     }
  151.                 }
  152.             }
  153.             if ($value instanceof ResourcePersistenceAwareInterface) {
  154.                 // if a datafield requires more than one field
  155.                 if (is_array($value->getColumnType())) {
  156.                     $multidata = [];
  157.                     foreach ($value->getColumnType() as $fkey => $fvalue) {
  158.                         $multidata[$key '__' $fkey] = $data[$key '__' $fkey];
  159.                     }
  160.                     $this->model->setValue($key$value->getDataFromResource($multidata));
  161.                 } else {
  162.                     $this->model->setValue($key$value->getDataFromResource($data[$key], $this->model, [
  163.                         'owner' => $this->model,
  164.                         'fieldname' => $key,
  165.                     ]));
  166.                 }
  167.             }
  168.         }
  169.     }
  170.     /**
  171.      * Save changes to database, it's an good idea to use save() instead
  172.      *
  173.      * @param bool|null $isUpdate
  174.      */
  175.     public function update($isUpdate null)
  176.     {
  177.         parent::update($isUpdate);
  178.         // get fields which shouldn't be updated
  179.         $fieldDefinitions $this->model->getClass()->getFieldDefinitions();
  180.         $untouchable = [];
  181.         $db Db::get();
  182.         foreach ($fieldDefinitions as $fieldName => $fd) {
  183.             if ($fd instanceof LazyLoadingSupportInterface && $fd->getLazyLoading()) {
  184.                 if (!$this->model->isLazyKeyLoaded($fieldName) || $fd instanceof DataObject\ClassDefinition\Data\ReverseObjectRelation) {
  185.                     //this is a relation subject to lazy loading - it has not been loaded
  186.                     $untouchable[] = $fieldName;
  187.                 }
  188.             }
  189.             if (!DataObject::isDirtyDetectionDisabled() && $fd->supportsDirtyDetection()) {
  190.                 if ($this->model instanceof Model\Element\DirtyIndicatorInterface && !$this->model->isFieldDirty($fieldName)) {
  191.                     if (!in_array($fieldName$untouchable)) {
  192.                         $untouchable[] = $fieldName;
  193.                     }
  194.                 }
  195.             }
  196.         }
  197.         // empty relation table except the untouchable fields (eg. lazy loading fields)
  198.         if (count($untouchable) > 0) {
  199.             $untouchables "'" implode("','"$untouchable) . "'";
  200.             $condition Helper::quoteInto($this->db'src_id = ? AND fieldname not in (' $untouchables ") AND ownertype = 'object'"$this->model->getId());
  201.         } else {
  202.             $condition 'src_id = ' $db->quote($this->model->getId()) . ' AND ownertype = "object"';
  203.         }
  204.         if (!DataObject::isDirtyDetectionDisabled()) {
  205.             $condition '(' $condition ' AND ownerType != "localizedfield" AND ownerType != "fieldcollection")';
  206.         }
  207.         $dataExists $this->db->fetchOne('SELECT `src_id` FROM `object_relations_'$this->model->getClassId().'`
  208.         WHERE '.$condition .' LIMIT 1');
  209.         if ($dataExists) {
  210.             $this->db->executeStatement('DELETE FROM object_relations_' $this->model->getClassId() . ' WHERE ' $condition);
  211.         }
  212.         $inheritedValues DataObject::doGetInheritedValues();
  213.         DataObject::setGetInheritedValues(false);
  214.         $data = [];
  215.         $data['oo_id'] = $this->model->getId();
  216.         foreach ($fieldDefinitions as $fieldName => $fd) {
  217.             $getter 'get' ucfirst($fieldName);
  218.             if ($fd instanceof CustomResourcePersistingInterface) {
  219.                 // for fieldtypes which have their own save algorithm eg. fieldcollections, relational data-types, ...
  220.                 $saveParams = ['isUntouchable' => in_array($fd->getName(), $untouchable),
  221.                     'isUpdate' => $isUpdate,
  222.                     'context' => [
  223.                         'containerType' => 'object',
  224.                     ],
  225.                     'owner' => $this->model,
  226.                     'fieldname' => $fieldName,
  227.                 ]
  228.                 ;
  229.                 if ($this->model instanceof Model\Element\DirtyIndicatorInterface) {
  230.                     $saveParams['newParent'] = $this->model->isFieldDirty('o_parentId');
  231.                 }
  232.                 $fd->save($this->model$saveParams);
  233.             }
  234.             if ($fd instanceof ResourcePersistenceAwareInterface) {
  235.                 // pimcore saves the values with getDataForResource
  236.                 $fieldDefinitionParams = [
  237.                     'isUpdate' => $isUpdate,
  238.                     'owner' => $this->model,
  239.                     'fieldname' => $fieldName,
  240.                 ];
  241.                 if (is_array($fd->getColumnType())) {
  242.                     $insertDataArray $fd->getDataForResource($this->model->$getter(), $this->model$fieldDefinitionParams);
  243.                     if (is_array($insertDataArray)) {
  244.                         $data array_merge($data$insertDataArray);
  245.                         $this->model->set($fieldName$fd->getDataFromResource($insertDataArray$this->model$fieldDefinitionParams));
  246.                     }
  247.                 } else {
  248.                     $insertData $fd->getDataForResource($this->model->$getter(), $this->model$fieldDefinitionParams);
  249.                     $data[$fieldName] = $insertData;
  250.                     $this->model->set($fieldName$fd->getDataFromResource($insertData$this->model$fieldDefinitionParams));
  251.                 }
  252.                 if ($this->model instanceof Model\Element\DirtyIndicatorInterface) {
  253.                     $this->model->markFieldDirty($fieldNamefalse);
  254.                 }
  255.             }
  256.         }
  257.         if ($isUpdate) {
  258.             Helper::insertOrUpdate($this->db'object_store_' $this->model->getClassId(), $data);
  259.         } else {
  260.             $this->db->insert('object_store_' $this->model->getClassId(), $data);
  261.         }
  262.         // get data for query table
  263.         $data = [];
  264.         $this->inheritanceHelper->resetFieldsToCheck();
  265.         $oldData $this->db->fetchAssociative('SELECT * FROM object_query_' $this->model->getClassId() . ' WHERE oo_id = ?', [$this->model->getId()]);
  266.         $inheritanceEnabled $this->model->getClass()->getAllowInherit();
  267.         $parentData null;
  268.         if ($inheritanceEnabled) {
  269.             // get the next suitable parent for inheritance
  270.             $parentForInheritance $this->model->getNextParentForInheritance();
  271.             if ($parentForInheritance) {
  272.                 // we don't use the getter (built in functionality to get inherited values) because we need to avoid race conditions
  273.                 // we cannot DataObject::setGetInheritedValues(true); and then $this->model->$method();
  274.                 // so we select the data from the parent object using FOR UPDATE, which causes a lock on this row
  275.                 // so the data of the parent cannot be changed while this transaction is on progress
  276.                 $parentData $this->db->fetchAssociative('SELECT * FROM object_query_' $this->model->getClassId() . ' WHERE oo_id = ? FOR UPDATE', [$parentForInheritance->getId()]);
  277.             }
  278.         }
  279.         foreach ($fieldDefinitions as $key => $fd) {
  280.             if ($fd instanceof QueryResourcePersistenceAwareInterface) {
  281.                 //exclude untouchables if value is not an array - this means data has not been loaded
  282.                 if (!in_array($key$untouchable)) {
  283.                     $method 'get' $key;
  284.                     $fieldValue $this->model->$method();
  285.                     $insertData $fd->getDataForQueryResource($fieldValue$this->model,
  286.                         [
  287.                             'isUpdate' => $isUpdate,
  288.                             'owner' => $this->model,
  289.                             'fieldname' => $key,
  290.                         ]);
  291.                     $isEmpty $fd->isEmpty($fieldValue);
  292.                     if (is_array($insertData)) {
  293.                         $columnNames array_keys($insertData);
  294.                         $data array_merge($data$insertData);
  295.                     } else {
  296.                         $columnNames = [$key];
  297.                         $data[$key] = $insertData;
  298.                     }
  299.                     // if the current value is empty and we have data from the parent, we just use it
  300.                     if ($isEmpty && $parentData) {
  301.                         foreach ($columnNames as $columnName) {
  302.                             if (array_key_exists($columnName$parentData)) {
  303.                                 $data[$columnName] = $parentData[$columnName];
  304.                                 if (is_array($insertData)) {
  305.                                     $insertData[$columnName] = $parentData[$columnName];
  306.                                 } else {
  307.                                     $insertData $parentData[$columnName];
  308.                                 }
  309.                             }
  310.                         }
  311.                     }
  312.                     if ($inheritanceEnabled && $fd->supportsInheritance()) {
  313.                         //get changed fields for inheritance
  314.                         if ($fd->isRelationType()) {
  315.                             if (is_array($insertData)) {
  316.                                 $doInsert false;
  317.                                 foreach ($insertData as $insertDataKey => $insertDataValue) {
  318.                                     $oldDataValue $oldData[$insertDataKey] ?? null;
  319.                                     $parentDataValue $parentData[$insertDataKey] ?? null;
  320.                                     if ($isEmpty && $oldDataValue == $parentDataValue) {
  321.                                         // do nothing, ... value is still empty and parent data is equal to current data in query table
  322.                                     } elseif ($oldDataValue != $insertDataValue) {
  323.                                         $doInsert true;
  324.                                         break;
  325.                                     }
  326.                                 }
  327.                                 if ($doInsert) {
  328.                                     $this->inheritanceHelper->addRelationToCheck($key$fdarray_keys($insertData));
  329.                                 }
  330.                             } else {
  331.                                 $oldDataValue $oldData[$key] ?? null;
  332.                                 $parentDataValue $parentData[$key] ?? null;
  333.                                 if ($isEmpty && $oldDataValue == $parentDataValue) {
  334.                                     // do nothing, ... value is still empty and parent data is equal to current data in query table
  335.                                 } elseif ($oldDataValue != $insertData) {
  336.                                     $this->inheritanceHelper->addRelationToCheck($key$fd);
  337.                                 }
  338.                             }
  339.                         } else {
  340.                             if (is_array($insertData)) {
  341.                                 foreach ($insertData as $insertDataKey => $insertDataValue) {
  342.                                     $oldDataValue $oldData[$insertDataKey] ?? null;
  343.                                     $parentDataValue $parentData[$insertDataKey] ?? null;
  344.                                     if ($isEmpty && $oldDataValue == $parentDataValue) {
  345.                                         // do nothing, ... value is still empty and parent data is equal to current data in query table
  346.                                     } elseif ($oldDataValue != $insertDataValue) {
  347.                                         $this->inheritanceHelper->addFieldToCheck($insertDataKey$fd);
  348.                                     }
  349.                                 }
  350.                             } else {
  351.                                 $oldDataValue $oldData[$key] ?? null;
  352.                                 $parentDataValue $parentData[$key] ?? null;
  353.                                 if ($isEmpty && $oldDataValue == $parentDataValue) {
  354.                                     // do nothing, ... value is still empty and parent data is equal to current data in query table
  355.                                 } elseif ($oldDataValue != $insertData) {
  356.                                     // data changed, do check and update
  357.                                     $this->inheritanceHelper->addFieldToCheck($key$fd);
  358.                                 }
  359.                             }
  360.                         }
  361.                     }
  362.                 } else {
  363.                     Logger::debug('Excluding untouchable query value for object [ ' $this->model->getId() . " ]  key [ $key ] because it has not been loaded");
  364.                 }
  365.             }
  366.         }
  367.         $data['oo_id'] = $this->model->getId();
  368.         Helper::insertOrUpdate($this->db'object_query_' $this->model->getClassId(), $data);
  369.         DataObject::setGetInheritedValues($inheritedValues);
  370.     }
  371.     public function saveChildData()
  372.     {
  373.         $this->inheritanceHelper->doUpdate($this->model->getId(), false, [
  374.             'inheritanceRelationContext' => [
  375.                 'ownerType' => 'object',
  376.             ],
  377.         ]);
  378.         $this->inheritanceHelper->resetFieldsToCheck();
  379.     }
  380.     /**
  381.      * Save object to database
  382.      */
  383.     public function delete()
  384.     {
  385.         // delete fields which have their own delete algorithm
  386.         if ($this->model->getClass()) {
  387.             foreach ($this->model->getClass()->getFieldDefinitions() as $fd) {
  388.                 if ($fd instanceof CustomResourcePersistingInterface) {
  389.                     $fd->delete($this->model);
  390.                 }
  391.             }
  392.         }
  393.         parent::delete();
  394.     }
  395. }