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

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