diff --git a/README.md b/README.md index 6afa405..a09268b 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,6 @@ DB::createImmutable([ #### Create -Single Row : - ```php use \InitPHP\Database\Facade\DB; $data = [ @@ -71,7 +69,7 @@ if($isInsert){ } ``` -Multi Row: +##### Create Batch ```php use \InitPHP\Database\Facade\DB; @@ -89,7 +87,7 @@ $data = [ ]; $isInsert = DB::table('post') - ->create($data); + ->createBatch($data); /** * This executes the following query. diff --git a/src/Database.php b/src/Database.php index 8e2647f..020ef72 100644 --- a/src/Database.php +++ b/src/Database.php @@ -415,29 +415,49 @@ public function create(array $set) if($isCreatedField){ $data[$this->_credentials['createdField']] = $createdFieldParameterName; } - }else{ - $i = 0; - foreach ($set as $row) { - $data[$i] = []; - $this->_validation->setData($row); - foreach ($row as $column => $value) { - if($this->_validation->validation($column, null) === FALSE){ - $this->_errors[] = $this->_validation->getError(); - return false; - } - $data[$i][$column] = $value; - } - if(empty($data[$i])){ - continue; - } - if($isCreatedField){ - $data[$i][$this->_credentials['createdField']] = $createdFieldParameterName; + } + + $res = $this->query($this->_insertQuery($data)); + $this->reset(); + return $res->numRows() > 0; + } + + /** + * @param array $set + * @return bool + */ + public function createBatch(array $set) + { + if($this->_credentials['writable'] === FALSE){ + throw new WritableException(''); + } + $isCreatedField = !empty($this->_credentials['createdField']); + if($isCreatedField){ + $createdFieldParameterName = Parameters::add($this->_credentials['createdField'], \date($this->_credentials['timestampFormat'])); + } + $data = []; + $i = 0; + foreach ($set as $row) { + $data[$i] = []; + $this->_validation->setData($row); + foreach ($row as $column => $value) { + if($this->_validation->validation($column, null) === FALSE){ + $this->_errors[] = $this->_validation->getError(); + return false; } - ++$i; + $data[$i][$column] = $value; + } + if(empty($data[$i])){ + continue; } + if($isCreatedField){ + $data[$i][$this->_credentials['createdField']] = $createdFieldParameterName; + } + ++$i; } - $res = $this->query($this->_insertQuery($data)); + $res = $this->query($this->_insertBatchQuery($data)); $this->reset(); + return $res->numRows() > 0; } @@ -636,21 +656,28 @@ public function _insertQuery(array $data): string $columns = []; $values = []; - if(\count($data) === \count($data, \COUNT_RECURSIVE)){ - foreach ($data as $column => $value) { - $column = \trim($column); - if($this->_credentials['allowedFields'] !== null && !\in_array($column, $this->_credentials['allowedFields'])){ - continue; - } - $columns[] = $column; - $values[] = Helper::isSQLParameterOrFunction($value) ? $value : Parameters::add($column, $value); - } - if(empty($columns)){ - return ''; + foreach ($data as $column => $value) { + $column = \trim($column); + if($this->_credentials['allowedFields'] !== null && !\in_array($column, $this->_credentials['allowedFields'])){ + continue; } - return $sql - . ' (' . \implode(', ', $columns) . ') VALUES (' . \implode(', ', $values) . ');'; + $columns[] = $column; + $values[] = Helper::isSQLParameterOrFunction($value) ? $value : Parameters::add($column, $value); + } + if(empty($columns)){ + return ''; } + return $sql + . ' (' . \implode(', ', $columns) . ') VALUES (' . \implode(', ', $values) . ');'; + } + + public function _insertBatchQuery($data): string + { + $sql = 'INSERT INTO' + . ' ' + . (empty($this->_STRUCTURE['table']) ? $this->getSchema() : end($this->_STRUCTURE['table'])); + $columns = []; + $values = []; foreach ($data as &$row) { $value = []; @@ -676,6 +703,7 @@ public function _insertQuery(array $data): string } $multiValues[] = '(' . \implode(', ', $value) . ')'; } + return $sql . ' (' . \implode(', ', $columns) . ') VALUES ' . \implode(', ', $multiValues) . ';'; } diff --git a/src/Facade/DB.php b/src/Facade/DB.php index a1ea6d1..121ceb9 100644 --- a/src/Facade/DB.php +++ b/src/Facade/DB.php @@ -44,6 +44,7 @@ * @method static int insertId() * @method static Result get(?string $table = null) * @method static bool create(array $set) + * @method static bool createBatch(array $set) * @method static Result read(array $selector = [], array $conditions = [], array $parameters = []) * @method static Result readOne(array $selector = [], array $conditions = [], array $parameters = []) * @method static bool update(array $set) diff --git a/src/Model.php b/src/Model.php index e06fb06..8119b00 100644 --- a/src/Model.php +++ b/src/Model.php @@ -237,38 +237,84 @@ final public function withPrimaryKey(string $column): self } /** - * @param array $data + * @param array $set * @return array|false */ - final public function create(array $data) + final public function create(array $set) { - return $this->insert($data); + return $this->insert($set); } /** - * @param array $data + * @param array $set + * @return array|false + */ + final public function createBatch(array $set) + { + return $this->insertBatch($set); + } + + /** + * @param array $set * @return array|false */ - final public function insert(array $data) + final public function insert(array $set) { if($this->isWritable() === FALSE){ throw new WritableException('"' . \get_called_class() . '" is not a writable model.'); } - if(($data = $this->callbacksFunctionHandler($data, 'beforeInsert')) === FALSE){ + + $data = $this->isCallbacksFunction('beforeInsert', 'afterInsert') ? $this->callbacksFunctionHandler($set, 'beforeInsert') : $set; + + if($data === FALSE){ + return false; + } + + if(parent::create($data) === FALSE){ return false; } - $create = parent::create($data); + return $this->isCallbacksFunction('afterInsert') ? $this->callbacksFunctionHandler($data, 'afterInsert') : true; + } - if($create === FALSE){ + /** + * @param array $set + * @return array|false + */ + final public function insertBatch(array $set) + { + if($this->isUpdatable() === FALSE){ + throw new UpdatableException('"' . \get_called_class() . '" is not a updatable model.'); + } + + if($this->isCallbacksFunction('beforeInsert', 'afterInsert')){ + foreach ($set as &$data) { + $data = $this->callbacksFunctionHandler($data, 'beforeInsert'); + if($data === FALSE){ + return false; + } + } + } + + if(parent::createBatch($set) === FALSE){ return false; } - return $data = $this->callbacksFunctionHandler($data, 'afterInsert'); + + if($this->isCallbacksFunction('afterInsert')){ + foreach ($set as &$row) { + $row = $this->callbacksFunctionHandler($row, 'afterInsert'); + if($row === FALSE){ + return false; + } + } + } + + return $set; } /** * @param Entity $entity - * @return array|false + * @return array|bool */ final public function save(Entity $entity) { @@ -310,20 +356,22 @@ final public function readOne(array $selector = [], array $conditions = [], arra /** * @param array $set - * @return array|false + * @return array|bool */ final public function update(array $set) { if($this->isUpdatable() === FALSE){ throw new UpdatableException('"' . \get_called_class() . '" is not a updatable model.'); } - if(($data = $this->callbacksFunctionHandler($set, 'beforeUpdate')) === FALSE){ + $data = $this->isCallbacksFunction('beforeUpdate', 'afterUpdate') ? $this->callbacksFunctionHandler($set, 'beforeUpdate') : $set; + if($data === FALSE){ return false; } if(parent::update($data) === FALSE){ return false; } - return $data = $this->callbacksFunctionHandler($data, 'afterUpdate'); + + return $this->isCallbacksFunction('afterUpdate') ? $this->callbacksFunctionHandler($data, 'afterUpdate') : true; } /** @@ -337,10 +385,12 @@ final public function updateBatch(array $set, string $referenceColumn) 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($this->isCallbacksFunction('beforeUpdate', 'afterUpdate')){ + foreach ($set as &$data) { + $data = $this->callbacksFunctionHandler($data, 'beforeUpdate'); + if($data === FALSE){ + return false; + } } } @@ -348,10 +398,12 @@ final public function updateBatch(array $set, string $referenceColumn) return false; } - foreach ($set as &$row) { - $row = $this->callbacksFunctionHandler($row, 'afterUpdate'); - if($row === FALSE){ - return false; + if($this->isCallbacksFunction('afterUpdate')){ + foreach ($set as &$row) { + $row = $this->callbacksFunctionHandler($row, 'afterUpdate'); + if($row === FALSE){ + return false; + } } } @@ -370,26 +422,29 @@ final public function delete($id = null) if($id !== null && !empty($this->getSchemaID())){ $this->where($this->getSchemaID(), $id); } - $clone = clone $this; - $parameters = Parameters::get(); - $res = $clone->query($clone->_readQuery(), $parameters); - $data = $res->asAssoc()->results(); - Parameters::merge($parameters); - unset($clone, $parameters); - if($data === null){ - return true; - } - $data = $this->callbacksFunctionHandler($data, 'beforeDelete'); - if($data === FALSE){ - return false; + if ($this->isCallbacksFunction('beforeDelete', 'afterDelete')) { + $clone = clone $this; + $parameters = Parameters::get(); + $res = $clone->query($clone->_readQuery(), $parameters); + $data = $res->asAssoc()->results(); + Parameters::merge($parameters); + unset($clone, $parameters); + + if($data === null){ + return true; + } + $data = $this->callbacksFunctionHandler($data, 'beforeDelete'); + if($data === FALSE){ + return false; + } } if(parent::delete() === FALSE){ return false; } - return $data = $this->callbacksFunctionHandler($data, 'afterDelete'); + return $this->isCallbacksFunction('afterDelete') ? $this->callbacksFunctionHandler($data, 'afterDelete') : true; } final public function purgeDeleted(): bool @@ -409,7 +464,7 @@ final public function purgeDeleted(): bool * @return $this * @throws \ReflectionException */ - final public function relation($model, ?string $fromColumn, ?string $targetColumn = null, string $joinType = 'INNER'): self + final public function relation($model, ?string $fromColumn = null, ?string $targetColumn = null, string $joinType = 'INNER'): self { $from = [ 'tableSchema' => $this->getSchema(), @@ -489,6 +544,24 @@ final public function isDeletable(): bool return $this->deletable ?? true; } + /** + * @param string ...$methods + * @return bool + */ + private function isCallbacksFunction(string ...$methods): bool + { + if(($this->allowedCallbacks ?? false) === FALSE){ + return false; + } + foreach ($methods as $method) { + $callbacks = $this->{$method} ?? null; + if(!empty($callbacks)){ + return true; + } + } + return false; + } + /** * @param array $data * @param string $method @@ -496,13 +569,10 @@ final public function isDeletable(): bool */ private function callbacksFunctionHandler(array $data, string $method) { - if(($this->allowedCallbacks ?? false) === FALSE){ + if(!$this->isCallbacksFunction($method)){ return $data; } $callbacks = $this->{$method}; - if(!\is_array($callbacks)){ - return $data; - } foreach ($callbacks as $callback) { if(\is_string($callback)){ diff --git a/tests/QueryBuilderUnitTest.php b/tests/QueryBuilderUnitTest.php index 1020873..47f4f99 100644 --- a/tests/QueryBuilderUnitTest.php +++ b/tests/QueryBuilderUnitTest.php @@ -210,7 +210,7 @@ public function testInsertStatementBuild() $this->db->reset(); } - public function testMultiInsertStatementBuild() + public function testInsertBatchStatementBuild() { Parameters::reset(); $this->db->from('post'); @@ -230,7 +230,7 @@ public function testMultiInsertStatementBuild() ]; $expected = 'INSERT INTO post (title, content, author, status) VALUES (:title, :content, :author, :status), (:title_1, :content_1, NULL, :status_1);'; - $this->assertEquals($expected, $this->db->_insertQuery($data)); + $this->assertEquals($expected, $this->db->_insertBatchQuery($data)); $this->db->reset(); }