diff --git a/README.md b/README.md index b99d838..6afa405 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,44 @@ if ($isUpdate) { } ``` +##### Update Batch + +```php +use \InitPHP\Database\Facade\DB; +$data = [ + [ + 'id' => 5, + 'title' => 'New Title #5', + 'content' => 'New Content #5', + ], + [ + 'id' => 10, + 'title' => 'New Title #10', + ] +]; + +$isUpdate = DB::from('post') + ->where('status', 1) + ->updateBatch($data, 'id'); + +/** +* This executes the following query. +* +* UPDATE post SET +* title = CASE +* WHEN id = 5 THEN 'New Title #5' +* WHEN id = 10 THEN 'New Title #10' +* ELSE title END, +* content = CASE +* WHEN id = 5 THEN 'New Content #5' +* ELSE content END +* WHERE status = 1 AND id IN (5, 10) +*/ +if ($isUpdate) { + // Success +} +``` + #### Delete ```php diff --git a/src/Database.php b/src/Database.php index f442c38..8e2647f 100644 --- a/src/Database.php +++ b/src/Database.php @@ -512,6 +512,49 @@ public function update(array $set) return $res->numRows() > 0; } + /** + * @param array $set + * @param string $referenceColumn + * @return bool + */ + public function updateBatch(array $set, string $referenceColumn) + { + if($this->_credentials['updatable'] === FALSE){ + throw new UpdatableException(''); + } + $schemaID = $this->getSchemaID(); + + $results = []; + $setUpdate = []; + + foreach ($set as $data) { + if(!\is_array($data) || \array_key_exists($referenceColumn, $data)){ + continue; + } + + $setUpdate[] = $data; + $this->_validation->setData($data); + foreach ($data as $column => $value) { + if($column == $referenceColumn){ + continue; + } + if($this->_validation->validation($column, $schemaID) === FALSE){ + $this->_errors[] = $this->_validation->getError(); + return false; + } + } + + if(!empty($this->_credentials['updatedField'])){ + $setUpdate[$this->_credentials['updatedField']] = \date($this->_credentials['timestampFormat']); + } + } + + $res = $this->query($this->_updateBatchQuery($setUpdate, $referenceColumn)); + $this->reset(); + + return $res->numRows() > 0; + } + /** * @param array $conditions * @return bool @@ -666,6 +709,60 @@ public function _updateQuery(array $data): string . $this->_limitQuery(); } + public function _updateBatchQuery(array $data, $referenceColumn): string + { + $updateData = []; $columns = []; $where = []; + $schemaID = $this->getSchemaID(); + foreach ($data as $set) { + if(!\is_array($set) || !isset($set[$referenceColumn])){ + continue; + } + $setData = []; + foreach ($set as $key => $value) { + if($key === $schemaID){ + continue; + } + if($this->_credentials['allowedFields'] !== null && $key != $referenceColumn && !\in_array($key, $this->_credentials['allowedFields'])){ + continue; + } + if($key == $referenceColumn){ + $where[] = $value; + continue; + } + $setData[$key] = Helper::isSQLParameterOrFunction($value) ? $value : Parameters::add($key, $value); + if(!\in_array($key, $columns)){ + $columns[] = $key; + } + } + $updateData[] = $setData; + } + + $update = []; + foreach ($columns as $column) { + $syntax = $column . ' = CASE'; + foreach ($updateData as $key => $values) { + if(!\array_key_exists($column, $values)){ + continue; + } + $syntax .= ' WHEN ' . $referenceColumn . ' = ' + . (Helper::isSQLParameterOrFunction($where[$key]) ? $where[$key] : Parameters::add($referenceColumn, $where[$key])) + . ' THEN ' + . $values[$column]; + } + $syntax .= ' ELSE ' . $column . ' END'; + $update[] = $syntax; + } + $this->in($referenceColumn, $where); + + return 'UPDATE ' + . (empty($this->_STRUCTURE['table']) ? $this->getSchema() : end($this->_STRUCTURE['table'])) + . ' SET ' + . \implode(', ', $update) + . $this->_whereQuery() + . $this->_havingQuery() + . $this->_limitQuery(); + } + public function _deleteQuery(): string { return 'DELETE FROM' diff --git a/src/Model.php b/src/Model.php index a8476fe..e06fb06 100644 --- a/src/Model.php +++ b/src/Model.php @@ -326,6 +326,38 @@ final public function update(array $set) return $data = $this->callbacksFunctionHandler($data, 'afterUpdate'); } + /** + * @param array $set + * @param string $referenceColumn + * @return array|false + */ + final public function updateBatch(array $set, string $referenceColumn) + { + if($this->isUpdatable() === FALSE){ + throw new UpdatableException('"' . \get_called_class() . '" is not a updatable model.'); + } + + foreach ($set as &$data) { + $data = $this->callbacksFunctionHandler($data, 'beforeUpdate'); + if($data === FALSE){ + return false; + } + } + + if(parent::updateBatch($set, $referenceColumn) === FALSE){ + return false; + } + + foreach ($set as &$row) { + $row = $this->callbacksFunctionHandler($row, 'afterUpdate'); + if($row === FALSE){ + return false; + } + } + + return $set; + } + /** * @param int|string|null $id * @return array|bool diff --git a/tests/QueryBuilderUnitTest.php b/tests/QueryBuilderUnitTest.php index 231bfdc..1020873 100644 --- a/tests/QueryBuilderUnitTest.php +++ b/tests/QueryBuilderUnitTest.php @@ -252,6 +252,30 @@ public function testUpdateStatementBuild() $this->db->reset(); } + public function testUpdateBatchStatementBuild() + { + Parameters::reset(); + $this->db->from('post') + ->where('status', true); + + $data = [ + [ + 'id' => 5, + 'title' => 'New Title #5', + 'content' => 'New Content #5', + ], + [ + 'id' => 10, + 'title' => 'New Title #10', + ] + ]; + + $expected = 'UPDATE post SET title = CASE WHEN id = :id THEN :title WHEN id = :id_1 THEN :title_1 ELSE title END, content = CASE WHEN id = :id_2 THEN :content ELSE content END WHERE status = :status AND id IN (5, 10)'; + + $this->assertEquals($expected, $this->db->_updateBatchQuery($data, 'id')); + $this->db->reset(); + } + public function testDeleteStatementBuild() { Parameters::reset();