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

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.         $this->db->deleteWhere('object_relations_' $this->model->getClassId(), $condition);
  207.         $inheritedValues DataObject::doGetInheritedValues();
  208.         DataObject::setGetInheritedValues(false);
  209.         $data = [];
  210.         $data['oo_id'] = $this->model->getId();
  211.         foreach ($fieldDefinitions as $fieldName => $fd) {
  212.             $getter 'get' ucfirst($fieldName);
  213.             if ($fd instanceof CustomResourcePersistingInterface) {
  214.                 // for fieldtypes which have their own save algorithm eg. fieldcollections, relational data-types, ...
  215.                 $saveParams = ['isUntouchable' => in_array($fd->getName(), $untouchable),
  216.                     'isUpdate' => $isUpdate,
  217.                     'context' => [
  218.                         'containerType' => 'object',
  219.                     ],
  220.                     'owner' => $this->model,
  221.                     'fieldname' => $fieldName,
  222.                 ]
  223.                 ;
  224.                 if ($this->model instanceof Model\Element\DirtyIndicatorInterface) {
  225.                     $saveParams['newParent'] = $this->model->isFieldDirty('o_parentId');
  226.                 }
  227.                 $fd->save($this->model$saveParams);
  228.             }
  229.             if ($fd instanceof ResourcePersistenceAwareInterface) {
  230.                 // pimcore saves the values with getDataForResource
  231.                 $fieldDefinitionParams = [
  232.                     'isUpdate' => $isUpdate,
  233.                     'owner' => $this->model,
  234.                     'fieldname' => $fieldName,
  235.                 ];
  236.                 if (is_array($fd->getColumnType())) {
  237.                     $insertDataArray $fd->getDataForResource($this->model->$getter(), $this->model$fieldDefinitionParams);
  238.                     if (is_array($insertDataArray)) {
  239.                         $data array_merge($data$insertDataArray);
  240.                         $this->model->set($fieldName$fd->getDataFromResource($insertDataArray$this->model$fieldDefinitionParams));
  241.                     }
  242.                 } else {
  243.                     $insertData $fd->getDataForResource($this->model->$getter(), $this->model$fieldDefinitionParams);
  244.                     $data[$fieldName] = $insertData;
  245.                     $this->model->set($fieldName$fd->getDataFromResource($insertData$this->model$fieldDefinitionParams));
  246.                 }
  247.                 if ($this->model instanceof Model\Element\DirtyIndicatorInterface) {
  248.                     $this->model->markFieldDirty($fieldNamefalse);
  249.                 }
  250.             }
  251.         }
  252.         $method $isUpdate 'insertOrUpdate' 'insert';
  253.         $this->db->$method('object_store_' $this->model->getClassId(), $data);
  254.         // get data for query table
  255.         $data = [];
  256.         $this->inheritanceHelper->resetFieldsToCheck();
  257.         $oldData $this->db->fetchRow('SELECT * FROM object_query_' $this->model->getClassId() . ' WHERE oo_id = ?'$this->model->getId());
  258.         $inheritanceEnabled $this->model->getClass()->getAllowInherit();
  259.         $parentData null;
  260.         if ($inheritanceEnabled) {
  261.             // get the next suitable parent for inheritance
  262.             $parentForInheritance $this->model->getNextParentForInheritance();
  263.             if ($parentForInheritance) {
  264.                 // we don't use the getter (built in functionality to get inherited values) because we need to avoid race conditions
  265.                 // we cannot DataObject::setGetInheritedValues(true); and then $this->model->$method();
  266.                 // so we select the data from the parent object using FOR UPDATE, which causes a lock on this row
  267.                 // so the data of the parent cannot be changed while this transaction is on progress
  268.                 $parentData $this->db->fetchRow('SELECT * FROM object_query_' $this->model->getClassId() . ' WHERE oo_id = ? FOR UPDATE'$parentForInheritance->getId());
  269.             }
  270.         }
  271.         foreach ($fieldDefinitions as $key => $fd) {
  272.             if ($fd instanceof QueryResourcePersistenceAwareInterface) {
  273.                 //exclude untouchables if value is not an array - this means data has not been loaded
  274.                 if (!in_array($key$untouchable)) {
  275.                     $method 'get' $key;
  276.                     $fieldValue $this->model->$method();
  277.                     $insertData $fd->getDataForQueryResource($fieldValue$this->model,
  278.                         [
  279.                             'isUpdate' => $isUpdate,
  280.                             'owner' => $this->model,
  281.                             'fieldname' => $key,
  282.                         ]);
  283.                     $isEmpty $fd->isEmpty($fieldValue);
  284.                     if (is_array($insertData)) {
  285.                         $columnNames array_keys($insertData);
  286.                         $data array_merge($data$insertData);
  287.                     } else {
  288.                         $columnNames = [$key];
  289.                         $data[$key] = $insertData;
  290.                     }
  291.                     // if the current value is empty and we have data from the parent, we just use it
  292.                     if ($isEmpty && $parentData) {
  293.                         foreach ($columnNames as $columnName) {
  294.                             if (array_key_exists($columnName$parentData)) {
  295.                                 $data[$columnName] = $parentData[$columnName];
  296.                                 if (is_array($insertData)) {
  297.                                     $insertData[$columnName] = $parentData[$columnName];
  298.                                 } else {
  299.                                     $insertData $parentData[$columnName];
  300.                                 }
  301.                             }
  302.                         }
  303.                     }
  304.                     if ($inheritanceEnabled && $fd->supportsInheritance()) {
  305.                         //get changed fields for inheritance
  306.                         if ($fd->isRelationType()) {
  307.                             if (is_array($insertData)) {
  308.                                 $doInsert false;
  309.                                 foreach ($insertData as $insertDataKey => $insertDataValue) {
  310.                                     $oldDataValue $oldData[$insertDataKey] ?? null;
  311.                                     $parentDataValue $parentData[$insertDataKey] ?? null;
  312.                                     if ($isEmpty && $oldDataValue == $parentDataValue) {
  313.                                         // do nothing, ... value is still empty and parent data is equal to current data in query table
  314.                                     } elseif ($oldDataValue != $insertDataValue) {
  315.                                         $doInsert true;
  316.                                         break;
  317.                                     }
  318.                                 }
  319.                                 if ($doInsert) {
  320.                                     $this->inheritanceHelper->addRelationToCheck($key$fdarray_keys($insertData));
  321.                                 }
  322.                             } else {
  323.                                 $oldDataValue $oldData[$key] ?? null;
  324.                                 $parentDataValue $parentData[$key] ?? null;
  325.                                 if ($isEmpty && $oldDataValue == $parentDataValue) {
  326.                                     // do nothing, ... value is still empty and parent data is equal to current data in query table
  327.                                 } elseif ($oldDataValue != $insertData) {
  328.                                     $this->inheritanceHelper->addRelationToCheck($key$fd);
  329.                                 }
  330.                             }
  331.                         } else {
  332.                             if (is_array($insertData)) {
  333.                                 foreach ($insertData as $insertDataKey => $insertDataValue) {
  334.                                     $oldDataValue $oldData[$insertDataKey] ?? null;
  335.                                     $parentDataValue $parentData[$insertDataKey] ?? null;
  336.                                     if ($isEmpty && $oldDataValue == $parentDataValue) {
  337.                                         // do nothing, ... value is still empty and parent data is equal to current data in query table
  338.                                     } elseif ($oldDataValue != $insertDataValue) {
  339.                                         $this->inheritanceHelper->addFieldToCheck($insertDataKey$fd);
  340.                                     }
  341.                                 }
  342.                             } else {
  343.                                 $oldDataValue $oldData[$key] ?? null;
  344.                                 $parentDataValue $parentData[$key] ?? null;
  345.                                 if ($isEmpty && $oldDataValue == $parentDataValue) {
  346.                                     // do nothing, ... value is still empty and parent data is equal to current data in query table
  347.                                 } elseif ($oldDataValue != $insertData) {
  348.                                     // data changed, do check and update
  349.                                     $this->inheritanceHelper->addFieldToCheck($key$fd);
  350.                                 }
  351.                             }
  352.                         }
  353.                     }
  354.                 } else {
  355.                     Logger::debug('Excluding untouchable query value for object [ ' $this->model->getId() . " ]  key [ $key ] because it has not been loaded");
  356.                 }
  357.             }
  358.         }
  359.         $data['oo_id'] = $this->model->getId();
  360.         $this->db->insertOrUpdate('object_query_' $this->model->getClassId(), $data);
  361.         DataObject::setGetInheritedValues($inheritedValues);
  362.     }
  363.     public function saveChildData()
  364.     {
  365.         $this->inheritanceHelper->doUpdate($this->model->getId(), false, [
  366.             'inheritanceRelationContext' => [
  367.                 'ownerType' => 'object',
  368.             ],
  369.         ]);
  370.         $this->inheritanceHelper->resetFieldsToCheck();
  371.     }
  372.     /**
  373.      * Save object to database
  374.      */
  375.     public function delete()
  376.     {
  377.         // delete fields which have their own delete algorithm
  378.         if ($this->model->getClass()) {
  379.             foreach ($this->model->getClass()->getFieldDefinitions() as $fd) {
  380.                 if ($fd instanceof CustomResourcePersistingInterface) {
  381.                     $fd->delete($this->model);
  382.                 }
  383.             }
  384.         }
  385.         parent::delete();
  386.     }
  387. }