diff --git a/src/core/Directus/Database/TableGateway/BaseTableGateway.php b/src/core/Directus/Database/TableGateway/BaseTableGateway.php index 4679422705..5a4a7ebbb1 100644 --- a/src/core/Directus/Database/TableGateway/BaseTableGateway.php +++ b/src/core/Directus/Database/TableGateway/BaseTableGateway.php @@ -20,6 +20,7 @@ use Directus\Database\TableGatewayFactory; use Directus\Database\SchemaService; use Directus\Exception\Exception; +use Directus\Exception\UnprocessableEntityException; use Directus\Filesystem\Files; use function Directus\get_directus_setting; use Directus\Permissions\Acl; @@ -326,19 +327,7 @@ public function findOneByArray(array $data) public function addOrUpdateRecordByArray(array $recordData, $collectionName = null) { $collectionName = is_null($collectionName) ? $this->table : $collectionName; - $collectionObject = $this->getTableSchema($collectionName); - foreach ($recordData as $columnName => $columnValue) { - $fieldObject = $collectionObject->getField($columnName); - // TODO: Should this be validate in here? should we let the database fails? - if (($fieldObject && is_array($columnValue) && (!$fieldObject->isJson() && !$fieldObject->isArray()))) { - // $table = is_null($tableName) ? $this->table : $tableName; - throw new SuppliedArrayAsColumnValue('Attempting to write an array as the value for column `' . $collectionName . '`.`' . $columnName . '.'); - } - } - - // @TODO: Dow we need to parse before insert? - // Commented out because date are not saved correctly in GMT - // $recordData = $this->parseRecord($recordData); + $this->validateRecordArray($recordData); $TableGateway = $this->makeTable($collectionName); $primaryKey = $TableGateway->primaryKeyFieldName; @@ -359,82 +348,102 @@ public function addOrUpdateRecordByArray(array $recordData, $collectionName = nu if ($rowExists) { $currentItem = $result->current()->toArray(); } + } - if ($collectionName === SchemaManager::COLLECTION_FILES) { - $originalFilename = ArrayUtils::get($currentItem, 'filename'); - $recordData = array_merge([ - 'filename' => $originalFilename - ], $recordData); - } + if ($rowExists) { + $result = $TableGateway->updateRecordByArray($recordData); + } else { + $result = $TableGateway->addRecordByArray($recordData); } - $afterAction = function ($collectionName, $recordData, $replace = false) use ($TableGateway) { - if ($collectionName == SchemaManager::COLLECTION_FILES && static::$container) { - $Files = static::$container->get('files'); + return $result; + } - $updateArray = []; - if ($Files->getSettings('file_naming') == 'file_id') { - $ext = $thumbnailExt = pathinfo($recordData['filename'], PATHINFO_EXTENSION); - $Files->rename($recordData['filename'], str_pad($recordData[$this->primaryKeyFieldName], 11, '0', STR_PAD_LEFT) . '.' . $ext, $replace); - $updateArray['filename'] = str_pad($recordData[$this->primaryKeyFieldName], 11, '0', STR_PAD_LEFT) . '.' . $ext; - $recordData['filename'] = $updateArray['filename']; - } + public function addRecordByArray(array $recordData) + { + $this->validateRecordArray($recordData); - if (!empty($updateArray)) { - $Update = new Update($collectionName); - $Update->set($updateArray); - $Update->where([$TableGateway->primaryKeyFieldName => $recordData[$TableGateway->primaryKeyFieldName]]); - $TableGateway->updateWith($Update); - } - } - }; + $TableGateway = $this->makeTable($this->table); + $primaryKey = $TableGateway->primaryKeyFieldName; + $TableGateway->insert($recordData); - if ($rowExists) { - $Update = new Update($collectionName); - $Update->set($recordData); - $Update->where([ - $primaryKey => $recordData[$primaryKey] - ]); - $TableGateway->updateWith($Update); + // Only get the last inserted id, if the column has auto increment value + $columnObject = $this->getTableSchema()->getField($primaryKey); + if ($columnObject->hasAutoIncrement()) { + $recordData[$primaryKey] = $TableGateway->getLastInsertValue(); + } - if ($collectionName == 'directus_files' && static::$container) { - if ($originalFilename && $recordData['filename'] !== $originalFilename) { - /** @var Files $Files */ - $Files = static::$container->get('files'); - $Files->delete(['filename' => $originalFilename]); - } - } + $this->afterAddOrUpdate($recordData); - $afterAction($collectionName, $recordData, true); + $columns = SchemaService::getAllNonAliasCollectionFieldNames($this->table); + return $TableGateway->fetchAll(function (Select $select) use ($recordData, $columns, $primaryKey) { + $select + ->columns($columns) + ->limit(1); + $select->where->equalTo($primaryKey, $recordData[$primaryKey]); + })->current(); + } - $this->runHook('postUpdate', [$TableGateway, $recordData, $this->adapter, null]); - } else { - $recordData = $this->applyHook('collection.insert:before', $recordData, [ - 'collection_name' => $collectionName + public function updateRecordByArray(array $recordData) + { + $collectionName = $this->table; + $this->validateRecordArray($recordData); + + $TableGateway = $this->makeTable($collectionName); + $primaryKey = $TableGateway->primaryKeyFieldName; + $hasPrimaryKeyData = isset($recordData[$primaryKey]); + $currentItem = null; + $originalFilename = null; + + if (!$hasPrimaryKeyData) { + throw new UnprocessableEntityException(); + } + + if ($collectionName === SchemaManager::COLLECTION_FILES) { + $select = new Select($collectionName); + $select->columns(['filename']); + $select->where([ + $primaryKey => $recordData[$primaryKey] ]); - $recordData = $this->applyHook('collection.insert.' . $collectionName . ':before', $recordData); - $TableGateway->insert($recordData); + $select->limit(1); + $result = $TableGateway->ignoreFilters()->selectWith($select); - // Only get the last inserted id, if the column has auto increment value - $columnObject = $this->getTableSchema()->getField($primaryKey); - if ($columnObject->hasAutoIncrement()) { - $recordData[$primaryKey] = $TableGateway->getLastInsertValue(); + if ($result->count() === 0) { + throw new ItemNotFoundException(); } - $afterAction($collectionName, $recordData); + $currentItem = $result->current()->toArray(); - $this->runHook('postInsert', [$TableGateway, $recordData, $this->adapter, null]); + $originalFilename = ArrayUtils::get($currentItem, 'filename'); + $recordData = array_merge([ + 'filename' => $originalFilename + ], $recordData); + } + + $Update = new Update($collectionName); + $Update->set($recordData); + $Update->where([ + $primaryKey => $recordData[$primaryKey] + ]); + $TableGateway->updateWith($Update); + + if ($collectionName === SchemaManager::COLLECTION_FILES && static::$container) { + if ($originalFilename && $recordData['filename'] !== $originalFilename) { + /** @var Files $Files */ + $Files = static::$container->get('files'); + $Files->delete(['filename' => $originalFilename]); + } } + $this->afterAddOrUpdate($recordData, true); + $columns = SchemaService::getAllNonAliasCollectionFieldNames($collectionName); - $recordData = $TableGateway->fetchAll(function ($select) use ($recordData, $columns, $primaryKey) { + return $TableGateway->fetchAll(function ($select) use ($recordData, $columns, $primaryKey) { $select ->columns($columns) ->limit(1); $select->where->equalTo($primaryKey, $recordData[$primaryKey]); })->current(); - - return $recordData; } public function drop($tableName = null) @@ -736,6 +745,20 @@ protected function executeInsert(Insert $insert) $this->runHook('collection.insert:before', [$insertTable, $insertDataAssoc]); $this->runHook('collection.insert.' . $insertTable . ':before', [$insertDataAssoc]); + $newInsertData = $this->applyHook('collection.insert:before', $insertDataAssoc, [ + 'collection_name' => $insertTable + ]); + $newInsertData = $this->applyHook('collection.insert.' . $insertTable . ':before', $newInsertData); + + // NOTE: set the primary key to null + // to default the value to whatever increment value is next + // avoiding the error of inserting nothing + if (empty($newInsertData)) { + $newInsertData[$this->primaryKeyFieldName] = null; + } + + $insert->values($newInsertData); + try { $result = parent::executeInsert($insert); } catch (UnexpectedValueException $e) { @@ -1626,6 +1649,54 @@ protected function validateStatusMapping(StatusMapping $statusMapping) } } + /** + * Validates a record array + * + * @param array $record + * + * @throws SuppliedArrayAsColumnValue + */ + protected function validateRecordArray(array $record) + { + $collectionObject = $this->getTableSchema(); + + foreach ($record as $columnName => $columnValue) { + $field = $collectionObject->getField($columnName); + // TODO: Should this be validate in here? should we let the database fails? + if (($field && is_array($columnValue) && (!$field->isJson() && !$field->isArray()))) { + // $table = is_null($tableName) ? $this->table : $tableName; + throw new SuppliedArrayAsColumnValue('Attempting to write an array as the value for column `' . $this->table . '`.`' . $field->getName() . '.'); + } + } + } + + /** + * @param array $record + * @param bool $replace + */ + protected function afterAddOrUpdate(array $record, $replace = false) + { + // TODO: Move this to a proper hook + if ($this->table == SchemaManager::COLLECTION_FILES && static::$container) { + $Files = static::$container->get('files'); + + $updateArray = []; + if ($Files->getSettings('file_naming') == 'file_id') { + $ext = $thumbnailExt = pathinfo($record['filename'], PATHINFO_EXTENSION); + $Files->rename($record['filename'], str_pad($record[$this->primaryKeyFieldName], 11, '0', STR_PAD_LEFT) . '.' . $ext, $replace); + $updateArray['filename'] = str_pad($record[$this->primaryKeyFieldName], 11, '0', STR_PAD_LEFT) . '.' . $ext; + $record['filename'] = $updateArray['filename']; + } + + if (!empty($updateArray)) { + $Update = new Update($this->table); + $Update->set($updateArray); + $Update->where([$this->primaryKeyFieldName => $record[$this->primaryKeyFieldName]]); + $this->updateWith($Update); + } + } + } + /** * Checks whether or not null should be sorted last * diff --git a/src/core/Directus/Database/TableGateway/RelationalTableGateway.php b/src/core/Directus/Database/TableGateway/RelationalTableGateway.php index e15bff6c69..c40af73434 100644 --- a/src/core/Directus/Database/TableGateway/RelationalTableGateway.php +++ b/src/core/Directus/Database/TableGateway/RelationalTableGateway.php @@ -105,25 +105,15 @@ public function deleteRecord($id, array $params = []) } /** + * @param mixed $id * @param array $data * @param array $params * * @return BaseRowGateway */ - public function updateRecord($data, array $params = []) + public function revertRecord($id, array $data, array $params = []) { - return $this->manageRecordUpdate($this->getTable(), $data, $params); - } - - /** - * @param $data - * @param array $params - * - * @return BaseRowGateway - */ - public function revertRecord($data, array $params = []) - { - return $this->updateRecord($data, array_merge($params, ['revert' => true])); + return $this->updateRecord($id, $data, array_merge($params, ['revert' => true])); } /** @@ -202,17 +192,14 @@ public function manageRecordUpdate($tableName, $recordData, array $params = [], $newRecordObject = null; $parentRecordChanged = $this->recordDataContainsNonPrimaryKeyData($recordData); - if ($parentRecordChanged) { + if ($parentRecordChanged || $recordIsNew) { // Update the parent row, w/ any new association fields replaced by their IDs - $newRecordObject = $TableGateway - ->addOrUpdateRecordByArray($parentRecordWithoutAlias); + $newRecordObject = $TableGateway->addOrUpdateRecordByArray($parentRecordWithoutAlias); if (!$newRecordObject) { return []; } - if ($newRecordObject) { - $newRecordObject = $newRecordObject->toArray(); - } + $newRecordObject = $newRecordObject->toArray(); } // Do it this way, because & byref for outcome of ternary operator spells trouble @@ -248,11 +235,6 @@ public function manageRecordUpdate($tableName, $recordData, array $params = [], $select->limit(1)->columns($columnNames); })->current(); - if (!$fullRecordData) { - $recordType = $recordIsNew ? 'new' : 'pre-existing'; - throw new \RuntimeException('Attempted to load ' . $recordType . ' record post-insert with empty result. Lookup via row id: ' . print_r($rowId, true)); - } - $fullRecordData = (array) $fullRecordData; if ($recordIsNew) { $deltaRecordData = $parentRecordWithoutAlias; @@ -391,6 +373,177 @@ public function manageRecordUpdate($tableName, $recordData, array $params = [], return $recordGateway; } + /** + * @param array $recordData + * @param array $params + * + * @return BaseRowGateway + */ + public function createRecord($recordData, array $params = []) + { + $tableGateway = $this; + $tableSchema = SchemaService::getCollection($this->getTable()); + + // Recursive functions will change this value (by reference) as necessary + $nestedCollectionRelationshipsChanged = false; + $nestedLogEntries = []; + + // Update and/or Add Many-to-One Associations + $recordData = $tableGateway->addOrUpdateManyToOneRelationships($tableSchema, $recordData, $nestedLogEntries, $nestedCollectionRelationshipsChanged); + + $parentRecordWithoutAlias = []; + foreach ($recordData as $key => $data) { + $column = $tableSchema->getField($key); + + // TODO: To work with files + // As `data` is not set as alias for files we are checking for actual aliases + if ($column && $column->isAlias()) { + continue; + } + + $parentRecordWithoutAlias[$key] = $data; + } + + $newRecordObject = $tableGateway->addRecordByArray($parentRecordWithoutAlias); + + $newRecordObject = $newRecordObject->toArray(); + + // Do it this way, because & byref for outcome of ternary operator spells trouble + $draftRecord = $newRecordObject; + + // Restore X2M relationship / alias fields to the record representation & process these relationships. + $collectionColumns = $tableSchema->getAliasFields(); + foreach ($collectionColumns as $collectionColumn) { + $colName = $collectionColumn->getName(); + if (isset($recordData[$colName])) { + $draftRecord[$colName] = $recordData[$colName]; + } + } + + $parentData = [ + 'item' => null, + 'collection' => $this->getTable() + ]; + + $draftRecord = $tableGateway->addOrUpdateToManyRelationships( + $tableSchema, + $draftRecord, + $nestedLogEntries, + $nestedCollectionRelationshipsChanged, + $parentData + ); + + $this->recordActivity(DirectusActivityTableGateway::ACTION_ADD, $newRecordObject, $nestedLogEntries, $params); + + // Yield record object + $recordGateway = new BaseRowGateway( + $tableGateway->primaryKeyFieldName, + $this->getTable(), + $this->adapter, + $this->acl + ); + $recordGateway->populate($this->parseRecord($draftRecord), true); + + return $recordGateway; + } + + /** + * @param mixed $id + * @param array $recordData + * @param array $params + * + * @return BaseRowGateway + */ + public function updateRecord($id, array $recordData, array $params = []) + { + $TableGateway = $this; + $tableSchema = SchemaService::getCollection($this->table); + // Recursive functions will change this value (by reference) as necessary + $nestedCollectionRelationshipsChanged = false; + // Recursive functions will append to this array by reference + $nestedLogEntries = []; + + // Update and/or Add Many-to-One Associations + $recordData = $TableGateway->addOrUpdateManyToOneRelationships($tableSchema, $recordData, $nestedLogEntries, $nestedCollectionRelationshipsChanged); + + $parentRecordWithoutAlias = []; + foreach ($recordData as $key => $data) { + $column = $tableSchema->getField($key); + + // TODO: To work with files + // As `data` is not set as alias for files we are checking for actual aliases + if ($column && $column->isAlias()) { + continue; + } + + $parentRecordWithoutAlias[$key] = $data; + } + + $parentRecordWithoutAlias[$this->primaryKeyFieldName] = $id; + + // If more than the record ID is present. + $newRecordObject = null; + $parentRecordChanged = $this->recordDataContainsNonPrimaryKeyData($recordData); + + if ($parentRecordChanged) { + // Update the parent row, w/ any new association fields replaced by their IDs + $newRecordObject = $TableGateway->updateRecordByArray($parentRecordWithoutAlias)->toArray(); + } + + // Do it this way, because & byref for outcome of ternary operator spells trouble + $draftRecord = &$parentRecordWithoutAlias; + + // Restore X2M relationship / alias fields to the record representation & process these relationships. + $collectionColumns = $tableSchema->getAliasFields(); + foreach ($collectionColumns as $collectionColumn) { + $colName = $collectionColumn->getName(); + if (isset($recordData[$colName])) { + $draftRecord[$colName] = $recordData[$colName]; + } + } + + // parent + $parentData = [ + 'item' => $id, + 'collection' => $this->table + ]; + + $draftRecord = $TableGateway->addOrUpdateToManyRelationships($tableSchema, $draftRecord, $nestedLogEntries, $nestedCollectionRelationshipsChanged, $parentData); + $deltaRecordData = array_intersect_key( + ArrayUtils::omit((array)$parentRecordWithoutAlias, $this->primaryKeyFieldName), + $newRecordObject + ); + + $statusField = $tableSchema->getStatusField(); + $logEntryAction = ArrayUtils::get($params, 'revert') === true + ? DirectusActivityTableGateway::ACTION_REVERT + : DirectusActivityTableGateway::ACTION_UPDATE; + + if ($statusField && $logEntryAction === DirectusActivityTableGateway::ACTION_UPDATE) { + try { + if ( + ArrayUtils::has($deltaRecordData, $statusField->getName()) + && in_array( + ArrayUtils::get($deltaRecordData, $tableSchema->getStatusField()->getName()), + $this->getStatusMapping()->getSoftDeleteStatusesValue() + ) + ) { + $logEntryAction = DirectusActivityTableGateway::ACTION_SOFT_DELETE; + } + } catch (\Exception $e) { + // the field doesn't have a status mapping + } + } + + $this->recordActivity($logEntryAction, $newRecordObject, $nestedLogEntries, $params); + + // Yield record object + $recordGateway = new BaseRowGateway($TableGateway->primaryKeyFieldName, $this->table, $this->adapter, $this->acl); + $recordGateway->populate($this->parseRecord($newRecordObject), true); + + return $recordGateway; + } + /** * @param Collection $schema The table schema array. * @param array $parentRow The parent record being updated. @@ -940,10 +1093,9 @@ public function fetchItems(array $params = [], \Closure $queryCallback = null) $results = $this->selectWith($builder->buildSelect())->toArray(); if (!$results && ArrayUtils::has($params, 'single')) { + $message = null; if (ArrayUtils::has($params, 'id')) { $message = sprintf('Item with id "%s" not found', $params['id']); - } else { - $message = 'Item not found'; } throw new Exception\ItemNotFoundException($message); @@ -2110,4 +2262,66 @@ public function countByStatus() return $stats; } + + protected function recordActivity($action, $record, $nestedItems, array $params = []) + { + if ($this->getTable() == SchemaManager::COLLECTION_ACTIVITY) { + return; + } + + $currentUserId = $this->acl ? $this->acl->getUserId() : null; + $rowId = $record[$this->primaryKeyFieldName]; + + // Save parent log entry + $parentLogEntry = BaseRowGateway::makeRowGatewayFromTableName('id', 'directus_activity', $this->adapter); + $logData = [ + 'type' => DirectusActivityTableGateway::makeLogTypeFromTableName($this->table), + 'action' => $action, + 'user' => $currentUserId, + 'datetime' => DateTimeUtils::nowInUTC()->toString(), + 'ip' => \Directus\get_request_ip(), + 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '', + 'collection' => $this->getTable(), + 'item' => ArrayUtils::get($record, $this->primaryKeyFieldName), + 'comment' => ArrayUtils::get($params, 'activity_comment') + ]; + $parentLogEntry->populate($logData, false); + $parentLogEntry->save(); + + // Add Revisions + $revisionTableGateway = new RelationalTableGateway(SchemaManager::COLLECTION_REVISIONS, $this->adapter); + $revisionTableGateway->insert([ + 'activity' => $parentLogEntry->getId(), + 'collection' => $this->getTable(), + 'item' => $rowId, + 'data' => json_encode($record), + 'delta' => json_encode($record), + 'parent_item' => null, + 'parent_collection' => null, + 'parent_changed' => null, + ]); + + // Update & insert nested activity entries + $ActivityGateway = new DirectusActivityTableGateway($this->adapter); + foreach ($nestedItems as $item) { + // TODO: ought to insert these in one batch + $ActivityGateway->insert(ArrayUtils::omit($item, [ + 'parent_item', + 'parent_collection', + 'data', + 'delta', + 'parent_changed', + ])); + $revisionTableGateway->insert([ + 'activity' => $ActivityGateway->lastInsertValue, + 'collection' => ArrayUtils::get($item, 'collection'), + 'item' => ArrayUtils::get($item, 'item'), + 'data' => ArrayUtils::get($item, 'data'), + 'delta' => ArrayUtils::get($item, 'delta'), + 'parent_item' => ArrayUtils::get($item, 'parent_item'), + 'parent_collection' => ArrayUtils::get($item, 'parent_collection'), + 'parent_changed' => ArrayUtils::get($item, 'parent_changed') + ]); + } + } } diff --git a/src/core/Directus/Services/AbstractService.php b/src/core/Directus/Services/AbstractService.php index 1af1802085..a682b6be09 100644 --- a/src/core/Directus/Services/AbstractService.php +++ b/src/core/Directus/Services/AbstractService.php @@ -4,6 +4,7 @@ use Directus\Application\Container; use Directus\Database\Exception\ForbiddenSystemTableDirectAccessException; +use Directus\Database\Exception\ItemNotFoundException; use Directus\Database\Schema\SchemaManager; use Directus\Database\TableGateway\RelationalTableGateway; use Directus\Database\TableGatewayFactory; @@ -330,14 +331,6 @@ protected function getCRUDParams(array $params) */ protected function validatePayload($collectionName, $fields, array $payload, array $params) { - $collection = $this->getSchemaManager()->getCollection($collectionName); - $payloadCount = count($payload); - $hasPrimaryKeyData = ArrayUtils::has($payload, $collection->getPrimaryKeyName()); - - if ($payloadCount === 0 || ($hasPrimaryKeyData && count($payload) === 1)) { - throw new UnprocessableEntityException('Payload cannot be empty'); - } - $columnsToValidate = []; // TODO: Validate empty request @@ -475,4 +468,42 @@ protected function unknownFieldsAllowed() { return []; } + + /** + * Checks whether the given id exists in the given collection + * + * @param string $collection + * @param mixed $id + * @param array $conditions + * + * @return bool + */ + protected function itemExists($collection, $id, array $conditions = []) + { + $tableGateway = $this->createTableGateway($collection); + $conditions = array_merge($conditions, [ + $tableGateway->primaryKeyFieldName => $id + ]); + + $select = $tableGateway->getSql()->select(); + $select->columns([$tableGateway->primaryKeyFieldName]); + $select->where($conditions); + $select->limit(1); + + return $tableGateway->selectWith($select)->count() === 1; + } + + /** + * @param string $collection + * @param mixed $id + * @param array $conditions + * + * @throws ItemNotFoundException + */ + protected function checkItemExists($collection, $id, array $conditions = []) + { + if (!$this->itemExists($collection, $id, $conditions)) { + throw new ItemNotFoundException(); + } + } } diff --git a/src/core/Directus/Services/ActivityService.php b/src/core/Directus/Services/ActivityService.php index 18c5c2efa4..7409e0fcb0 100644 --- a/src/core/Directus/Services/ActivityService.php +++ b/src/core/Directus/Services/ActivityService.php @@ -3,14 +3,12 @@ namespace Directus\Services; use Directus\Application\Container; -use Directus\Database\Exception\ItemNotFoundException; use Directus\Database\RowGateway\BaseRowGateway; use Directus\Database\Schema\SchemaManager; use Directus\Database\TableGateway\DirectusActivityTableGateway; use Directus\Database\TableGateway\DirectusRolesTableGateway; use Directus\Util\ArrayUtils; use Directus\Util\DateTimeUtils; -use Zend\Db\Sql\Select; class ActivityService extends AbstractService { @@ -59,7 +57,7 @@ public function createComment(array $data, array $params = []) // make sure to create new one instead of update unset($data[$tableGateway->primaryKeyFieldName]); - $newComment = $tableGateway->updateRecord($data, $this->getCRUDParams($params)); + $newComment = $tableGateway->createRecord($data, $this->getCRUDParams($params)); return $tableGateway->wrapData( $newComment->toArray(), @@ -80,16 +78,12 @@ public function updateComment($id, $comment, array $params = []) $this->enforcePermissions($this->collection, $data, $params); - $tableGateway = $this->getTableGateway(); - $select = new Select($this->collection); - $select->columns(['id']); - $select->where(['id' => $id, 'type' => DirectusActivityTableGateway::TYPE_COMMENT]); - if (!$tableGateway->selectWith($select)->count()) { - throw new ItemNotFoundException(); - } + $this->checkItemExists($this->collection, $id, [ + 'type' => DirectusActivityTableGateway::TYPE_COMMENT + ]); - // make sure to create new one instead of update - $newComment = $tableGateway->updateRecord($data, $this->getCRUDParams($params)); + $tableGateway = $this->getTableGateway(); + $newComment = $tableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); return $tableGateway->wrapData( $newComment->toArray(), @@ -101,16 +95,10 @@ public function updateComment($id, $comment, array $params = []) public function deleteComment($id, array $params = []) { $this->enforcePermissions($this->collection, [], $params); + $this->checkItemExists($this->collection, $id); $tableGateway = $this->getTableGateway(); - $select = new Select($this->collection); - $select->columns(['id']); - $select->where(['id' => $id, 'type' => DirectusActivityTableGateway::TYPE_COMMENT]); - if (!$tableGateway->selectWith($select)->count()) { - throw new ItemNotFoundException(); - } - - $tableGateway->updateRecord(['id' => $id, 'deleted_comment' => true]); + $tableGateway->updateRecord($id, ['deleted_comment' => true]); } /** diff --git a/src/core/Directus/Services/FilesServices.php b/src/core/Directus/Services/FilesServices.php index dbc027eefb..4432716f48 100644 --- a/src/core/Directus/Services/FilesServices.php +++ b/src/core/Directus/Services/FilesServices.php @@ -30,7 +30,7 @@ public function create(array $data, array $params = []) $validationConstraints = $this->createConstraintFor($this->collection); $this->validate($data, array_merge(['data' => 'required'], $validationConstraints)); - $newFile = $tableGateway->updateRecord($data, $this->getCRUDParams($params)); + $newFile = $tableGateway->createRecord($data, $this->getCRUDParams($params)); return $tableGateway->wrapData( \Directus\append_storage_information($newFile->toArray()), @@ -58,10 +58,11 @@ public function update($id, array $data, array $params = []) { $this->enforcePermissions($this->collection, $data, $params); + $this->checkItemExists($this->collection, $id); $this->validatePayload($this->collection, array_keys($data), $data, $params); + $tableGateway = $this->createTableGateway($this->collection); - $data[$tableGateway->primaryKeyFieldName] = $id; - $newFile = $tableGateway->updateRecord($data, $this->getCRUDParams($params)); + $newFile = $tableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); return $tableGateway->wrapData( \Directus\append_storage_information($newFile->toArray()), @@ -102,7 +103,7 @@ public function createFolder(array $data, array $params = []) $foldersTableGateway = $this->createTableGateway($collection); - $newFolder = $foldersTableGateway->updateRecord($data, $this->getCRUDParams($params)); + $newFolder = $foldersTableGateway->createRecord($data, $this->getCRUDParams($params)); return $foldersTableGateway->wrapData( $newFolder->toArray(), @@ -128,11 +129,12 @@ public function findFolderByIds($id, array $params = []) public function updateFolder($id, array $data, array $params = []) { - $this->enforcePermissions('directus_folders', $data, $params); - $foldersTableGateway = $this->createTableGateway('directus_folders'); + $collectionName = 'directus_folders'; + $this->enforcePermissions($collectionName, $data, $params); + $this->checkItemExists($collectionName, $id); - $data['id'] = $id; - $group = $foldersTableGateway->updateRecord($data, $this->getCRUDParams($params)); + $foldersTableGateway = $this->createTableGateway('directus_folders'); + $group = $foldersTableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); return $foldersTableGateway->wrapData( $group->toArray(), diff --git a/src/core/Directus/Services/ItemsService.php b/src/core/Directus/Services/ItemsService.php index 4532445c92..048f377e1b 100644 --- a/src/core/Directus/Services/ItemsService.php +++ b/src/core/Directus/Services/ItemsService.php @@ -2,10 +2,10 @@ namespace Directus\Services; +use Directus\Database\Exception\ItemNotFoundException; use Directus\Database\RowGateway\BaseRowGateway; use Directus\Database\Schema\SchemaManager; use Directus\Exception\ForbiddenException; -use Directus\Util\ArrayUtils; use Directus\Validator\Exception\InvalidRequestException; use Zend\Db\TableGateway\TableGateway; @@ -17,9 +17,7 @@ public function createItem($collection, $payload, $params = []) $this->validatePayload($collection, null, $payload, $params); $tableGateway = $this->createTableGateway($collection); - - // TODO: Throw an exception if ID exist in payload - $newRecord = $tableGateway->updateRecord($payload, $this->getCRUDParams($params)); + $newRecord = $tableGateway->createRecord($payload, $this->getCRUDParams($params)); try { $item = $this->find($collection, $newRecord->getId()); @@ -115,18 +113,20 @@ public function findOne($collection, array $params = []) * @param array $params * * @return array + * + * @throws ItemNotFoundException */ public function update($collection, $id, $payload, array $params = []) { $this->enforcePermissions($collection, $payload, $params); $this->validatePayload($collection, array_keys($payload), $payload, $params); + $this->checkItemExists($collection, $id); $tableGateway = $this->createTableGateway($collection); // Fetch the entry even if it's not "published" $params['status'] = '*'; - $payload[$tableGateway->primaryKeyFieldName] = $id; - $newRecord = $tableGateway->updateRecord($payload, $this->getCRUDParams($params)); + $newRecord = $tableGateway->updateRecord($id, $payload, $this->getCRUDParams($params)); try { $item = $this->find($collection, $newRecord->getId()); diff --git a/src/core/Directus/Services/PermissionsService.php b/src/core/Directus/Services/PermissionsService.php index a2286570c9..8e7cbbab95 100644 --- a/src/core/Directus/Services/PermissionsService.php +++ b/src/core/Directus/Services/PermissionsService.php @@ -4,7 +4,6 @@ use Directus\Application\Container; use Directus\Database\Schema\SchemaManager; -use Directus\Exception\ErrorException; use Directus\Util\ArrayUtils; class PermissionsService extends AbstractService @@ -35,7 +34,7 @@ public function create(array $data, array $params = []) $this->validatePayload($this->collection, null, $data, $params); $tableGateway = $this->getTableGateway(); - $newGroup = $tableGateway->updateRecord($data, $this->getCRUDParams($params)); + $newGroup = $tableGateway->createRecord($data, $this->getCRUDParams($params)); return $tableGateway->wrapData( $newGroup->toArray(), @@ -71,10 +70,10 @@ public function update($id, array $data, array $params = []) { $this->enforcePermissions($this->collection, $data, $params); $this->validatePayload($this->collection, array_keys($data), $data, $params); + $this->checkItemExists($this->collection, $id); $tableGateway = $this->getTableGateway(); - $data['id'] = $id; - $newGroup = $tableGateway->updateRecord($data, $this->getCRUDParams($params)); + $newGroup = $tableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); return $tableGateway->wrapData( $newGroup->toArray(), diff --git a/src/core/Directus/Services/RevisionsService.php b/src/core/Directus/Services/RevisionsService.php index 0c85c61d44..3f7c777fff 100644 --- a/src/core/Directus/Services/RevisionsService.php +++ b/src/core/Directus/Services/RevisionsService.php @@ -6,8 +6,6 @@ use Directus\Database\Exception\RevisionInvalidDeltaException; use Directus\Database\Exception\RevisionNotFoundException; use Directus\Database\Schema\SchemaManager; -use Directus\Database\SchemaService; -use Directus\Exception\Exception; use Zend\Db\TableGateway\TableGateway; class RevisionsService extends AbstractService @@ -165,11 +163,8 @@ public function revert($collectionName, $item, $revision, array $params = []) throw new RevisionInvalidDeltaException($revision); } - $collection = SchemaService::getCollection($collectionName); $tableGateway = $this->createTableGateway($collectionName); - - $data[$collection->getPrimaryKeyName()] = $item; - $tableGateway->revertRecord($data); + $tableGateway->revertRecord($item, $data); return $this->getDataAndSetResponseCacheTags( [$tableGateway, 'getItems'], diff --git a/src/core/Directus/Services/RolesService.php b/src/core/Directus/Services/RolesService.php index 78750c157b..bc9c174ea0 100644 --- a/src/core/Directus/Services/RolesService.php +++ b/src/core/Directus/Services/RolesService.php @@ -44,9 +44,7 @@ public function create(array $data, array $params = []) $this->enforcePermissions($this->collection, $data, $params); $groupsTableGateway = $this->createTableGateway($this->collection); - // make sure to create new one instead of update - unset($data[$groupsTableGateway->primaryKeyFieldName]); - $newGroup = $groupsTableGateway->updateRecord($data, $this->getCRUDParams($params)); + $newGroup = $groupsTableGateway->createRecord($data, $this->getCRUDParams($params)); return $groupsTableGateway->wrapData( $newGroup->toArray(), @@ -102,11 +100,10 @@ public function update($id, array $data, array $params = []) { $this->validatePayload($this->collection, array_keys($data), $data, $params); $this->enforcePermissions($this->collection, $data, $params); + $this->checkItemExists($this->collection, $id); $groupsTableGateway = $this->getTableGateway(); - - $data['id'] = $id; - $group = $groupsTableGateway->updateRecord($data, $this->getCRUDParams($params)); + $group = $groupsTableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); return $groupsTableGateway->wrapData( $group->toArray(), diff --git a/src/core/Directus/Services/TablesService.php b/src/core/Directus/Services/TablesService.php index bbc3a09414..4178159a2e 100644 --- a/src/core/Directus/Services/TablesService.php +++ b/src/core/Directus/Services/TablesService.php @@ -344,7 +344,7 @@ public function createTable($name, array $data = [], array $params = []) $item = ArrayUtils::omit($data, 'fields'); $item['collection'] = $name; - $table = $collectionsTableGateway->updateRecord($item); + $table = $collectionsTableGateway->manageRecordUpdate('directus_collections', $item); // ---------------------------------------------------------------------------- @@ -426,7 +426,7 @@ public function updateTable($name, array $data, array $params = []) $item = ArrayUtils::omit($data, 'fields'); $item['collection'] = $name; - $collection = $collectionsTableGateway->updateRecord($item); + $collection = $collectionsTableGateway->manageRecordUpdate('directus_collections', $item); // ---------------------------------------------------------------------------- return $tableGateway->wrapData( @@ -750,7 +750,8 @@ protected function addFieldInfo($collection, $field, array $data) $collectionObject = $this->getSchemaManager()->getCollection('directus_fields'); - return $this->getFieldsTableGateway()->updateRecord( + return $this->getFieldsTableGateway()->manageRecordUpdate( + 'directus_fields', ArrayUtils::pick($data, $collectionObject->getFieldsName()) ); } @@ -762,7 +763,8 @@ protected function updateFieldInfo($id, array $data) $collectionObject = $this->getSchemaManager()->getCollection('directus_fields'); - return $this->getFieldsTableGateway()->updateRecord( + return $this->getFieldsTableGateway()->manageRecordUpdate( + 'directus_fields', ArrayUtils::pick($data, $collectionObject->getFieldsName()) ); } @@ -1087,7 +1089,7 @@ protected function updateColumnRelation($collectionName, array $column) $data['id'] = $row['id']; } - return $relationsTableGateway->updateRecord($data); + return $relationsTableGateway->manageRecordUpdate('directus_relations', $data); } /**