From 0404bca02b3599be57a42f6b6025a591f273ca6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Muhammet=20=C5=9EAFAK?=
Date: Sun, 3 Dec 2023 20:48:08 +0300
Subject: [PATCH 1/2] v3
---
README.md | 167 +-
composer.json | 6 +-
src/Connection/Connection.php | 146 ++
.../Exceptions/ConnectionException.php | 8 +
.../Interfaces/ConnectionInterface.php | 60 +
src/DBAL/CRUD.php | 165 ++
src/DBAL/Database.php | 292 +++
.../Exceptions/SQLQueryExecuteException.php | 9 +
src/DBAL/Interfaces/CRUDInterface.php | 82 +
src/DBAL/Interfaces/DatabaseInterface.php | 99 +
src/DBAL/Interfaces/ResultInterface.php | 82 +
src/DBAL/Result.php | 144 ++
src/Database.php | 844 ---------
src/Exceptions/ConnectionException.php | 18 -
src/Exceptions/DeletableException.php | 18 -
src/Exceptions/ModelCallbacksException.php | 18 -
src/Exceptions/ModelException.php | 18 -
src/Exceptions/ModelRelationsException.php | 18 -
src/Exceptions/QueryBuilderException.php | 18 -
src/Exceptions/QueryGeneratorException.php | 18 -
src/Exceptions/ReadableException.php | 18 -
src/Exceptions/SQLQueryExecuteException.php | 18 -
src/Exceptions/UpdatableException.php | 18 -
src/Exceptions/ValidationException.php | 18 -
src/Exceptions/ValueException.php | 18 -
src/Exceptions/WritableException.php | 18 -
src/Facade/DB.php | 271 +--
src/Helpers/Helper.php | 123 --
src/Helpers/Parameters.php | 72 -
src/Helpers/Validation.php | 342 ----
src/Init.php | 37 -
src/Model.php | 627 -------
src/{ => ORM}/Entity.php | 74 +-
src/ORM/Exceptions/DeletableException.php | 7 +
src/ORM/Exceptions/EntityNotMethod.php | 14 +
src/ORM/Exceptions/ModelException.php | 10 +
src/ORM/Exceptions/ReadableException.php | 7 +
src/ORM/Exceptions/UpdatableException.php | 7 +
src/ORM/Exceptions/WritableException.php | 7 +
src/ORM/Interfaces/EntityInterface.php | 18 +
src/ORM/Interfaces/ModelInterface.php | 107 ++
src/ORM/Model.php | 248 +++
src/QueryBuilder.php | 1618 -----------------
.../Exceptions/QueryBuilderException.php | 8 +
.../Exceptions/QueryGeneratorException.php | 9 +
.../Interfaces/ParameterInterface.php | 48 +
.../Interfaces/QueryBuilderInterface.php | 758 ++++++++
src/QueryBuilder/Parameters.php | 104 ++
src/QueryBuilder/QueryBuilder.php | 1455 +++++++++++++++
src/QueryBuilder/RawQuery.php | 53 +
src/Raw.php | 41 -
src/Result.php | 180 --
src/Utils/Datatables.php | 323 ++--
src/Utils/Helper.php | 37 +
src/Utils/Pagination.php | 230 ---
tests/QueryBuilderUnitTest.php | 155 +-
56 files changed, 4516 insertions(+), 4812 deletions(-)
create mode 100644 src/Connection/Connection.php
create mode 100644 src/Connection/Exceptions/ConnectionException.php
create mode 100644 src/Connection/Interfaces/ConnectionInterface.php
create mode 100644 src/DBAL/CRUD.php
create mode 100644 src/DBAL/Database.php
create mode 100644 src/DBAL/Exceptions/SQLQueryExecuteException.php
create mode 100644 src/DBAL/Interfaces/CRUDInterface.php
create mode 100644 src/DBAL/Interfaces/DatabaseInterface.php
create mode 100644 src/DBAL/Interfaces/ResultInterface.php
create mode 100644 src/DBAL/Result.php
delete mode 100644 src/Database.php
delete mode 100644 src/Exceptions/ConnectionException.php
delete mode 100644 src/Exceptions/DeletableException.php
delete mode 100644 src/Exceptions/ModelCallbacksException.php
delete mode 100644 src/Exceptions/ModelException.php
delete mode 100644 src/Exceptions/ModelRelationsException.php
delete mode 100644 src/Exceptions/QueryBuilderException.php
delete mode 100644 src/Exceptions/QueryGeneratorException.php
delete mode 100644 src/Exceptions/ReadableException.php
delete mode 100644 src/Exceptions/SQLQueryExecuteException.php
delete mode 100644 src/Exceptions/UpdatableException.php
delete mode 100644 src/Exceptions/ValidationException.php
delete mode 100644 src/Exceptions/ValueException.php
delete mode 100644 src/Exceptions/WritableException.php
delete mode 100644 src/Helpers/Helper.php
delete mode 100644 src/Helpers/Parameters.php
delete mode 100644 src/Helpers/Validation.php
delete mode 100644 src/Init.php
delete mode 100644 src/Model.php
rename src/{ => ORM}/Entity.php (59%)
create mode 100644 src/ORM/Exceptions/DeletableException.php
create mode 100644 src/ORM/Exceptions/EntityNotMethod.php
create mode 100644 src/ORM/Exceptions/ModelException.php
create mode 100644 src/ORM/Exceptions/ReadableException.php
create mode 100644 src/ORM/Exceptions/UpdatableException.php
create mode 100644 src/ORM/Exceptions/WritableException.php
create mode 100644 src/ORM/Interfaces/EntityInterface.php
create mode 100644 src/ORM/Interfaces/ModelInterface.php
create mode 100644 src/ORM/Model.php
delete mode 100644 src/QueryBuilder.php
create mode 100644 src/QueryBuilder/Exceptions/QueryBuilderException.php
create mode 100644 src/QueryBuilder/Exceptions/QueryGeneratorException.php
create mode 100644 src/QueryBuilder/Interfaces/ParameterInterface.php
create mode 100644 src/QueryBuilder/Interfaces/QueryBuilderInterface.php
create mode 100644 src/QueryBuilder/Parameters.php
create mode 100644 src/QueryBuilder/QueryBuilder.php
create mode 100644 src/QueryBuilder/RawQuery.php
delete mode 100644 src/Raw.php
delete mode 100644 src/Result.php
create mode 100644 src/Utils/Helper.php
delete mode 100644 src/Utils/Pagination.php
diff --git a/README.md b/README.md
index 159d687..98f6c8c 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Manage your database with or without abstraction. This library is built on the P
## Requirements
-- PHP 7.4 and later.
+- PHP 8.0 and later.
- PHP PDO extension.
## Supported Databases
@@ -20,15 +20,9 @@ Databases supported by PDO and suitable drivers are available at [https://www.ph
composer require initphp/database
```
-or include the `src/init.php` file from this repo in your system.
-
-```php
-require_once "src/Init.php";
-```
-
## Usage
-### QueryBuilder and CRUD
+### QueryBuilder & DBAL and CRUD
```php
require_once "vendor/autoload.php";
@@ -53,8 +47,7 @@ $data = [
'content' => 'Post Content',
];
-$isInsert = DB::table('post')
- ->create($data);
+$isInsert = DB::create('post', $data);
/**
* This executes the following query.
@@ -67,8 +60,7 @@ $isInsert = DB::table('post')
if($isInsert){
// Success
} else {
- $errors = DB::getError();
- foreach ($errors as $errMsg) {
+ foreach (DB::getErrors() as $errMsg) {
echo $errMsg;
}
}
@@ -91,8 +83,7 @@ $data = [
],
];
-$isInsert = DB::table('post')
- ->createBatch($data);
+$isInsert = DB::createBatch('post', $data);
/**
* This executes the following query.
@@ -107,8 +98,7 @@ $isInsert = DB::table('post')
if($isInsert){
// Success
} else {
- $errors = DB::getError();
- foreach ($errors as $errMsg) {
+ foreach (DB::getErrors() as $errMsg) {
echo $errMsg;
}
}
@@ -119,14 +109,7 @@ if($isInsert){
```php
use \InitPHP\Database\Facade\DB;
-DB::select('user.name as author_name', 'post.id', 'post.title')
- ->from('post')
- ->selfJoin('user', 'user.id=post.author')
- ->where('post.status', 1)
- ->orderBy('post.id', 'ASC')
- ->orderBy('post.created_at', 'DESC')
- ->offset(20)->limit(10);
-
+
/**
* This executes the following query.
*
@@ -136,11 +119,19 @@ DB::select('user.name as author_name', 'post.id', 'post.title')
* ORDER BY post ASC, post.created_at DESC
* LIMIT 20, 10
*/
-$res = DB::read();
+$res = DB::select('user.name as author_name', 'post.id', 'post.title')
+ ->from('post')
+ ->selfJoin('user', 'user.id=post.author')
+ ->where('post.status', 1)
+ ->orderBy('post.id', 'ASC')
+ ->orderBy('post.created_at', 'DESC')
+ ->offset(20)->limit(10)
+ ->read('post');
+
if($res->numRows() > 0){
$results = $res->asAssoc()
- ->results();
+ ->rows();
foreach ($results as $row) {
echo $row['title'] . ' by ' . $row['author_name'] . '
';
}
@@ -156,9 +147,8 @@ $data = [
'content' => 'New Content',
];
-$isUpdate = DB::from('post')
- ->where('id', 13)
- ->update($data);
+$isUpdate = DB::where('id', 13)
+ ->update('post', $data);
/**
* This executes the following query.
@@ -170,8 +160,7 @@ $isUpdate = DB::from('post')
if ($isUpdate) {
// Success
} else {
- $errors = DB::getError();
- foreach ($errors as $errMsg) {
+ foreach (DB::getErrors() as $errMsg) {
echo $errMsg;
}
}
@@ -193,9 +182,8 @@ $data = [
]
];
-$isUpdate = DB::from('post')
- ->where('status', 1)
- ->updateBatch($data, 'id');
+$isUpdate = DB::where('status', '!=', 0)
+ ->updateBatch('post', $data, 'id');
/**
* This executes the following query.
@@ -208,13 +196,12 @@ $isUpdate = DB::from('post')
* content = CASE
* WHEN id = 5 THEN 'New Content #5'
* ELSE content END
-* WHERE status = 1 AND id IN (5, 10)
+* WHERE status != 0 AND id IN (5, 10)
*/
if ($isUpdate) {
// Success
} else {
- $errors = DB::getError();
- foreach ($errors as $errMsg) {
+ foreach (DB::getErrors() as $errMsg) {
echo $errMsg;
}
}
@@ -225,9 +212,8 @@ if ($isUpdate) {
```php
use \InitPHP\Database\Facade\DB;
-$isDelete = DB::from('post')
- ->where('id', 13)
- ->delete();
+$isDelete = DB::where('id', 13)
+ ->delete('post');
/**
* This executes the following query.
@@ -237,8 +223,7 @@ $isDelete = DB::from('post')
if ($isUpdate) {
// Success
} else {
- $errors = DB::getError();
- foreach ($errors as $errMsg) {
+ foreach (DB::getErrors() as $errMsg) {
echo $errMsg;
}
}
@@ -263,13 +248,15 @@ $res = DB::select(DB::raw("CONCAT(name, ' ', surname) AS fullname"))
->where(DB::raw("title = '' AND (status = 1 OR status = 0)"))
->limit(5)
->get('users');
+
/**
* SELECT CONCAT(name, ' ', surname) AS fullname
* FROM users
* WHERE title = '' AND (status = 1 OR status = 0)
* LIMIT 5
*/
-$results = $res->asAssoc()->results();
+$results = $res->asAssoc()
+ ->rows();
foreach ($results as $row) {
echo $row['fullname'];
}
@@ -308,10 +295,22 @@ namespace App\Model;
class Posts extends \InitPHP\Database\Model
{
+ /**
+ * If your model will use a connection other than your global connection, provide connection information.
+ * @var array|null Default : NULL
+ */
+ protected array $credentials = [
+ 'dsn' => '',
+ 'username' => 'root',
+ 'password' => '',
+ 'charset' => 'utf8mb4',
+ 'collation' => 'utf8mb4_unicode_ci',
+ ];
+
/**
* If not specified, \InitPHP\Database\Entity::class is used by default.
*
- * @var \InitPHP\Database\Entity|string
+ * @var \InitPHP\Database\Orm\Entity|string
*/
protected $entity = \App\Entities\PostEntity::class;
@@ -320,14 +319,14 @@ class Posts extends \InitPHP\Database\Model
*
* @var string
*/
- protected string $table = 'post';
+ protected string $schema = 'posts';
/**
* The name of the PRIMARY KEY column. If not, define it as NULL.
*
* @var null|string
*/
- protected ?string $primaryKey = 'id';
+ protected ?string $schemaId = 'id';
/**
* Specify FALSE if you want the data to be permanently deleted.
@@ -357,53 +356,6 @@ class Posts extends \InitPHP\Database\Model
*/
protected ?string $deletedField = 'deleted_at';
- /**
- * An array that defines the columns that will be allowed to be used in Insert and Update operations.
- * If you want to give access to all columns; You can specify it as NULL.
- *
- * @var null|string[]
- */
- protected ?array $allowedFields = [
- 'title', 'content', // ...
- ];
-
- /**
- * Turns the use of callable functions on or off.
- *
- * @var bool
- */
- protected bool $allowedCallbacks = false;
-
- /**
- * @var string[]|\Closure[]
- */
- protected array $beforeInsert = [];
-
- /**
- * @var string[]|\Closure[]
- */
- protected array $afterInsert = [];
-
- /**
- * @var string[]|\Closure[]
- */
- protected array $beforeUpdate = [];
-
- /**
- * @var string[]|\Closure[]
- */
- protected array $afterUpdate = [];
-
- /**
- * @var string[]|\Closure[]
- */
- protected array $beforeDelete = [];
-
- /**
- * @var string[]|\Closure[]
- */
- protected array $afterDelete = [];
-
protected bool $readable = true;
protected bool $writable = true;
@@ -411,25 +363,6 @@ class Posts extends \InitPHP\Database\Model
protected bool $deletable = true;
protected bool $updatable = true;
-
- protected array $validation = [
- 'id' => ['is_unique', 'int'],
- 'title' => ['required', 'string', 'length(0,255)'],
- ];
-
- protected array $validationMsg = [
- 'id' => [],
- 'title' => [
- 'required' => '{field} cannot be left blank.',
- 'string' => '{field} must be a string.',
- ],
- ];
-
- protected array $validationLabels = [
- 'id' => 'Post ID',
- 'title' => 'Post Title',
- // ...
- ];
}
```
@@ -439,7 +372,7 @@ The most basic example of a entity class would look like this.
```php
namespace App\Entities;
-class PostEntity extends \InitPHP\Database\Entity
+class PostEntity extends \InitPHP\Database\ORM\Entity
{
/**
* An example of a getter method for the "post_title" column.
@@ -466,7 +399,7 @@ class PostEntity extends \InitPHP\Database\Entity
}
```
-## Developement Tools
+## Development Tools
Below I have mentioned some developer tools that you can use during and after development.
@@ -549,16 +482,16 @@ DB::createImmutable([
### Profiler Mode
-Profiler mode is a developer tool available in v2.2 and above. It is a feature that allows you to see the executed queries along with their execution times.
+Profiler mode is a developer tool available in v3 and above. It is a feature that allows you to see the executed queries along with their execution times.
```php
use InitPHP\Database\Facade\DB;
-DB::enableQueryProfiler();
+DB::enableQueryLog();
DB::table('users')->where('name', 'John')->get();
-var_dump(DB::getProfilerQueries());
+var_dump(DB::getQueryLogs());
/**
* The output of the above example looks like this;
diff --git a/composer.json b/composer.json
index 52151be..ffb1fd3 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "initphp/database",
- "description": "InitPHP DB (QueryBuilder, DBAL and ORM) Library",
+ "description": "InitPHP DataBase (QueryBuilder, DBAL and ORM) Library",
"type": "library",
"license": "MIT",
"autoload": {
@@ -23,10 +23,10 @@
],
"minimum-stability": "stable",
"require": {
- "php": ">=7.4",
+ "php": ">=8.0",
"ext-pdo": "*"
},
"require-dev": {
- "phpunit/phpunit": "9.5"
+ "phpunit/phpunit": "^10.4"
}
}
diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php
new file mode 100644
index 0000000..233b8e1
--- /dev/null
+++ b/src/Connection/Connection.php
@@ -0,0 +1,146 @@
+ '',
+ 'username' => 'root',
+ 'password' => '',
+ 'charset' => 'utf8mb4',
+ 'collation' => 'utf8mb4_unicode_ci',
+
+ 'debug' => false,
+ 'log' => null,
+ ];
+
+ private ?PDO $pdo = null;
+
+ private array $transaction = [
+ 'status' => false,
+ 'enable' => false,
+ 'testMode' => false,
+ ];
+
+ public function __construct(?array $credentials = null)
+ {
+ !empty($credentials) && $this->credentials = array_merge($this->credentials, $credentials);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getCredentials(?string $key = null, mixed $default = null): mixed
+ {
+ if ($key === null) {
+ return $this->credentials;
+ }
+
+ return $this->credentials[$key] ?? $default;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getPDO(): PDO
+ {
+ !isset($this->pdo) && $this->connect();
+
+ return $this->pdo;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function connect(): bool
+ {
+ try {
+ $options = [
+ PDO::ATTR_EMULATE_PREPARES => false,
+ PDO::ATTR_PERSISTENT => true,
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_CLASS,
+ ];
+
+ $this->pdo = new PDO($this->getCredentials('dsn'), $this->getCredentials('username'), $this->getCredentials('password'), $options);
+
+ if ($charset = $this->getCredentials('charset')) {
+ if ($collation = $this->getCredentials('collation')) {
+ $this->pdo->exec("SET NAMES '" . $charset . "' COLLATE '" . $collation . "'");
+ }
+ $this->pdo->exec("SET CHARACTER SET '" . $charset . "'");
+ }
+
+ return true;
+ } catch (Exception $e) {
+ throw new ConnectionException($e->getMessage(), (int)$e->getCode(), $e->getPrevious());
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function beginTransaction(bool $testMode = false): bool
+ {
+ $this->transaction = [
+ 'status' => true,
+ 'enable' => true,
+ 'testMode' => $testMode,
+ ];
+
+ return $this->getPDO()->beginTransaction();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function completeTransaction(): bool
+ {
+ return $this->transaction['status'] === false || $this->transaction['testMode'] === true ? $this->rollBack() : $this->commit();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function commit(): bool
+ {
+ $this->transaction = [
+ 'status' => false,
+ 'enable' => false,
+ 'testMode' => false,
+ ];
+
+ return $this->getPDO()->commit();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function rollBack(): bool
+ {
+ $this->transaction = [
+ 'status' => false,
+ 'enable' => false,
+ 'testMode' => false,
+ ];
+
+ return $this->getPDO()->rollBack();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function disconnect(): bool
+ {
+ $this->pdo = null;
+
+ return true;
+ }
+
+}
diff --git a/src/Connection/Exceptions/ConnectionException.php b/src/Connection/Exceptions/ConnectionException.php
new file mode 100644
index 0000000..226baf2
--- /dev/null
+++ b/src/Connection/Exceptions/ConnectionException.php
@@ -0,0 +1,8 @@
+db = &$db;
+ }
+
+ public function __call(string $name, array $arguments)
+ {
+ $res = $this->db->{$name}(...$arguments);
+
+ return ($res instanceof DatabaseInterface) ? $this : $res;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function create(?string $table = null, ?array $set = null): bool
+ {
+ $builder = $this->db->getQueryBuilder();
+
+ !empty($set) && $builder->set($set);
+
+ !empty($table) && $builder->from($table);
+
+ $res = $this->db->query($builder->generateInsertQuery());
+ $this->db->getQueryBuilder()->getParameter()->reset();
+
+ return $res->numRows() > 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function createBatch(?string $table = null, ?array $set = null): bool
+ {
+ $builder = $this->db->getQueryBuilder();
+
+ if (!empty($set)) {
+ foreach ($set as $row) {
+ !empty($row) && is_array($row)
+ && $builder->set($row);
+ }
+ }
+
+ !empty($table) && $builder->from($table);
+
+ $res = $this->db->query($builder->generateBatchInsertQuery());
+ $this->db->getQueryBuilder()->resetStructure();
+
+ return $res->numRows() > 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function read(?string $table = null, array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
+ {
+ $builder = $this->db->getQueryBuilder();
+
+ !empty($parameters) && $builder->getParameter()->merge($parameters);
+
+ !empty($table) && $builder->from($table);
+
+
+ $res = $this->db->query($builder->generateSelectQuery($selector, $conditions));
+ $this->db->getQueryBuilder()->resetStructure();
+
+ return $res;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readOne(?string $table = null, array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
+ {
+ $builder = $this->db->getQueryBuilder();
+
+ !empty($parameters) && $builder->getParameter()->merge($parameters);
+
+ !empty($table) && $builder->from($table);
+
+ $res = $this->db->query($builder->limit(1)
+ ->generateSelectQuery($selector, $conditions));
+
+ $this->db->getQueryBuilder()->resetStructure();
+
+ return $res;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function update(?string $table = null, ?array $set = null): bool
+ {
+ $builder = $this->db->getQueryBuilder();
+
+ !empty($set) && $builder->set($set);
+ !empty($table) && $builder->from($table);
+
+ $res = $this->db->query($builder
+ ->generateUpdateQuery());
+
+ $this->db->getQueryBuilder()->resetStructure();
+
+ return $res->numRows() > 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function updateBatch(?string $table = null, ?array $set = null, ?string $referenceColumn = null): bool
+ {
+ $builder = $this->db->getQueryBuilder();
+
+ if (!empty($set)) {
+ foreach ($set as $row) {
+ if (!empty($row) && is_array($row)) {
+ $builder->set($row);
+ }
+ }
+ }
+ !empty($table) && $builder->from($table);
+
+ $res = $this->db->query($builder
+ ->generateUpdateBatchQuery($referenceColumn));
+
+ $this->db->getQueryBuilder()->resetStructure();
+
+ return $res->numRows() > 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function delete(?string $table = null, ?array $conditions = []): bool
+ {
+ $builder = $this->db->getQueryBuilder();
+
+ if (!empty($conditions)) {
+ foreach ($conditions as $column => $value) {
+ if (is_string($column)) {
+ $builder->where($column, $value);
+ } else {
+ $builder->where($value);
+ }
+ }
+ }
+ !empty($table) && $builder->from($table);
+
+ $res = $this->db->query($builder->generateDeleteQuery());
+
+ return $res->numRows() > 0;
+ }
+
+}
diff --git a/src/DBAL/Database.php b/src/DBAL/Database.php
new file mode 100644
index 0000000..22b6b5c
--- /dev/null
+++ b/src/DBAL/Database.php
@@ -0,0 +1,292 @@
+ true,
+ 'queryLog' => false,
+ ];
+
+ private ResultInterface $lastResult;
+
+ private array $queryLogs = [];
+
+ private array $errors = [];
+
+ public function __construct(ConnectionInterface $connection, QueryBuilderInterface $builder)
+ {
+ $this->connection = $connection;
+ $this->builder = $builder;
+ $this->queryOptions = array_merge($this->queryOptions, $connection->getCredentials());
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function __call(string $name, array $arguments)
+ {
+ if (method_exists($this->connection, $name)) {
+ $res = $this->connection->{$name}(...$arguments);
+
+ return ($res instanceof ConnectionInterface) ? $this : $res;
+ }
+
+ if (method_exists($this->builder, $name)) {
+ $res = $this->connection->{$name}(...$arguments);
+
+ return ($res instanceof QueryBuilderInterface) ? $this : $res;
+ }
+
+ throw new Exception("There is no method called \"" . $name . "\"");
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getErrors(): array
+ {
+ return $this->errors ?? [];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getQueryBuilder(): QueryBuilderInterface
+ {
+ return $this->builder;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getConnection(): ConnectionInterface
+ {
+ return $this->connection;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function builder(): QueryBuilderInterface
+ {
+ return $this->getQueryBuilder()->newBuilder();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function newInstance(ConnectionInterface|array $connectionOrCredentials, ?QueryBuilderInterface $builder = null): self
+ {
+ return new self(($connectionOrCredentials instanceof ConnectionInterface) ? $connectionOrCredentials : new Connection($connectionOrCredentials), $builder ?? $this->builder);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get(?string $table = null, ?array $selection = null, ?array $conditions = null): ResultInterface
+ {
+ !empty($table) && $this->getQueryBuilder()->addFrom($table);
+ $res = $this->query($this->getQueryBuilder()->generateSelectQuery($selection ?? [], $conditions ?? []));
+ $this->getQueryBuilder()
+ ->resetStructure();
+
+ return $res;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function query(string $rawSQL, ?array $arguments = null, ?array $options = null): ResultInterface
+ {
+ $arguments = array_merge($this->getQueryBuilder()->getParameter()->all(), ($arguments ?? []));
+ $options = array_merge($this->queryOptions, ($options ?? []));
+
+ try {
+ $timerStart = $options['profiler'] ? microtime(true) : 0;
+ $stmt = $this->getConnection()->getPDO()->prepare($rawSQL);
+ if ($stmt === false) {
+ throw new SQLQueryExecuteException('The SQL query could not be prepared.');
+ }
+ if (!empty($arguments)) {
+ foreach ($arguments as $key => $value) {
+ $stmt->bindValue($key, $value, $this->__getValueBindType($value));
+ }
+ }
+ if ($options['parameterReset']) {
+ $this->getQueryBuilder()
+ ->getParameter()
+ ->reset();
+ }
+ $execute = $stmt->execute();
+ if ($options['queryLog']) {
+ $timer = round((microtime(true) - $timerStart), 5);
+ $this->queryLogs[] = [
+ 'query' => $rawSQL,
+ 'time' => $timer,
+ 'args' => $arguments,
+ ];
+ }
+ if ($execute === false) {
+ throw new SQLQueryExecuteException('The SQL query could not be executed.');
+ }
+ $errorCode = $stmt->errorCode();
+ if($errorCode !== null && !empty(trim($errorCode, "0 \t\n\r\0\x0B"))){
+ $errorInfo = $stmt->errorInfo();
+ if(isset($errorInfo[2])){
+ $msg = $errorCode . ' - ' . $errorInfo[2];
+ $this->errors[] = $msg;
+ !empty($options['log']) && $this->createLog(($msg . ' SQL : ' . $rawSQL), $options['log']);
+ }
+ }
+ $this->lastResult = new Result($stmt);
+
+ return !empty($options['fetch_mode'])
+ ? $this->lastResult->setFetchMode($options['fetch_mode'])
+ : $this->lastResult;
+
+ } catch (Exception $e) {
+ $message = $e->getMessage();
+ $sqlQuery = empty($arguments) ? $rawSQL : strtr($rawSQL, $arguments);
+ !empty($options['log']) && $this->createLog(($message . ' SQL : ' . $sqlQuery), $options['log']);
+ if ($options['debug'] === true) {
+ $message .= ' SQL : ' . $sqlQuery;
+ }
+ if ($options['parameterReset']) {
+ $this->getQueryBuilder()
+ ->getParameter()
+ ->reset();
+ }
+ throw new SQLQueryExecuteException($message, (int)$e->getCode());
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function count(): int
+ {
+ $builder = $this->getQueryBuilder()->clone()
+ ->resetStructure('select', false);
+
+ $builder->selectCount('*', 'row_count');
+
+ $res = $this->query($builder->generateSelectQuery(), null, [
+ 'parameterReset' => false,
+ ]);
+
+ return $res->asAssoc()->row()['row_count'] ?? 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function insertId(): int
+ {
+ $id = $this->getConnection()->getPDO()->lastInsertId();
+
+ return $id === false ? 0 : (int)$id;
+ }
+
+
+ /**
+ * @inheritDoc
+ */
+ public function enableQueryLog(): DatabaseInterface
+ {
+ $this->queryOptions['queryLog'] = true;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function disableQueryLog(): DatabaseInterface
+ {
+ $this->queryOptions['queryLog'] = false;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getQueryLogs(): array
+ {
+ return $this->queryLogs;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function transaction(Closure $closure, int $attempt = 1, bool $testMode = false): bool
+ {
+ $attempt < 1 && $attempt = 1;
+ $res = false;
+ for ($i = 0; $i < $attempt; ++$i) {
+ try {
+ $this->getConnection()->beginTransaction($testMode);
+ call_user_func_array($closure, [$this]);
+ $res = $testMode ? $this->getConnection()->rollBack() : $this->getConnection()->commit();
+ break;
+ } catch (Throwable $e) {
+ $res = $this->getConnection()->rollBack();
+ $this->createLog('Transaction Rollback : ' . $e->getMessage());
+ }
+ }
+
+ return $res;
+ }
+
+ protected function __getValueBindType($value): int
+ {
+ return match (true) {
+ is_bool($value), is_int($value) => PDO::PARAM_INT,
+ is_null($value) => PDO::PARAM_NULL,
+ default => PDO::PARAM_STR,
+ };
+ }
+
+ private function createLog(string $message, mixed $handler = null): void
+ {
+ $handler === null && $handler = $this->getConnection()->getCredentials('log');
+ if (empty($handler)) {
+ return;
+ }
+ if (is_callable($handler)) {
+ call_user_func_array($handler, [$message]);
+ } else if (is_object($handler) && method_exists($handler, 'critical')) {
+ $handler->critical($message);
+ } else if (is_string($handler)) {
+ $path = strtr($handler, [
+ '{timestamp}' => time(),
+ '{date}' => date("Y-m-d"),
+ '{year}' => date("Y"),
+ '{month}' => date("m"),
+ '{day}' => date("d"),
+ '{hour}' => date("H"),
+ '{minute}' => date("i"),
+ '{second}' => date("s"),
+ ]);
+ @file_put_contents($path, $message, FILE_APPEND);
+ }
+
+ }
+
+}
diff --git a/src/DBAL/Exceptions/SQLQueryExecuteException.php b/src/DBAL/Exceptions/SQLQueryExecuteException.php
new file mode 100644
index 0000000..c80352a
--- /dev/null
+++ b/src/DBAL/Exceptions/SQLQueryExecuteException.php
@@ -0,0 +1,9 @@
+\PDO::FETCH_*
+ * @return self
+ */
+ public function setFetchMode(int $mode): self;
+
+ /**
+ * @param string $class
+ * @return self
+ */
+ public function asClass(string $class = Entity::class): self;
+
+ /**
+ * @return self
+ */
+ public function asObject(): self;
+
+
+ /**
+ * @return self
+ */
+ public function asAssoc(): self;
+
+ /**
+ * @return self
+ */
+ public function asArray(): self;
+
+ /**
+ * @return self
+ */
+ public function asLazy(): self;
+
+ /**
+ * @return array|object|false
+ */
+ public function row(): array|object|false;
+
+ /**
+ * @return array[]|object[]|false
+ */
+ public function rows(): array|false;
+
+
+}
\ No newline at end of file
diff --git a/src/DBAL/Result.php b/src/DBAL/Result.php
new file mode 100644
index 0000000..cf2febe
--- /dev/null
+++ b/src/DBAL/Result.php
@@ -0,0 +1,144 @@
+setStatement($statement);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setStatement(PDOStatement $statement): self
+ {
+ $this->statement = $statement;
+ $this->query = $statement->queryString;
+ $this->numRows = $statement->rowCount();
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function withStatement(PDOStatement $statement): self
+ {
+ return new self($statement);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getStatement(): PDOStatement
+ {
+ return $this->statement;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function numRows(): int
+ {
+ return $this->numRows;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function query(): string
+ {
+ return $this->query;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setFetchMode(int $mode): self
+ {
+ $this->getStatement()->setFetchMode($mode);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function asClass(string $class = Entity::class): self
+ {
+ $this->getStatement()->setFetchMode(PDO::FETCH_CLASS, $class);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function asObject(): self
+ {
+ $this->getStatement()->setFetchMode(PDO::FETCH_OBJ);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function asAssoc(): self
+ {
+ $this->getStatement()->setFetchMode(PDO::FETCH_ASSOC);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function asArray(): self
+ {
+ $this->getStatement()->setFetchMode(PDO::FETCH_BOTH);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function asLazy(): self
+ {
+ $this->getStatement()->setFetchMode(PDO::FETCH_LAZY);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function row(): array|object|false
+ {
+ return $this->getStatement()->fetch();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function rows(): array|false
+ {
+ return $this->getStatement()->fetchAll();
+ }
+
+}
diff --git a/src/Database.php b/src/Database.php
deleted file mode 100644
index 52a1440..0000000
--- a/src/Database.php
+++ /dev/null
@@ -1,844 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.1
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database;
-
-use InitPHP\Database\Utils\{Pagination,
- Datatables};
-use InitPHP\Database\Exceptions\{QueryBuilderException,
- QueryGeneratorException,
- WritableException,
- ReadableException,
- UpdatableException,
- DeletableException,
- SQLQueryExecuteException,
- ConnectionException};
-use \InitPHP\Database\Helpers\{Helper, Parameters, Validation};
-use \PDO;
-use \RuntimeException;
-use \PDOException;
-use \Exception;
-use \InvalidArgumentException;
-use \Closure;
-
-use function microtime;
-use function round;
-use function count;
-use function current;
-use function substr;
-use function array_merge;
-use function is_numeric;
-use function is_bool;
-use function is_string;
-use function is_int;
-use function is_null;
-use function is_callable;
-use function is_object;
-use function is_array;
-use function in_array;
-use function trim;
-use function ltrim;
-use function stripslashes;
-use function serialize;
-use function strtr;
-use function str_replace;
-use function strtoupper;
-use function call_user_func_array;
-use function file_put_contents;
-use function time;
-use function date;
-use function method_exists;
-
-use const COUNT_RECURSIVE;
-use const FILE_APPEND;
-
-class Database extends QueryBuilder
-{
-
- public const ENTITY = 0;
- public const ASSOC = 1;
- public const ARRAY = 2;
- public const OBJECT = 3;
- public const LAZY = 4;
-
- private PDO $_pdo;
-
- private bool $_isGlobal = false;
-
- private static PDO $_globalPDO;
-
- private array $_credentials = [
- 'dsn' => '',
- 'username' => 'root',
- 'password' => null,
- 'charset' => 'utf8mb4',
- 'collation' => 'utf8mb4_unicode_ci',
- 'tableSchema' => null,
- 'tableSchemaID' => null,
- 'entity' => Entity::class,
- 'createdField' => null,
- 'updatedField' => null,
- 'deletedField' => null,
- 'allowedFields' => null,
- 'timestampFormat' => 'c',
- 'validation' => [
- 'methods' => [],
- 'messages' => [],
- 'labels' => [],
- ],
- 'readable' => true,
- 'writable' => true,
- 'deletable' => true,
- 'updatable' => true,
- 'return' => null,
- 'debug' => false,
- 'log' => null,
- 'profiler' => false,
- ];
-
- private Result $_last;
-
- private array $_transaction = [
- 'status' => false,
- 'enable' => false,
- 'testMode' => false,
- ];
-
- private array $_errors = [];
-
- private array $_queryLogs = [];
-
- private Validation $_validation;
-
- public function __construct(array $credentials = [])
- {
- $this->setCredentials($credentials);
- $this->_validation = new Validation($this->_credentials['validation']['methods'], $this->_credentials['validation']['messages'], $this->_credentials['validation']['labels'], $this);
- }
-
- public function __call($name, $arguments)
- {
- if(Helper::str_starts_with($name, 'findBy') === FALSE){
- throw new RuntimeException('There is no "' . $name . '" method.');
- }
- $this->where(Helper::camelCaseToSnakeCase(substr($name, 6)), current($arguments));
-
- return $this;
- }
-
- final public function newInstance(array $credentials = []): Database
- {
- $instance = new Database(empty($credentials) ? $this->_credentials : array_merge($this->_credentials, $credentials));
- $instance->_isGlobal = false;
-
- return $instance;
- }
-
- final public function clone(): Database
- {
- $clone = new self($this->_credentials);
- $clone->_isGlobal = $this->_isGlobal;
-
- return $clone;
- }
-
- final public function enableQueryProfiler(): void
- {
- $this->_credentials['profiler'] = true;
- }
-
- final public function disableQueryProfiler(): void
- {
- $this->_credentials['profiler'] = false;
- }
-
- final public function getProfilerQueries(): array
- {
- return $this->_queryLogs;
- }
-
- final public function setCredentials(array $credentials): self
- {
- $this->_credentials = array_merge($this->_credentials, $credentials);
- return $this;
- }
-
- final public function getCredentials(?string $key = null)
- {
- return ($key === null) ? $this->_credentials : ($this->_credentials[$key] ?? null);
- }
-
- final public function isError(): bool
- {
- return !empty($this->_errors);
- }
-
- final public function getError(): array
- {
- return $this->_errors;
- }
-
- final public function connectionAsGlobal(): void
- {
- self::$_globalPDO = $this->getPDO();
- $this->_isGlobal = true;
- }
-
- public function getPDO(): PDO
- {
- if(isset(self::$_globalPDO) && $this->_isGlobal){
- return self::$_globalPDO;
- }
- if(!isset($this->_pdo)){
- try {
- $options = [
- PDO::ATTR_EMULATE_PREPARES => false,
- PDO::ATTR_PERSISTENT => true,
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
- ];
- switch ($this->_credentials['return']) {
- case null:
- case self::ARRAY:
- $options[PDO::ATTR_DEFAULT_FETCH_MODE] = PDO::FETCH_BOTH;
- break;
- case self::ASSOC:
- $options[PDO::ATTR_DEFAULT_FETCH_MODE] = PDO::FETCH_ASSOC;
- break;
- case self::ENTITY:
- $options[PDO::ATTR_DEFAULT_FETCH_MODE] = PDO::FETCH_CLASS;
- break;
- case self::OBJECT:
- $options[PDO::ATTR_DEFAULT_FETCH_MODE] = PDO::FETCH_OBJ;
- break;
- case self::LAZY:
- $options[PDO::ATTR_DEFAULT_FETCH_MODE] = PDO::FETCH_LAZY;
- break;
- default:
- $options[PDO::ATTR_DEFAULT_FETCH_MODE] = PDO::FETCH_BOTH;
- }
-
- $this->_pdo = new PDO($this->_credentials['dsn'], $this->_credentials['username'], $this->_credentials['password'], $options);
- if(!empty($this->_credentials['charset'])){
- if(!empty($this->_credentials['collation'])) {
- $this->_pdo->exec("SET NAMES '" . $this->_credentials['charset'] . "' COLLATE '" . $this->_credentials['collation'] . "'");
- }
- $this->_pdo->exec("SET CHARACTER SET '" . $this->_credentials['charset'] . "'");
- }
- } catch (PDOException $e) {
- throw new ConnectionException($e->getMessage(), (int)$e->getCode());
- }
- }
-
- return $this->_pdo;
- }
-
- final public function withPDO(PDO $pdo): self
- {
- $with = clone $this;
- $with->_pdo = $pdo;
- $with->_isGlobal = false;
-
- return $with;
- }
-
- final public function getSchemaID(): ?string
- {
- return $this->_credentials['tableSchemaID'];
- }
-
- final public function setSchemaID(?string $column): self
- {
- $this->_credentials['tableSchemaID'] = $column;
-
- return $this;
- }
-
- final public function withSchemaID(?string $column): self
- {
- $with = clone $this;
-
- return $with->setSchemaID($column);
- }
-
- final public function getSchema(): ?string
- {
- return $this->_credentials['tableSchema'];
- }
-
- final public function setSchema(?string $table): self
- {
- $this->_credentials['tableSchema'] = $table;
-
- return $this;
- }
-
- final public function withSchema(?string $table): self
- {
- $with = clone $this;
-
- return $with->setSchema($table);
- }
-
- final public function beginTransaction(bool $testMode = false): bool
- {
- $this->_transaction = [
- 'status' => true,
- 'enable' => true,
- 'testMode' => $testMode,
- ];
-
- return (bool)$this->getPDO()->beginTransaction();
- }
-
- final public function completeTransaction(): bool
- {
- return ($this->_transaction['status'] === FALSE || $this->_transaction['testMode'] === TRUE) ? $this->rollBack() : $this->commit();
- }
-
- final public function commit(): bool
- {
- $this->_transaction = [
- 'status' => false,
- 'enable' => false,
- 'testMode' => false,
- ];
-
- return (bool)$this->getPDO()->commit();
- }
-
- final public function rollBack(): bool
- {
- $this->_transaction = [
- 'status' => false,
- 'enable' => false,
- 'testMode' => false,
- ];
-
- return (bool)$this->getPDO()->rollBack();
- }
-
- final public function transaction(Closure $closure): bool
- {
- try {
- $this->beginTransaction(false);
- call_user_func_array($closure, [$this]);
- $res = $this->completeTransaction();
- } catch (Exception $e) {
- $res = $this->rollBack();
- }
-
- return $res;
- }
-
- final public function connection(array $credentials = []): self
- {
- return $this->newInstance($credentials);
- }
-
- /**
- * @param $value
- * @return false|float|int|string
- */
- final public function escape_str($value)
- {
- if(is_numeric($value)){
- return $value;
- }
- if(is_bool($value)){
- return $value === FALSE ? 'FALSE' : 'TRUE';
- }
- if($value === null){
- return 'NULL';
- }
- if(is_string($value)){
- $value = str_replace("\\", "", trim(stripslashes($value), "' \\\t\n\r\0\x0B"));
- return "'" . str_replace("'", "\\'", $value) . "'";
- }
- if(is_object($value)){
- return serialize($value);
- }
- if(is_array($value)){
- return serialize($value);
- }
- return false;
- }
-
- /**
- * @param string $sqlQuery
- * @param array $parameters
- * @return Result
- */
- final public function query(string $sqlQuery, array $parameters = []): Result
- {
- $arguments = Parameters::get(true);
- $parameters = empty($parameters) ? $arguments : array_merge($arguments, $parameters);
- try {
- $timerStart = $this->_credentials['profiler'] ? microtime(true) : 0;
- $stmt = $this->getPDO()->prepare($sqlQuery);
- if($stmt === FALSE){
- throw new Exception('The SQL query could not be prepared.');
- }
- if(!empty($parameters)){
- foreach ($parameters as $key => $value) {
- $stmt->bindValue(':' . ltrim($key, ':'), $value, $this->_bind($value));
- }
- }
- $execute = $stmt->execute();
- if ($this->_credentials['profiler']) {
- $timer = round((microtime(true) - $timerStart), 5);
- $this->_queryLogs[] = [
- 'query' => $sqlQuery,
- 'time' => $timer,
- 'args' => $parameters,
- ];
- }
- if($execute === FALSE){
- throw new Exception('The SQL query could not be executed.');
- }
- $errorCode = $stmt->errorCode();
- if($errorCode !== null && !empty(trim($errorCode, "0 \t\n\r\0\x0B"))){
- $errorInfo = $stmt->errorInfo();
- if(isset($errorInfo[2])){
- $this->_errors[] = $errorCode . ' - ' . $errorInfo[2];
- }
- }
- $this->_last = new Result($stmt);
- if($this->_credentials['return'] === null){
- return $this->_last;
- }
- switch ($this->_credentials['return']) {
- case self::ASSOC:
- return $this->_last->asAssoc();
- case self::ARRAY:
- return $this->_last->asArray();
- case self::ENTITY:
- return $this->_last->asEntity($this->_credentials['entity'] ?? Entity::class);
- case self::OBJECT:
- return $this->_last->asObject();
- case self::LAZY:
- return $this->_last->asLazy();
- default:
- return $this->_last;
- }
- } catch (Exception $e) {
- $message = $e->getMessage();
- $sqlMessage = 'SQL : '
- . (empty($parameters) ? $sqlQuery : strtr($sqlQuery, $parameters));
- if(!empty($this->_credentials['log'])){
- $this->_logCreate($message . ' ' . $sqlMessage);
- }
- if($this->_credentials['debug'] === TRUE){
- $message .= $message . ' ' . $sqlMessage;
- }
- throw new SQLQueryExecuteException($message, (int)$e->getCode());
- }
- }
-
- /**
- * @return int
- */
- final public function insertId(): int
- {
- $id = $this->getPDO()->lastInsertId();
-
- return $id === FALSE ? 0 : (int)$id;
- }
-
- /**
- * @param string|null $table
- * @return Result
- * @throws QueryGeneratorException
- */
- final public function get(?string $table = null): Result
- {
- if(!empty($table)){
- $this->addFrom($table);
- }
- $res = $this->query($this->generateSelectQuery());
- $this->reset();
-
- return $res;
- }
-
- /**
- * @param array|null $set
- * @return bool
- * @throws QueryBuilderException
- * @throws QueryGeneratorException
- */
- public function create(?array $set = null)
- {
- if($this->_credentials['writable'] === FALSE){
- throw new WritableException('');
- }
-
- if($set !== null && count($set) === count($set, COUNT_RECURSIVE)){
- $this->_validation->setData($set);
- foreach ($set as $column => $value) {
- if($this->_validation->validation($column, null) === FALSE){
- $this->_errors[] = $this->_validation->getError();
- return false;
- }
- $this->set($column, $value);
- }
- }
-
- $res = $this->query($this->generateInsertQuery());
- $this->reset();
-
- return $res->numRows() > 0;
- }
-
- /**
- * @param array|null $set
- * @return bool
- * @throws QueryBuilderException
- * @throws QueryGeneratorException
- */
- public function insert(?array $set = null)
- {
- return $this->create($set);
- }
-
- /**
- * @param array|null $set
- * @return bool
- * @throws QueryBuilderException
- * @throws QueryGeneratorException
- */
- public function createBatch(?array $set = null)
- {
- if($this->_credentials['writable'] === FALSE){
- throw new WritableException('');
- }
-
- if ($set !== null && count($set) !== count($set, COUNT_RECURSIVE)) {
- foreach ($set as $row) {
- $data = [];
- $this->_validation->setData($row);
- foreach ($row as $column => $value) {
- if ($this->_validation->validation($column, null) === FALSE) {
- $this->_errors[] = $this->_validation->getError();
- return false;
- }
- $data[$column] = $value;
- }
- if (empty($data)) {
- continue;
- }
- $this->set($data);
- }
- }
-
- $res = $this->query($this->generateBatchInsertQuery());
- $this->reset();
-
- return $res->numRows() > 0;
- }
-
-
- /**
- * @param array $set
- * @return bool
- * @throws QueryBuilderException
- * @throws QueryGeneratorException
- */
- public function insertBatch(array $set)
- {
- return $this->createBatch($set);
- }
-
- /**
- * @param array $selector
- * @param array $conditions
- * @param array $parameters
- * @return Result
- */
- public function read(array $selector = [], array $conditions = [], array $parameters = [])
- {
- if($this->_credentials['readable'] === FALSE){
- throw new ReadableException('');
- }
- Parameters::merge($parameters);
- $query = $this->generateSelectQuery($selector, $conditions);
-
- $res = $this->query($query);
- $this->reset();
-
- return $res;
- }
-
- /**
- * @param array $selector
- * @param array $conditions
- * @param array $parameters
- * @return Result
- * @throws QueryGeneratorException
- */
- public function readOne(array $selector = [], array $conditions = [], array $parameters = [])
- {
- if($this->_credentials['readable'] === FALSE){
- throw new ReadableException('');
- }
- Parameters::merge($parameters);
- $query = $this->limit(1)
- ->generateSelectQuery($selector, $conditions);
-
- $res = $this->query($query);
- $this->reset();
-
- return $res;
- }
-
- /**
- * @param array|null $set
- * @return bool
- * @throws QueryBuilderException
- * @throws QueryGeneratorException
- */
- public function update(?array $set = null)
- {
- if($this->_credentials['updatable'] === FALSE){
- throw new UpdatableException('');
- }
- if ($set !== null) {
- $schemaID = $this->getSchemaID();
- $this->_validation->setData($set);
- foreach ($set as $column => $value) {
- if ($this->_validation->validation($column, $schemaID) === FALSE) {
- $this->_errors[] = $this->_validation->getError();
- return false;
- }
- }
- $this->set($set);
- }
-
- $res = $this->query($this->generateUpdateQuery());
- $this->reset();
-
- return $res->numRows() > 0;
- }
-
- /**
- * @param string $referenceColumn
- * @param array|null $set
- * @return bool
- * @throws Exceptions\QueryBuilderException
- * @throws Exceptions\QueryGeneratorException
- */
- public function updateBatch(string $referenceColumn, ?array $set = null)
- {
- if($this->_credentials['updatable'] === FALSE){
- throw new UpdatableException('');
- }
-
- if ($set !== null) {
- foreach ($set as $data) {
- $this->_validation->setData($data);
- foreach ($data as $column => $value) {
- if (in_array($column, [$this->getSchemaID(), $referenceColumn])) {
- continue;
- }
- if ($this->_validation->validation($column, $this->getSchemaID()) === FALSE) {
- $this->_errors[] = $this->_validation->getError();
- return false;
- }
- }
- $this->set($data);
- }
- }
-
- $res = $this->query($this->generateUpdateBatchQuery($referenceColumn));
- $this->reset();
-
- return $res->numRows() > 0;
- }
-
-
- /**
- * @param array $conditions
- * @return bool
- * @throws QueryBuilderException
- * @throws QueryGeneratorException
- */
- public function delete(array $conditions = [])
- {
- if($this->_credentials['deletable'] === FALSE){
- throw new DeletableException('');
- }
- foreach ($conditions as $column => $value) {
- if (is_string($column)) {
- $this->where($column, $value);
- } else {
- $this->where($value);
- }
- }
- if(!empty($this->_credentials['deletedField'])){
- if($this->isOnlyDeletes !== FALSE){
- $this->isNot($this->_credentials['deletedField'], null);
- $query = $this->generateDeleteQuery();
- $this->isOnlyDeletes = false;
- }else{
- $this->is($this->_credentials['deletedField'], null)
- ->set($this->_credentials['deletedField'], date($this->_credentials['timestampFormat']), false);
- $query = $this->generateUpdateQuery();
- }
- }else{
- $query = $this->generateDeleteQuery();
- }
- $res = $this->query($query);
-
- $this->reset();
-
- return $res->numRows() > 0;
- }
-
- /**
- * @param int $limit
- * @param int $offset
- * @return Result
- */
- public function all(int $limit = 100, int $offset = 0): Result
- {
- return $this->limit($limit)
- ->offset($offset)
- ->read();
- }
-
- /**
- * @return Result
- * @throws QueryGeneratorException
- */
- public function first()
- {
- return $this->readOne();
- }
-
- public function group(Closure $group, string $logical = 'AND'): self
- {
- $logical = str_replace(['&&', '||'], ['AND', 'OR'], strtoupper($logical));
- if(!in_array($logical, ['AND', 'OR'], true)){
- throw new InvalidArgumentException('Logical operator OR, AND, && or || it could be.');
- }
-
- $clone = clone $this;
- $clone->reset();
-
- call_user_func_array($group, [$clone]);
-
- if($where = $clone->__generateWhereQuery()){
- $this->_STRUCTURE['where'][$logical][] = '(' . $where . ')';
- }
-
- if($having = $clone->__generateHavingQuery()){
- $this->_STRUCTURE['having'][$logical][] = '(' . $having . ')';
- }
- unset($clone);
- return $this;
- }
-
- public function onlyDeleted(): self
- {
- $this->isOnlyDeletes = true;
-
- return $this;
- }
-
- public function onlyUndeleted(): self
- {
- $this->isOnlyDeletes = false;
-
- return $this;
- }
-
- /**
- * QueryBuilder resetlemeden SELECT cümlesi kurar ve satır sayısını döndürür.
- *
- * @return int
- */
- public function count(): int
- {
- $select = $this->_STRUCTURE['select'];
- $this->_STRUCTURE['select'] = ['COUNT(*) AS row_count'];
- $this->__generateSoftDeleteQuery(false);
- $parameters = Parameters::get(false);
- $res = $this->query($this->generateSelectQuery());
- $count = $res->toArray()['row_count'] ?? 0;
- unset($res);
- Parameters::merge($parameters);
- $this->_STRUCTURE['select'] = $select;
-
- return (int)$count;
- }
-
- public function pagination(int $page = 1, int $per_page_limit = 10, string $link = '?page={page}'): Pagination
- {
- $total_row = $this->count();
- $this->offset(($page - 1) * $per_page_limit)
- ->limit($per_page_limit);
- $res = $this->query($this->generateSelectQuery());
- $this->reset();
-
- return new Pagination($res, $page, $per_page_limit, $total_row, $link);
- }
-
- public function datatables(array $columns, int $method = Datatables::GET_REQUEST): string
- {
- return (new Datatables($this, $columns, $method))->__toString();
- }
-
- final public function isAllowedFields(string $column): bool
- {
- return empty($this->_credentials['allowedFields']) || in_array($column, $this->_credentials['allowedFields']);
- }
-
- protected function _logCreate(string $message): void
- {
- if(empty($this->_credentials['log'])){
- return;
- }
- if(is_callable($this->_credentials['log'])){
- call_user_func_array($this->_credentials['log'], [$message]);
- return;
- }
- if(is_string($this->_credentials['log'])){
- $path = strtr($this->_credentials['log'], [
- '{timestamp}' => time(),
- '{date}' => date("Y-m-d"),
- '{year}' => date("Y"),
- '{month}' => date("m"),
- '{day}' => date("d"),
- '{hour}' => date("H"),
- '{minute}' => date("i"),
- '{second}' => date("s"),
- ]);
- @file_put_contents($path, $message, FILE_APPEND);
- return;
- }
- if(is_object($this->_credentials['log']) && method_exists($this->_credentials['log'], 'critical')){
- $this->_credentials['log']->critical($message);
- }
- }
-
- private function _bind($value)
- {
- if(is_bool($value) || is_int($value)){
- return PDO::PARAM_INT;
- }
- if(is_null($value)){
- return PDO::PARAM_NULL;
- }
- return PDO::PARAM_STR;
- }
-
-}
diff --git a/src/Exceptions/ConnectionException.php b/src/Exceptions/ConnectionException.php
deleted file mode 100644
index fc7d56f..0000000
--- a/src/Exceptions/ConnectionException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class ConnectionException extends \RuntimeException
-{
-}
diff --git a/src/Exceptions/DeletableException.php b/src/Exceptions/DeletableException.php
deleted file mode 100644
index fb08baf..0000000
--- a/src/Exceptions/DeletableException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class DeletableException extends \RuntimeException
-{
-}
diff --git a/src/Exceptions/ModelCallbacksException.php b/src/Exceptions/ModelCallbacksException.php
deleted file mode 100644
index 778609e..0000000
--- a/src/Exceptions/ModelCallbacksException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class ModelCallbacksException extends ModelException
-{
-}
diff --git a/src/Exceptions/ModelException.php b/src/Exceptions/ModelException.php
deleted file mode 100644
index 324fa40..0000000
--- a/src/Exceptions/ModelException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class ModelException extends \RuntimeException
-{
-}
diff --git a/src/Exceptions/ModelRelationsException.php b/src/Exceptions/ModelRelationsException.php
deleted file mode 100644
index c6a610d..0000000
--- a/src/Exceptions/ModelRelationsException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class ModelRelationsException extends ModelException
-{
-}
diff --git a/src/Exceptions/QueryBuilderException.php b/src/Exceptions/QueryBuilderException.php
deleted file mode 100644
index 00c39d4..0000000
--- a/src/Exceptions/QueryBuilderException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.8
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class QueryBuilderException extends \Exception
-{
-}
diff --git a/src/Exceptions/QueryGeneratorException.php b/src/Exceptions/QueryGeneratorException.php
deleted file mode 100644
index 4edb6a2..0000000
--- a/src/Exceptions/QueryGeneratorException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.8
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class QueryGeneratorException extends QueryBuilderException
-{
-}
diff --git a/src/Exceptions/ReadableException.php b/src/Exceptions/ReadableException.php
deleted file mode 100644
index 38eba70..0000000
--- a/src/Exceptions/ReadableException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class ReadableException extends \RuntimeException
-{
-}
diff --git a/src/Exceptions/SQLQueryExecuteException.php b/src/Exceptions/SQLQueryExecuteException.php
deleted file mode 100644
index 83dc91e..0000000
--- a/src/Exceptions/SQLQueryExecuteException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class SQLQueryExecuteException extends \RuntimeException
-{
-}
diff --git a/src/Exceptions/UpdatableException.php b/src/Exceptions/UpdatableException.php
deleted file mode 100644
index 9977ce6..0000000
--- a/src/Exceptions/UpdatableException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class UpdatableException extends \RuntimeException
-{
-}
diff --git a/src/Exceptions/ValidationException.php b/src/Exceptions/ValidationException.php
deleted file mode 100644
index 15711a3..0000000
--- a/src/Exceptions/ValidationException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class ValidationException extends \RuntimeException
-{
-}
diff --git a/src/Exceptions/ValueException.php b/src/Exceptions/ValueException.php
deleted file mode 100644
index a81775d..0000000
--- a/src/Exceptions/ValueException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.8
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class ValueException extends QueryBuilderException
-{
-}
diff --git a/src/Exceptions/WritableException.php b/src/Exceptions/WritableException.php
deleted file mode 100644
index 97d5ca5..0000000
--- a/src/Exceptions/WritableException.php
+++ /dev/null
@@ -1,18 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Exceptions;
-
-class WritableException extends \RuntimeException
-{
-}
diff --git a/src/Facade/DB.php b/src/Facade/DB.php
index 2039a48..224f668 100644
--- a/src/Facade/DB.php
+++ b/src/Facade/DB.php
@@ -7,144 +7,155 @@
* @author Muhammet ŞAFAK
* @copyright Copyright © 2022 Muhammet ŞAFAK
* @license ./LICENSE MIT
- * @version 2.0.7
+ * @version 3.0
* @link https://www.muhammetsafak.com.tr
*/
namespace InitPHP\Database\Facade;
-use InitPHP\Database\{Database, Raw, Result, Utils\Pagination};
+use \InitPHP\Database\Connection\{Connection,
+ Interfaces\ConnectionInterface};
+use \InitPHP\Database\DBAL\{CRUD,
+ Database,
+ Interfaces\CRUDInterface,
+ Interfaces\ResultInterface};
+use \InitPHP\Database\QueryBuilder\{QueryBuilder,
+ RawQuery,
+ Interfaces\ParameterInterface,
+ Interfaces\QueryBuilderInterface};
+use PDO;
+use Closure;
/**
- * @mixin Database
- * @method static Database newInstance(array $credentials = [])
- * @method static bool isError()
- * @method static string[] getError()
- * @method static \PDO getPDO()
- * @method static Database withPDO(\PDO $pdo)
- * @method static null|string getSchemaID()
- * @method static Database setSchemaID(null|string $column)
- * @method static Database withSchemaID(null|string $column)
- * @method static null|string getSchema()
- * @method static Database setSchema(null|string $table)
- * @method static Database withSchema(null|string $table)
+ * @mixin CRUDInterface
+ * @method static mixed getCredentials(?string $key = null, mixed $default = null)
+ * @method static PDO getPDO()
* @method static bool beginTransaction(bool $testMode = false)
* @method static bool completeTransaction()
* @method static bool commit()
* @method static bool rollBack()
- * @method static bool transaction(\Closure $closure)
- * @method static Raw raw(string $rawQuery)
- * @method static Database connection(array $credentials = [])
- * @method static false|float|int|string escape_str(mixed $value)
- * @method static Database setParameter(string $key, mixed $value)
- * @method static Database setParameters(array $parameters = [])
- * @method static Result query(string $sqlQuery, array $parameters = [])
- * @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 bool insert(array $set)
- * @method static bool insertBatch(array $set)
+ * @method static bool disconnect()
+ * @method static CRUDInterface enableQueryLog()
+ * @method static CRUDInterface disableQueryLog()
+ * @method static CRUDInterface getQueryLogs()
+ * @method static QueryBuilderInterface getQueryBuilder()
+ * @method static ConnectionInterface getConnection()
+ * @method static QueryBuilderInterface builder()
+ * @method static CRUDInterface newInstance(ConnectionInterface|array $connectionOrCredentials, ?QueryBuilderInterface $builder = null)
+ * @method static ResultInterface get(?string $table = null, ?array $selection = null, ?array $conditions = null)
+ * @method static ResultInterface query(string $rawSQL, ?array $arguments = null, ?array $options = null)
* @method static int count()
- * @method static Pagination pagination(int $page = 1, int $per_page_limit = 10, string $link = '?page={page}')
- * @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)
- * @method static bool delete(array $conditions = [])
- * @method static Result all(int $limit = 100, int $offset = 0)
- * @method static Database onlyDeleted()
- * @method static Database onlyUndeleted()
- * @method static void reset()
- * @method static Database select(string|Raw ...$columns)
- * @method static Database selectCount(string $column, ?string $alias = null)
- * @method static Database selectMax(string $column, ?string $alias = null)
- * @method static Database selectMin(string $column, ?string $alias = null)
- * @method static Database selectAvg(string $column, ?string $alias = null)
- * @method static Database selectAs(string $column, ?string $alias = null)
- * @method static Database selectUpper(string $column, ?string $alias = null)
- * @method static Database selectLower(string $column, ?string $alias = null)
- * @method static Database selectLength(string $column, ?string $alias = null)
- * @method static Database selectMid(string $column, int $offset, int $length, ?string $alias = null)
- * @method static Database selectLeft(string $column, int $length, ?string $alias = null)
- * @method static Database selectRight(string $column, int $length, ?string $alias = null)
- * @method static Database selectDistinct(string $column, ?string $alias = null)
- * @method static Database selectCoalesce(string $column, $default = '0', ?string $alias = null)
- * @method static Database selectSum(string $column, ?string $alias = null)
- * @method static Database selectConcat(?string $alias = null, string ...$columnOrStr)
- * @method static Database from(string|Raw ...$tables)
- * @method static Database table(string|Raw $table)
- * @method static Database groupBy(string|Raw ...$columns)
- * @method static Database join(string|Raw $table, ?string $onStmt = null, string $type = 'INNER')
- * @method static Database selfJoin(string $table, string $onStmt)
- * @method static Database innerJoin(string $table, string $onStmt)
- * @method static Database leftJoin(string $table, string $onStmt)
- * @method static Database rightJoin(string $table, string $onStmt)
- * @method static Database leftOuterJoin(string $table, string $onStmt)
- * @method static Database rightOuterJoin(string $table, string $onStmt)
- * @method static Database naturalJoin(string $table)
- * @method static Database orderBy(string $column, string $soft = 'ASC')
- * @method static Database where(string|Raw $column, mixed $value = null, string $mark = '=', string $logical = 'AND')
- * @method static Database having(string|Raw $column, mixed $value, string $mark = '=', string $logical = 'AND')
- * @method static Database andWhere(string|Raw $column, $value, string $mark = '=')
- * @method static Database orWhere(string|Raw $column, $value, string $mark = '=')
- * @method static Database between(string $column, array $values, string $logical = 'AND')
- * @method static Database orBetween(string $column, array $values)
- * @method static Database andBetween(string $column, array $values)
- * @method static Database notBetween(string $column, array $values, string $logical = 'AND')
- * @method static Database orNotBetween(string $column, array $values)
- * @method static Database andNotBetween(string $column, array $values)
- * @method static Database findInSet(string $column, $value, string $logical = 'AND')
- * @method static Database orFindInSet(string $column, $value)
- * @method static Database andFindInSet(string $column, $value)
- * @method static Database notFindInSet(string $column, $value, string $logical = 'AND')
- * @method static Database andNotFindInSet(string $column, $value)
- * @method static Database orNotFindInSet(string $column, $value)
- * @method static Database in(string $column, array $value, string $logical = 'AND')
- * @method static Database orIn(string $column, array $value)
- * @method static Database andIn(string $column, array $value)
- * @method static Database notIn(string $column, array $value, string $logical = 'AND')
- * @method static Database orNotIn(string $column, array $value)
- * @method static Database andNotIn(string $column, array $value)
- * @method static Database regexp(string $column, string $value, string $logical = 'AND')
- * @method static Database andRegexp(string $column, string $value)
- * @method static Database orRegexp(string $column, string $value)
- * @method static Database like(string $column, $value, string $logical = 'AND')
- * @method static Database orLike(string $column, $value)
- * @method static Database andLike(string $column, $value)
- * @method static Database startLike(string $column, $value, string $logical = 'AND')
- * @method static Database orStartLike(string $column, $value)
- * @method static Database andStartLike(string $column, $value)
- * @method static Database endLike(string $column, $value, string $logical = 'AND')
- * @method static Database orEndLike(string $column, $value)
- * @method static Database andEndLike(string $column, $value)
- * @method static Database notLike(string $column, $value, string $logical = 'AND')
- * @method static Database orNotLike(string $column, $value)
- * @method static Database andNotLike(string $column, $value)
- * @method static Database startNotLike(string $column, $value, string $logical = 'AND')
- * @method static Database orStartNotLike(string $column, $value)
- * @method static Database andStartNotLike(string $column, $value)
- * @method static Database endNotLike(string $column, $value, string $logical = 'AND')
- * @method static Database orEndNotLike(string $column, $value)
- * @method static Database andEndNotLike(string $column, $value)
- * @method static Database soundex(string $column, $value, string $logical = 'AND')
- * @method static Database orSoundex(string $column, $value)
- * @method static Database andSoundex(string $column, $value)
- * @method static Database is(string $column, $value, string $logical = 'AND')
- * @method static Database orIs(string $column, $value = null)
- * @method static Database andIs(string $column, $value = null)
- * @method static Database isNot(string $column, $value = null, string $logical = 'AND')
- * @method static Database orIsNot(string $column, $value = null)
- * @method static Database andIsNot(string $column, $value = null)
- * @method static Database offset(int $offset = 0)
- * @method static Database limit(int $limit)
- * @method static void enableQueryProfiler()
- * @method static void disableQueryProfiler()
- * @method static array getProfilerQueries()
+ * @method static int insertId()
+ * @method static bool transaction(Closure $closure, int $attempt = 1, bool $testMode = false)
+ * @method static bool create(?string $table = null, ?array $set = null)
+ * @method static bool createBatch(?string $table = null, ?array $set = null)
+ * @method static ResultInterface read(?string $table = null, array $selector = [], array $conditions = [], array $parameters = [])
+ * @method static ResultInterface readOne(?string $table = null, array $selector = [], array $conditions = [], array $parameters = [])
+ * @method static bool update(?string $table = null, ?array $set = null)
+ * @method static bool updateBatch(?string $table = null, ?array $set = null, ?string $referenceColumn = null)
+ * @method static bool delete(?string $table = null, ?array $conditions = [])
+ * @method static CRUDInterface newBuilder()
+ * @method static CRUDInterface importQB(array $structure, bool $merge = false)
+ * @method static array exportQB()
+ * @method static ParameterInterface getParameter()
+ * @method static CRUDInterface setParameter(string $key, mixed $value)
+ * @method static CRUDInterface setParameters(array $parameters = [])
+ * @method static CRUDInterface select(string|RawQuery|string[]|RawQuery[] ...$columns)
+ * @method static CRUDInterface selectCount(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectCountDistinct(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectMax(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectMin(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectAvg(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectAs(RawQuery|string $column, string $alias)
+ * @method static CRUDInterface selectUpper(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectLower(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectLength(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectMid(RawQuery|string $column, int $offset, int $length, ?string $alias = null)
+ * @method static CRUDInterface selectLeft(RawQuery|string $column, int $length, ?string $alias = null)
+ * @method static CRUDInterface selectRight(RawQuery|string $column, int $length, ?string $alias = null)
+ * @method static CRUDInterface selectDistinct(RawQuery|string $column, ?string $alias = null)
+ * @method static CRUDInterface selectCoalesce(RawQuery|string $column, mixed $default = '0', ?string $alias = null)
+ * @method static CRUDInterface selectSum(string|RawQuery $column, ?string $alias = null)
+ * @method static CRUDInterface selectConcat(array $columns, ?string $alias = null)
+ * @method static CRUDInterface from(RawQuery|string $table, ?string $alias = null)
+ * @method static CRUDInterface addFrom(RawQuery|string $table, ?string $alias = null)
+ * @method static CRUDInterface table(string|RawQuery $table)
+ * @method static CRUDInterface groupBy(string|RawQuery|array ...$columns)
+ * @method static CRUDInterface join(RawQuery|string $table, RawQuery|string|Closure $onStmt = null, string $type = 'INNER')
+ * @method static CRUDInterface selfJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
+ * @method static CRUDInterface innerJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
+ * @method static CRUDInterface leftJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
+ * @method static CRUDInterface rightJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
+ * @method static CRUDInterface leftOuterJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
+ * @method static CRUDInterface rightOuterJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
+ * @method static CRUDInterface naturalJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
+ * @method static CRUDInterface orderBy(RawQuery|string $column, string $soft = 'ASC')
+ * @method static CRUDInterface where(RawQuery|string $column, string $operator = '=', mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface having(RawQuery|string $column, string $operator = '=', mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface on(RawQuery|string $column, string $operator = '=', mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface set(RawQuery|array|string $column, mixed $value = null, bool $strict = true)
+ * @method static CRUDInterface addSet(RawQuery|array|string $column, mixed $value = null, bool $strict = true)
+ * @method static CRUDInterface andWhere(string|RawQuery $column, string $operator = '=', mixed $value = null)
+ * @method static CRUDInterface orWhere(string|RawQuery $column, string $operator = '=', mixed $value = null)
+ * @method static CRUDInterface between(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND')
+ * @method static CRUDInterface orBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
+ * @method static CRUDInterface andBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
+ * @method static CRUDInterface notBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND')
+ * @method static CRUDInterface orNotBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
+ * @method static CRUDInterface andNotBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
+ * @method static CRUDInterface findInSet(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface andFindInSet(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface orFindInSet(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface notFindInSet(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface andNotFindInSet(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface orNotFindInSet(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface whereIn(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface whereNotIn(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface orWhereIn(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface orWhereNotIn(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface andWhereIn(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface andWhereNotIn(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface regexp(string|RawQuery $column, string|RawQuery $value, string $logical = 'AND')
+ * @method static CRUDInterface andRegexp(string|RawQuery $column, string|RawQuery $value)
+ * @method static CRUDInterface orRegexp(string|RawQuery $column, string|RawQuery $value)
+ * @method static CRUDInterface soundex(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface andSoundex(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface orSoundex(string|RawQuery $column, mixed $value = null)
+ * @method static CRUDInterface whereIsNull(string|RawQuery $column, string $logical = 'AND')
+ * @method static CRUDInterface orWhereIsNull(string|RawQuery $column)
+ * @method static CRUDInterface andWhereIsNull(string|RawQuery $column)
+ * @method static CRUDInterface whereIsNotNull(string|RawQuery $column, string $logical = 'AND')
+ * @method static CRUDInterface orWhereIsNotNull(string|RawQuery $column)
+ * @method static CRUDInterface andWhereIsNotNull(string|RawQuery $column)
+ * @method static CRUDInterface offset(int $offset = 0)
+ * @method static CRUDInterface limit(int $limit)
+ * @method static CRUDInterface like(string|RawQuery|array $column, mixed $value = null, string $type = 'both', string $logical = 'AND')
+ * @method static CRUDInterface orLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
+ * @method static CRUDInterface andLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
+ * @method static CRUDInterface notLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both', string $logical = 'AND')
+ * @method static CRUDInterface orNotLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
+ * @method static CRUDInterface andNotLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
+ * @method static CRUDInterface startLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface orStartLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static CRUDInterface andStartLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static CRUDInterface notStartLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface orStartNotLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static CRUDInterface andStartNotLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static CRUDInterface endLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface orEndLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static CRUDInterface andEndLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static CRUDInterface notEndLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
+ * @method static CRUDInterface orEndNotLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static CRUDInterface andEndNotLike(string|RawQuery|array $column, mixed $value = null)
+ * @method static RawQuery subQuery(Closure $closure, ?string $alias = null, bool $isIntervalQuery = true)
+ * @method static CRUDInterface group(Closure $closure)
+ * @method static RawQuery raw(mixed $rawQuery)
+ * @method static string[] getErrors()
*/
class DB
{
- private static Database $databaseInstance;
+ private static CRUDInterface $databaseInstance;
public function __call($name, $arguments)
{
@@ -156,19 +167,21 @@ public static function __callStatic($name, $arguments)
return self::getDatabase()->{$name}(...$arguments);
}
- public static function createImmutable(array $credentials): Database
+ public static function createImmutable(array|ConnectionInterface $credentialsOrConnection): CRUDInterface
{
- return self::$databaseInstance = new Database($credentials);
+ if (isset(self::$databaseInstance)) {
+ return self::$databaseInstance;
+ }
+
+ return self::$databaseInstance = new CRUD(new Database(($credentialsOrConnection instanceof ConnectionInterface) ? $credentialsOrConnection : new Connection($credentialsOrConnection), new QueryBuilder()));
}
- public static function connect(array $credentials): Database
+ public static function connect(array|ConnectionInterface $credentialsOrConnection): CRUDInterface
{
- return isset(self::$databaseInstance)
- ? self::getDatabase()->newInstance($credentials)
- : self::createImmutable($credentials);
+ return new CRUD(new Database((($credentialsOrConnection instanceof ConnectionInterface) ? $credentialsOrConnection : new Connection($credentialsOrConnection)), new QueryBuilder()));
}
- private static function getDatabase(): Database
+ public static function getDatabase(): CRUDInterface
{
return self::$databaseInstance;
}
diff --git a/src/Helpers/Helper.php b/src/Helpers/Helper.php
deleted file mode 100644
index 763b866..0000000
--- a/src/Helpers/Helper.php
+++ /dev/null
@@ -1,123 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.1
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Helpers;
-
-use InitPHP\Database\Exceptions\ModelRelationsException;
-use InitPHP\Database\Model;
-use InitPHP\Database\Raw;
-
-final class Helper
-{
-
- private static array $modelInstance = [];
-
- public static function str_starts_with(string $haystack, string $needle): bool
- {
- if(\function_exists('str_starts_with')){
- return \str_starts_with($haystack, $needle);
- }
- return 0 === \strncmp($haystack, $needle, \strlen($needle));
- }
-
- public static function str_contains(string $haystack, string $needle): bool
- {
- if(\function_exists('str_contains')){
- return \str_contains($haystack, $needle);
- }
- return $needle === '' || FALSE !== \strpos($haystack, $needle);
- }
-
- public static function str_ends_with(string $haystack, string $needle): bool
- {
- if(\function_exists('str_ends_with')){
- return \str_ends_with($haystack, $needle);
- }
- if($needle === '' || $needle === $haystack){
- return true;
- }
- if($haystack === ''){
- return false;
- }
- $need_len = \strlen($needle);
-
- return $need_len <= \strlen($haystack) && 0 === \substr_compare($haystack, $needle, (0 - $need_len));
- }
-
- public static function isSQLParameterOrFunction($value): bool
- {
- return ((\is_string($value)) && (
- $value === '?'
- || (bool)\preg_match('/^:[\w]+$/', $value)
- || (bool)\preg_match('/^[a-zA-Z_]+[\.]+[a-zA-Z_]+$/', $value)
- || (bool)\preg_match('/^[a-zA-Z_]+\(\)$/', $value)
- )) || ($value instanceof Raw) || \is_int($value);
- }
-
- public static function isSQLParameter($value): bool
- {
- return (\is_string($value)) && ($value === '?' || (bool)\preg_match('/^:[\w]+$/', $value));
- }
-
- public static function camelCaseToSnakeCase($camelCase): string
- {
- $camelCase = \lcfirst($camelCase);
- $split = \preg_split('', $camelCase, -1, \PREG_SPLIT_NO_EMPTY);
- $snake_case = '';
- $i = 0;
- foreach ($split as $row) {
- $snake_case .= ($i === 0 ? '_' : '')
- . \strtolower($row);
- ++$i;
- }
- return \lcfirst($snake_case);
- }
-
- public static function snakeCaseToCamelCase($snake_case): string
- {
- return \lcfirst(self::snakeCaseToPascalCase($snake_case));
- }
-
- public static function snakeCaseToPascalCase($snake_case): string
- {
- $split = \explode('_', \strtolower($snake_case));
- $camelCase = '';
- foreach ($split as $row) {
- $camelCase .= \ucfirst($row);
- }
- return $camelCase;
- }
-
- public static function getModelInstance($model): Model
- {
- if ($model instanceof Model) {
- $class = \get_class($model);
- return self::$modelInstance[$class] = $model;
- } elseif (\is_string($model) && \class_exists($model)) {
- if (isset(self::$modelInstance[$model])) {
- return self::$modelInstance[$model];
- }
- } else {
- throw new \InvalidArgumentException();
- }
-
- $reflection = new \ReflectionClass($model);
- if ($reflection->isSubclassOf(Model::class) === FALSE) {
- throw new ModelRelationsException('The target class must be a subclass of \\InitPHP\\Database\\Model.');
- }
- $class = $reflection->getName();
-
- return self::$modelInstance[$class] = $reflection->newInstance();
- }
-
-}
diff --git a/src/Helpers/Parameters.php b/src/Helpers/Parameters.php
deleted file mode 100644
index 413cc1d..0000000
--- a/src/Helpers/Parameters.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.8
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Helpers;
-
-use InitPHP\Database\Raw;
-
-final class Parameters
-{
-
- private static array $parameters = [];
-
- public static function get(bool $reset = true): array
- {
- $parameters = self::$parameters;
- if($reset === TRUE){
- self::reset();
- }
- return $parameters;
- }
-
- public static function set(string $key, $value): void
- {
- $key = ':' . \ltrim(\str_replace('.', '', $key), ':');
- self::$parameters[$key] = $value;
- }
-
- public static function add($key, $value): string
- {
- if ($value === null) {
- return 'NULL';
- }
- if ($key instanceof Raw) {
- $key = \md5((string)$key);
- }
- $originKey = \ltrim(\str_replace('.', '', $key), ':');
- $i = 0;
- do{
- $key = ':' . ($i === 0 ? $originKey : $originKey . '_' . $i);
- ++$i;
- $hasParameter = isset(self::$parameters[$key]);
- }while($hasParameter);
- self::$parameters[$key] = $value;
- return $key;
- }
-
- public static function merge(...$array): void
- {
- self::$parameters = \array_merge(self::$parameters, ...$array);
- }
-
- public static function getParam(string $key)
- {
- return self::$parameters[$key] ?? $key;
- }
-
- public static function reset()
- {
- self::$parameters = [];
- }
-
-}
diff --git a/src/Helpers/Validation.php b/src/Helpers/Validation.php
deleted file mode 100644
index 7a0f314..0000000
--- a/src/Helpers/Validation.php
+++ /dev/null
@@ -1,342 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Helpers;
-
-use InitPHP\Database\Database;
-use InitPHP\Database\Exceptions\ValidationException;
-use InitPHP\Database\Raw;
-
-final class Validation
-{
-
- protected const AVAILABLE_VALIDATION_METHODS = [
- 'required', 'string', 'int', 'float', 'numeric',
- 'alpha', 'alphaNumeric', 'boolean', 'mail', 'url',
- 'min', 'max', 'length', 'range', 'regex',
- 'ip', 'ipv4', 'ipv6', 'equal', 'startWith', 'endWith',
- 'strContains', 'only',
- ];
-
- protected const DEFAULT_MESSAGES = [
- 'required' => '{field} cannot be empty.',
- 'string' => 'Must be {field} string.',
- 'int' => 'Must be {field} integer.',
- 'float' => 'Must be {field} float.',
- 'unsigned' => 'Must be {field} unsigned.',
- 'numeric' => 'Must be {field} numeric.',
- 'alpha' => 'Must be {field} alphabetical.',
- 'alphaNumeric' => 'Must be {field} alphanumeric.',
- 'boolean' => 'Must be {field} boolean.',
- 'mail' => 'Must be {field} mail.',
- 'url' => 'Must be {field} url.',
- 'min' => '{field} must be {1} or greater.',
- 'max' => '{field} must be {1} or less.',
- 'length' => '{field} can be minimum of {1} characters and a maximum of {2} characters.',
- 'range' => '{field} can be at least {1} and at most {2}.',
- 'regex' => '{field} is not in proper format/pattern.',
- 'ip' => 'It should be {field} IP Address.',
- 'ipv4' => '{field} should be an IPv4 Address.',
- 'ipv6' => '{field} should be an IPv6 Address.',
- 'equal' => '{field} can only be {1}',
- 'startWith' => '{field} must start with {1}',
- 'endWith' => '{field} must end with {1}',
- 'strContains' => 'Must contain {field}, {1}',
- 'only' => '{field} can only be; {arguments}',
- //'is_unique' => '{field} must be unique.',
- ];
-
- private array $data = [];
-
- private array $methods = [];
- private array $labels = [];
- private array $messages = [];
- private string $error;
-
- private Database $db;
-
- public function __construct(array $methods, array $messages, array $labels, Database &$db)
- {
- $this->methods = $methods;
- $this->messages = $messages;
- $this->labels = $labels;
- $this->db = &$db;
- }
-
- public function setData($data, array $parameters = []): self
- {
- $this->data = $data;
- foreach ($parameters as $key => $value) {
- $id = \array_search($key, $this->data);
- if($id === FALSE){
- continue;
- }
- $this->data[$id] = $value;
- }
- return $this;
- }
-
- public function getError(): ?string
- {
- return $this->error ?? null;
- }
-
- public function validation(string $column, $schemaID = null): bool
- {
- unset($this->error);
- if(!isset($this->methods[$column])){
- return true;
- }
- $methods = $this->methods[$column];
- if(\is_string($methods)){
- $methods = \explode('|', $methods);
- }
-
- if(($key = \array_search('optional', $methods, true)) !== FALSE){
- if(!isset($this->data[$column])){
- return true;
- }
- unset($methods[$key]);
- }
-
- $data = $this->data[$column] ?? null;
- if ($data instanceof Raw) {
- return true;
- }
-
- if(Helper::isSQLParameter($data)){
- $data = Parameters::getParam($data);
- }
- if(($key = \array_search('is_unique', $methods, true)) !== FALSE){
- if($this->is_unique($data, $column, $schemaID) === FALSE){
- $this->error = $this->get_message($column, 'is_unique');
- return false;
- }
- unset($methods[$key]);
- }
-
- foreach ($methods as $method) {
- $method = $this->method_parse($method);
- if(!\in_array($method['method'], self::AVAILABLE_VALIDATION_METHODS)){
- throw new ValidationException('The validation method "' . $method['method'] . '" defined for column "' . $column . '" is not available.');
- }
- $arguments = $method['arguments'];
- \array_unshift($arguments, $data);
- $method = $method['method'];
- $res = $this->{$method}(...$arguments);
- if($res === FALSE){
- $this->error = $this->get_message($column, $method, $arguments);
- return false;
- }
- }
- return true;
- }
-
- protected function is_unique($data, $column, $schemaID = null): bool
- {
- if($this->db->getSchemaID() === null){
- throw new ValidationException('You need a model with a PRIMARY KEY to use the is_unique validation.');
- }
- $db = $this->db->clone();
- $db->reset();
- $parameters = Parameters::get(true);
- $db->select($db->getSchemaID())->offset(0)
- ->limit(1);
- if(!empty($schemaID)){
- $db->where($db->getSchemaID(), $schemaID, '!=');
- }
- $db->where($column, $data, '=');
- $res = $db->get($db->getSchema());
- unset($db);
- Parameters::merge($parameters);
- return $res->numRows() < 1;
- }
-
- protected function required($data): bool
- {
- if(!\is_iterable($data) && !\is_object($data)){
- $data = \trim((string)$data);
- return $data !== '';
- }
- return !empty($data);
- }
-
- protected function string($data): bool
- {
- return \is_string($data);
- }
-
- protected function int($data): bool
- {
- return (bool)\preg_match('/^([+|\-]*)[0-9]+$/', (string)$data);
- }
-
- protected function float($data): bool
- {
- return (bool)\preg_match('/^[+|\-]*[0-9]+[.]+[0-9]+$/', (string)$data);
- }
-
- protected function unsigned($data): bool
- {
- return !((bool)\preg_match('/^[+|\-]+/', (string)$data));
- }
-
- protected function numeric($data):bool
- {
- return (bool)\preg_match('/^[+|\-]*[0-9]+([.]+[0-9]+)*$/', (string)$data);
- }
-
- protected function alpha($data): bool
- {
- $pattern = '[a-zA-ZğĞŞşÜüİıÖöÇç]';
- return (bool)\preg_match('/^' . $pattern . '$/', (string)$data);
- }
-
- protected function alphaNumeric($data): bool
- {
- $pattern = '(?:([+|\-]*[0-9]+([.]+[0-9]+)*)|([0-9a-zA-ZğĞŞşÜüİıÖöÇç]))+';
- return (bool)\preg_match('/^' . $pattern . '$/', (string)$data);
- }
-
- protected function boolean($data): bool
- {
- return (bool)\preg_match('/^(true|false|0|1)]+$/', (string)$data);
- }
-
- protected function mail($data): bool
- {
- return (bool)\filter_var($data, \FILTER_VALIDATE_EMAIL);
- }
-
- protected function url($data): bool
- {
- return (bool)\filter_var($data, \FILTER_VALIDATE_URL);
- }
-
- protected function min($data, $min = 0): bool
- {
- return $data >= $min;
- }
-
- protected function max($data, $max = 1): bool
- {
- return $data <= $max;
- }
-
- protected function length($data, $min = 0, $max = null): bool
- {
- $len = \strlen($data);
- $res = $len >= $min;
- if($res !== FALSE && $max !== null){
- return $len <= $max;
- }
- return $res;
- }
-
- protected function range($data, $min = \PHP_INT_MIN, $max = \PHP_INT_MAX)
- {
- return $this->min($data, $min) && $this->max($data, $max);
- }
-
- protected function regex($data, $pattern): bool
- {
- return (bool)\preg_match($pattern, (string)$data);
- }
-
- protected function ip($data): bool
- {
- return (bool)\filter_var($data, \FILTER_VALIDATE_IP);
- }
-
- protected function ipv4($data): bool
- {
- return (bool)\filter_var($data, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
- }
-
- protected function ipv6($data): bool
- {
- return (bool)\filter_var($data, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6);
- }
-
- protected function equal($data, $assert): bool
- {
- return $data == $assert;
- }
-
- protected function startWith($data, $with): bool
- {
- return Helper::str_starts_with($data, $with);
- }
-
- protected function endWith($data, $with): bool
- {
- return Helper::str_ends_with($data, $with);
- }
-
- protected function strContains($data, $search): bool
- {
- return Helper::str_contains($data, $search);
- }
-
- protected function only($data, ...$only): bool
- {
- foreach ($only as $row) {
- if($row == $data){
- return true;
- }
- }
- return false;
- }
-
- private function method_parse(string $str): array
- {
- $arguments = [];
- $method = $str;
- if(\preg_match('/([\w]+)\((.+)\)/', $str, $params)){
- $method = $params[1];
- if(isset($params[2])){
- $split = \explode(',', $params[2]);
- foreach ($split as $argument) {
- $arguments[] = \trim(\trim($argument), '"\'');
- }
- }
- }
-
- return [
- 'method' => $method,
- 'arguments' => $arguments,
- ];
- }
-
- private function get_message(string $column, string $method, $arguments = []): string
- {
- $msg = $this->messages[$column][$method] ?? self::DEFAULT_MESSAGES[$method];
- $label = $this->labels[$column] ?? $column;
-
- $replace = [
- '{field}' => $label
- ];
- \array_shift($arguments);
- if(!empty($arguments)){
- $replace['{arguments}'] = \implode(', ', $arguments);
- $i = 1;
- foreach ($arguments as $argument) {
- $replace['{' . $i . '}'] = $argument;
- ++$i;
- }
- }
- return \strtr($msg, $replace);
- }
-
-
-}
diff --git a/src/Init.php b/src/Init.php
deleted file mode 100644
index bc3aa38..0000000
--- a/src/Init.php
+++ /dev/null
@@ -1,37 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.8
- * @link https://www.muhammetsafak.com.tr
- */
-
-
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Helpers/Helper.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Helpers/Parameters.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Helpers/Validation.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/ConnectionException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/DeletableException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/ModelException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/ModelCallbacksException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/ModelRelationsException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/ReadableException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/SQLQueryExecuteException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/UpdatableException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/ValidationException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/WritableException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Exceptions/ValueException.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'QueryBuilder.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Facade/DB.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Database.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Entity.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Model.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Raw.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Result.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Utils/Pagination.php';
-require_once __DIR__ . DIRECTORY_SEPARATOR . 'Utils/Datatables.php';
diff --git a/src/Model.php b/src/Model.php
deleted file mode 100644
index f781bbb..0000000
--- a/src/Model.php
+++ /dev/null
@@ -1,627 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.1
- * @link https://www.muhammetsafak.com.tr
- */
-declare(strict_types=1);
-
-namespace InitPHP\Database;
-
-use InitPHP\Database\Exceptions\{DeletableException,
- ModelCallbacksException,
- ModelException,
- ModelRelationsException,
- QueryBuilderException,
- QueryGeneratorException,
- ReadableException,
- UpdatableException,
- WritableException};
-use InitPHP\Database\Helpers\Helper;
-use InitPHP\Database\Helpers\Parameters;
-
-abstract class Model extends Database
-{
-
- /**
- * @var string[]
- */
- protected array $connection;
-
- /**
- * Dönüş için kullanılacak Entity sınıfı ya da nesnesi.
- *
- * @var Entity|string
- */
- protected $entity = Entity::class;
-
- /**
- * Modelin kullanacağı tablo adını tanımlar. Belirtilmez ya da boş bir değer belirtilirse model sınıfınızın adı kullanılır.
- *
- * @var string
- */
- protected string $table;
-
- /**
- * Tablonuzun PRIMARY KEY sütununun adını tanımlar. Eğer tablonuzda böyle bir sütun yoksa FALSE ya da NULL tanımlayın.
- *
- * @var null|string
- */
- protected ?string $primaryKey = 'id';
-
- /**
- * Yumuşak silmenin kullanılıp kullanılmayacağını tanımlar. Eğer FALSE ise veri kalıcı olarak silinir. TRUE ise $deletedField doğru tanımlanmış bir sütun adı olmalıdır.
- *
- * @var bool
- */
- protected bool $useSoftDeletes = true;
-
- /**
- * Verinin eklenme zamanını ISO 8601 formatında tutacak sütun adı.
- *
- * @var string|null
- */
- protected ?string $createdField = 'created_at';
-
- /**
- * Verinin son güncellenme zamanını ISO 8601 formatında tutacak sütun adı. Bu sütunun varsayılan değeri NULL olmalıdır.
- *
- * @var string|null
- */
- protected ?string $updatedField = 'updated_at';
-
- /**
- * Yumuşak silme aktifse verinin silinme zamanını ISO 8601 formatında tutacak sütun adı. Bu sütun varsayılan değeri NULL olmalıdır.
- *
- * @var string|null
- */
- protected ?string $deletedField = 'deleted_at';
-
- /**
- * Ekleme ve güncelleme gibi işlemlerde tanımlanmasına izin verilecek sütun isimlerini tutan dizi.
- *
- * @var string[]
- */
- protected ?array $allowedFields = null;
-
- /**
- * Ekleme, Silme ve Güncelleme işlemlerinde geri çağrılabilir yöntemlerin kullanılıp kullanılmayacağını tanımlar.
- *
- * @var bool
- */
- protected bool $allowedCallbacks = false;
-
- /**
- * Insert işlemi öncesinde çalıştırılacak yöntemleri tanımlar. Bu yöntemlere eklenmek istenen veri bir ilişkisel dizi olarak gönderilir ve geriye eklenecek veri dizisini döndürmesi gerekir.
- *
- * @var string[]|\Closure[]
- */
- protected array $beforeInsert = [];
-
- /**
- * Insert işlemi yürütüldükten sonra çalıştırılacak yöntemleri tanımlar. Eklenen veriyi ilişkisel bir dizi olarak alır ve yine bu diziyi döndürmesi gerekir.
- *
- * @var string[]|\Closure[]
- */
- protected array $afterInsert = [];
-
- /**
- * Update işlemi yürütülmeden önce çalıştırılacak yöntemleri tanımlar. Güncellenecek sütun ve değerleri ilişkisel bir dizi olarak gönderilir ve yine bu dizi döndürmesi gerekir.
- *
- * @var string[]|\Closure[]
- */
- protected array $beforeUpdate = [];
-
- /**
- * Update işlemi yürütüldükten sonra çalıştırılacak yöntemleri tanımlar. Güncellenmiş sütun ve değerleri ilişkisel bir dizi olarak gönderilir ve yine bu dizi döndürmesi gerekir.
- *
- * @var string[]|\Closure[]
- */
- protected array $afterUpdate = [];
-
- /**
- * Delete işlemi yürülmeden önce çalıştırılacak yöntemleri tanımlar.Etkilenecek satırların çoklu ilişkisel dizisi parametre olarak gönderilir ve yine bu dizi döndürmesi gerekir.
- *
- * @var string[]|\Closure[]
- */
- protected array $beforeDelete = [];
-
- /**
- * Delete işlemi yürütüldükten sonra çalıştırılacak yöntemleri tanımlar. Etkilenmiş satırların çoklu ilişkisel dizisi parametre olarak gönderilir ve yine bu dizi döndürmesi gerekir.
- *
- * @var string[]|\Closure[]
- */
- protected array $afterDelete = [];
-
- /**
- * Bu modelin veriyi okuyabilir mi olduğunu tanımlar.
- *
- * @var bool
- */
- protected bool $readable = true;
-
- /**
- * Bu modelin bir veri yazabilir mi olduğunu tanımlar.
- *
- * @var bool
- */
- protected bool $writable = true;
-
- /**
- * Bu modelin bir veri silebilir mi olduğunu tanımlar.
- *
- * @var bool
- */
- protected bool $deletable = true;
-
- /**
- * Bu modelin bir veriyi güncelleyebilir mi olduğunu tanımlar.
- *
- * @var bool
- */
- protected bool $updatable = true;
-
- /**
- * Hangi sütunların hangi doğrulama yöntemine uyması gerektiğini tanımlayan dizi.
- *
- * @var array
- */
- protected array $validation = [];
-
- /**
- * Sütun ve doğrulama yöntemlerine özel oluşacak hata mesajlarını özelleştirmenize/yerelleştirmeniz için kullanılan dizi.
- *
- * @var array
- */
- protected array $validationMsg = [];
-
- /**
- * Sütun ve doğrulama yöntemlerine özel oluşturulacak hata mesajlarında {field} yerini alacak sütun adı yerine kullanılacak değerleri tanımlayan ilişkisel dizi.
- *
- * @var array
- */
- protected array $validationLabels = [];
-
- public function __construct()
- {
- $credentials = $this->connection ?? [];
- if(!isset($this->table)){
- $modelClass = \get_called_class();
- $modelReflection = new \ReflectionClass($modelClass);
- $this->table = \strtolower($modelReflection->getShortName());
- unset($modelClass, $modelReflection);
- }
- if($this->useSoftDeletes !== FALSE){
- if(empty($this->deletedField)){
- throw new ModelException('There must be a delete column to use soft delete.');
- }
- }
- $credentials['tableSchema'] = $this->table;
- $credentials['tableSchemaID'] = $this->primaryKey ?? null;
- $credentials['allowedFields'] = $this->allowedFields ?? null;
- $credentials['createdField'] = $this->createdField ?? null;
- $credentials['updatedField'] = $this->updatedField ?? null;
- $credentials['deletedField'] = $this->deletedField ?? null;
- if($credentials['allowedFields'] !== null){
- if($credentials['createdField'] !== null && !\in_array($credentials['createdField'], $credentials['allowedFields'])){
- $credentials['allowedFields'][] = $credentials['createdField'];
- }
- if($credentials['updatedField'] !== null && !\in_array($credentials['updatedField'], $credentials['allowedFields'])){
- $credentials['allowedFields'][] = $credentials['updatedField'];
- }
- if($credentials['deletedField'] !== null && !\in_array($credentials['deletedField'], $credentials['allowedFields'])){
- $credentials['allowedFields'][] = $credentials['deletedField'];
- }
- }
- $credentials['entity'] = $this->entity ?? Entity::class;
- $credentials['validation'] = [
- 'methods' => $this->validation ?? [],
- 'messages' => $this->validationMsg ?? [],
- 'labels' => $this->validationLabels ?? [],
- ];
- $credentials['readable'] = $this->readable ?? true;
- $credentials['updatable'] = $this->updatable ?? true;
- $credentials['deletable'] = $this->deletable ?? true;
- $credentials['writable'] = $this->writable ?? true;
- $credentials['return'] = $this->return ?? self::ENTITY;
- parent::__construct($credentials);
- }
-
- final public function withPrimaryKey(string $column): self
- {
- $clone = clone $this;
- $clone->primaryKey = $column;
- $clone->setSchemaID($column);
- return $clone;
- }
-
- /**
- * @param array $set
- * @return array|false
- */
- final public function create(?array $set = null)
- {
- return $this->insert($set);
- }
-
- /**
- * @param array $set
- * @return array|false
- */
- final public function createBatch(?array $set = null)
- {
- return $this->insertBatch($set);
- }
-
- /**
- * @param array $set
- * @return array|bool
- */
- final public function insert(?array $set = null)
- {
- if($this->isWritable() === FALSE){
- throw new WritableException('"' . \get_called_class() . '" is not a writable model.');
- }
-
- $data = $this->isCallbacksFunction('beforeInsert', 'afterInsert') ? $this->callbacksFunctionHandler($set, 'beforeInsert') : $set;
-
- if($data === FALSE){
- return false;
- }
-
- if(parent::create($data) === FALSE){
- return false;
- }
-
- return $this->isCallbacksFunction('afterInsert') ? $this->callbacksFunctionHandler($data, 'afterInsert') : true;
- }
-
- /**
- * @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;
- }
-
- 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|bool
- */
- final public function save(Entity $entity)
- {
- $data = $entity->toArray();
- $schemaID = $this->getSchemaID();
- if($schemaID !== null && isset($data[$schemaID])){
- return $this->update($data);
- }
- return $this->insert($data);
- }
-
- /**
- * @param array $selector
- * @param array $conditions
- * @param array $parameters
- * @return Result
- */
- final public function read(array $selector = [], array $conditions = [], array $parameters = [])
- {
- if($this->isReadable() === FALSE){
- throw new ReadableException('"' . \get_called_class() . '" is not a readable model.');
- }
- return parent::read($selector, $conditions, $parameters);
- }
-
- /**
- * @param array $selector
- * @param array $conditions
- * @param array $parameters
- * @return Result
- */
- final public function readOne(array $selector = [], array $conditions = [], array $parameters = [])
- {
- if($this->isReadable() === FALSE){
- throw new ReadableException('"' . \get_called_class() . '" is not a readable model.');
- }
- return parent::readOne($selector, $conditions, $parameters);
- }
-
- /**
- * @param array $set
- * @return array|bool
- */
- final public function update(?array $set = null)
- {
- if($this->isUpdatable() === FALSE){
- throw new UpdatableException('"' . \get_called_class() . '" is not a updatable model.');
- }
- $data = $this->isCallbacksFunction('beforeUpdate', 'afterUpdate') ? $this->callbacksFunctionHandler($set, 'beforeUpdate') : $set;
- if($data === FALSE){
- return false;
- }
- if(parent::update($data) === FALSE){
- return false;
- }
-
- return $this->isCallbacksFunction('afterUpdate') ? $this->callbacksFunctionHandler($data, 'afterUpdate') : true;
- }
-
- /**
- * @param string $referenceColumn
- * @param array|null $set
- * @return array|false
- * @throws QueryBuilderException
- * @throws QueryGeneratorException
- */
- final public function updateBatch(string $referenceColumn, ?array $set = null)
- {
- if($this->isUpdatable() === FALSE){
- throw new UpdatableException('"' . \get_called_class() . '" is not a updatable model.');
- }
-
- if($this->isCallbacksFunction('beforeUpdate', 'afterUpdate')){
- foreach ($set as &$data) {
- $data = $this->callbacksFunctionHandler($data, 'beforeUpdate');
- if($data === FALSE){
- return false;
- }
- }
- }
-
- if(parent::updateBatch($referenceColumn, $set) === FALSE){
- return false;
- }
-
- if($this->isCallbacksFunction('afterUpdate')){
- foreach ($set as &$row) {
- $row = $this->callbacksFunctionHandler($row, 'afterUpdate');
- if($row === FALSE){
- return false;
- }
- }
- }
-
- return $set;
- }
-
- /**
- * @param int|string|null $id
- * @return array|bool
- */
- final public function delete($id = null)
- {
- if($this->isDeletable() === FALSE){
- throw new DeletableException('"' . \get_called_class() . '" is not a deletable model.');
- }
- if($id !== null && !empty($this->getSchemaID())){
- $this->where($this->getSchemaID(), $id);
- }
-
- 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 $this->isCallbacksFunction('afterDelete') ? $this->callbacksFunctionHandler($data, 'afterDelete') : true;
- }
-
- final public function purgeDeleted(): bool
- {
- if($this->isDeletable() === FALSE){
- return false;
- }
- $this->onlyDeleted();
- return parent::delete();
- }
-
- /**
- * İki model arasında bir JOIN ilişkisi kurar.
- *
- * @param string|Model $model
- * @param string|null $foreignKey
- * @param string|null $localKey
- * @param string $joinType
- * @return $this
- */
- final public function relation($model, ?string $foreignKey = null, ?string $localKey = null, string $joinType = 'INNER'): self
- {
- $from = [
- 'tableSchema' => $this->getSchema(),
- 'tableSchemaID' => $this->getSchemaID(),
- ];
- $target = $this->getModelTableNameAndPrimaryKeyColumn($model);
-
- if($localKey === null || $localKey === '{primaryKey}'){
- if(empty($from['tableSchemaID'])){
- throw new ModelRelationsException('To use relationships, the model must have a primary key column.');
- }
- }else{
- $from['tableSchemaID'] = $localKey;
- }
- if($foreignKey === null) {
- $target['tableSchemaID'] = (Helper::str_ends_with($from['tableSchema'], 's') ? \substr($from['tableSchema'], -1) : $from['tableSchema'])
- . '_' . $from['tableSchemaID'];
- } elseif ($foreignKey === '{primaryKey}') {
- if(empty($target['tableSchemaID'])){
- throw new ModelRelationsException('To use relationships, the model must have a primary key column.');
- }
- }else{
- $target['tableSchemaID'] = $foreignKey;
- }
- $this->join($target['tableSchema'], ($from['tableSchema'] . '.' . $from['tableSchemaID'] . ' = ' . $target['tableSchema'] . '.' . $target['tableSchemaID']), $joinType);
-
- return $this;
- }
-
- /**
- * @return bool
- */
- final public function isWritable(): bool
- {
- return $this->writable ?? true;
- }
-
- /**
- * @return bool
- */
- final public function isReadable(): bool
- {
- return $this->readable ?? true;
- }
-
- /**
- * @return bool
- */
- final public function isUpdatable(): bool
- {
- return $this->updatable ?? true;
- }
-
- /**
- * @return bool
- */
- 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
- * @return array|false
- */
- private function callbacksFunctionHandler(array $data, string $method)
- {
- if(!$this->isCallbacksFunction($method)){
- return $data;
- }
- $callbacks = $this->{$method};
-
- foreach ($callbacks as $callback) {
- if(\is_string($callback)){
- if(\method_exists($this, $callback) === FALSE){
- continue;
- }
- $data = \call_user_func_array([$this, $callback], [$data]);
- if($data === FALSE){
- return false;
- }
- if(!\is_array($data)){
- throw new ModelCallbacksException('Callbacks methods must return an array or false.');
- }
- continue;
- }
- if(!\is_callable($callback)){
- throw new ModelCallbacksException('Callbacks methods can only be model methods or callable.');
- }
- $data = \call_user_func_array($callback, [$data]);
- if($data === FALSE){
- return false;
- }
- if(!\is_array($data)){
- throw new ModelCallbacksException('Callbacks methods must return an array or false.');
- }
- }
-
- return $data;
- }
-
- private function getModelTableNameAndPrimaryKeyColumn($model): array
- {
- $reflection = new \ReflectionClass($model);
- if ($reflection->isSubclassOf(Model::class) === FALSE) {
- throw new ModelRelationsException('The target class must be a subclass of \\InitPHP\\Database\\Model.');
- }
- if (\defined('PHP_VERSION_ID') && \PHP_VERSION_ID >= 80000) {
- $tableReflection = $reflection->getProperty('table');
- $primaryKeyReflection = $reflection->getProperty('primaryKey');
- if (null === $tableName = $tableReflection->getDefaultValue()) {
- $tableName = \strtolower($reflection->getShortName());
- }
- $primaryKey = $primaryKeyReflection->getDefaultValue();
-
- return [
- 'tableSchema' => $tableName,
- 'tableSchemaID' => $primaryKey,
- ];
- }
-
- /** @var $model Model */
- if (!\is_object($model)) {
- $model = $reflection->newInstance();
- }
-
- return [
- 'tableSchema' => $model->getSchema(),
- 'tableSchemaID' => $model->getSchemaID(),
- ];
- }
-
-}
diff --git a/src/Entity.php b/src/ORM/Entity.php
similarity index 59%
rename from src/Entity.php
rename to src/ORM/Entity.php
index fbbd912..65f6956 100644
--- a/src/Entity.php
+++ b/src/ORM/Entity.php
@@ -1,21 +1,12 @@
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database;
-
-use InitPHP\Database\Helpers\Helper;
-
-class Entity
+
+namespace InitPHP\Database\ORM;
+
+use InitPHP\Database\ORM\Exceptions\EntityNotMethod;
+use InitPHP\Database\ORM\Interfaces\EntityInterface;
+use InitPHP\Database\Utils\Helper;
+
+class Entity implements EntityInterface
{
protected array $__attributes = [];
@@ -27,27 +18,31 @@ public function __construct(?array $data = [])
$this->setUp($data);
}
+ /**
+ * @param $name
+ * @param $arguments
+ * @return mixed
+ * @throws EntityNotMethod
+ */
public function __call($name, $arguments)
{
- if(Helper::str_ends_with($name, 'Attribute') === FALSE){
- throw new \RuntimeException('There is no "' . $name . '" method.');
- }
- switch (\substr($name, 0, 3)) {
- case 'get':
- $attr = Helper::camelCaseToSnakeCase(\substr($name, 3, -9));
- return $this->__attributes[$attr] ?? null;
- case 'set':
- $attr = Helper::camelCaseToSnakeCase(\substr($name, 3, -9));
- return $this->__attributes[$attr] = $arguments[0];
- default:
- throw new \RuntimeException('There is no "' . $name . '" method.');
+ if (str_ends_with($name, 'Attribute') === false) {
+ throw new EntityNotMethod($name);
}
+
+ $attr = Helper::camelCaseToSnakeCase(substr($name, 3, -9));
+
+ return match (substr($name, 0, 3)) {
+ 'get' => $this->__attributes[$attr] ?? null,
+ 'set' => $this->__attributes[$attr] = $arguments[0],
+ default => throw new EntityNotMethod($name)
+ };
}
public function __set($name, $value)
{
$methodName = 'set' . Helper::snakeCaseToPascalCase($name) . 'Attribute';
- if(\method_exists($this, $methodName)){
+ if(method_exists($this, $methodName)){
$this->{$methodName}($value);
return $value;
}
@@ -57,7 +52,7 @@ public function __set($name, $value)
public function __get($name)
{
$methodName = 'get' . Helper::snakeCaseToPascalCase($name) . 'Attribute';
- if(\method_exists($this, $methodName)){
+ if(method_exists($this, $methodName)){
return $this->{$methodName}();
}
return $this->__attributes[$name] ?? null;
@@ -80,16 +75,26 @@ public function __debugInfo()
return $this->__attributes;
}
+ /**
+ * @inheritDoc
+ */
public function toArray(): array
{
return $this->__attributes;
}
+ /**
+ * @inheritDoc
+ */
public function getAttributes(): array
{
return $this->toArray();
}
+ /**
+ * @param array|null $data
+ * @return $this
+ */
protected function setUp(?array $data = null): self
{
$this->syncOriginal()
@@ -97,6 +102,10 @@ protected function setUp(?array $data = null): self
return $this;
}
+ /**
+ * @param array|null $data
+ * @return $this
+ */
protected function fill(?array $data = null): self
{
if($data !== null){
@@ -107,6 +116,9 @@ protected function fill(?array $data = null): self
return $this;
}
+ /**
+ * @return $this
+ */
protected function syncOriginal(): self
{
$this->__attributesOriginal = $this->__attributes;
diff --git a/src/ORM/Exceptions/DeletableException.php b/src/ORM/Exceptions/DeletableException.php
new file mode 100644
index 0000000..67c7696
--- /dev/null
+++ b/src/ORM/Exceptions/DeletableException.php
@@ -0,0 +1,7 @@
+schema)) {
+ $modelClass = get_called_class();
+ $modelReflection = new ReflectionClass($modelClass);
+ $this->schema = Helper::camelCaseToSnakeCase($modelReflection->getShortName());
+ unset($modelClass, $modelReflection);
+ }
+ if ($this->useSoftDeletes !== false && empty($this->deletedField)) {
+ throw new ModelException('There must be a delete column to use soft delete.');
+ }
+
+ $this->crud = empty($this->credentials) ? DB::getDatabase() : DB::connect($this->credentials);
+ }
+
+ public function __call(string $name, array $arguments)
+ {
+ $res = $this->crud->{$name}(...$arguments);
+
+ return ($res instanceof CRUDInterface) ? $this : $res;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function create(array $set = []): bool
+ {
+ if (!$this->writable) {
+ throw new WritableException();
+ }
+
+ !empty($this->createdField) && $set[$this->createdField] = date($this->timestampFormat);
+
+ return $this->crud->create($this->schema, $set);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function createBatch(array $set = []): bool
+ {
+ if (!$this->writable) {
+ throw new WritableException();
+ }
+ $createdField = $this->createdField;
+ if (!empty($createdField) && !empty($set)) {
+ foreach ($set as &$row) {
+ $row[$createdField] = date($this->timestampFormat);
+ }
+ }
+
+ return $this->crud->createBatch($this->schema, $set);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function read(array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
+ {
+ if (!$this->readable) {
+ throw new ReadableException();
+ }
+ if ($this->useSoftDeletes) {
+ if ($this->isOnlyDelete) {
+ $this->onlyDeleted();
+ } else {
+ $this->ignoreDeleted();
+ }
+ $this->isOnlyDelete = false;
+ }
+
+ return $this->crud
+ ->read($this->schema, $selector, $conditions, $parameters)
+ ->asClass($this->entity);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readOne(array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
+ {
+ if (!$this->readable) {
+ throw new ReadableException();
+ }
+ if ($this->useSoftDeletes) {
+ if ($this->isOnlyDelete) {
+ $this->onlyDeleted();
+ } else {
+ $this->ignoreDeleted();
+ }
+ $this->isOnlyDelete = false;
+ }
+
+ return $this->crud
+ ->readOne($this->schema, $selector, $conditions, $parameters)
+ ->asClass($this->entity);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function update(array $set = []): bool
+ {
+ if (!$this->updatable) {
+ throw new UpdatableException();
+ }
+
+ if (!empty($this->schemaId) && isset($set[$this->schemaId])) {
+ $this->where($this->schemaId, $set[$this->schemaId]);
+ unset($set[$this->schemaId]);
+ }
+
+ !empty($this->updatedField) && $set[$this->updatedField] = date($this->timestampFormat);
+
+ $this->ignoreDeleted();
+
+ return $this->crud->update($this->schema, $set);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function updateBatch(array $set = [], ?string $referenceColumn = null): bool
+ {
+ if (!$this->updatable) {
+ throw new UpdatableException();
+ }
+ $updatedField = $this->updatedField;
+ if (!empty($updatedField) && !empty($set)) {
+ foreach ($set as &$row) {
+ $row[$updatedField] = date($this->timestampFormat);
+ }
+ }
+ $this->ignoreDeleted();
+
+ return $this->crud->updateBatch($this->schema, $set, $referenceColumn ?? $this->schemaId);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function delete(?array $conditions = null, bool $purge = false): bool
+ {
+ if (!$this->deletable) {
+ throw new DeletableException();
+ }
+ if ($this->useSoftDeletes && $purge === false) {
+ $this->ignoreDeleted()
+ ->set($this->deletedField, date($this->timestampFormat));
+
+ if (!empty($conditions)) {
+ foreach ($conditions as $column => $value) {
+ if (is_string($column)) {
+ $this->crud->getQueryBuilder()->where($column, $value);
+ } else {
+ $this->crud->getQueryBuilder()->where($value);
+ }
+ }
+ }
+
+ return $this->crud->update($this->schema);
+ }
+
+ return $this->crud->delete($this->schema, $conditions);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function save(EntityInterface $entity): bool
+ {
+ $data = $entity->toArray();
+
+ return !empty($this->schemaId) && isset($data[$this->schemaId]) ? $this->update($data) : $this->create($data);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function ignoreDeleted(): self
+ {
+ $this->useSoftDeletes && $this->crud->getQueryBuilder()
+ ->whereIsNull($this->deletedField);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function onlyDeleted(): self
+ {
+ $this->useSoftDeletes && $this->crud->getQueryBuilder()
+ ->whereIsNotNull($this->deletedField);
+
+ return $this;
+ }
+
+}
diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php
deleted file mode 100644
index 780509c..0000000
--- a/src/QueryBuilder.php
+++ /dev/null
@@ -1,1618 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.1
- * @link https://www.muhammetsafak.com.tr
- */
-declare(strict_types=1);
-
-namespace InitPHP\Database;
-
-use \InitPHP\Database\Helpers\{Helper, Parameters};
-use \InitPHP\Database\Exceptions\{QueryBuilderException, QueryGeneratorException, ValueException};
-
-class QueryBuilder
-{
-
- protected const STRUCTURE = [
- 'select' => [],
- 'table' => [],
- 'join' => [],
- 'where' => [
- 'AND' => [],
- 'OR' => [],
- ],
- 'having' => [
- 'AND' => [],
- 'OR' => [],
- ],
- 'group_by' => [],
- 'order_by' => [],
- 'offset' => null,
- 'limit' => null,
- 'set' => [],
- 'on' => [
- 'AND' => [],
- 'OR' => [],
- ],
- ];
-
- protected array $_STRUCTURE = self::STRUCTURE;
-
- protected bool $isOnlyDeletes = false;
-
- public function reset(): void
- {
- $this->_STRUCTURE = self::STRUCTURE;
- }
-
- public function importQB(array $structure): self
- {
- $this->_STRUCTURE = $structure;
-
- return $this;
- }
-
- public function exportQB(): array
- {
- return $this->_STRUCTURE;
- }
-
- /**
- * @param string $key
- * @param string|int|float|bool|null $value
- * @return $this
- */
- final public function setParameter(string $key, $value): self
- {
- Parameters::set($key, $value);
- return $this;
- }
-
- /**
- * @param array $parameters
- * @return $this
- */
- final public function setParameters(array $parameters = []): self
- {
- foreach ($parameters as $key => $value) {
- Parameters::set($key, $value);
- }
- return $this;
- }
-
- /**
- * @param string|Raw ...$columns
- * @return $this
- */
- final public function select(...$columns): self
- {
- foreach ($columns as $column) {
- $this->_STRUCTURE['select'][] = (string)$column;
- }
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectCount($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'COUNT(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectMax($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'MAX(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectMin($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'MIN(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectAvg($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'AVG(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string $alias
- * @return $this
- */
- final public function selectAs($column, string $alias): self
- {
- $this->_STRUCTURE['select'][] = $column . ' AS ' . $alias;
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectUpper($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'UPPER(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectLower($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'LOWER(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectLength($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'LENGTH(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param int $offset
- * @param int $length
- * @param string|null $alias
- * @return $this
- */
- final public function selectMid($column, int $offset, int $length, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'MID(' . $column . ', ' . $offset . ', ' . $length . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param int $length
- * @param string|null $alias
- * @return $this
- */
- final public function selectLeft($column, int $length, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'LEFT(' . $column . ', ' . $length . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param int $length
- * @param string|null $alias
- * @return $this
- */
- final public function selectRight($column, int $length, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'RIGHT(' . $column . ', ' . $length . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param string|null $alias
- * @return $this
- */
- final public function selectDistinct($column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'DISTINCT(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw $column
- * @param mixed $default
- * @param string|null $alias
- * @return $this
- */
- final public function selectCoalesce($column, $default = '0', ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'COALESCE(' . $column . ', ' . $default . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- final public function selectSum(string $column, ?string $alias = null): self
- {
- $this->_STRUCTURE['select'][] = 'SUM(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|null $alias
- * @param string|Raw ...$columnOrStr
- * @return $this
- */
- final public function selectConcat(?string $alias = null, ...$columnOrStr): self
- {
- foreach ($columnOrStr as &$item) {
- $item = (string)$item;
- }
-
- $this->_STRUCTURE['select'][] = 'CONCAT(' . \implode(', ', $columnOrStr) . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @param string|Raw ...$tables
- * @return $this
- */
- final public function from(...$tables): self
- {
- $this->_STRUCTURE['table'] = [];
- $this->addFrom(...$tables);
-
- return $this;
- }
-
- /**
- * @param string|Raw ...$tables
- * @return $this
- */
- final public function addFrom(...$tables): self
- {
- foreach ($tables as $table) {
- $table = (string)$table;
- if(!\in_array($table, $this->_STRUCTURE['table'], true)){
- $this->_STRUCTURE['table'][] = $table;
- }
- }
- return $this;
- }
-
- /**
- * @param string|Raw $table
- * @return $this
- */
- final public function table($table): self
- {
- $this->_STRUCTURE['table'] = [(string)$table];
-
- return $this;
- }
-
- /**
- * @param string|Raw ...$columns
- * @return $this
- */
- final public function groupBy(...$columns): self
- {
- foreach ($columns as $column){
- $column = (string)$column;
- if(!\in_array($column, $this->_STRUCTURE['group_by'])){
- $this->_STRUCTURE['group_by'][] = $column;
- }
- }
- return $this;
- }
-
- /**
- * @param string|Raw $table
- * @param string|Raw|\Closure|null $onStmt
- * @param string $type
- * @return $this
- */
- final public function join($table, $onStmt = null, string $type = 'INNER'): self
- {
- $table = (string)$table;
-
- if ($onStmt instanceof \Closure) {
- $queryBuilder = new self();
- $queryBuilder->_STRUCTURE = self::STRUCTURE;
- $onStmt = \call_user_func_array($onStmt, [$queryBuilder]);
- if ($onStmt === null) {
- if ($where = $queryBuilder->__generateWhereQuery()) {
- $this->where($this->raw($where));
- }
- if ($having = $queryBuilder->__generateHavingQuery()) {
- if (Helper::str_starts_with($having, ' HAVING ')) {
- $having = \substr($having, 8);
- }
- $this->having($this->raw($having));
- }
- $onStmt = $queryBuilder->__generateOnQuery();
- }
-
- }
-
- $type = \trim(\strtoupper($type));
- switch ($type) {
- case 'SELF' :
- $this->addFrom($table);
- $onStmt !== null && $this->where((\is_string($onStmt) ? $this->raw($onStmt) : $onStmt));
- break;
- case 'NATURAL':
- case 'NATURAL JOIN':
- $this->_STRUCTURE['join'][$table] = 'NATURAL JOIN ' . $table;
- break;
- default:
- $this->_STRUCTURE['join'][$table] = \trim(($type . ' JOIN ' . $table . ' ON ' . $onStmt));
- }
- return $this;
- }
-
- /**
- * @param string|Raw $table
- * @param string|Raw $onStmt
- * @return $this
- */
- final public function selfJoin($table, $onStmt): self
- {
- return $this->join($table, $onStmt, 'SELF');
- }
-
- /**
- * @param string|Raw $table
- * @param string|Raw $onStmt
- * @return $this
- */
- final public function innerJoin($table, $onStmt): self
- {
- return $this->join($table, $onStmt, 'INNER');
- }
-
- /**
- * @param string|Raw $table
- * @param string|Raw $onStmt
- * @return $this
- */
- final public function leftJoin($table, $onStmt): self
- {
- return $this->join($table, $onStmt, 'LEFT');
- }
-
- /**
- * @param string|Raw $table
- * @param string|Raw $onStmt
- * @return $this
- */
- final public function rightJoin($table, $onStmt): self
- {
- return $this->join($table, $onStmt, 'RIGHT');
- }
-
- /**
- * @param string|Raw $table
- * @param string|Raw $onStmt
- * @return $this
- */
- final public function leftOuterJoin($table, $onStmt): self
- {
- return $this->join($table, $onStmt, 'LEFT OUTER');
- }
-
- /**
- * @param string|Raw $table
- * @param string|Raw $onStmt
- * @return $this
- */
- final public function rightOuterJoin($table, $onStmt): self
- {
- return $this->join($table, $onStmt, 'RIGHT OUTER');
- }
-
- /**
- * @param string|Raw $table
- * @return $this
- */
- final public function naturalJoin($table): self
- {
- return $this->join($table, null, 'NATURAL');
- }
-
- /**
- * @param string|Raw $column
- * @param string $soft [ASC|DESC]
- * @return $this
- */
- final public function orderBy($column, string $soft = 'ASC'): self
- {
- $soft = \trim(\strtoupper($soft));
- if(!\in_array($soft, ['ASC', 'DESC'], true)){
- throw new \InvalidArgumentException('It can only sort as ASC or DESC.');
- }
- $orderBy = \trim((string)$column) . ' ' . $soft;
- if(!\in_array($orderBy, $this->_STRUCTURE['order_by'], true)){
- $this->_STRUCTURE['order_by'][] = $orderBy;
- }
-
- return $this;
- }
-
- /**
- * @param Raw|string $column
- * @param mixed $value
- * @param string $mark [=|!=|>|<|>=|<=]
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function where($column, $value = null, string $mark = '=', string $logical = 'AND'): self
- {
- $logical = \strtoupper(\strtr($logical, ['&&' => 'AND', '||' => 'OR']));
- if(!\in_array($logical, ['AND', 'OR'], true)){
- throw new \InvalidArgumentException('Logical operator OR, AND, && or || it could be.');
- }
-
- $this->_STRUCTURE['where'][$logical][] = $this->whereOrHavingStatementPrepare($column, $value, $mark);
-
- return $this;
- }
-
- /**
- * @param Raw|string $column
- * @param mixed $value
- * @param string $mark [=|!=|>|<|>=|<=]
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function having($column, $value = null, string $mark = '=', string $logical = 'AND'): self
- {
- $logical = \strtoupper(\strtr($logical, ['&&' => 'AND', '||' => 'OR']));
- if(!\in_array($logical, ['AND', 'OR'], true)){
- throw new \InvalidArgumentException('Logical operator OR, AND, && or || it could be.');
- }
-
- $this->_STRUCTURE['having'][$logical][] = $this->whereOrHavingStatementPrepare($column, $value, $mark);
-
- return $this;
- }
-
- final public function on($column, $value = null, string $mark = '=', string $logical = 'AND'): self
- {
- $logical = \strtoupper(\strtr($logical, ['&&' => 'AND', '||' => 'OR']));
- if (!\in_array($logical, ['AND', 'OR'])) {
- throw new \InvalidArgumentException('Logical operator OR, AND, && or || it could be.');
- }
-
- $this->_STRUCTURE['on'][$logical][] = $this->whereOrHavingStatementPrepare($column, $value, $mark);
-
- return $this;
- }
-
- /**
- * @param string|Raw|array $column
- * @param mixed $value
- * @param bool $strict
- * @return $this
- * @throws QueryBuilderException
- */
- final public function set($column, $value = null, bool $strict = true): self
- {
- return $this->addSet($column, $value, $strict);
- }
-
- /**
- * @param string|Raw|array $column
- * @param mixed $value
- * @param bool $strict
- * @return $this
- * @throws QueryBuilderException
- */
- final public function addSet($column, $value = null, bool $strict = true): self
- {
- if (\is_array($column) && $value === null) {
- $set = [];
- foreach ($column as $name => $value) {
- $this->checkSetData($name, $value, $strict);
- $set[$name] = $value;
- }
- $this->_STRUCTURE['set'][] = $set;
- } else {
- $this->checkSetData($column, $value, $strict);
- $this->_STRUCTURE['set'][][$column] = $value;
- }
-
- return $this;
- }
-
- /**
- * @param string $column
- * @return bool
- */
- public function isAllowedFields(string $column): bool
- {
- return !($this instanceof Database) || $this->isAllowedFields($column);
- }
-
- /**
- * @param string|Raw $column
- * @param mixed $value
- * @param string $mark [=|!=|>|<|>=|<=]
- * @return $this
- */
- final public function andWhere($column, $value, string $mark = '='): self
- {
- return $this->where($column, $value, $mark, 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param mixed $value
- * @param string $mark [=|!=|>|<|>=|<=]
- * @return $this
- */
- final public function orWhere($column, $value, string $mark = '='): self
- {
- return $this->where($column, $value, $mark, 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $values
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function between($column, $values, string $logical = 'AND'): self
- {
- return $this->where($column, $values, 'BETWEEN', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $values
- * @return $this
- */
- final public function orBetween($column, $values): self
- {
- return $this->where($column, $values, 'BETWEEN', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $values
- * @return $this
- */
- final public function andBetween($column, $values): self
- {
- return $this->where($column, $values, 'BETWEEN', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $values
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function notBetween($column, $values, string $logical = 'AND'): self
- {
- return $this->where($column, $values, 'NOTBETWEEN', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $values
- * @return $this
- */
- final public function orNotBetween($column, $values): self
- {
- return $this->where($column, $values, 'NOTBETWEEN', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $values
- * @return $this
- */
- final public function andNotBetween($column, $values): self
- {
- return $this->where($column, $values, 'NOTBETWEEN', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function findInSet($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'FINDINSET', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function orFindInSet($column, $value): self
- {
- return $this->where($column, $value, 'FINDINSET', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function andFindInSet($column, $value): self
- {
- return $this->where($column, $value, 'FINDINSET', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function notFindInSet(string $column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'NOTFINDINSET', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function andNotFindInSet($column, $value): self
- {
- return $this->where($column, $value, 'NOTFINDINSET', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function orNotFindInSet($column, $value): self
- {
- return $this->where($column, $value, 'NOTFINDINSET', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function in($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'IN', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function orIn($column, $value): self
- {
- return $this->where($column, $value, 'IN', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function andIn($column, $value): self
- {
- return $this->where($column, $value, 'IN', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function notIn($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'NOTIN', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function orNotIn($column, $value): self
- {
- return $this->where($column, $value, 'NOTIN', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param array|Raw|string $value
- * @return $this
- */
- final public function andNotIn($column, $value): self
- {
- return $this->where($column, $value, 'NOTIN', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function regexp($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'REGEXP', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andRegexp($column, $value): self
- {
- return $this->where($column, $value, 'REGEXP', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orRegexp($column, $value): self
- {
- return $this->where($column, $value, 'REGEXP', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function like($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'LIKE', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orLike($column, $value): self
- {
- return $this->where($column, $value, 'LIKE', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andLike($column, $value): self
- {
- return $this->where($column, $value, 'LIKE', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function startLike($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'STARTLIKE', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orStartLike($column, $value): self
- {
- return $this->where($column, $value, 'STARTLIKE', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andStartLike($column, $value): self
- {
- return $this->where($column, $value, 'STARTLIKE', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function endLike($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'ENDLIKE', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orEndLike($column, $value): self
- {
- return $this->where($column, $value, 'ENDLIKE', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andEndLike($column, $value): self
- {
- return $this->where($column, $value, 'ENDLIKE', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function notLike($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'NOTLIKE', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orNotLike($column, $value): self
- {
- return $this->where($column, $value, 'NOTLIKE', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andNotLike($column, $value): self
- {
- return $this->where($column, $value, 'NOTLIKE', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function startNotLike($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'STARTNOTLIKE', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orStartNotLike($column, $value): self
- {
- return $this->where($column, $value, 'STARTNOTLIKE', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andStartNotLike($column, $value): self
- {
- return $this->where($column, $value, 'STARTNOTLIKE', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function endNotLike($column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'ENDNOTLIKE', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orEndNotLike($column, $value): self
- {
- return $this->where($column, $value, 'ENDNOTLIKE', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andEndNotLike($column, $value): self
- {
- return $this->where($column, $value, 'ENDNOTLIKE', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function soundex(string $column, $value, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'SOUNDEX', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function orSoundex(string $column, $value): self
- {
- return $this->where($column, $value, 'SOUNDEX', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param string|Raw $value
- * @return $this
- */
- final public function andSoundex($column, $value): self
- {
- return $this->where($column, $value, 'SOUNDEX', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param null $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function is($column, $value = null, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'IS', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param null $value
- * @return $this
- */
- final public function orIs($column, $value = null): self
- {
- return $this->where($column, $value, 'IS', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param null $value
- * @return $this
- */
- final public function andIs($column, $value = null): self
- {
- return $this->where($column, $value, 'IS', 'AND');
- }
-
- /**
- * @param string|Raw $column
- * @param null $value
- * @param string $logical [AND|OR]
- * @return $this
- */
- final public function isNot($column, $value = null, string $logical = 'AND'): self
- {
- return $this->where($column, $value, 'ISNOT', $logical);
- }
-
- /**
- * @param string|Raw $column
- * @param null $value
- * @return $this
- */
- final public function orIsNot($column, $value = null): self
- {
- return $this->where($column, $value, 'ISNOT', 'OR');
- }
-
- /**
- * @param string|Raw $column
- * @param null $value
- * @return $this
- */
- final public function andIsNot($column, $value = null): self
- {
- return $this->where($column, $value, 'ISNOT', 'AND');
- }
-
- /**
- * @param int $offset
- * @return $this
- */
- final public function offset(int $offset = 0): self
- {
- $this->_STRUCTURE['offset'] = (int)\abs($offset);
- return $this;
- }
-
- /**
- * @param int $limit
- * @return $this
- */
- final public function limit(int $limit): self
- {
- $this->_STRUCTURE['limit'] = (int)\abs($limit);
-
- return $this;
- }
-
- final public function exceptDeletedData(): self
- {
- if (!($this instanceof Database)) {
- return $this;
- }
- if (empty($this->getCredentials('deletedField'))) {
- return $this;
- }
-
- return $this->isNot($this->getCredentials('deletedField'), null);
- }
-
- final public function onlyDeletedData(): self
- {
- if (!($this instanceof Database)) {
- return $this;
- }
- if (empty($this->getCredentials('deletedField'))) {
- return $this;
- }
-
- return $this->is($this->getCredentials('deletedField'), null);
- }
-
- final public function raw(string $rawQuery): Raw
- {
- return new Raw($rawQuery);
- }
-
- final public function subQuery(\Closure $closure, ?string $alias = null, bool $isIntervalQuery = true): Raw
- {
- $queryBuilder = new self();
- \call_user_func_array($closure, [$queryBuilder]);
-
- if ($alias !== null && $isIntervalQuery !== TRUE) {
- throw new QueryBuilderException('To define alias to a subquery, it must be an inner query.');
- }
-
- $rawQuery = ($isIntervalQuery === TRUE ? '(' : '')
- . $queryBuilder->generateSelectQuery()
- . ($isIntervalQuery === TRUE ? ')' : '')
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this->raw($rawQuery);
- }
-
- final public function generateInsertQuery(): string
- {
- if (!empty($this->_STRUCTURE['table'])) {
- $table = \end($this->_STRUCTURE['table']);
- } elseif ($this instanceof Database) {
- $table = $this->getSchema();
-
- $createdField = $this->getCredentials('createdField');
- if (!empty($createdField)) {
- $this->addSet($createdField, \date($this->getCredentials('timestampFormat')), false);
- }
- }
-
- if (!isset($table)) {
- throw new QueryGeneratorException('Table name not found when creating insert query.');
- }
-
- $columns = [];
- $values = [];
- $set = array_merge(...$this->_STRUCTURE['set']);
-
- foreach ($set as $column => $value) {
- $columns[] = $column;
- $values[] = $value;
- }
- if (empty($columns)) {
- throw new QueryGeneratorException('The data set for the insert could not be found.');
- }
-
- return 'INSERT INTO ' . $table . ' (' . \implode(', ', $columns) . ') VALUES (' . \implode(', ', $values) . ');';
- }
-
- final public function generateBatchInsertQuery(): string
- {
- $singleStartValue = [];
- if (!empty($this->_STRUCTURE['table'])) {
- $table = \end($this->_STRUCTURE['table']);
- } elseif ($this instanceof Database) {
- $table = $this->getSchema();
-
- $createdField = $this->getCredentials('createdField');
- if (!empty($createdField)) {
- $singleStartValue = [
- $createdField => "'" . \date($this->getCredentials('timestampFormat')) . "'",
- ];
- }
- }
-
- if (!isset($table)) {
- throw new QueryGeneratorException('Table name not found when creating insert query.');
- }
-
- $columns = \array_keys(\array_merge(...$this->_STRUCTURE['set']));
-
- if (empty($columns)) {
- throw new QueryGeneratorException('The data set for the insert could not be found.');
- }
-
- $values = [];
- foreach ($this->_STRUCTURE['set'] as $set) {
- $value = $singleStartValue;
- foreach ($columns as $column) {
- $value[$column] = $set[$column] ?? 'NULL';
- }
- $values[] = '(' . \implode(', ', $value) . ')';
- }
-
- return 'INSERT INTO ' . $table . ' (' . \implode(', ', $columns) . ') VALUES ' . \implode(', ', $values) . ';';
- }
-
- final public function generateSelectQuery(array $selector = [], array $conditions = []): string
- {
- if(!empty($selector)){
- $this->select(...$selector);
- }
- if(!empty($conditions)){
- foreach ($conditions as $column => $value) {
- if (\is_string($column)) {
- $this->where($column, $value);
- } else {
- $this->where($value);
- }
- }
- }
- if ($this instanceof Database) {
- $table = $this->getSchema();
- }
- if (empty($this->_STRUCTURE['table']) && isset($table)) {
- $this->_STRUCTURE['table'][] = $table;
- }
-
- if (empty($this->_STRUCTURE['table'])) {
- throw new QueryGeneratorException('Table name not found.');
- }
- $this->__generateSoftDeleteQuery();
-
- return 'SELECT '
- . (empty($this->_STRUCTURE['select']) ? '*' : \implode(', ', $this->_STRUCTURE['select']))
- . ' FROM '
- . \implode(', ', $this->_STRUCTURE['table'])
- . (!empty($this->_STRUCTURE['join']) ? ' ' . \implode(' ', $this->_STRUCTURE['join']) : '')
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) ? $where : '1')
- . (!empty($this->_STRUCTURE['group_by']) ? ' GROUP BY ' . \implode(', ', $this->_STRUCTURE['group_by']) : '')
- . ($this->__generateHavingQuery() ?? '')
- . (!empty($this->_STRUCTURE['order_by']) ? ' ORDER BY ' . \implode(', ', $this->_STRUCTURE['order_by']) : '')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- final public function generateUpdateQuery(): string
- {
- $primaryKey = null;
- if (!empty($this->_STRUCTURE['table'])) {
- $table = \end($this->_STRUCTURE['table']);
- } elseif ($this instanceof Database) {
- $table = $this->getSchema();
-
- $updatedField = $this->getCredentials('updatedField');
- if (!empty($updatedField)) {
- $this->addSet($updatedField, \date($this->getCredentials('timestampFormat')), false);
- }
- $primaryKey = $this->getSchemaID();
- }
- if (!isset($table)) {
- throw new QueryGeneratorException('Table name not found.');
- }
-
- $set = array_merge(...$this->_STRUCTURE['set']);
-
- $updateSet = [];
- foreach ($set as $column => $value) {
- if ($primaryKey !== null && $column === $primaryKey) {
- $this->where($column, $value);
- continue;
- }
- $updateSet[] = $column . ' = ' . $value;
- }
- if (empty($updateSet)) {
- throw new QueryGeneratorException('The data set for the insert could not be found.');
- }
- $this->exceptDeletedData();
-
- return 'UPDATE ' . $table . ' SET ' . \implode(', ', $updateSet)
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) ? $where : '1')
- . ($this->__generateHavingQuery() ?? '')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- final public function generateUpdateBatchQuery(string $referenceColumn)
- {
- $primaryKey = null;
- $update = [];
- if (!empty($this->_STRUCTURE['table'])) {
- $table = \end($this->_STRUCTURE['table']);
- } elseif ($this instanceof Database) {
- $table = $this->getSchema();
-
- $updatedField = $this->getCredentials('updatedField');
- if (!empty($updatedField)) {
- $update[] = $updatedField . " = '" . \date($this->getCredentials('timestampFormat')) . "'";
- }
- $primaryKey = $this->getSchemaID();
- }
- if (!isset($table)) {
- throw new QueryGeneratorException('Table name not found.');
- }
-
- $data = $this->_STRUCTURE['set'];
-
- $updateData = [];
- $columns = [];
- $where = [];
- foreach ($data as $set) {
- if (!isset($set[$referenceColumn])) {
- throw new QueryGeneratorException('The reference column does not exist in one or more of the set arrays.');
- }
- $setData = [];
- foreach ($set as $key => $value) {
- if ($primaryKey !== null && $key === $primaryKey) {
- continue;
- }
- if ($key == $referenceColumn) {
- $where[] = $value;
- continue;
- }
- $setData[$key] = $value;
- if (!\in_array($key, $columns)) {
- $columns[] = $key;
- }
- }
- $updateData[] = $setData;
- }
-
- 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];
- }
- $update[] = $syntax . ' ELSE ' . $column . ' END';
- }
-
- $this->in($referenceColumn, $where)
- ->exceptDeletedData();
-
- return 'UPDATE ' . $table . ' SET ' . \implode(', ', $update)
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) ? $where : '1')
- . ($this->__generateHavingQuery() ?? '')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- final public function generateDeleteQuery(): string
- {
- if (!empty($this->_STRUCTURE['table'])) {
- $table = \end($this->_STRUCTURE['table']);
- } elseif ($this instanceof Database) {
- $table = $this->getSchema();
- }
- if (!isset($table)) {
- throw new QueryGeneratorException('Table name not found.');
- }
-
- return 'DELETE FROM'
- . ' '
- . $table
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) !== null ? $where : '1')
- . ($this->__generateHavingQuery() ?? '')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- protected function __generateHavingQuery(): ?string
- {
- $isAndEmpty = empty($this->_STRUCTURE['having']['AND']);
- $isOrEmpty = empty($this->_STRUCTURE['having']['OR']);
- if($isAndEmpty && $isOrEmpty){
- return null;
- }
- return ' HAVING '
- . (!$isAndEmpty ? \implode(' AND ', $this->_STRUCTURE['having']['AND']) : '')
- . (!$isAndEmpty && !$isOrEmpty ? ' AND ' : '')
- . (!$isOrEmpty ? \implode(' OR ', $this->_STRUCTURE['having']['OR']) : '');
- }
-
- protected function __generateWhereQuery(): ?string
- {
- $isAndEmpty = empty($this->_STRUCTURE['where']['AND']);
- $isOrEmpty = empty($this->_STRUCTURE['where']['OR']);
- if($isAndEmpty && $isOrEmpty){
- return null;
- }
- return (!$isAndEmpty ? \implode(' AND ', $this->_STRUCTURE['where']['AND']) : '')
- . (!$isAndEmpty && !$isOrEmpty ? ' AND ' : '')
- . (!$isOrEmpty ? \implode(' OR ', $this->_STRUCTURE['where']['OR']) : '');
- }
-
- protected function __generateOnQuery(): ?string
- {
- $isAndEmpty = empty($this->_STRUCTURE['on']['AND']);
- $isOrEmpty = empty($this->_STRUCTURE['on']['OR']);
- if($isAndEmpty && $isOrEmpty){
- return null;
- }
- return (!$isAndEmpty ? \implode(' AND ', $this->_STRUCTURE['on']['AND']) : '')
- . (!$isAndEmpty && !$isOrEmpty ? ' AND ' : '')
- . (!$isOrEmpty ? \implode(' OR ', $this->_STRUCTURE['on']['OR']) : '');
- }
-
- protected function __generateLimitQuery(): ?string
- {
- if($this->_STRUCTURE['limit'] === null && $this->_STRUCTURE['offset'] === null){
- return null;
- }
- $sql = ' LIMIT ';
- if($this->_STRUCTURE['offset'] !== null){
- $sql .= $this->_STRUCTURE['offset'] . ', ';
- }
- $sql .= $this->_STRUCTURE['limit'] ?? '10000';
- return $sql;
- }
-
- protected function __generateSoftDeleteQuery(bool $reset = true): void
- {
- if ($this->isOnlyDeletes) {
- $this->onlyDeletedData();
- } else {
- $this->exceptDeletedData();
- }
- if ($reset) {
- $this->isOnlyDeletes = false;
- }
- }
-
- /**
- * @param string|Raw $column
- * @param mixed $value
- * @param string $mark
- * @return string
- * @throws ValueException
- */
- private function whereOrHavingStatementPrepare($column, $value, string $mark = '='): string
- {
- $mark = \trim($mark);
- if ($value !== null && \in_array($mark, ['=', '!=', '<=', '>=', '>', '<'], true)) {
- return $column . ' ' . $mark . ' '
- . (Helper::isSQLParameterOrFunction($value) ? $value : Parameters::add($column, $value));
- }
-
- $markUpperCase = \strtoupper($mark);
- $searchMark = \str_replace([' ', '_'], '', $markUpperCase);
-
- if ($value === null && !\in_array($searchMark, ['IS', 'ISNOT'])) {
- return (string)$column;
- }
-
- switch ($searchMark) {
- case 'IS':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = $value !== null ? Parameters::add($column, $value) : 'NULL';
- }
- return $column . ' IS ' . $value;
- case 'ISNOT':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = $value !== null ? Parameters::add($column, $value) : 'NULL';
- }
- return $column . ' IS NOT ' . $value;
- case 'LIKE':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = (substr($value, -1) == '%' || substr($value, 0, 1) == '%')
- ? $value
- : '%' . $value . '%';
-
- $value = Parameters::add($column, $value);
- }
- return $column . ' LIKE ' . $value;
- case 'STARTLIKE':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = Parameters::add($column, '%' . trim($value, '%'));
- }
- return $column . ' LIKE ' . $value;
- case 'ENDLIKE':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = Parameters::add($column, trim($value, '%') . '%');
- }
- return $column . ' LIKE ' . $value;
- case 'NOTLIKE':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = (substr($value, -1) == '%' || substr($value, 0, 1) == '%')
- ? $value
- : '%' . $value . '%';
-
- $value = Parameters::add($column, $value);
- }
- return $column . ' NOT LIKE ' . $value;
- case 'STARTNOTLIKE':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = Parameters::add($column, '%' . trim($value, '%'));
- }
- return $column . ' NOT LIKE ' . $value;
- case 'ENDNOTLIKE':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = Parameters::add($column, trim($value, '%') . '%');
- }
- return $column . ' NOT LIKE ' . $value;
- case 'REGEXP':
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = Parameters::add($column, $value);
- }
- return $column . ' REGEXP ' . $value;
- case 'BETWEEN':
- case 'NOTBETWEEN':
- if (\is_array($value) && \count($value) == 2) {
- $valueStmt = (Helper::isSQLParameterOrFunction($value[0]) ? $value[0] : Parameters::add($column, $value[0]))
- . ' AND '
- . (Helper::isSQLParameterOrFunction($value[1]) ? $value[1] : Parameters::add($column, $value[1]));
- } elseif (Helper::isSQLParameterOrFunction($value)) {
- $valueStmt = (string)$value;
- } else {
- throw new ValueException('An incorrect value was defined.');
- }
- return $column . ' '
- . ($searchMark === 'NOTBETWEEN' ? 'NOT ':'')
- . 'BETWEEN ' . $valueStmt;
- case 'IN':
- case 'NOTIN':
- if(\is_array($value)){
- $values = [];
- foreach ($value as $val) {
- if(\is_numeric($val) || \is_int($val)){
- if (!\in_array($val, $values)) {
- $values[] = $val;
- }
- continue;
- }
- if($val === null){
- if (!\in_array('NULL', $values)) {
- $values[] = 'NULL';
- }
- continue;
- }
- $values[] = Helper::isSQLParameterOrFunction($val) ? $val : Parameters::add($column, $val);
- }
- $value = '(' . \implode(', ', \array_unique($values)) . ')';
- } elseif (Helper::isSQLParameterOrFunction($value)) {
- $value = (string)$value;
- }else{
- throw new ValueException('An incorrect value was defined.');
- }
- return $column
- . ($searchMark === 'NOTIN' ? ' NOT ' : ' ')
- . 'IN ' . $value;
- case 'FINDINSET':
- case 'NOTFINDINSET':
- if(\is_array($value)){
- $value = \implode(", ", $value);
- } elseif (!Helper::isSQLParameterOrFunction($value)) {
- $value = Parameters::add($column, $value);
- }
- return ($searchMark === 'NOTFINDINSET' ? 'NOT ' : '')
- . 'FIND_IN_SET(' . $value . ', ' . $column . ')';
- case 'SOUNDEX':
- if(!\is_string($value) && !($value instanceof Raw)){
- throw new ValueException('Only a string value can be defined for Soundex.');
- }
- if(!Helper::isSQLParameterOrFunction($value)){
- $value = Parameters::add($column, $value);
- }
- return "SOUNDEX(" . $column . ") LIKE CONCAT('%', TRIM(TRAILING '0' FROM SOUNDEX(" . $value . ")), '%')";
- }
- if($value === null && (bool)\preg_match('/([\w_]+)\((.+)\)$/iu', $column, $matches) !== FALSE){
- return \strtoupper($matches[1]) . '(' . $matches[2] . ')';
- }
- return $column . ' ' . $mark . ' ' . Parameters::add($column, $value);
- }
-
- private function checkSetData (&$column, &$value, $strict): void
- {
- $allowedColumnsCheck = ($strict === TRUE) && ($this instanceof Database) && !($column instanceof Raw);
- if ($allowedColumnsCheck && !$this->isAllowedFields($column)) {
- throw new QueryBuilderException('"' . $column . '" is not allowed.');
- }
- $column = (string)$column;
- $value = Helper::isSQLParameterOrFunction($value) ? $value : Parameters::add($column, $value);
- }
-
-}
diff --git a/src/QueryBuilder/Exceptions/QueryBuilderException.php b/src/QueryBuilder/Exceptions/QueryBuilderException.php
new file mode 100644
index 0000000..2d5c661
--- /dev/null
+++ b/src/QueryBuilder/Exceptions/QueryBuilderException.php
@@ -0,0 +1,8 @@
+parameters = [];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function set(string $key, mixed $value): self
+ {
+ $this->parameters[':' . ltrim(str_replace('.', '', $key), ':')] = $value;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function add(RawQuery|string $key, mixed $value): string
+ {
+ if ($value === null) {
+ return 'NULL';
+ }
+ if ($key instanceof RawQuery) {
+ $key = md5((string)$key);
+ }
+ $originKey = ltrim(str_replace('.', '', $key), ':');
+ $i = 0;
+ do {
+ $key = ':' . ($i === 0 ? $originKey : $originKey . '_' . $i);
+ ++$i;
+ $hasParameter = isset($this->parameters[$key]);
+ } while($hasParameter);
+
+ $this->parameters[$key] = $value;
+
+ return $key;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function merge(array|ParameterInterface ...$arrays): self
+ {
+ foreach ($arrays as $array) {
+ if ($array instanceof ParameterInterface) {
+ $array = $array->all();
+ }
+ foreach ($array as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get(?string $key = null, mixed $default = null): mixed
+ {
+ if ($key === null) {
+ return $this->parameters;
+ }
+
+ $key = ':' . ltrim($key, ':');
+ if (isset($this->parameters[$key])) {
+ return $this->parameters[$key];
+ }
+
+ return ($default instanceof Closure) ? call_user_func_array($default, []) : $default;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function all(): array
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function reset(): self
+ {
+ $this->parameters = [];
+
+ return $this;
+ }
+
+}
diff --git a/src/QueryBuilder/QueryBuilder.php b/src/QueryBuilder/QueryBuilder.php
new file mode 100644
index 0000000..19ee7e7
--- /dev/null
+++ b/src/QueryBuilder/QueryBuilder.php
@@ -0,0 +1,1455 @@
+ [],
+ 'table' => [],
+ 'join' => [],
+ 'where' => [
+ 'AND' => [],
+ 'OR' => [],
+ ],
+ 'having' => [
+ 'AND' => [],
+ 'OR' => [],
+ ],
+ 'group_by' => [],
+ 'order_by' => [],
+ 'offset' => null,
+ 'limit' => null,
+ 'set' => [],
+ 'on' => [
+ 'AND' => [],
+ 'OR' => [],
+ ],
+ ];
+
+ protected array $structure;
+
+ protected ParameterInterface $parameters;
+
+ public function __construct()
+ {
+ $this->structure = self::STRUCTURE;
+ $this->parameters = new Parameters();
+ }
+
+ /**
+ * @return string
+ * @throws QueryGeneratorException
+ */
+ public function __toString(): string
+ {
+ if (empty($this->structure['set'])) {
+ return $this->generateSelectQuery();
+ }
+
+ $isBatch = $this->isBatch();
+ $isInsert = empty($this->structure['where']['OR']) && empty($this->structure['where']['AND']) && empty($this->structure['having']['OR']) && empty($this->structure['having']['AND']);
+
+ if ($isInsert) {
+ return $isBatch ? $this->generateBatchInsertQuery() : $this->generateInsertQuery();
+ }
+
+ return $this->generateUpdateQuery();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function newBuilder(): self
+ {
+ return new self();
+ }
+
+ /**
+ * @param string[]|string|null $ignoreOrCare
+ * @param null|bool $isIgnore
+ * @return $this
+ */
+ public function resetStructure(null|array|string $ignoreOrCare = null, ?bool $isIgnore = null): self
+ {
+ if ($ignoreOrCare === null) {
+ $this->structure = self::STRUCTURE;
+ } else {
+ if (is_string($ignoreOrCare)) {
+ $ignoreOrCare = [$ignoreOrCare];
+ }
+
+ $newStructure = self::STRUCTURE;
+ foreach ($ignoreOrCare as $key) {
+ if (!isset($this->structure[$key])) {
+ continue;
+ }
+ if ($isIgnore) {
+ $newStructure[$key] = $this->structure[$key];
+ } else {
+ $newStructure[$key] = self::STRUCTURE[$key] ?? [];
+ }
+ }
+
+ $this->structure = $newStructure;
+ }
+
+ return $this;
+ }
+
+ public function clone(): self
+ {
+ return (clone $this);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function importQB(array $structure, bool $merge = false): self
+ {
+ $this->structure = array_merge(($merge ? $this->structure : self::STRUCTURE), $structure);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function exportQB(): array
+ {
+ return $this->structure;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getParameter(): ParameterInterface
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setParameter(string $key, mixed $value): self
+ {
+ $this->parameters->set($key, $value);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setParameters(array $parameters = []): self
+ {
+ foreach ($parameters as $key => $value) {
+ $this->parameters->set($key, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function select(...$columns): self
+ {
+ foreach ($columns as $column) {
+ $column = (string)$column;
+ $this->structure['select'][] = $column;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function clearSelect(): self
+ {
+ $this->structure['select'] = [];
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectCount(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'COUNT(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectCountDistinct(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'COUNT(DISTINCT ' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectMax(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'MAX(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectMin(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'MIN(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectAvg(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'AVG(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectAs(RawQuery|string $column, string $alias): self
+ {
+ $this->structure['select'][] = $column . ' AS ' . $alias;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectUpper(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'UPPER(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectLower(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'LOWER(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectLength(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'LENGTH(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectMid(RawQuery|string $column, int $offset, int $length, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'MID(' . $column . ', ' . $offset . ', ' . $length . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectLeft(RawQuery|string $column, int $length, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'LEFT(' . $column . ', ' . $length . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectRight(RawQuery|string $column, int $length, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'RIGHT(' . $column . ', ' . $length . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectDistinct(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'DISTINCT(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectCoalesce(RawQuery|string $column, $default = '0', ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'COALESCE(' . $column . ', ' . $default . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectSum(RawQuery|string $column, ?string $alias = null): self
+ {
+ $this->structure['select'][] = 'SUM(' . $column . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selectConcat(array $columns, ?string $alias = null): self
+ {
+ foreach ($columns as &$column) {
+ $column = (string)$column;
+ }
+ $this->structure['select'][] = 'CONCAT(' . implode(', ', $columns) . ')'
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function from(RawQuery|string $table, ?string $alias = null): self
+ {
+ $this->structure['table'] = [];
+
+ return $this->addFrom($table, $alias);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function addFrom(RawQuery|string $table, ?string $alias = null): self
+ {
+ $table = $table . ($alias !== null ? ' AS ' . $alias : '');
+ if (!in_array($table, $this->structure['table'], true)) {
+ $this->structure['table'][] = $table;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function table(RawQuery|string $table): self
+ {
+ $this->structure['table'] = [(string)$table];
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function groupBy(string|RawQuery|array ...$columns): self
+ {
+ foreach ($columns as $column) {
+ if (is_array($column)) {
+ $this->groupBy(...$column);
+ continue;
+ }
+
+ $column = (string)$column;
+ if (!in_array($column, $this->structure['group_by'])) {
+ $this->structure['group_by'][] = $column;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function join(RawQuery|string $table, RawQuery|Closure|string $onStmt = null, string $type = 'INNER'): self
+ {
+ $table = (string)$table;
+
+ if ($onStmt instanceof Closure) {
+ $builder = $this->clone()->resetStructure();
+ $onStmt = call_user_func_array($onStmt, [&$builder]);
+ if ($onStmt === null) {
+ if ($where = $builder->__generateWhereQuery()) {
+ $this->where($this->raw($where));
+ }
+ if ($having = $builder->__generateHavingQuery()) {
+ if (str_starts_with($having, ' HAVING ')) {
+ $having = substr($having, 8);
+ }
+ $this->having($this->raw($having));
+ }
+ $onStmt = $builder->__generateOnQuery();
+ }
+ }
+
+ $type = trim(strtoupper($type));
+ switch ($type) {
+ case 'SELF':
+ $this->addFrom($table);
+ $this->where(is_string($onStmt) ? $this->raw($onStmt) : $onStmt);
+ break;
+ case 'NATURAL':
+ case 'NATURAL JOIN':
+ $this->structure['join'][$table] = 'NATURAL JOIN ' . $table;
+ break;
+ default:
+ $this->structure['join'][$table] = trim($type . ' JOIN ' . $table . ' ON ' . $onStmt);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function selfJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
+ {
+ return $this->join($table, $onStmt, 'SELF');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function innerJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
+ {
+ return $this->join($table, $onStmt);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function leftJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
+ {
+ return $this->join($table, $onStmt, 'LEFT');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function rightJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
+ {
+ return $this->join($table, $onStmt, 'RIGHT');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function leftOuterJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
+ {
+ return $this->join($table, $onStmt, 'LEFT OUTER');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function rightOuterJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
+ {
+ return $this->join($table, $onStmt, 'RIGHT OUTER');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function naturalJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
+ {
+ return $this->join($table, null, 'NATURAL');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orderBy(RawQuery|string $column, string $soft = 'ASC'): self
+ {
+ $soft = trim(strtoupper($soft));
+ if (!in_array($soft, ['ASC', 'DESC'], true)) {
+ throw new InvalidArgumentException('It can only sort as ASC or DESC.');
+ }
+ $orderBy = trim((string)$column) . ' ' . $soft;
+
+ !in_array($orderBy, $this->structure['order_by'], true) && $this->structure['order_by'][] = $orderBy;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function where(RawQuery|string $column, mixed $operator = '=', mixed $value = null, string $logical = 'AND'): self
+ {
+
+ $this->whereOrHavingPrepare($operator, $value, $logical);
+
+ $this->structure['where'][$logical][] = $this->whereOrHavingStatementPrepare($column, $operator, $value);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function having(RawQuery|string $column, mixed $operator = '=', mixed $value = null, string $logical = 'AND'): self
+ {
+ $this->whereOrHavingPrepare($operator, $value, $logical);
+ $this->structure['having'][$logical][] = $this->whereOrHavingStatementPrepare($column, $operator, $value);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function on(RawQuery|string $column, mixed $operator = '=', mixed $value = null, string $logical = 'AND'): self
+ {
+ $this->whereOrHavingPrepare($operator, $value, $logical);
+
+ $this->structure['on'][$logical][] = $this->whereOrHavingStatementPrepare($column, $operator, $value);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function set(RawQuery|array|string $column, mixed $value = null, bool $strict = true): self
+ {
+ return $this->addSet($column, $value, $strict);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function addSet(RawQuery|array|string $column, mixed $value = null, bool $strict = true): self
+ {
+ if (is_array($column) && $value === null) {
+ $set = [];
+ foreach ($column as $name => $value) {
+ $name = (string)$name;
+ $set[$name] = $this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($name, $value);
+ }
+ $this->structure['set'][] = $set;
+
+ return $this;
+ }
+
+ $column = (string)$column;
+ $value = $this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value);
+
+ $this->structure['set'][][$column] = $value;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andWhere(RawQuery|string $column, mixed $operator = '=', mixed $value = null): self
+ {
+ return $this->where($column, $operator, $value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orWhere(RawQuery|string $column, mixed $operator = '=', mixed $value = null): self
+ {
+ return $this->where($column, $operator, $value, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function between(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND'): self
+ {
+ if (is_array($firstValue) && count($firstValue) == 2 && $lastValue === null) {
+ $value = $firstValue;
+ } else {
+ $value = [$firstValue, $lastValue];
+ }
+
+ return $this->where($column, 'BETWEEN', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
+ {
+ return $this->between($column, [$firstValue, $lastValue], 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
+ {
+ return $this->between($column, $firstValue, $lastValue);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function notBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND'): self
+ {
+ if (is_array($firstValue) && count($firstValue) == 2 && $lastValue === null) {
+ $value = $firstValue;
+ } else {
+ $value = [$firstValue, $lastValue];
+ }
+
+ return $this->where($column, 'NOT BETWEEN', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orNotBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
+ {
+ return $this->notBetween($column, $firstValue, $lastValue, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andNotBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
+ {
+ return $this->notBetween($column, $firstValue, $lastValue);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function findInSet(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'FIND_IN_SET', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andFindInSet(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'FIND_IN_SET', $value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orFindInSet(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'FIND_IN_SET', $value, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function notFindInSet(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'NOT FIND_IN_SET', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andNotFindInSet(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'NOT FIND_IN_SET', $value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orNotFindInSet(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'NOT FIND_IN_SET', $value, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function whereIn(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'IN', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function whereNotIn(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'NOT IN', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orWhereIn(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'IN', $value, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orWhereNotIn(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'NOT IN', $value, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andWhereIn(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'IN', $value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andWhereNotIn(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'IN', $value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function regexp(RawQuery|string $column, RawQuery|string $value, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'REGEXP', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andRegexp(RawQuery|string $column, RawQuery|string $value): self
+ {
+ return $this->where($column, 'REGEXP', $value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orRegexp(RawQuery|string $column, RawQuery|string $value): self
+ {
+ return $this->where($column, 'REGEXP', $value, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function soundex(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'SOUNDEX', $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andSoundex(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'SOUNDEX', $value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orSoundex(RawQuery|string $column, mixed $value = null): self
+ {
+ return $this->where($column, 'SOUNDEX', $value, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function whereIsNull(RawQuery|string $column, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'IS', null, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orWhereIsNull(RawQuery|string $column): self
+ {
+ return $this->where($column, 'IS', null, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andWhereIsNull(RawQuery|string $column): self
+ {
+ return $this->where($column, 'IS');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function whereIsNotNull(RawQuery|string $column, string $logical = 'AND'): self
+ {
+ return $this->where($column, 'IS NOT', null, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orWhereIsNotNull(RawQuery|string $column): self
+ {
+ return $this->where($column, 'IS NOT', null, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andWhereIsNotNull(RawQuery|string $column): self
+ {
+ return $this->where($column, 'IS NOT');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function offset(int $offset = 0): self
+ {
+ $this->structure['offset'] = (int)abs($offset);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function limit(int $limit): self
+ {
+ $this->structure['limit'] = (int)abs($limit);
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function like(RawQuery|array|string $column, mixed $value = null, string $type = 'both', string $logical = 'AND'): self
+ {
+ $operator = match (strtolower($type)) {
+ 'before', 'start' => 'START LIKE',
+ 'after', 'end' => 'END LIKE',
+ default => 'LIKE'
+ };
+
+ return $this->where($column, $operator, $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
+ {
+ return $this->like($column, $value, $type);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
+ {
+ return $this->like($column, $value, $type, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function notLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both', string $logical = 'AND'): self
+ {
+ $operator = match (strtolower($type)) {
+ 'before', 'start' => 'NOT START LIKE',
+ 'after', 'end' => 'NOT END LIKE',
+ default => 'NOT LIKE'
+ };
+
+ return $this->where($column, $operator, $value, $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orNotLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
+ {
+ return $this->notLike($column, $value, $type, 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andNotLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
+ {
+ return $this->notLike($column, $value, $type);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function startLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->like($column, $value, 'before', $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orStartLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->like($column, $value, 'before', 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andStartLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->like($column, $value, 'before');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function notStartLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->notLike($column, $value, 'before', $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orStartNotLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->notLike($column, $value, 'before', 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andStartNotLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->notLike($column, $value, 'before');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function endLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->like($column, $value, 'after', $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orEndLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->like($column, $value, 'after', 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andEndLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->like($column, $value, 'after');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function notEndLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
+ {
+ return $this->notLike($column, $value, 'after', $logical);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function orEndNotLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->notLike($column, $value, 'after', 'OR');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function andEndNotLike(RawQuery|array|string $column, mixed $value = null): self
+ {
+ return $this->notLike($column, $value, 'after');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function subQuery(Closure $closure, ?string $alias = null, bool $isIntervalQuery = true): RawQuery
+ {
+ $builder = $this->clone()->resetStructure();
+
+ call_user_func_array($closure, [&$builder]);
+ if ($alias !== null && $isIntervalQuery !== TRUE) {
+ throw new QueryBuilderException('To define alias to a subquery, it must be an inner query.');
+ }
+
+ $rawQuery = ($isIntervalQuery ? '(' : '')
+ . $builder->generateSelectQuery()
+ . ($isIntervalQuery ? ')' : '')
+ . ($alias !== null ? ' AS ' . $alias : '');
+
+ return $this->raw($rawQuery);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function group(Closure $closure, string $logical = 'AND'): self
+ {
+ $logical = str_replace(['&&', '||'], ['AND', 'OR'], strtoupper($logical));
+ if(!in_array($logical, ['AND', 'OR'], true)){
+ throw new QueryBuilderException('Logical operator OR, AND, && or || it could be.');
+ }
+
+ $builder = $this->clone();
+ call_user_func_array($closure, [$builder->resetStructure()]);
+
+
+
+ foreach (['where', 'on', 'having'] as $stmt) {
+ $statement = $builder->__generateStructure($stmt);
+ !empty($statement) && $this->structure[$stmt][$logical][] = '(' . $statement . ')';
+ }
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function raw(mixed $rawQuery): RawQuery
+ {
+ return new RawQuery($rawQuery);
+ }
+
+ /**
+ * @return string
+ * @throws QueryGeneratorException
+ */
+ public function generateInsertQuery(): string
+ {
+ $columns = [];
+ $values = [];
+ $set = array_merge(...$this->structure['set']);
+ foreach ($set as $column => $value) {
+ $columns[] = $column;
+ $values[] = $value;
+ }
+ if (empty($columns)) {
+ throw new QueryGeneratorException('The data set for the insert could not be found.');
+ }
+
+ return 'INSERT INTO'
+ . ' ' . $this->__generateSchemaName() . ' '
+ . '(' . implode(', ', $columns) . ')'
+ . ' VALUES '
+ . '(' . implode(', ', $values) . ');';
+ }
+
+ /**
+ * @return string
+ * @throws QueryGeneratorException
+ */
+ public function generateBatchInsertQuery(): string
+ {
+ $columns = array_keys(array_merge(...$this->structure['set']));
+ if (empty($columns)) {
+ throw new QueryGeneratorException('The data set for the insert could not be found.');
+ }
+ $values = [];
+ foreach ($this->structure['set'] as $set) {
+ $value = [];
+ foreach ($columns as $column) {
+ $value[$column] = $set[$column] ?? 'NULL';
+ }
+ $values[] = '(' . implode(', ', $value) . ')';
+ }
+
+ return 'INSERT INTO'
+ . ' ' . $this->__generateSchemaName() . ' '
+ . '(' . implode(', ', $columns) . ')'
+ . ' VALUES '
+ . implode(', ', $values) . ';';
+ }
+
+ /**
+ * @return string
+ * @throws QueryGeneratorException
+ */
+ public function generateDeleteQuery(): string
+ {
+ return 'DELETE FROM'
+ . ' '
+ . $this->__generateSchemaName()
+ . ' WHERE '
+ . (($where = $this->__generateWhereQuery()) !== null ? $where : '1')
+ . ($this->__generateLimitQuery() ?? '');
+ }
+
+ /**
+ * @param array $selector
+ * @param array $conditions
+ * @return string
+ */
+ public function generateSelectQuery(array $selector = [], array $conditions = []): string
+ {
+ !empty($selector) && $this->select(...$selector);
+ if (!empty($conditions)) {
+ foreach ($conditions as $column => $value) {
+ if (is_string($column)) {
+ $this->where($column, $value);
+ } else {
+ $this->where($value);
+ }
+ }
+ }
+
+ return 'SELECT '
+ . (empty($this->structure['select']) ? '*' : implode(', ', $this->structure['select']))
+ . ' FROM '
+ . implode(', ', $this->structure['table'])
+ . (!empty($this->structure['join']) ? ' ' . implode(' ', $this->structure['join']) : '')
+ . ' WHERE '
+ . (($where = $this->__generateWhereQuery()) ? $where : '1')
+ . (!empty($this->structure['group_by']) ? ' GROUP BY ' . implode(', ', $this->structure['group_by']) : '')
+ . ($this->__generateHavingQuery() ?? '')
+ . (!empty($this->structure['order_by']) ? ' ORDER BY ' . implode(', ', $this->structure['order_by']) : '')
+ . ($this->__generateLimitQuery() ?? '');
+ }
+
+ /**
+ * @return string
+ * @throws QueryGeneratorException
+ */
+ public function generateUpdateQuery(): string
+ {
+ $set = array_merge(...$this->structure['set']);
+ $updateSet = [];
+ foreach ($set as $column => $value) {
+ $updateSet[] = $column . ' = ' . $value;
+ }
+ if (empty($updateSet)) {
+ throw new QueryGeneratorException('The data set for the insert could not be found.');
+ }
+
+ return 'UPDATE ' . $this->__generateSchemaName()
+ . ' SET ' . implode(', ', $updateSet)
+ . ' WHERE '
+ . (($where = $this->__generateWhereQuery()) ? $where : '1')
+ . ($this->__generateHavingQuery() ?? '')
+ . ($this->__generateLimitQuery() ?? '');
+ }
+
+ /**
+ * @param string $referenceColumn
+ * @return string
+ * @throws QueryGeneratorException
+ */
+ public function generateUpdateBatchQuery(string $referenceColumn): string
+ {
+ $update = [];
+ $data = $this->structure['set'];
+ $updateData = $columns = $where = [];
+ foreach ($data as $set) {
+ if (!isset($set[$referenceColumn])) {
+ throw new QueryGeneratorException('The reference column does not exist in one or more of the set arrays.');
+ }
+ $setData = [];
+ $where[] = $set[$referenceColumn];
+ unset($set[$referenceColumn]);
+ foreach ($set as $key => $value) {
+ $setData[$key] = $value;
+ (!in_array($key, $columns)) && $columns[] = $key;
+ }
+ $updateData[] = $setData;
+ }
+ foreach ($columns as $column) {
+ $syntax = $column . ' = CASE';
+ foreach ($updateData as $key => $values) {
+ if (!array_key_exists($column, $values)) {
+ continue;
+ }
+ $syntax .= ' WHEN ' . $referenceColumn . ' = '
+ . ($this->isSQLParameterOrFunction($where[$key]) ? $where[$key] : $this->parameters->add($referenceColumn, $where[$key]))
+ . ' THEN '
+ . $values[$column];
+ }
+ $update[] = $syntax . ' ELSE ' . $column .' END';
+ }
+
+ $this->whereIn($referenceColumn, $where);
+
+ return 'UPDATE ' . $this->__generateSchemaName()
+ . ' SET '
+ . implode(', ', $update)
+ . ' WHERE '
+ . (($where = $this->__generateWhereQuery()) ? $where : '1')
+ . ($this->__generateHavingQuery() ?? '')
+ . ($this->__generateLimitQuery() ?? '');
+ }
+
+ protected function isSQLParameter($value): bool
+ {
+ return (is_string($value)) && ($value === '?' || preg_match('/^:[(\w)]+$/', $value));
+ }
+
+ protected function isSQLParameterOrFunction($value): bool
+ {
+ return ((is_string($value)) && (
+ $value === '?'
+ || preg_match('/^:[(\w)]+$/', $value)
+ || preg_match('/^[a-zA-Z_]+[.]+[a-zA-Z_]+$/', $value)
+ || preg_match('/^[a-zA-Z_]+\(\)$/', $value)
+ )) || ($value instanceof RawQuery) || is_int($value);
+ }
+
+ public function isBatch(): bool
+ {
+ foreach ($this->structure['set'] as $set) {
+ if (is_array($set) && count($set) > 1) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function whereOrHavingStatementPrepare($column, $operator, $value): string
+ {
+ $operator = trim($operator);
+ $column = (string)$column;
+
+ if ($value !== null && in_array($operator, [
+ '=', '!=', '>', '<', '>=', '<=', '<>',
+ '+', '-', '*', '/', '%',
+ '+=', '-=', '*=', '/=', '%=', '&=', '^-=', '|*='
+ ], true)) {
+ return $column . ' ' . $operator . ' '
+ . ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value));
+ }
+ $upperCaseOperator = strtoupper($operator);
+ $searchOperator = str_replace([' ', '_'], '', $upperCaseOperator);
+ if ($value === null && !in_array($searchOperator, ['IS', 'ISNOT'])) {
+ return $column;
+ }
+
+ switch ($searchOperator) {
+ case 'IS':
+ return $column . ' IS '
+ . ((($value === null) ? 'NULL' : ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value))));
+ case 'ISNOT':
+ return $column . ' IS NOT '
+ . ((($value === null) ? 'NULL' : ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value))));
+ case 'LIKE':
+ case 'NOTLIKE':
+ case 'STARTLIKE':
+ case 'NOTSTARTLIKE':
+ case 'ENDLIKE':
+ case 'NOTENDLIKE':
+ if (!$this->isSQLParameter($value)) {
+ $value = (in_array($searchOperator, ['LIKE', 'NOTLIKE', 'STARTLIKE', 'NOTSTARTLIKE']) ? '%' : '')
+ . $value
+ . (in_array($searchOperator, ['LIKE', 'NOTLIKE', 'ENDLIKE', 'NOTENDLIKE']) ? '%' : '');
+
+ $value = $this->parameters->add($column, $value);
+ }
+
+ return $column
+ . (in_array($searchOperator, ['NOTSTARTLIKE', 'NOTLIKE', 'NOTENDLIKE']) ? ' NOT' : '')
+ . ' LIKE ' . $value;
+ case 'BETWEEN':
+ case 'NOTBETWEEN':
+ return $column . ' '
+ . ($searchOperator === 'NOTBETWEEN' ? 'NOT ' : '')
+ . 'BETWEEN '
+ . ($this->isSQLParameterOrFunction($value[0]) ? $value[0] : $this->parameters->add($column, $value[0]))
+ . ' AND '
+ . ($this->isSQLParameterOrFunction($value[1]) ? $value[1] : $this->parameters->add($column, $value[1]));
+ case 'IN':
+ case 'NOTIN':
+ if (is_array($value)) {
+ $values = [];
+ array_map(function ($item) use (&$values, $column) {
+ if (is_numeric($item)) {
+ $values[] = $item;
+ } else {
+ $values[] = $this->isSQLParameterOrFunction($item) ? $item : $this->parameters->add($column, $item);
+ }
+ }, array_unique($value));
+ $value = '(' . implode(', ', $values) . ')';
+ }
+ return $column
+ . ($searchOperator === 'NOTIN' ? ' NOT' : '')
+ . ' IN ' . $value;
+ case 'REGEXP':
+ return $column . ' REGEXP '
+ . ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value));
+ case 'FINDINSET':
+ case 'NOTFINDINSET':
+ if (is_array($value)) {
+ $value = implode(', ', $value);
+ } elseif ($this->isSQLParameterOrFunction($value)) {
+ $value = $this->parameters->add($column, $value);
+ }
+ return ($searchOperator === 'NOTFINDINSET' ? 'NOT ' : '')
+ . 'FIND_IN_SET(' . $value . ', ' . $column . ')';
+ case 'SOUNDEX':
+ if (!$this->isSQLParameterOrFunction($value)) {
+ $value = $this->parameters->add($column, $value);
+ }
+ return "SOUNDEX(" . $column . ") LIKE CONCAT('%', TRIM(TRAILING '0' FROM SOUNDEX(" . $value . ")), '%')";
+ default:
+ if ($value === null && preg_match('/([\w_]+)\((.+)\)$/iu', $column, $matches) !== FALSE) {
+ return strtoupper($matches[1]) . '(' . $matches[2] . ')';
+ }
+ return $column . ' ' . $operator . ' ' . $this->parameters->add($column, $value);
+ }
+ }
+
+ /**
+ * @return string
+ * @throws QueryGeneratorException
+ */
+ private function __generateSchemaName(): string
+ {
+ if (!empty($this->structure['table'])) {
+ $table = end($this->structure['table']);
+ } else {
+ throw new QueryGeneratorException('Table name not found when query.');
+ }
+
+ return $table;
+ }
+
+ private function __generateLimitQuery(): ?string
+ {
+ if ($this->structure['limit'] === null && $this->structure['offset'] === null) {
+ return null;
+ }
+ $statement = ' ';
+ if ($this->structure['limit'] === null) {
+ $statement .= 'OFFSET ' . $this->structure['offset'];
+ } else {
+ $statement .= 'LIMIT '
+ . ($this->structure['offset'] !== null ? $this->structure['offset'] . ', ' : '')
+ . $this->structure['limit'];
+ }
+
+ return $statement;
+ }
+
+ private function __generateOnQuery(): ?string
+ {
+ return $this->__generateStructure('on');
+ }
+
+ private function __generateHavingQuery(): ?string
+ {
+ $stmt = $this->__generateStructure('having');
+
+ return $stmt === null ? null : ' HAVING ' . $stmt;
+ }
+
+ private function __generateWhereQuery(): ?string
+ {
+ return $this->__generateStructure('where');
+ }
+
+ private function __generateStructure(string $key): ?string
+ {
+ $isAndEmpty = empty($this->structure[$key]['AND']);
+ $isOrEmpty = empty($this->structure[$key]['OR']);
+ if ($isOrEmpty && $isAndEmpty) {
+ return null;
+ }
+
+ return (!$isAndEmpty ? implode(' AND ', $this->structure[$key]['AND']) : '')
+ . (!$isAndEmpty && !$isOrEmpty ? ' AND ' : '')
+ . (!$isOrEmpty ? implode(' OR ', $this->structure[$key]['OR']) : '');
+ }
+
+ private function whereOrHavingPrepare(&$operator, &$value, &$logical): void
+ {
+ $logical = strtoupper(strtr($logical, [
+ '&&' => 'AND',
+ '||' => 'OR',
+ ]));
+ if (!in_array($logical, ['AND', 'OR'], true)) {
+ throw new InvalidArgumentException('Logical operator OR, AND, && or || it could be.');
+ }
+
+ if ($value === null && !in_array($operator, [
+ 'IS', 'IS NOT',
+ '=', '!=', '>', '<', '>=', '<=', '<>',
+ '+', '-', '*', '/', '%',
+ '+=', '-=', '*=', '/=', '%=', '&=', '^-=', '|*='
+ ])) {
+ $value = $operator;
+ $operator = '=';
+ }
+ }
+
+}
diff --git a/src/QueryBuilder/RawQuery.php b/src/QueryBuilder/RawQuery.php
new file mode 100644
index 0000000..85b41f3
--- /dev/null
+++ b/src/QueryBuilder/RawQuery.php
@@ -0,0 +1,53 @@
+set($rawQuery);
+ }
+
+ public function __toString(): string
+ {
+ return $this->get();
+ }
+
+ public function set(mixed $rawQuery): self
+ {
+ if (is_string($rawQuery)) {
+ $this->raw = $rawQuery;
+ } else if ($rawQuery instanceof Closure) {
+ $builder = new QueryBuilder();
+ $res = call_user_func_array($rawQuery, [&$builder]);
+ if (is_string($res)) {
+ $this->raw = $res;
+ } else if (is_object($res) && method_exists($res, '__toString')) {
+ $this->raw = $res->__toString();
+ } else {
+ $this->raw = $builder->__toString();
+ }
+ } else {
+ $this->raw = (string)$rawQuery;
+ }
+
+ return $this;
+ }
+
+ public function get(): string
+ {
+ return $this->raw ?? '';
+ }
+
+ public static function raw($rawQuery): RawQuery
+ {
+ return new self($rawQuery);
+ }
+
+}
diff --git a/src/Raw.php b/src/Raw.php
deleted file mode 100644
index 66709dc..0000000
--- a/src/Raw.php
+++ /dev/null
@@ -1,41 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database;
-
-final class Raw
-{
-
- private string $raw;
-
- public function __construct(string $rawQuery)
- {
- $this->raw = \trim($rawQuery);
- }
-
- public function __toString(): string
- {
- return $this->get();
- }
-
- public function get(): string
- {
- return $this->raw;
- }
-
- public static function raw(string $sqlQuery): self
- {
- return new self($sqlQuery);
- }
-
-}
diff --git a/src/Result.php b/src/Result.php
deleted file mode 100644
index 02d855f..0000000
--- a/src/Result.php
+++ /dev/null
@@ -1,180 +0,0 @@
-
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database;
-
-final class Result
-{
-
- private \PDOStatement $statement;
-
- private string $query;
-
- private int $num_rows;
-
- public function __construct(\PDOStatement $statement)
- {
- $this->setStatement($statement);
- }
-
- public function setStatement(\PDOStatement $statement): self
- {
- $this->statement = $statement;
- $this->query = $statement->queryString;
- $this->num_rows = (int)$statement->rowCount();
- return $this;
- }
-
- public function withStatement(\PDOStatement $statement): self
- {
- $with = clone $this;
- return $with->setStatement($statement);
- }
-
- public function getStatement(): \PDOStatement
- {
- return $this->statement;
- }
-
- public function numRows(): int
- {
- return $this->num_rows;
- }
-
- public function rowCount(): int
- {
- return $this->numRows();
- }
-
- public function query(): string
- {
- return $this->query;
- }
-
- /**
- * @param string $entityClass
- * @return array|object|null
- */
- public function toEntity(string $entityClass = Entity::class)
- {
- if($this->num_rows === 0){
- return null;
- }
- $this->asEntity($entityClass);
- return $this->num_rows === 1 ? $this->getStatement()->fetch() : $this->getStatement()->fetchAll();
- }
-
- public function asEntity(string $entityClass = Entity::class): self
- {
- $this->getStatement()->setFetchMode(\PDO::FETCH_CLASS, $entityClass);
- return $this;
- }
-
- public function toAssoc(): array
- {
- if($this->num_rows === 0){
- return [];
- }
- $this->asAssoc();
- return $this->num_rows === 1 ? $this->getStatement()->fetch() : $this->getStatement()->fetchAll();
- }
-
- public function asAssoc(): self
- {
- $this->getStatement()->setFetchMode(\PDO::FETCH_ASSOC);
- return $this;
- }
-
- public function toArray(): array
- {
- if($this->num_rows === 0){
- return [];
- }
- $this->asArray();
- return $this->num_rows === 1 ? $this->getStatement()->fetch() : $this->getStatement()->fetchAll();
- }
-
- public function asArray(): self
- {
- $this->getStatement()->setFetchMode(\PDO::FETCH_BOTH);
- return $this;
- }
-
- /**
- * @return object|array|null
- */
- public function toObject()
- {
- if($this->num_rows === 0){
- return null;
- }
- $this->asObject();
- return $this->num_rows === 1 ? $this->getStatement()->fetch() : $this->getStatement()->fetchAll();
- }
-
- public function asObject(): self
- {
- $this->getStatement()->setFetchMode(\PDO::FETCH_OBJ);
- return $this;
- }
-
- public function toLazy()
- {
- if($this->num_rows === 0){
- return null;
- }
- $this->asLazy();
- return $this->num_rows === 1 ? $this->getStatement()->fetch() : $this->getStatement()->fetchAll();
- }
-
- public function asLazy(): self
- {
- $this->getStatement()->setFetchMode(\PDO::FETCH_LAZY);
- return $this;
- }
-
- public function result()
- {
- if($this->num_rows <= 0){
- return null;
- }
- $res = $this->getStatement()->fetch();
-
- return $res !== FALSE ? $res : null;
- }
-
- public function row()
- {
- return $this->result();
- }
-
- /**
- * @return array|object[]|null
- */
- public function results(): ?array
- {
- if($this->num_rows <= 0){
- return null;
- }
- $res = $this->getStatement()->fetchAll();
-
- return $res !== FALSE ? $res : null;
- }
-
- public function rows(): ?array
- {
- return $this->results();
- }
-
-
-}
diff --git a/src/Utils/Datatables.php b/src/Utils/Datatables.php
index d689cfc..32eb8ed 100644
--- a/src/Utils/Datatables.php
+++ b/src/Utils/Datatables.php
@@ -13,173 +13,270 @@
namespace InitPHP\Database\Utils;
-use InitPHP\Database\Database;
+use \InitPHP\Database\DBAL\Interfaces\{DatabaseInterface, CRUDInterface};
+use InitPHP\Database\DBAL\Exceptions\SQLQueryExecuteException;
+use InitPHP\Database\ORM\Interfaces\ModelInterface;
+use InitPHP\Database\QueryBuilder\Interfaces\QueryBuilderInterface;
+use Closure;
-final class Datatables
+/**
+ * @mixin QueryBuilderInterface
+ */
+class Datatables
{
- public const GET_REQUEST = 0;
-
- public const POST_REQUEST = 1;
+ private DatabaseInterface|CRUDInterface|ModelInterface $db;
- private Database $db;
+ private array $request;
- private array $request = [];
+ private array $response = [
+ 'draw' => 0,
+ 'recordsTotal' => 0,
+ 'recordsFiltered' => 0,
+ 'data' => [],
+ 'post' => [],
+ ];
private array $columns = [];
- private int $total_row = 0;
+ private array $renders = [];
- private int $total_filtered_row = 0;
+ private array $builder = [];
- private array $results;
+ private bool $orderByReset = true;
- private int $draw = 0;
+ private array $permanentSelect = [];
- public function __construct(Database $db, $columns, $method = self::GET_REQUEST)
+ public function __construct(DatabaseInterface|ModelInterface|CRUDInterface $db)
{
- $this->db = $db;
+ $this->request = array_merge($_GET ?? [], $_POST ?? []);
- switch ($method) {
- case self::GET_REQUEST:
- $this->request = $_GET ?? [];
- break;
- case self::POST_REQUEST:
- $this->request = $_POST ?? [];
- break;
+ if ($requestBody = @file_get_contents("php://input")) {
+ if (is_array($jsonBody = json_decode($requestBody, true))) {
+ $this->request = array_merge($this->request, $jsonBody);
+ }
}
- $this->columns = $columns;
-
- $this->total_row = $this->db->count();
+ $this->db = $db;
+ }
- $this->filterQuery();
- $this->orderQuery();
- $this->total_filtered_row = $this->db->count();
- $this->limitQuery();
- $this->results = $this->db->get()->toArray();
+ public function __call(string $name, array $arguments)
+ {
+ $this->builder[] = [
+ 'method' => $name,
+ 'arguments' => $arguments,
+ ];
- if(isset($this->request['draw'])){
- $this->draw = (int)$this->request['draw'];
- }
+ return $this;
}
+ /**
+ * @return string
+ * @throws SQLQueryExecuteException
+ */
public function __toString(): string
{
- return \json_encode($this->getResults());
+ return json_encode($this->toArray());
}
- public function getResults(): array
+ /**
+ * @return array
+ * @throws SQLQueryExecuteException
+ */
+ public function toArray(): array
{
- return [
- 'draw' => $this->draw,
- 'recordsTotal' => $this->total_row,
- 'recordsFiltered' => $this->total_filtered_row,
- 'data' => $this->output_prepare()
- ];
+ $this->handle();
+ return $this->response;
}
+ /**
+ * @return $this
+ * @throws SQLQueryExecuteException
+ */
+ public function handle(): self
+ {
+ $this->filterQuery();
+
+ $totalRow = $this->getCount();
- private function orderQuery()
+ $res = $this->orderQuery()
+ ->limitQuery()
+ ->getResults();
+
+ if (!empty($this->renders)) {
+ foreach ($res as &$row) {
+ foreach ($row as $column => &$value) {
+ if (!isset($this->renders[$column])) {
+ continue;
+ }
+ $value = call_user_func_array($this->renders[$column], [$value, &$row]);
+ }
+ }
+ }
+
+ $this->response = [
+ 'draw' => $this->request['draw'] ?? 0,
+ 'recordsTotal' => $totalRow,
+ 'recordsFiltered' => $totalRow,
+ 'data' => $res,
+ 'post' => $this->request,
+ ];
+
+ return $this;
+ }
+
+ /**
+ * @param string ...$select
+ * @return $this
+ */
+ public function addPermanentSelect(string ...$select): self
{
- $columns = $this->columns;
- if(!isset($this->request['order'])){
- return;
+ foreach ($select as $sel) {
+ $this->select($sel);
+ $this->permanentSelect[] = $sel;
}
- $count = \count($this->request['order']);
- $dtColumns = $this->pluck($columns, 'dt');
- for($i = 0; $i < $count; ++$i){
- $columnId = \intval($this->request['order'][$i]['column']);
- $reqColumn = $this->request['columns'][$columnId];
- $columnId = \array_search($reqColumn['data'], $dtColumns);
- $column = $columns[$columnId];
- if(($reqColumn['orderable'] ?? 'false') != 'true' || !isset($column['db'])){
- continue;
- }
- $dir = ($this->request['order'][$i]['dir'] ?? 'asc') === 'asc' ? 'ASC' : 'DESC';
- $this->db->orderBy($column['db'], $dir);
+
+ return $this;
+ }
+
+ /**
+ * @param string|null ...$columns
+ * @return $this
+ */
+ public function setColumns(?string ...$columns): self
+ {
+ $dt = count($this->columns) - 1;
+ foreach ($columns as $column) {
+ $this->columns[] = [
+ 'db' => $column,
+ 'dt' => ++$dt,
+ ];
}
+
+ return $this;
}
- private function filterQuery()
+ /**
+ * @param string $column
+ * @param Closure $render
+ * @return $this
+ */
+ public function addRender(string $column, Closure $render): self
{
- $columns = $this->columns;
- $dtColumns = $this->pluck($columns, 'dt');
- $str = $this->request['search']['value'] ?? '';
- if($str === '' || !isset($this->request['columns'])){
- return;
+ $this->renders[$column] = $render;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function orderBySave(): self
+ {
+ $this->orderByReset = false;
+
+ return $this;
+ }
+
+ /**
+ * @return int
+ * @throws SQLQueryExecuteException
+ */
+ private function getCount(): int
+ {
+ if (!empty($this->permanentSelect)) {
+ foreach ($this->permanentSelect as $select) {
+ $this->db->select($select);
+ }
}
- $columnsCount = \count($this->request['columns']);
- $this->db->group(function (Database $db) use ($str, $dtColumns, $columns, $columnsCount) {
- for ($i = 0; $i < $columnsCount; ++$i) {
- $reqColumn = $this->request['columns'][$i];
- $columnId = \array_search($reqColumn['data'], $dtColumns);
- $column = $columns[$columnId];
- if(empty($column['db'])){
- continue;
- }
- $db->orLike($column['db'], $str);
+ $isGroupBy = false;
+ foreach ($this->builder as $process) {
+ if (str_starts_with($process['method'], 'select') || str_starts_with($process['method'], 'orderBy')) {
+ continue;
}
- });
- if(isset($this->request['columns'])){
- for ($i = 0; $i < $columnsCount; ++$i) {
- $reqColumn = $this->request['columns'][$i];
- $columnId = \array_search($reqColumn['data'], $dtColumns);
- $column = $columns[$columnId];
- $str = $reqColumn['search']['value'] ?? '';
- if(($reqColumn['searchable'] ?? 'false') != 'true' || $str == '' || empty($column['db'])){
- continue;
+ if ($process['method'] === 'groupBy') {
+ if ($isGroupBy === false) {
+ $this->db->selectCountDistinct(current($process['arguments']), 'data_length');
+ $isGroupBy = true;
}
- $this->db->like($column['db'], $str);
+ continue;
}
+ $this->db->{$process['method']}(...$process['arguments']);
}
+ $isGroupBy === false && $this->db->selectCount('*', 'data_length');
+ $res = $this->db->get();
+
+ return $res->numRows() > 0 ? $res->asAssoc()->row()['data_length'] : 0;
}
- private function limitQuery()
+ /**
+ * @return array
+ * @throws SQLQueryExecuteException
+ */
+ private function getResults(): array
{
- if(isset($this->request['start']) && $this->request['length'] != -1){
- $this->db->offset((int)$this->request['start'])
- ->limit((int)$this->request['length']);
+ foreach ($this->builder as $process) {
+ $this->db->{$process['method']}(...$process['arguments']);
}
+ $res = $this->db->get();
+
+ return $res->numRows() > 0 ? $res->asAssoc()->rows() : [];
}
- private function output_prepare(): array
+ /**
+ * @return self
+ */
+ private function orderQuery(): self
{
- $out = [];
- $columns = $this->columns;
- $data = $this->results;
- $dataCount = \count($data);
- $columnCount = \count($columns);
-
- for ($i = 0; $i < $dataCount; ++$i) {
- $row = [];
-
- for ($y = 0; $y < $columnCount; ++$y) {
- $column = $columns[$y];
- if(isset($column['formatter'])){
- $row[$column['dt']] = \call_user_func_array($column['formatter'], (empty($column['db']) ? [$data[$i]] : [$data[$i][$column['db']], $data[$i]]));
- }else{
- $row[$column['dt']] = !empty($column['db']) ? $data[$i][$column['db']] : '';
+ if (empty($this->request['order']) || !is_array($this->request['order'])) {
+ return $this;
+ }
+ if ($this->orderByReset) {
+ foreach ($this->builder as $key => $builder) {
+ if (str_starts_with($builder['method'], 'orderBy')) {
+ unset($this->builder[$key]);
}
}
-
- $out[] = $row;
+ }
+ $columns = $this->columns;
+ $count = count($this->request['order']);
+ for ($i = 0; $i < $count; ++$i) {
+ $columnId = intval($this->request['order'][$i]['column']);
+ $column = $columns[$columnId];
+ if (!isset($column['db'])) {
+ continue;
+ }
+ $dir = strtolower($this->request['order'][$i]['dir']) === 'asc' ? 'ASC' : 'DESC';
+ $this->db->orderBy($this->db->raw($column['db']), $dir);
}
-
- return $out;
+ return $this;
}
- private function pluck(array $array, string $prop): array
+ /**
+ * @return void
+ */
+ private function filterQuery(): void
{
- $out = [];
- $len = \count($array);
- for ($i = 0; $i < $len; ++$i) {
- if(empty($array[$i][$prop])){
- continue;
+ $search = $this->request['search']['value'] ?? null;
+ if (empty($search) || empty($this->columns)) {
+ return;
+ }
+ $this->db->group(function (QueryBuilderInterface $builder) use ($search) {
+ foreach ($this->columns as $column) {
+ $builder->orLike($this->db->raw($column['db']), $search);
}
- $out[$i] = $array[$i][$prop];
+ return $builder;
+ });
+ }
+
+ private function limitQuery(): self
+ {
+ if (isset($this->request['start']) && $this->request['length'] != -1) {
+ $this->offset($this->request['start'])
+ ->limit($this->request['length']);
}
- return $out;
+
+ return $this;
}
}
diff --git a/src/Utils/Helper.php b/src/Utils/Helper.php
new file mode 100644
index 0000000..42076b9
--- /dev/null
+++ b/src/Utils/Helper.php
@@ -0,0 +1,37 @@
+
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 2.0.7
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Utils;
-
-use InitPHP\Database\Result;
-
-/**
- * @mixin Result
- */
-final class Pagination
-{
-
- private Result $result;
-
- protected int $page = 1;
- protected int $perPageLimit = 10;
- protected int $totalPage = 0;
- protected int $totalRow = 0;
- protected int $howDisplayedPage = 8;
- protected string $linkTemplate = '';
-
- public function __construct(Result &$res, int $page, int $perPageLimit, int $totalRow, string $linkTemplate)
- {
- $this->result = &$res;
-
- $this->page = $page;
- $this->perPageLimit = $perPageLimit;
- $this->totalRow = $totalRow;
- $this->linkTemplate = $linkTemplate;
-
- $this->totalPage = \ceil(($this->totalRow / $this->perPageLimit));
- }
-
- public function __call($name, $arguments)
- {
- return $this->getResult()->{$name}(...$arguments);
- }
-
- public function getResult(): Result
- {
- return $this->result;
- }
-
- public function getPage(): int
- {
- return $this->page;
- }
-
- public function getTotalPage(): int
- {
- return $this->totalPage;
- }
-
- public function getTotalRow(): int
- {
- return $this->totalRow;
- }
-
- public function setDisplayedPage(int $displayedPage): self
- {
- $this->howDisplayedPage = ($displayedPage % 2 !== 0) ? $displayedPage + 1 : $displayedPage;
-
- return $this;
- }
-
- public function getDisplayedPage(): int
- {
- return $this->howDisplayedPage;
- }
-
- public function setBaseLink($link): self
- {
- $this->linkTemplate = $link;
-
- return $this;
- }
-
- public function getPages(): array
- {
- $pages = [];
- $beforeAfter = $this->howDisplayedPage / 2;
- $beforeLimit = ($beforeAfter > $this->page) ? ($beforeAfter - ($beforeAfter - $this->page)) : $beforeAfter;
- $afterLimit = $this->howDisplayedPage - $beforeLimit;
-
- for ($i = ($this->page - 1); $i >= ($this->page - $beforeLimit); --$i) {
- $pages[] = [
- 'url' => $this->_linkGenerator($i),
- 'page' => $i,
- 'active' => false,
- ];
- }
- $pages = \array_reverse($pages, false);
-
- $pages[] = [
- 'url' => $this->_linkGenerator($this->page),
- 'page' => $this->page,
- 'active' => true,
- ];
-
- for ($i = ($this->page + 1); $i <= ($this->page + $afterLimit); ++$i) {
- if($i > $this->totalPage){
- break;
- }
- $pages[] = [
- 'url' => $this->_linkGenerator($i),
- 'page' => $i,
- 'active' => false,
- ];
- }
-
- return $pages;
- }
-
- public function nextPage(): ?array
- {
- if($this->page < $this->totalPage && $this->totalPage > 1){
- $page = $this->page + 1;
- return [
- 'url' => $this->_linkGenerator($page),
- 'page' => $page,
- ];
- }
- return null;
- }
-
- public function prevPage(): ?array
- {
- if($this->page > 1){
- $page = $this->page - 1;
- return [
- 'url' => $this->_linkGenerator($page),
- 'page' => $page,
- ];
- }
- return null;
- }
-
- public function toHTML(array $attrs = []): string
- {
- $pages = $this->getPages();
- $prev = $this->prevPage();
- $next = $this->nextPage();
- $li_attr_prepare = '';
- $li_a_attr_prepare = '';
- $li_attr_prepare_active = ' class="active"';
- if(isset($attrs['li'])){
- $li_attr_prepare = $this->_attrGenerator($attrs['li']);
- $li_a_attr_prepare = $this->_attrGenerator(($attrs['li']['a'] ?? []));
- if(isset($attrs['li']['class'])){
- $attrs['li']['class'] .= ' active';
- }else{
- $attrs['li']['class'] = 'active';
- }
- $li_attr_prepare_active = $this->_attrGenerator($attrs['li']);
- }
-
- $html = '';
- return $html;
- }
-
-
- private function _attrGenerator(array $assoc): string
- {
- if($assoc === []){
- return '';
- }
- $res = '';
- foreach ($assoc as $name => $value) {
- if(\is_array($value)){
- continue;
- }
- $res .= ' ' . $name . '="' . $value . '"';
- }
- return $res;
- }
-
-
- private function _linkGenerator($page)
- {
- if(empty($this->linkTemplate)){
- return $page;
- }
- return \strtr($this->linkTemplate, [
- '{page}' => $page,
- ]);
- }
-
-}
diff --git a/tests/QueryBuilderUnitTest.php b/tests/QueryBuilderUnitTest.php
index ee1bc94..4aaade8 100644
--- a/tests/QueryBuilderUnitTest.php
+++ b/tests/QueryBuilderUnitTest.php
@@ -3,18 +3,16 @@
namespace Test\InitPHP\Database;
-use InitPHP\Database\Database;
-use InitPHP\Database\Helpers\Parameters;
-use InitPHP\Database\QueryBuilder;
+use InitPHP\Database\QueryBuilder\QueryBuilder;
class QueryBuilderUnitTest extends \PHPUnit\Framework\TestCase
{
- protected Database $db;
+ protected QueryBuilder $db;
protected function setUp(): void
{
- $this->db = new Database();
+ $this->db = new QueryBuilder();
parent::setUp();
}
@@ -26,7 +24,7 @@ public function testSelectBuilder()
$expected = "SELECT id, name FROM user WHERE 1";
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testBlankBuild()
@@ -36,7 +34,7 @@ public function testBlankBuild()
$expected = 'SELECT * FROM post WHERE 1';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testSelfJoinBuild()
@@ -48,7 +46,7 @@ public function testSelfJoinBuild()
$expected = "SELECT post.id, post.title, user.name AS authorName FROM post, user WHERE user.id = post.user";
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testInnerJoinBuild()
@@ -60,7 +58,7 @@ public function testInnerJoinBuild()
$expected = "SELECT post.id, post.title, user.name as authorName FROM post INNER JOIN user ON user.id = post.user WHERE 1";
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testLeftJoinBuild()
@@ -72,7 +70,7 @@ public function testLeftJoinBuild()
$expected = "SELECT post.id, post.title, user.name as authorName FROM post LEFT JOIN user ON user.id=post.user WHERE 1";
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testRightJoinBuild()
@@ -84,7 +82,7 @@ public function testRightJoinBuild()
$expected = "SELECT post.id, post.title, user.name as authorName FROM post RIGHT JOIN user ON user.id=post.user WHERE 1";
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testLeftOuterJoinBuild()
@@ -96,7 +94,7 @@ public function testLeftOuterJoinBuild()
$expected = "SELECT post.id, post.title, user.name as authorName FROM post LEFT OUTER JOIN user ON user.id=post.user WHERE 1";
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testRightOuterJoinBuild()
@@ -108,7 +106,7 @@ public function testRightOuterJoinBuild()
$expected = "SELECT post.id, post.title, user.name as authorName FROM post RIGHT OUTER JOIN user ON user.id=post.user WHERE 1";
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testLimitStatement()
@@ -120,7 +118,7 @@ public function testLimitStatement()
$expected = 'SELECT id FROM book WHERE 1 LIMIT 5';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testOffsetStatement()
@@ -129,11 +127,10 @@ public function testOffsetStatement()
->from('book')
->offset(5);
- // Offset is specified If no limit is specified; The limit is 10000.
- $expected = 'SELECT id FROM book WHERE 1 LIMIT 5, 10000';
+ $expected = 'SELECT id FROM book WHERE 1 OFFSET 5';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testOffsetLimitStatement()
@@ -146,7 +143,7 @@ public function testOffsetLimitStatement()
$expected = 'SELECT id FROM book WHERE 1 LIMIT 50, 25';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testNegativeOffsetLimitStatement()
@@ -160,7 +157,7 @@ public function testNegativeOffsetLimitStatement()
$expected = 'SELECT id FROM book WHERE 1 LIMIT 25, 20';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testSelectDistinctStatement()
@@ -170,15 +167,18 @@ public function testSelectDistinctStatement()
$expected = 'SELECT DISTINCT(name) FROM book WHERE 1';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
+ }
+ public function testSelectDistinctJoinStatement()
+ {
$this->db->selectDistinct('author.name')
->from('book')
->innerJoin('author', 'author.id=book.author');
$expected = 'SELECT DISTINCT(author.name) FROM book INNER JOIN author ON author.id=book.author WHERE 1';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testOrderByStatement()
@@ -192,7 +192,7 @@ public function testOrderByStatement()
$expected = 'SELECT name FROM book WHERE 1 ORDER BY authorId ASC, id DESC LIMIT 10';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testInsertStatementBuild()
@@ -210,12 +210,12 @@ public function testInsertStatementBuild()
$expected = 'INSERT INTO post (title, content, author, status) VALUES (:title, :content, 5, :status);';
$this->assertEquals($expected, $this->db->generateInsertQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testInsertBatchStatementBuild()
{
- Parameters::reset();
+
$this->db->from('post');
$this->db->set([
@@ -232,14 +232,14 @@ public function testInsertBatchStatementBuild()
$expected = 'INSERT INTO post (title, content, author, status) VALUES (:title, :content, 5, :status), (:title_1, :content_1, NULL, :status_1);';
$this->assertEquals($expected, $this->db->generateBatchInsertQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testUpdateStatementBuild()
{
- Parameters::reset();
+
$this->db->from('post')
- ->where('status', true)
+ ->where('status', '=', true)
->limit(5);
$data = [
@@ -251,14 +251,14 @@ public function testUpdateStatementBuild()
$expected = 'UPDATE post SET title = :title, status = :status_1 WHERE status = :status LIMIT 5';
$this->assertEquals($expected, $this->db->generateUpdateQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testUpdateBatchStatementBuild()
{
- Parameters::reset();
+
$this->db->from('post')
- ->where('status', true);
+ ->where('status', '=', true);
$this->db->set([
'id' => 5,
@@ -272,20 +272,20 @@ public function testUpdateBatchStatementBuild()
$expected = 'UPDATE post SET title = CASE WHEN id = 5 THEN :title WHEN id = 10 THEN :title_1 ELSE title END, content = CASE WHEN id = 5 THEN :content ELSE content END WHERE status = :status AND id IN (5, 10)';
$this->assertEquals($expected, $this->db->generateUpdateBatchQuery('id'));
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testDeleteStatementBuild()
{
- Parameters::reset();
+
$this->db->from('post')
- ->where('authorId', 5)
+ ->where('authorId', '=', 5)
->limit(100);
$expected = 'DELETE FROM post WHERE authorId = 5 LIMIT 100';
$this->assertEquals($expected, $this->db->generateDeleteQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testWhereSQLFunctionStatementBuild()
@@ -296,7 +296,7 @@ public function testWhereSQLFunctionStatementBuild()
$expected = 'SELECT * FROM post WHERE date BETWEEN :date AND CURDATE()';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testWhereRegexpSQLStatementBuild()
@@ -307,13 +307,11 @@ public function testWhereRegexpSQLStatementBuild()
$expected = 'SELECT * FROM post WHERE title REGEXP :title';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testSelectCoalesceSQLStatementBuild()
{
-
- Parameters::reset();
$this->db->select('post.title')
->selectCoalesce('stat.view', 0)
->from('post')
@@ -323,9 +321,11 @@ public function testSelectCoalesceSQLStatementBuild()
$expected = 'SELECT post.title, COALESCE(stat.view, 0) FROM post LEFT JOIN stat ON stat.id=post.id WHERE post.id = 5';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
+ }
- Parameters::reset();
+ public function testSelectCoalesceDefaultValue()
+ {
$this->db->select('post.title')
->selectCoalesce('stat.view', 'post.view', 'views')
->from('post')
@@ -335,14 +335,12 @@ public function testSelectCoalesceSQLStatementBuild()
$expected = 'SELECT post.title, COALESCE(stat.view, post.view) AS views FROM post LEFT JOIN stat ON stat.id=post.id WHERE post.id = 5';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testTableAliasSQLStatementBuild()
{
-
- Parameters::reset();
$this->db->select('p.title')
->select('s.view as s_view')
->from('post as p')
@@ -352,9 +350,11 @@ public function testTableAliasSQLStatementBuild()
$expected = 'SELECT p.title, s.view as s_view FROM post as p LEFT JOIN stat as s ON s.id=p.id WHERE p.id = 5';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
+ }
- Parameters::reset();
+ public function testTableJoinAliasSQLStatementBuild()
+ {
$this->db->select('p.title')
->select('s.view as s_view')
->from('post p')
@@ -364,49 +364,61 @@ public function testTableAliasSQLStatementBuild()
$expected = 'SELECT p.title, s.view as s_view FROM post p LEFT JOIN stat s ON s.id=p.id WHERE p.id = 5';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testWhereGroupStatement()
{
- Parameters::reset();
+ $this->db->select('id')
+ ->from('users')
+ ->where('status', 1)
+ ->group(function (QueryBuilder $builder) {
+ $builder->where('type', 3)
+ ->where('type', 4);
+ });
+
+ $expected = 'SELECT id FROM users WHERE status = 1 AND (type = 3 AND type = 4)';
+
+ $this->assertEquals($expected, $this->db->generateSelectQuery());
+ $this->db->resetStructure();
+ }
+
+ public function testWhereGroupMultipleStatement()
+ {
$this->db->select('id, title, content, url')
->from('posts')
->where('status', 1)
- ->group(function (Database $db) {
+ ->group(function (QueryBuilder $db) {
$db->where('user_id', 1)
- ->where('datetime', date("Y-m-d"), '>=');
+ ->where('datetime', '>=', date("Y-m-d"));
}, 'or')
- ->group(function (Database $db) {
- $db->group(function (Database $db) {
- $db->where('id', 1)
- ->where('status', 0);
+ ->group(function (QueryBuilder $db) {
+ $db->group(function (QueryBuilder $db) {
+ $db->where('id', 2)
+ ->where('status', 3);
}, 'or')
- ->group(function (Database $db) {
- $db->where('id', 2)
- ->where('status', 1);
+ ->group(function (QueryBuilder $db) {
+ $db->where('id', 4)
+ ->where('status', 5);
}, 'or');
}, 'or');
-
-
- $expected = 'SELECT id, title, content, url FROM posts WHERE status = 1 AND (user_id = 1 AND datetime >= :datetime) OR ((id = 1 AND status = 0) OR (id = 2 AND status = 1))';
+ $expected = 'SELECT id, title, content, url FROM posts WHERE status = 1 AND (user_id = 1 AND datetime >= :datetime) OR ((id = 2 AND status = 3) OR (id = 4 AND status = 5))';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
-
- Parameters::reset();
+ $this->db->resetStructure();
}
+
public function testJoinClosureGive()
{
- Parameters::reset();
+
$this->db->select('u.id', 'u.name', 'u.status, p.title')
->from('users AS u')
->where('u.status', 1)
->join('posts AS p', function (QueryBuilder $builder) {
$builder->on('p.user_id', 'u.id')
- ->where('p.publisher_time', $builder->raw('NOW()'), '>=');
+ ->where('p.publisher_time', '>=', $builder->raw('NOW()'));
})
->join('categories AS c', function (QueryBuilder $builder) {
$builder->on('c.id', 'p.category_id')
@@ -417,25 +429,26 @@ public function testJoinClosureGive()
$expected = 'SELECT u.id, u.name, u.status, p.title FROM users AS u INNER JOIN posts AS p ON p.user_id = u.id INNER JOIN categories AS c ON c.id = p.category_id AND c.blog_id = u.blog_id WHERE u.status = 1 AND p.publisher_time >= NOW() AND c.status = 1 HAVING COUNT(p.category_id) > 1 LIMIT 5';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
public function testSubQuery()
{
- Parameters::reset();
+
$this->db->select('u.name')
->from('users AS u')
- ->in('u.id', $this->db->subQuery(function (QueryBuilder $builder) {
+ ->whereIn('u.id', $this->db->subQuery(function (QueryBuilder $builder) {
$builder->select('id')
->from('roles')
->where('name', 'admin');
}));
$expected = 'SELECT u.name FROM users AS u WHERE u.id IN (SELECT id FROM roles WHERE name = :name)';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
-
+ $this->db->resetStructure();
+ }
- Parameters::reset();
+ public function testSubQueryJoinTable()
+ {
$this->db->select('u.name, p.title')
->from('users AS u')
->join($this->db->subQuery(function (QueryBuilder $builder) {
@@ -446,7 +459,7 @@ public function testSubQuery()
$expected = 'SELECT u.name, p.title FROM users AS u JOIN (SELECT id, title, user_id FROM posts WHERE user_id = 5) AS p ON p.user_id = u.id WHERE 1';
$this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->reset();
+ $this->db->resetStructure();
}
}
From 3c6eb3813f0472bbb2c11b8971763f4dc6f890f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Muhammet=20=C5=9EAFAK?=
Date: Sat, 9 Dec 2023 23:45:58 +0300
Subject: [PATCH 2/2] InitORM
---
README.md | 69 +-
composer.json | 3 +-
src/Connection/Connection.php | 146 --
.../Exceptions/ConnectionException.php | 8 -
.../Interfaces/ConnectionInterface.php | 60 -
src/DB.php | 52 +
src/DBAL/CRUD.php | 165 --
src/DBAL/Database.php | 292 ----
.../Exceptions/SQLQueryExecuteException.php | 9 -
src/DBAL/Interfaces/CRUDInterface.php | 82 -
src/DBAL/Interfaces/DatabaseInterface.php | 99 --
src/DBAL/Interfaces/ResultInterface.php | 82 -
src/DBAL/Result.php | 144 --
src/Database.php | 8 +
src/Entity.php | 8 +
src/Facade/DB.php | 189 ---
src/Model.php | 8 +
src/ORM/Entity.php | 128 --
src/ORM/Exceptions/DeletableException.php | 7 -
src/ORM/Exceptions/EntityNotMethod.php | 14 -
src/ORM/Exceptions/ModelException.php | 10 -
src/ORM/Exceptions/ReadableException.php | 7 -
src/ORM/Exceptions/UpdatableException.php | 7 -
src/ORM/Exceptions/WritableException.php | 7 -
src/ORM/Interfaces/EntityInterface.php | 18 -
src/ORM/Interfaces/ModelInterface.php | 107 --
src/ORM/Model.php | 248 ---
.../Exceptions/QueryBuilderException.php | 8 -
.../Exceptions/QueryGeneratorException.php | 9 -
.../Interfaces/ParameterInterface.php | 48 -
.../Interfaces/QueryBuilderInterface.php | 758 ---------
src/QueryBuilder/Parameters.php | 104 --
src/QueryBuilder/QueryBuilder.php | 1455 -----------------
src/QueryBuilder/RawQuery.php | 53 -
src/Utils/Datatables.php | 30 +-
src/Utils/Helper.php | 37 -
tests/QueryBuilderUnitTest.php | 465 ------
37 files changed, 120 insertions(+), 4824 deletions(-)
delete mode 100644 src/Connection/Connection.php
delete mode 100644 src/Connection/Exceptions/ConnectionException.php
delete mode 100644 src/Connection/Interfaces/ConnectionInterface.php
create mode 100644 src/DB.php
delete mode 100644 src/DBAL/CRUD.php
delete mode 100644 src/DBAL/Database.php
delete mode 100644 src/DBAL/Exceptions/SQLQueryExecuteException.php
delete mode 100644 src/DBAL/Interfaces/CRUDInterface.php
delete mode 100644 src/DBAL/Interfaces/DatabaseInterface.php
delete mode 100644 src/DBAL/Interfaces/ResultInterface.php
delete mode 100644 src/DBAL/Result.php
create mode 100644 src/Database.php
create mode 100644 src/Entity.php
delete mode 100644 src/Facade/DB.php
create mode 100644 src/Model.php
delete mode 100644 src/ORM/Entity.php
delete mode 100644 src/ORM/Exceptions/DeletableException.php
delete mode 100644 src/ORM/Exceptions/EntityNotMethod.php
delete mode 100644 src/ORM/Exceptions/ModelException.php
delete mode 100644 src/ORM/Exceptions/ReadableException.php
delete mode 100644 src/ORM/Exceptions/UpdatableException.php
delete mode 100644 src/ORM/Exceptions/WritableException.php
delete mode 100644 src/ORM/Interfaces/EntityInterface.php
delete mode 100644 src/ORM/Interfaces/ModelInterface.php
delete mode 100644 src/ORM/Model.php
delete mode 100644 src/QueryBuilder/Exceptions/QueryBuilderException.php
delete mode 100644 src/QueryBuilder/Exceptions/QueryGeneratorException.php
delete mode 100644 src/QueryBuilder/Interfaces/ParameterInterface.php
delete mode 100644 src/QueryBuilder/Interfaces/QueryBuilderInterface.php
delete mode 100644 src/QueryBuilder/Parameters.php
delete mode 100644 src/QueryBuilder/QueryBuilder.php
delete mode 100644 src/QueryBuilder/RawQuery.php
delete mode 100644 src/Utils/Helper.php
delete mode 100644 tests/QueryBuilderUnitTest.php
diff --git a/README.md b/README.md
index 98f6c8c..6415f09 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ composer require initphp/database
```php
require_once "vendor/autoload.php";
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
// Connection
DB::createImmutable([
@@ -41,7 +41,7 @@ DB::createImmutable([
#### Create
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
$data = [
'title' => 'Post Title',
'content' => 'Post Content',
@@ -59,17 +59,13 @@ $isInsert = DB::create('post', $data);
*/
if($isInsert){
// Success
-} else {
- foreach (DB::getErrors() as $errMsg) {
- echo $errMsg;
- }
}
```
##### Create Batch
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
$data = [
[
@@ -97,17 +93,13 @@ $isInsert = DB::createBatch('post', $data);
if($isInsert){
// Success
-} else {
- foreach (DB::getErrors() as $errMsg) {
- echo $errMsg;
- }
}
```
#### Read
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
/**
@@ -120,6 +112,7 @@ use \InitPHP\Database\Facade\DB;
* LIMIT 20, 10
*/
+/** @var \InitORM\DBAL\DataMapper\Interfaces\DataMapperInterface $res */
$res = DB::select('user.name as author_name', 'post.id', 'post.title')
->from('post')
->selfJoin('user', 'user.id=post.author')
@@ -127,7 +120,7 @@ $res = DB::select('user.name as author_name', 'post.id', 'post.title')
->orderBy('post.id', 'ASC')
->orderBy('post.created_at', 'DESC')
->offset(20)->limit(10)
- ->read('post');
+ ->read();
if($res->numRows() > 0){
$results = $res->asAssoc()
@@ -141,7 +134,7 @@ if($res->numRows() > 0){
#### Update
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
$data = [
'title' => 'New Title',
'content' => 'New Content',
@@ -159,17 +152,13 @@ $isUpdate = DB::where('id', 13)
*/
if ($isUpdate) {
// Success
-} else {
- foreach (DB::getErrors() as $errMsg) {
- echo $errMsg;
- }
}
```
##### Update Batch
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
$data = [
[
'id' => 5,
@@ -183,7 +172,7 @@ $data = [
];
$isUpdate = DB::where('status', '!=', 0)
- ->updateBatch('post', $data, 'id');
+ ->updateBatch('id', 'post', $data);
/**
* This executes the following query.
@@ -200,17 +189,13 @@ $isUpdate = DB::where('status', '!=', 0)
*/
if ($isUpdate) {
// Success
-} else {
- foreach (DB::getErrors() as $errMsg) {
- echo $errMsg;
- }
}
```
#### Delete
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
$isDelete = DB::where('id', 13)
->delete('post');
@@ -222,18 +207,15 @@ $isDelete = DB::where('id', 13)
*/
if ($isUpdate) {
// Success
-} else {
- foreach (DB::getErrors() as $errMsg) {
- echo $errMsg;
- }
}
```
### RAW
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
+/** @var \InitORM\DBAL\DataMapper\Interfaces\DataMapperInterface $res */
$res = DB::query("SELECT id FROM post WHERE user_id = :id", [
':id' => 5
]);
@@ -242,12 +224,13 @@ $res = DB::query("SELECT id FROM post WHERE user_id = :id", [
#### Builder for RAW
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
+/** @var \InitORM\DBAL\DataMapper\Interfaces\DataMapperInterface $res */
$res = DB::select(DB::raw("CONCAT(name, ' ', surname) AS fullname"))
->where(DB::raw("title = '' AND (status = 1 OR status = 0)"))
->limit(5)
- ->get('users');
+ ->read('users');
/**
* SELECT CONCAT(name, ' ', surname) AS fullname
@@ -269,7 +252,7 @@ This library was developed with the thought that you would work with a single da
If you want to work with a different non-global connection, use the `connect()` method.
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
DB::connect([
'dsn' => 'mysql:host=localhost;port=3306;dbname=test;charset=utf8mb4',
@@ -310,7 +293,7 @@ class Posts extends \InitPHP\Database\Model
/**
* If not specified, \InitPHP\Database\Entity::class is used by default.
*
- * @var \InitPHP\Database\Orm\Entity|string
+ * @var string<\InitPHP\Database\Entity>
*/
protected $entity = \App\Entities\PostEntity::class;
@@ -324,9 +307,9 @@ class Posts extends \InitPHP\Database\Model
/**
* The name of the PRIMARY KEY column. If not, define it as NULL.
*
- * @var null|string
+ * @var string
*/
- protected ?string $schemaId = 'id';
+ protected string $schemaId = 'id';
/**
* Specify FALSE if you want the data to be permanently deleted.
@@ -372,7 +355,7 @@ The most basic example of a entity class would look like this.
```php
namespace App\Entities;
-class PostEntity extends \InitPHP\Database\ORM\Entity
+class PostEntity extends \InitPHP\Database\Entity
{
/**
* An example of a getter method for the "post_title" column.
@@ -406,7 +389,7 @@ Below I have mentioned some developer tools that you can use during and after de
### Logger
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
DB::createImmutable([
'dsn' => 'mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4;',
@@ -424,7 +407,7 @@ _Note :_ You can define variables such as `{year}`, `{month}`, `{day}` in the fi
- You can also define an object with the `critical` method. The database library will pass the log message to this method as a parameter. Or define it as callable array to use any method of the object.
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
class Logger {
@@ -450,7 +433,7 @@ DB::createImmutable([
- Similarly it is possible to define it in a callable method.
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
DB::createImmutable([
'dsn' => 'mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4;',
@@ -469,7 +452,7 @@ DB::createImmutable([
Debug mode is used to include the executed SQL statement in the error message. *__It should only be activated in the development environment__*.
```php
-use \InitPHP\Database\Facade\DB;
+use \InitPHP\Database\DB;
DB::createImmutable([
'dsn' => 'mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4;',
@@ -485,11 +468,11 @@ DB::createImmutable([
Profiler mode is a developer tool available in v3 and above. It is a feature that allows you to see the executed queries along with their execution times.
```php
-use InitPHP\Database\Facade\DB;
+use InitPHP\Database\DB;
DB::enableQueryLog();
-DB::table('users')->where('name', 'John')->get();
+DB::table('users')->where('name', 'John')->read();
var_dump(DB::getQueryLogs());
diff --git a/composer.json b/composer.json
index ffb1fd3..98d8e7e 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,8 @@
"minimum-stability": "stable",
"require": {
"php": ">=8.0",
- "ext-pdo": "*"
+ "ext-pdo": "*",
+ "initorm/orm": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^10.4"
diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php
deleted file mode 100644
index 233b8e1..0000000
--- a/src/Connection/Connection.php
+++ /dev/null
@@ -1,146 +0,0 @@
- '',
- 'username' => 'root',
- 'password' => '',
- 'charset' => 'utf8mb4',
- 'collation' => 'utf8mb4_unicode_ci',
-
- 'debug' => false,
- 'log' => null,
- ];
-
- private ?PDO $pdo = null;
-
- private array $transaction = [
- 'status' => false,
- 'enable' => false,
- 'testMode' => false,
- ];
-
- public function __construct(?array $credentials = null)
- {
- !empty($credentials) && $this->credentials = array_merge($this->credentials, $credentials);
- }
-
- /**
- * @inheritDoc
- */
- public function getCredentials(?string $key = null, mixed $default = null): mixed
- {
- if ($key === null) {
- return $this->credentials;
- }
-
- return $this->credentials[$key] ?? $default;
- }
-
- /**
- * @inheritDoc
- */
- public function getPDO(): PDO
- {
- !isset($this->pdo) && $this->connect();
-
- return $this->pdo;
- }
-
- /**
- * @inheritDoc
- */
- public function connect(): bool
- {
- try {
- $options = [
- PDO::ATTR_EMULATE_PREPARES => false,
- PDO::ATTR_PERSISTENT => true,
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_CLASS,
- ];
-
- $this->pdo = new PDO($this->getCredentials('dsn'), $this->getCredentials('username'), $this->getCredentials('password'), $options);
-
- if ($charset = $this->getCredentials('charset')) {
- if ($collation = $this->getCredentials('collation')) {
- $this->pdo->exec("SET NAMES '" . $charset . "' COLLATE '" . $collation . "'");
- }
- $this->pdo->exec("SET CHARACTER SET '" . $charset . "'");
- }
-
- return true;
- } catch (Exception $e) {
- throw new ConnectionException($e->getMessage(), (int)$e->getCode(), $e->getPrevious());
- }
- }
-
- /**
- * @inheritDoc
- */
- public function beginTransaction(bool $testMode = false): bool
- {
- $this->transaction = [
- 'status' => true,
- 'enable' => true,
- 'testMode' => $testMode,
- ];
-
- return $this->getPDO()->beginTransaction();
- }
-
- /**
- * @inheritDoc
- */
- public function completeTransaction(): bool
- {
- return $this->transaction['status'] === false || $this->transaction['testMode'] === true ? $this->rollBack() : $this->commit();
- }
-
- /**
- * @inheritDoc
- */
- public function commit(): bool
- {
- $this->transaction = [
- 'status' => false,
- 'enable' => false,
- 'testMode' => false,
- ];
-
- return $this->getPDO()->commit();
- }
-
- /**
- * @inheritDoc
- */
- public function rollBack(): bool
- {
- $this->transaction = [
- 'status' => false,
- 'enable' => false,
- 'testMode' => false,
- ];
-
- return $this->getPDO()->rollBack();
- }
-
- /**
- * @inheritDoc
- */
- public function disconnect(): bool
- {
- $this->pdo = null;
-
- return true;
- }
-
-}
diff --git a/src/Connection/Exceptions/ConnectionException.php b/src/Connection/Exceptions/ConnectionException.php
deleted file mode 100644
index 226baf2..0000000
--- a/src/Connection/Exceptions/ConnectionException.php
+++ /dev/null
@@ -1,8 +0,0 @@
-{$name}(...$arguments);
+ }
+
+ public static function __callStatic($name, $arguments)
+ {
+ return self::getDatabase()->{$name}(...$arguments);
+ }
+
+ public static function createImmutable(array|ConnectionInterface $connection): void
+ {
+ self::$db = self::connect($connection);
+ }
+
+ /**
+ * @param array|ConnectionInterface $connection
+ * @return DatabaseInterface
+ */
+ public static function connect(array|ConnectionInterface $connection): DatabaseInterface
+ {
+ return new Database($connection);
+ }
+
+ public static function getDatabase(): DatabaseInterface
+ {
+ if (!isset(self::$db)) {
+ throw new DatabaseException('To create an immutable, first use the "createImmutable()" method.');
+ }
+
+ return self::$db;
+ }
+
+
+}
diff --git a/src/DBAL/CRUD.php b/src/DBAL/CRUD.php
deleted file mode 100644
index 92fcc17..0000000
--- a/src/DBAL/CRUD.php
+++ /dev/null
@@ -1,165 +0,0 @@
-db = &$db;
- }
-
- public function __call(string $name, array $arguments)
- {
- $res = $this->db->{$name}(...$arguments);
-
- return ($res instanceof DatabaseInterface) ? $this : $res;
- }
-
- /**
- * @inheritDoc
- */
- public function create(?string $table = null, ?array $set = null): bool
- {
- $builder = $this->db->getQueryBuilder();
-
- !empty($set) && $builder->set($set);
-
- !empty($table) && $builder->from($table);
-
- $res = $this->db->query($builder->generateInsertQuery());
- $this->db->getQueryBuilder()->getParameter()->reset();
-
- return $res->numRows() > 0;
- }
-
- /**
- * @inheritDoc
- */
- public function createBatch(?string $table = null, ?array $set = null): bool
- {
- $builder = $this->db->getQueryBuilder();
-
- if (!empty($set)) {
- foreach ($set as $row) {
- !empty($row) && is_array($row)
- && $builder->set($row);
- }
- }
-
- !empty($table) && $builder->from($table);
-
- $res = $this->db->query($builder->generateBatchInsertQuery());
- $this->db->getQueryBuilder()->resetStructure();
-
- return $res->numRows() > 0;
- }
-
- /**
- * @inheritDoc
- */
- public function read(?string $table = null, array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
- {
- $builder = $this->db->getQueryBuilder();
-
- !empty($parameters) && $builder->getParameter()->merge($parameters);
-
- !empty($table) && $builder->from($table);
-
-
- $res = $this->db->query($builder->generateSelectQuery($selector, $conditions));
- $this->db->getQueryBuilder()->resetStructure();
-
- return $res;
- }
-
- /**
- * @inheritDoc
- */
- public function readOne(?string $table = null, array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
- {
- $builder = $this->db->getQueryBuilder();
-
- !empty($parameters) && $builder->getParameter()->merge($parameters);
-
- !empty($table) && $builder->from($table);
-
- $res = $this->db->query($builder->limit(1)
- ->generateSelectQuery($selector, $conditions));
-
- $this->db->getQueryBuilder()->resetStructure();
-
- return $res;
- }
-
- /**
- * @inheritDoc
- */
- public function update(?string $table = null, ?array $set = null): bool
- {
- $builder = $this->db->getQueryBuilder();
-
- !empty($set) && $builder->set($set);
- !empty($table) && $builder->from($table);
-
- $res = $this->db->query($builder
- ->generateUpdateQuery());
-
- $this->db->getQueryBuilder()->resetStructure();
-
- return $res->numRows() > 0;
- }
-
- /**
- * @inheritDoc
- */
- public function updateBatch(?string $table = null, ?array $set = null, ?string $referenceColumn = null): bool
- {
- $builder = $this->db->getQueryBuilder();
-
- if (!empty($set)) {
- foreach ($set as $row) {
- if (!empty($row) && is_array($row)) {
- $builder->set($row);
- }
- }
- }
- !empty($table) && $builder->from($table);
-
- $res = $this->db->query($builder
- ->generateUpdateBatchQuery($referenceColumn));
-
- $this->db->getQueryBuilder()->resetStructure();
-
- return $res->numRows() > 0;
- }
-
- /**
- * @inheritDoc
- */
- public function delete(?string $table = null, ?array $conditions = []): bool
- {
- $builder = $this->db->getQueryBuilder();
-
- if (!empty($conditions)) {
- foreach ($conditions as $column => $value) {
- if (is_string($column)) {
- $builder->where($column, $value);
- } else {
- $builder->where($value);
- }
- }
- }
- !empty($table) && $builder->from($table);
-
- $res = $this->db->query($builder->generateDeleteQuery());
-
- return $res->numRows() > 0;
- }
-
-}
diff --git a/src/DBAL/Database.php b/src/DBAL/Database.php
deleted file mode 100644
index 22b6b5c..0000000
--- a/src/DBAL/Database.php
+++ /dev/null
@@ -1,292 +0,0 @@
- true,
- 'queryLog' => false,
- ];
-
- private ResultInterface $lastResult;
-
- private array $queryLogs = [];
-
- private array $errors = [];
-
- public function __construct(ConnectionInterface $connection, QueryBuilderInterface $builder)
- {
- $this->connection = $connection;
- $this->builder = $builder;
- $this->queryOptions = array_merge($this->queryOptions, $connection->getCredentials());
- }
-
- /**
- * @throws Exception
- */
- public function __call(string $name, array $arguments)
- {
- if (method_exists($this->connection, $name)) {
- $res = $this->connection->{$name}(...$arguments);
-
- return ($res instanceof ConnectionInterface) ? $this : $res;
- }
-
- if (method_exists($this->builder, $name)) {
- $res = $this->connection->{$name}(...$arguments);
-
- return ($res instanceof QueryBuilderInterface) ? $this : $res;
- }
-
- throw new Exception("There is no method called \"" . $name . "\"");
- }
-
- /**
- * @inheritDoc
- */
- public function getErrors(): array
- {
- return $this->errors ?? [];
- }
-
- /**
- * @inheritDoc
- */
- public function getQueryBuilder(): QueryBuilderInterface
- {
- return $this->builder;
- }
-
- /**
- * @inheritDoc
- */
- public function getConnection(): ConnectionInterface
- {
- return $this->connection;
- }
-
- /**
- * @inheritDoc
- */
- public function builder(): QueryBuilderInterface
- {
- return $this->getQueryBuilder()->newBuilder();
- }
-
- /**
- * @inheritDoc
- */
- public function newInstance(ConnectionInterface|array $connectionOrCredentials, ?QueryBuilderInterface $builder = null): self
- {
- return new self(($connectionOrCredentials instanceof ConnectionInterface) ? $connectionOrCredentials : new Connection($connectionOrCredentials), $builder ?? $this->builder);
- }
-
- /**
- * @inheritDoc
- */
- public function get(?string $table = null, ?array $selection = null, ?array $conditions = null): ResultInterface
- {
- !empty($table) && $this->getQueryBuilder()->addFrom($table);
- $res = $this->query($this->getQueryBuilder()->generateSelectQuery($selection ?? [], $conditions ?? []));
- $this->getQueryBuilder()
- ->resetStructure();
-
- return $res;
- }
-
- /**
- * @inheritDoc
- */
- public function query(string $rawSQL, ?array $arguments = null, ?array $options = null): ResultInterface
- {
- $arguments = array_merge($this->getQueryBuilder()->getParameter()->all(), ($arguments ?? []));
- $options = array_merge($this->queryOptions, ($options ?? []));
-
- try {
- $timerStart = $options['profiler'] ? microtime(true) : 0;
- $stmt = $this->getConnection()->getPDO()->prepare($rawSQL);
- if ($stmt === false) {
- throw new SQLQueryExecuteException('The SQL query could not be prepared.');
- }
- if (!empty($arguments)) {
- foreach ($arguments as $key => $value) {
- $stmt->bindValue($key, $value, $this->__getValueBindType($value));
- }
- }
- if ($options['parameterReset']) {
- $this->getQueryBuilder()
- ->getParameter()
- ->reset();
- }
- $execute = $stmt->execute();
- if ($options['queryLog']) {
- $timer = round((microtime(true) - $timerStart), 5);
- $this->queryLogs[] = [
- 'query' => $rawSQL,
- 'time' => $timer,
- 'args' => $arguments,
- ];
- }
- if ($execute === false) {
- throw new SQLQueryExecuteException('The SQL query could not be executed.');
- }
- $errorCode = $stmt->errorCode();
- if($errorCode !== null && !empty(trim($errorCode, "0 \t\n\r\0\x0B"))){
- $errorInfo = $stmt->errorInfo();
- if(isset($errorInfo[2])){
- $msg = $errorCode . ' - ' . $errorInfo[2];
- $this->errors[] = $msg;
- !empty($options['log']) && $this->createLog(($msg . ' SQL : ' . $rawSQL), $options['log']);
- }
- }
- $this->lastResult = new Result($stmt);
-
- return !empty($options['fetch_mode'])
- ? $this->lastResult->setFetchMode($options['fetch_mode'])
- : $this->lastResult;
-
- } catch (Exception $e) {
- $message = $e->getMessage();
- $sqlQuery = empty($arguments) ? $rawSQL : strtr($rawSQL, $arguments);
- !empty($options['log']) && $this->createLog(($message . ' SQL : ' . $sqlQuery), $options['log']);
- if ($options['debug'] === true) {
- $message .= ' SQL : ' . $sqlQuery;
- }
- if ($options['parameterReset']) {
- $this->getQueryBuilder()
- ->getParameter()
- ->reset();
- }
- throw new SQLQueryExecuteException($message, (int)$e->getCode());
- }
- }
-
- /**
- * @inheritDoc
- */
- public function count(): int
- {
- $builder = $this->getQueryBuilder()->clone()
- ->resetStructure('select', false);
-
- $builder->selectCount('*', 'row_count');
-
- $res = $this->query($builder->generateSelectQuery(), null, [
- 'parameterReset' => false,
- ]);
-
- return $res->asAssoc()->row()['row_count'] ?? 0;
- }
-
- /**
- * @inheritDoc
- */
- public function insertId(): int
- {
- $id = $this->getConnection()->getPDO()->lastInsertId();
-
- return $id === false ? 0 : (int)$id;
- }
-
-
- /**
- * @inheritDoc
- */
- public function enableQueryLog(): DatabaseInterface
- {
- $this->queryOptions['queryLog'] = true;
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function disableQueryLog(): DatabaseInterface
- {
- $this->queryOptions['queryLog'] = false;
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function getQueryLogs(): array
- {
- return $this->queryLogs;
- }
-
- /**
- * @inheritDoc
- */
- public function transaction(Closure $closure, int $attempt = 1, bool $testMode = false): bool
- {
- $attempt < 1 && $attempt = 1;
- $res = false;
- for ($i = 0; $i < $attempt; ++$i) {
- try {
- $this->getConnection()->beginTransaction($testMode);
- call_user_func_array($closure, [$this]);
- $res = $testMode ? $this->getConnection()->rollBack() : $this->getConnection()->commit();
- break;
- } catch (Throwable $e) {
- $res = $this->getConnection()->rollBack();
- $this->createLog('Transaction Rollback : ' . $e->getMessage());
- }
- }
-
- return $res;
- }
-
- protected function __getValueBindType($value): int
- {
- return match (true) {
- is_bool($value), is_int($value) => PDO::PARAM_INT,
- is_null($value) => PDO::PARAM_NULL,
- default => PDO::PARAM_STR,
- };
- }
-
- private function createLog(string $message, mixed $handler = null): void
- {
- $handler === null && $handler = $this->getConnection()->getCredentials('log');
- if (empty($handler)) {
- return;
- }
- if (is_callable($handler)) {
- call_user_func_array($handler, [$message]);
- } else if (is_object($handler) && method_exists($handler, 'critical')) {
- $handler->critical($message);
- } else if (is_string($handler)) {
- $path = strtr($handler, [
- '{timestamp}' => time(),
- '{date}' => date("Y-m-d"),
- '{year}' => date("Y"),
- '{month}' => date("m"),
- '{day}' => date("d"),
- '{hour}' => date("H"),
- '{minute}' => date("i"),
- '{second}' => date("s"),
- ]);
- @file_put_contents($path, $message, FILE_APPEND);
- }
-
- }
-
-}
diff --git a/src/DBAL/Exceptions/SQLQueryExecuteException.php b/src/DBAL/Exceptions/SQLQueryExecuteException.php
deleted file mode 100644
index c80352a..0000000
--- a/src/DBAL/Exceptions/SQLQueryExecuteException.php
+++ /dev/null
@@ -1,9 +0,0 @@
-\PDO::FETCH_*
- * @return self
- */
- public function setFetchMode(int $mode): self;
-
- /**
- * @param string $class
- * @return self
- */
- public function asClass(string $class = Entity::class): self;
-
- /**
- * @return self
- */
- public function asObject(): self;
-
-
- /**
- * @return self
- */
- public function asAssoc(): self;
-
- /**
- * @return self
- */
- public function asArray(): self;
-
- /**
- * @return self
- */
- public function asLazy(): self;
-
- /**
- * @return array|object|false
- */
- public function row(): array|object|false;
-
- /**
- * @return array[]|object[]|false
- */
- public function rows(): array|false;
-
-
-}
\ No newline at end of file
diff --git a/src/DBAL/Result.php b/src/DBAL/Result.php
deleted file mode 100644
index cf2febe..0000000
--- a/src/DBAL/Result.php
+++ /dev/null
@@ -1,144 +0,0 @@
-setStatement($statement);
- }
-
- /**
- * @inheritDoc
- */
- public function setStatement(PDOStatement $statement): self
- {
- $this->statement = $statement;
- $this->query = $statement->queryString;
- $this->numRows = $statement->rowCount();
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function withStatement(PDOStatement $statement): self
- {
- return new self($statement);
- }
-
- /**
- * @inheritDoc
- */
- public function getStatement(): PDOStatement
- {
- return $this->statement;
- }
-
- /**
- * @inheritDoc
- */
- public function numRows(): int
- {
- return $this->numRows;
- }
-
- /**
- * @inheritDoc
- */
- public function query(): string
- {
- return $this->query;
- }
-
- /**
- * @inheritDoc
- */
- public function setFetchMode(int $mode): self
- {
- $this->getStatement()->setFetchMode($mode);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function asClass(string $class = Entity::class): self
- {
- $this->getStatement()->setFetchMode(PDO::FETCH_CLASS, $class);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function asObject(): self
- {
- $this->getStatement()->setFetchMode(PDO::FETCH_OBJ);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function asAssoc(): self
- {
- $this->getStatement()->setFetchMode(PDO::FETCH_ASSOC);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function asArray(): self
- {
- $this->getStatement()->setFetchMode(PDO::FETCH_BOTH);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function asLazy(): self
- {
- $this->getStatement()->setFetchMode(PDO::FETCH_LAZY);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function row(): array|object|false
- {
- return $this->getStatement()->fetch();
- }
-
- /**
- * @inheritDoc
- */
- public function rows(): array|false
- {
- return $this->getStatement()->fetchAll();
- }
-
-}
diff --git a/src/Database.php b/src/Database.php
new file mode 100644
index 0000000..e866994
--- /dev/null
+++ b/src/Database.php
@@ -0,0 +1,8 @@
+
- * @copyright Copyright © 2022 Muhammet ŞAFAK
- * @license ./LICENSE MIT
- * @version 3.0
- * @link https://www.muhammetsafak.com.tr
- */
-
-namespace InitPHP\Database\Facade;
-
-use \InitPHP\Database\Connection\{Connection,
- Interfaces\ConnectionInterface};
-use \InitPHP\Database\DBAL\{CRUD,
- Database,
- Interfaces\CRUDInterface,
- Interfaces\ResultInterface};
-use \InitPHP\Database\QueryBuilder\{QueryBuilder,
- RawQuery,
- Interfaces\ParameterInterface,
- Interfaces\QueryBuilderInterface};
-use PDO;
-use Closure;
-
-/**
- * @mixin CRUDInterface
- * @method static mixed getCredentials(?string $key = null, mixed $default = null)
- * @method static PDO getPDO()
- * @method static bool beginTransaction(bool $testMode = false)
- * @method static bool completeTransaction()
- * @method static bool commit()
- * @method static bool rollBack()
- * @method static bool disconnect()
- * @method static CRUDInterface enableQueryLog()
- * @method static CRUDInterface disableQueryLog()
- * @method static CRUDInterface getQueryLogs()
- * @method static QueryBuilderInterface getQueryBuilder()
- * @method static ConnectionInterface getConnection()
- * @method static QueryBuilderInterface builder()
- * @method static CRUDInterface newInstance(ConnectionInterface|array $connectionOrCredentials, ?QueryBuilderInterface $builder = null)
- * @method static ResultInterface get(?string $table = null, ?array $selection = null, ?array $conditions = null)
- * @method static ResultInterface query(string $rawSQL, ?array $arguments = null, ?array $options = null)
- * @method static int count()
- * @method static int insertId()
- * @method static bool transaction(Closure $closure, int $attempt = 1, bool $testMode = false)
- * @method static bool create(?string $table = null, ?array $set = null)
- * @method static bool createBatch(?string $table = null, ?array $set = null)
- * @method static ResultInterface read(?string $table = null, array $selector = [], array $conditions = [], array $parameters = [])
- * @method static ResultInterface readOne(?string $table = null, array $selector = [], array $conditions = [], array $parameters = [])
- * @method static bool update(?string $table = null, ?array $set = null)
- * @method static bool updateBatch(?string $table = null, ?array $set = null, ?string $referenceColumn = null)
- * @method static bool delete(?string $table = null, ?array $conditions = [])
- * @method static CRUDInterface newBuilder()
- * @method static CRUDInterface importQB(array $structure, bool $merge = false)
- * @method static array exportQB()
- * @method static ParameterInterface getParameter()
- * @method static CRUDInterface setParameter(string $key, mixed $value)
- * @method static CRUDInterface setParameters(array $parameters = [])
- * @method static CRUDInterface select(string|RawQuery|string[]|RawQuery[] ...$columns)
- * @method static CRUDInterface selectCount(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectCountDistinct(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectMax(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectMin(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectAvg(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectAs(RawQuery|string $column, string $alias)
- * @method static CRUDInterface selectUpper(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectLower(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectLength(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectMid(RawQuery|string $column, int $offset, int $length, ?string $alias = null)
- * @method static CRUDInterface selectLeft(RawQuery|string $column, int $length, ?string $alias = null)
- * @method static CRUDInterface selectRight(RawQuery|string $column, int $length, ?string $alias = null)
- * @method static CRUDInterface selectDistinct(RawQuery|string $column, ?string $alias = null)
- * @method static CRUDInterface selectCoalesce(RawQuery|string $column, mixed $default = '0', ?string $alias = null)
- * @method static CRUDInterface selectSum(string|RawQuery $column, ?string $alias = null)
- * @method static CRUDInterface selectConcat(array $columns, ?string $alias = null)
- * @method static CRUDInterface from(RawQuery|string $table, ?string $alias = null)
- * @method static CRUDInterface addFrom(RawQuery|string $table, ?string $alias = null)
- * @method static CRUDInterface table(string|RawQuery $table)
- * @method static CRUDInterface groupBy(string|RawQuery|array ...$columns)
- * @method static CRUDInterface join(RawQuery|string $table, RawQuery|string|Closure $onStmt = null, string $type = 'INNER')
- * @method static CRUDInterface selfJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
- * @method static CRUDInterface innerJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
- * @method static CRUDInterface leftJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
- * @method static CRUDInterface rightJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
- * @method static CRUDInterface leftOuterJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
- * @method static CRUDInterface rightOuterJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
- * @method static CRUDInterface naturalJoin(string|RawQuery $table, string|RawQuery|Closure $onStmt)
- * @method static CRUDInterface orderBy(RawQuery|string $column, string $soft = 'ASC')
- * @method static CRUDInterface where(RawQuery|string $column, string $operator = '=', mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface having(RawQuery|string $column, string $operator = '=', mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface on(RawQuery|string $column, string $operator = '=', mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface set(RawQuery|array|string $column, mixed $value = null, bool $strict = true)
- * @method static CRUDInterface addSet(RawQuery|array|string $column, mixed $value = null, bool $strict = true)
- * @method static CRUDInterface andWhere(string|RawQuery $column, string $operator = '=', mixed $value = null)
- * @method static CRUDInterface orWhere(string|RawQuery $column, string $operator = '=', mixed $value = null)
- * @method static CRUDInterface between(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND')
- * @method static CRUDInterface orBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
- * @method static CRUDInterface andBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
- * @method static CRUDInterface notBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND')
- * @method static CRUDInterface orNotBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
- * @method static CRUDInterface andNotBetween(string|RawQuery $column, mixed $firstValue = null, mixed $lastValue = null)
- * @method static CRUDInterface findInSet(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface andFindInSet(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface orFindInSet(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface notFindInSet(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface andNotFindInSet(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface orNotFindInSet(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface whereIn(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface whereNotIn(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface orWhereIn(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface orWhereNotIn(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface andWhereIn(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface andWhereNotIn(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface regexp(string|RawQuery $column, string|RawQuery $value, string $logical = 'AND')
- * @method static CRUDInterface andRegexp(string|RawQuery $column, string|RawQuery $value)
- * @method static CRUDInterface orRegexp(string|RawQuery $column, string|RawQuery $value)
- * @method static CRUDInterface soundex(string|RawQuery $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface andSoundex(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface orSoundex(string|RawQuery $column, mixed $value = null)
- * @method static CRUDInterface whereIsNull(string|RawQuery $column, string $logical = 'AND')
- * @method static CRUDInterface orWhereIsNull(string|RawQuery $column)
- * @method static CRUDInterface andWhereIsNull(string|RawQuery $column)
- * @method static CRUDInterface whereIsNotNull(string|RawQuery $column, string $logical = 'AND')
- * @method static CRUDInterface orWhereIsNotNull(string|RawQuery $column)
- * @method static CRUDInterface andWhereIsNotNull(string|RawQuery $column)
- * @method static CRUDInterface offset(int $offset = 0)
- * @method static CRUDInterface limit(int $limit)
- * @method static CRUDInterface like(string|RawQuery|array $column, mixed $value = null, string $type = 'both', string $logical = 'AND')
- * @method static CRUDInterface orLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
- * @method static CRUDInterface andLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
- * @method static CRUDInterface notLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both', string $logical = 'AND')
- * @method static CRUDInterface orNotLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
- * @method static CRUDInterface andNotLike(string|RawQuery|array $column, mixed $value = null, string $type = 'both')
- * @method static CRUDInterface startLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface orStartLike(string|RawQuery|array $column, mixed $value = null)
- * @method static CRUDInterface andStartLike(string|RawQuery|array $column, mixed $value = null)
- * @method static CRUDInterface notStartLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface orStartNotLike(string|RawQuery|array $column, mixed $value = null)
- * @method static CRUDInterface andStartNotLike(string|RawQuery|array $column, mixed $value = null)
- * @method static CRUDInterface endLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface orEndLike(string|RawQuery|array $column, mixed $value = null)
- * @method static CRUDInterface andEndLike(string|RawQuery|array $column, mixed $value = null)
- * @method static CRUDInterface notEndLike(string|RawQuery|array $column, mixed $value = null, string $logical = 'AND')
- * @method static CRUDInterface orEndNotLike(string|RawQuery|array $column, mixed $value = null)
- * @method static CRUDInterface andEndNotLike(string|RawQuery|array $column, mixed $value = null)
- * @method static RawQuery subQuery(Closure $closure, ?string $alias = null, bool $isIntervalQuery = true)
- * @method static CRUDInterface group(Closure $closure)
- * @method static RawQuery raw(mixed $rawQuery)
- * @method static string[] getErrors()
- */
-class DB
-{
-
- private static CRUDInterface $databaseInstance;
-
- public function __call($name, $arguments)
- {
- return self::getDatabase()->{$name}(...$arguments);
- }
-
- public static function __callStatic($name, $arguments)
- {
- return self::getDatabase()->{$name}(...$arguments);
- }
-
- public static function createImmutable(array|ConnectionInterface $credentialsOrConnection): CRUDInterface
- {
- if (isset(self::$databaseInstance)) {
- return self::$databaseInstance;
- }
-
- return self::$databaseInstance = new CRUD(new Database(($credentialsOrConnection instanceof ConnectionInterface) ? $credentialsOrConnection : new Connection($credentialsOrConnection), new QueryBuilder()));
- }
-
- public static function connect(array|ConnectionInterface $credentialsOrConnection): CRUDInterface
- {
- return new CRUD(new Database((($credentialsOrConnection instanceof ConnectionInterface) ? $credentialsOrConnection : new Connection($credentialsOrConnection)), new QueryBuilder()));
- }
-
- public static function getDatabase(): CRUDInterface
- {
- return self::$databaseInstance;
- }
-
-}
diff --git a/src/Model.php b/src/Model.php
new file mode 100644
index 0000000..0d82c40
--- /dev/null
+++ b/src/Model.php
@@ -0,0 +1,8 @@
+setUp($data);
- }
-
- /**
- * @param $name
- * @param $arguments
- * @return mixed
- * @throws EntityNotMethod
- */
- public function __call($name, $arguments)
- {
- if (str_ends_with($name, 'Attribute') === false) {
- throw new EntityNotMethod($name);
- }
-
- $attr = Helper::camelCaseToSnakeCase(substr($name, 3, -9));
-
- return match (substr($name, 0, 3)) {
- 'get' => $this->__attributes[$attr] ?? null,
- 'set' => $this->__attributes[$attr] = $arguments[0],
- default => throw new EntityNotMethod($name)
- };
- }
-
- public function __set($name, $value)
- {
- $methodName = 'set' . Helper::snakeCaseToPascalCase($name) . 'Attribute';
- if(method_exists($this, $methodName)){
- $this->{$methodName}($value);
- return $value;
- }
- return $this->__attributes[$name] = $value;
- }
-
- public function __get($name)
- {
- $methodName = 'get' . Helper::snakeCaseToPascalCase($name) . 'Attribute';
- if(method_exists($this, $methodName)){
- return $this->{$methodName}();
- }
- return $this->__attributes[$name] ?? null;
- }
-
- public function __isset($name)
- {
- return isset($this->__attributes[$name]);
- }
-
- public function __unset($name)
- {
- if(isset($this->__attributes[$name])){
- unset($this->__attributes[$name]);
- }
- }
-
- public function __debugInfo()
- {
- return $this->__attributes;
- }
-
- /**
- * @inheritDoc
- */
- public function toArray(): array
- {
- return $this->__attributes;
- }
-
- /**
- * @inheritDoc
- */
- public function getAttributes(): array
- {
- return $this->toArray();
- }
-
- /**
- * @param array|null $data
- * @return $this
- */
- protected function setUp(?array $data = null): self
- {
- $this->syncOriginal()
- ->fill($data);
- return $this;
- }
-
- /**
- * @param array|null $data
- * @return $this
- */
- protected function fill(?array $data = null): self
- {
- if($data !== null){
- foreach ($data as $key => $value) {
- $this->__set($key, $value);
- }
- }
- return $this;
- }
-
- /**
- * @return $this
- */
- protected function syncOriginal(): self
- {
- $this->__attributesOriginal = $this->__attributes;
- return $this;
- }
-
-}
diff --git a/src/ORM/Exceptions/DeletableException.php b/src/ORM/Exceptions/DeletableException.php
deleted file mode 100644
index 67c7696..0000000
--- a/src/ORM/Exceptions/DeletableException.php
+++ /dev/null
@@ -1,7 +0,0 @@
-schema)) {
- $modelClass = get_called_class();
- $modelReflection = new ReflectionClass($modelClass);
- $this->schema = Helper::camelCaseToSnakeCase($modelReflection->getShortName());
- unset($modelClass, $modelReflection);
- }
- if ($this->useSoftDeletes !== false && empty($this->deletedField)) {
- throw new ModelException('There must be a delete column to use soft delete.');
- }
-
- $this->crud = empty($this->credentials) ? DB::getDatabase() : DB::connect($this->credentials);
- }
-
- public function __call(string $name, array $arguments)
- {
- $res = $this->crud->{$name}(...$arguments);
-
- return ($res instanceof CRUDInterface) ? $this : $res;
- }
-
- /**
- * @inheritDoc
- */
- public function create(array $set = []): bool
- {
- if (!$this->writable) {
- throw new WritableException();
- }
-
- !empty($this->createdField) && $set[$this->createdField] = date($this->timestampFormat);
-
- return $this->crud->create($this->schema, $set);
- }
-
- /**
- * @inheritDoc
- */
- public function createBatch(array $set = []): bool
- {
- if (!$this->writable) {
- throw new WritableException();
- }
- $createdField = $this->createdField;
- if (!empty($createdField) && !empty($set)) {
- foreach ($set as &$row) {
- $row[$createdField] = date($this->timestampFormat);
- }
- }
-
- return $this->crud->createBatch($this->schema, $set);
- }
-
- /**
- * @inheritDoc
- */
- public function read(array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
- {
- if (!$this->readable) {
- throw new ReadableException();
- }
- if ($this->useSoftDeletes) {
- if ($this->isOnlyDelete) {
- $this->onlyDeleted();
- } else {
- $this->ignoreDeleted();
- }
- $this->isOnlyDelete = false;
- }
-
- return $this->crud
- ->read($this->schema, $selector, $conditions, $parameters)
- ->asClass($this->entity);
- }
-
- /**
- * @inheritDoc
- */
- public function readOne(array $selector = [], array $conditions = [], array $parameters = []): ResultInterface
- {
- if (!$this->readable) {
- throw new ReadableException();
- }
- if ($this->useSoftDeletes) {
- if ($this->isOnlyDelete) {
- $this->onlyDeleted();
- } else {
- $this->ignoreDeleted();
- }
- $this->isOnlyDelete = false;
- }
-
- return $this->crud
- ->readOne($this->schema, $selector, $conditions, $parameters)
- ->asClass($this->entity);
- }
-
- /**
- * @inheritDoc
- */
- public function update(array $set = []): bool
- {
- if (!$this->updatable) {
- throw new UpdatableException();
- }
-
- if (!empty($this->schemaId) && isset($set[$this->schemaId])) {
- $this->where($this->schemaId, $set[$this->schemaId]);
- unset($set[$this->schemaId]);
- }
-
- !empty($this->updatedField) && $set[$this->updatedField] = date($this->timestampFormat);
-
- $this->ignoreDeleted();
-
- return $this->crud->update($this->schema, $set);
- }
-
- /**
- * @inheritDoc
- */
- public function updateBatch(array $set = [], ?string $referenceColumn = null): bool
- {
- if (!$this->updatable) {
- throw new UpdatableException();
- }
- $updatedField = $this->updatedField;
- if (!empty($updatedField) && !empty($set)) {
- foreach ($set as &$row) {
- $row[$updatedField] = date($this->timestampFormat);
- }
- }
- $this->ignoreDeleted();
-
- return $this->crud->updateBatch($this->schema, $set, $referenceColumn ?? $this->schemaId);
- }
-
- /**
- * @inheritDoc
- */
- public function delete(?array $conditions = null, bool $purge = false): bool
- {
- if (!$this->deletable) {
- throw new DeletableException();
- }
- if ($this->useSoftDeletes && $purge === false) {
- $this->ignoreDeleted()
- ->set($this->deletedField, date($this->timestampFormat));
-
- if (!empty($conditions)) {
- foreach ($conditions as $column => $value) {
- if (is_string($column)) {
- $this->crud->getQueryBuilder()->where($column, $value);
- } else {
- $this->crud->getQueryBuilder()->where($value);
- }
- }
- }
-
- return $this->crud->update($this->schema);
- }
-
- return $this->crud->delete($this->schema, $conditions);
- }
-
- /**
- * @inheritDoc
- */
- public function save(EntityInterface $entity): bool
- {
- $data = $entity->toArray();
-
- return !empty($this->schemaId) && isset($data[$this->schemaId]) ? $this->update($data) : $this->create($data);
- }
-
- /**
- * @inheritDoc
- */
- public function ignoreDeleted(): self
- {
- $this->useSoftDeletes && $this->crud->getQueryBuilder()
- ->whereIsNull($this->deletedField);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function onlyDeleted(): self
- {
- $this->useSoftDeletes && $this->crud->getQueryBuilder()
- ->whereIsNotNull($this->deletedField);
-
- return $this;
- }
-
-}
diff --git a/src/QueryBuilder/Exceptions/QueryBuilderException.php b/src/QueryBuilder/Exceptions/QueryBuilderException.php
deleted file mode 100644
index 2d5c661..0000000
--- a/src/QueryBuilder/Exceptions/QueryBuilderException.php
+++ /dev/null
@@ -1,8 +0,0 @@
-parameters = [];
- }
-
- /**
- * @inheritDoc
- */
- public function set(string $key, mixed $value): self
- {
- $this->parameters[':' . ltrim(str_replace('.', '', $key), ':')] = $value;
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function add(RawQuery|string $key, mixed $value): string
- {
- if ($value === null) {
- return 'NULL';
- }
- if ($key instanceof RawQuery) {
- $key = md5((string)$key);
- }
- $originKey = ltrim(str_replace('.', '', $key), ':');
- $i = 0;
- do {
- $key = ':' . ($i === 0 ? $originKey : $originKey . '_' . $i);
- ++$i;
- $hasParameter = isset($this->parameters[$key]);
- } while($hasParameter);
-
- $this->parameters[$key] = $value;
-
- return $key;
- }
-
- /**
- * @inheritDoc
- */
- public function merge(array|ParameterInterface ...$arrays): self
- {
- foreach ($arrays as $array) {
- if ($array instanceof ParameterInterface) {
- $array = $array->all();
- }
- foreach ($array as $key => $value) {
- $this->set($key, $value);
- }
- }
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function get(?string $key = null, mixed $default = null): mixed
- {
- if ($key === null) {
- return $this->parameters;
- }
-
- $key = ':' . ltrim($key, ':');
- if (isset($this->parameters[$key])) {
- return $this->parameters[$key];
- }
-
- return ($default instanceof Closure) ? call_user_func_array($default, []) : $default;
- }
-
- /**
- * @inheritDoc
- */
- public function all(): array
- {
- return $this->parameters;
- }
-
- /**
- * @inheritDoc
- */
- public function reset(): self
- {
- $this->parameters = [];
-
- return $this;
- }
-
-}
diff --git a/src/QueryBuilder/QueryBuilder.php b/src/QueryBuilder/QueryBuilder.php
deleted file mode 100644
index 19ee7e7..0000000
--- a/src/QueryBuilder/QueryBuilder.php
+++ /dev/null
@@ -1,1455 +0,0 @@
- [],
- 'table' => [],
- 'join' => [],
- 'where' => [
- 'AND' => [],
- 'OR' => [],
- ],
- 'having' => [
- 'AND' => [],
- 'OR' => [],
- ],
- 'group_by' => [],
- 'order_by' => [],
- 'offset' => null,
- 'limit' => null,
- 'set' => [],
- 'on' => [
- 'AND' => [],
- 'OR' => [],
- ],
- ];
-
- protected array $structure;
-
- protected ParameterInterface $parameters;
-
- public function __construct()
- {
- $this->structure = self::STRUCTURE;
- $this->parameters = new Parameters();
- }
-
- /**
- * @return string
- * @throws QueryGeneratorException
- */
- public function __toString(): string
- {
- if (empty($this->structure['set'])) {
- return $this->generateSelectQuery();
- }
-
- $isBatch = $this->isBatch();
- $isInsert = empty($this->structure['where']['OR']) && empty($this->structure['where']['AND']) && empty($this->structure['having']['OR']) && empty($this->structure['having']['AND']);
-
- if ($isInsert) {
- return $isBatch ? $this->generateBatchInsertQuery() : $this->generateInsertQuery();
- }
-
- return $this->generateUpdateQuery();
- }
-
- /**
- * @inheritDoc
- */
- public function newBuilder(): self
- {
- return new self();
- }
-
- /**
- * @param string[]|string|null $ignoreOrCare
- * @param null|bool $isIgnore
- * @return $this
- */
- public function resetStructure(null|array|string $ignoreOrCare = null, ?bool $isIgnore = null): self
- {
- if ($ignoreOrCare === null) {
- $this->structure = self::STRUCTURE;
- } else {
- if (is_string($ignoreOrCare)) {
- $ignoreOrCare = [$ignoreOrCare];
- }
-
- $newStructure = self::STRUCTURE;
- foreach ($ignoreOrCare as $key) {
- if (!isset($this->structure[$key])) {
- continue;
- }
- if ($isIgnore) {
- $newStructure[$key] = $this->structure[$key];
- } else {
- $newStructure[$key] = self::STRUCTURE[$key] ?? [];
- }
- }
-
- $this->structure = $newStructure;
- }
-
- return $this;
- }
-
- public function clone(): self
- {
- return (clone $this);
- }
-
- /**
- * @inheritDoc
- */
- public function importQB(array $structure, bool $merge = false): self
- {
- $this->structure = array_merge(($merge ? $this->structure : self::STRUCTURE), $structure);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function exportQB(): array
- {
- return $this->structure;
- }
-
- /**
- * @inheritDoc
- */
- public function getParameter(): ParameterInterface
- {
- return $this->parameters;
- }
-
- /**
- * @inheritDoc
- */
- public function setParameter(string $key, mixed $value): self
- {
- $this->parameters->set($key, $value);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function setParameters(array $parameters = []): self
- {
- foreach ($parameters as $key => $value) {
- $this->parameters->set($key, $value);
- }
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function select(...$columns): self
- {
- foreach ($columns as $column) {
- $column = (string)$column;
- $this->structure['select'][] = $column;
- }
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function clearSelect(): self
- {
- $this->structure['select'] = [];
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectCount(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'COUNT(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectCountDistinct(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'COUNT(DISTINCT ' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectMax(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'MAX(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectMin(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'MIN(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectAvg(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'AVG(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectAs(RawQuery|string $column, string $alias): self
- {
- $this->structure['select'][] = $column . ' AS ' . $alias;
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectUpper(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'UPPER(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectLower(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'LOWER(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectLength(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'LENGTH(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectMid(RawQuery|string $column, int $offset, int $length, ?string $alias = null): self
- {
- $this->structure['select'][] = 'MID(' . $column . ', ' . $offset . ', ' . $length . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectLeft(RawQuery|string $column, int $length, ?string $alias = null): self
- {
- $this->structure['select'][] = 'LEFT(' . $column . ', ' . $length . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectRight(RawQuery|string $column, int $length, ?string $alias = null): self
- {
- $this->structure['select'][] = 'RIGHT(' . $column . ', ' . $length . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectDistinct(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'DISTINCT(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectCoalesce(RawQuery|string $column, $default = '0', ?string $alias = null): self
- {
- $this->structure['select'][] = 'COALESCE(' . $column . ', ' . $default . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectSum(RawQuery|string $column, ?string $alias = null): self
- {
- $this->structure['select'][] = 'SUM(' . $column . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selectConcat(array $columns, ?string $alias = null): self
- {
- foreach ($columns as &$column) {
- $column = (string)$column;
- }
- $this->structure['select'][] = 'CONCAT(' . implode(', ', $columns) . ')'
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function from(RawQuery|string $table, ?string $alias = null): self
- {
- $this->structure['table'] = [];
-
- return $this->addFrom($table, $alias);
- }
-
- /**
- * @inheritDoc
- */
- public function addFrom(RawQuery|string $table, ?string $alias = null): self
- {
- $table = $table . ($alias !== null ? ' AS ' . $alias : '');
- if (!in_array($table, $this->structure['table'], true)) {
- $this->structure['table'][] = $table;
- }
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function table(RawQuery|string $table): self
- {
- $this->structure['table'] = [(string)$table];
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function groupBy(string|RawQuery|array ...$columns): self
- {
- foreach ($columns as $column) {
- if (is_array($column)) {
- $this->groupBy(...$column);
- continue;
- }
-
- $column = (string)$column;
- if (!in_array($column, $this->structure['group_by'])) {
- $this->structure['group_by'][] = $column;
- }
- }
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function join(RawQuery|string $table, RawQuery|Closure|string $onStmt = null, string $type = 'INNER'): self
- {
- $table = (string)$table;
-
- if ($onStmt instanceof Closure) {
- $builder = $this->clone()->resetStructure();
- $onStmt = call_user_func_array($onStmt, [&$builder]);
- if ($onStmt === null) {
- if ($where = $builder->__generateWhereQuery()) {
- $this->where($this->raw($where));
- }
- if ($having = $builder->__generateHavingQuery()) {
- if (str_starts_with($having, ' HAVING ')) {
- $having = substr($having, 8);
- }
- $this->having($this->raw($having));
- }
- $onStmt = $builder->__generateOnQuery();
- }
- }
-
- $type = trim(strtoupper($type));
- switch ($type) {
- case 'SELF':
- $this->addFrom($table);
- $this->where(is_string($onStmt) ? $this->raw($onStmt) : $onStmt);
- break;
- case 'NATURAL':
- case 'NATURAL JOIN':
- $this->structure['join'][$table] = 'NATURAL JOIN ' . $table;
- break;
- default:
- $this->structure['join'][$table] = trim($type . ' JOIN ' . $table . ' ON ' . $onStmt);
- }
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function selfJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
- {
- return $this->join($table, $onStmt, 'SELF');
- }
-
- /**
- * @inheritDoc
- */
- public function innerJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
- {
- return $this->join($table, $onStmt);
- }
-
- /**
- * @inheritDoc
- */
- public function leftJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
- {
- return $this->join($table, $onStmt, 'LEFT');
- }
-
- /**
- * @inheritDoc
- */
- public function rightJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
- {
- return $this->join($table, $onStmt, 'RIGHT');
- }
-
- /**
- * @inheritDoc
- */
- public function leftOuterJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
- {
- return $this->join($table, $onStmt, 'LEFT OUTER');
- }
-
- /**
- * @inheritDoc
- */
- public function rightOuterJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
- {
- return $this->join($table, $onStmt, 'RIGHT OUTER');
- }
-
- /**
- * @inheritDoc
- */
- public function naturalJoin(RawQuery|string $table, RawQuery|Closure|string $onStmt): self
- {
- return $this->join($table, null, 'NATURAL');
- }
-
- /**
- * @inheritDoc
- */
- public function orderBy(RawQuery|string $column, string $soft = 'ASC'): self
- {
- $soft = trim(strtoupper($soft));
- if (!in_array($soft, ['ASC', 'DESC'], true)) {
- throw new InvalidArgumentException('It can only sort as ASC or DESC.');
- }
- $orderBy = trim((string)$column) . ' ' . $soft;
-
- !in_array($orderBy, $this->structure['order_by'], true) && $this->structure['order_by'][] = $orderBy;
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function where(RawQuery|string $column, mixed $operator = '=', mixed $value = null, string $logical = 'AND'): self
- {
-
- $this->whereOrHavingPrepare($operator, $value, $logical);
-
- $this->structure['where'][$logical][] = $this->whereOrHavingStatementPrepare($column, $operator, $value);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function having(RawQuery|string $column, mixed $operator = '=', mixed $value = null, string $logical = 'AND'): self
- {
- $this->whereOrHavingPrepare($operator, $value, $logical);
- $this->structure['having'][$logical][] = $this->whereOrHavingStatementPrepare($column, $operator, $value);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function on(RawQuery|string $column, mixed $operator = '=', mixed $value = null, string $logical = 'AND'): self
- {
- $this->whereOrHavingPrepare($operator, $value, $logical);
-
- $this->structure['on'][$logical][] = $this->whereOrHavingStatementPrepare($column, $operator, $value);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function set(RawQuery|array|string $column, mixed $value = null, bool $strict = true): self
- {
- return $this->addSet($column, $value, $strict);
- }
-
- /**
- * @inheritDoc
- */
- public function addSet(RawQuery|array|string $column, mixed $value = null, bool $strict = true): self
- {
- if (is_array($column) && $value === null) {
- $set = [];
- foreach ($column as $name => $value) {
- $name = (string)$name;
- $set[$name] = $this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($name, $value);
- }
- $this->structure['set'][] = $set;
-
- return $this;
- }
-
- $column = (string)$column;
- $value = $this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value);
-
- $this->structure['set'][][$column] = $value;
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function andWhere(RawQuery|string $column, mixed $operator = '=', mixed $value = null): self
- {
- return $this->where($column, $operator, $value);
- }
-
- /**
- * @inheritDoc
- */
- public function orWhere(RawQuery|string $column, mixed $operator = '=', mixed $value = null): self
- {
- return $this->where($column, $operator, $value, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function between(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND'): self
- {
- if (is_array($firstValue) && count($firstValue) == 2 && $lastValue === null) {
- $value = $firstValue;
- } else {
- $value = [$firstValue, $lastValue];
- }
-
- return $this->where($column, 'BETWEEN', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
- {
- return $this->between($column, [$firstValue, $lastValue], 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
- {
- return $this->between($column, $firstValue, $lastValue);
- }
-
- /**
- * @inheritDoc
- */
- public function notBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null, string $logical = 'AND'): self
- {
- if (is_array($firstValue) && count($firstValue) == 2 && $lastValue === null) {
- $value = $firstValue;
- } else {
- $value = [$firstValue, $lastValue];
- }
-
- return $this->where($column, 'NOT BETWEEN', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orNotBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
- {
- return $this->notBetween($column, $firstValue, $lastValue, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andNotBetween(RawQuery|string $column, mixed $firstValue = null, mixed $lastValue = null): self
- {
- return $this->notBetween($column, $firstValue, $lastValue);
- }
-
- /**
- * @inheritDoc
- */
- public function findInSet(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->where($column, 'FIND_IN_SET', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function andFindInSet(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'FIND_IN_SET', $value);
- }
-
- /**
- * @inheritDoc
- */
- public function orFindInSet(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'FIND_IN_SET', $value, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function notFindInSet(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->where($column, 'NOT FIND_IN_SET', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function andNotFindInSet(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'NOT FIND_IN_SET', $value);
- }
-
- /**
- * @inheritDoc
- */
- public function orNotFindInSet(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'NOT FIND_IN_SET', $value, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function whereIn(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->where($column, 'IN', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function whereNotIn(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->where($column, 'NOT IN', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orWhereIn(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'IN', $value, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function orWhereNotIn(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'NOT IN', $value, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andWhereIn(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'IN', $value);
- }
-
- /**
- * @inheritDoc
- */
- public function andWhereNotIn(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'IN', $value);
- }
-
- /**
- * @inheritDoc
- */
- public function regexp(RawQuery|string $column, RawQuery|string $value, string $logical = 'AND'): self
- {
- return $this->where($column, 'REGEXP', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function andRegexp(RawQuery|string $column, RawQuery|string $value): self
- {
- return $this->where($column, 'REGEXP', $value);
- }
-
- /**
- * @inheritDoc
- */
- public function orRegexp(RawQuery|string $column, RawQuery|string $value): self
- {
- return $this->where($column, 'REGEXP', $value, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function soundex(RawQuery|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->where($column, 'SOUNDEX', $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function andSoundex(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'SOUNDEX', $value);
- }
-
- /**
- * @inheritDoc
- */
- public function orSoundex(RawQuery|string $column, mixed $value = null): self
- {
- return $this->where($column, 'SOUNDEX', $value, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function whereIsNull(RawQuery|string $column, string $logical = 'AND'): self
- {
- return $this->where($column, 'IS', null, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orWhereIsNull(RawQuery|string $column): self
- {
- return $this->where($column, 'IS', null, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andWhereIsNull(RawQuery|string $column): self
- {
- return $this->where($column, 'IS');
- }
-
- /**
- * @inheritDoc
- */
- public function whereIsNotNull(RawQuery|string $column, string $logical = 'AND'): self
- {
- return $this->where($column, 'IS NOT', null, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orWhereIsNotNull(RawQuery|string $column): self
- {
- return $this->where($column, 'IS NOT', null, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andWhereIsNotNull(RawQuery|string $column): self
- {
- return $this->where($column, 'IS NOT');
- }
-
- /**
- * @inheritDoc
- */
- public function offset(int $offset = 0): self
- {
- $this->structure['offset'] = (int)abs($offset);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function limit(int $limit): self
- {
- $this->structure['limit'] = (int)abs($limit);
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function like(RawQuery|array|string $column, mixed $value = null, string $type = 'both', string $logical = 'AND'): self
- {
- $operator = match (strtolower($type)) {
- 'before', 'start' => 'START LIKE',
- 'after', 'end' => 'END LIKE',
- default => 'LIKE'
- };
-
- return $this->where($column, $operator, $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
- {
- return $this->like($column, $value, $type);
- }
-
- /**
- * @inheritDoc
- */
- public function andLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
- {
- return $this->like($column, $value, $type, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function notLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both', string $logical = 'AND'): self
- {
- $operator = match (strtolower($type)) {
- 'before', 'start' => 'NOT START LIKE',
- 'after', 'end' => 'NOT END LIKE',
- default => 'NOT LIKE'
- };
-
- return $this->where($column, $operator, $value, $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orNotLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
- {
- return $this->notLike($column, $value, $type, 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andNotLike(RawQuery|array|string $column, mixed $value = null, string $type = 'both'): self
- {
- return $this->notLike($column, $value, $type);
- }
-
- /**
- * @inheritDoc
- */
- public function startLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->like($column, $value, 'before', $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orStartLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->like($column, $value, 'before', 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andStartLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->like($column, $value, 'before');
- }
-
- /**
- * @inheritDoc
- */
- public function notStartLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->notLike($column, $value, 'before', $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orStartNotLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->notLike($column, $value, 'before', 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andStartNotLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->notLike($column, $value, 'before');
- }
-
- /**
- * @inheritDoc
- */
- public function endLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->like($column, $value, 'after', $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orEndLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->like($column, $value, 'after', 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andEndLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->like($column, $value, 'after');
- }
-
- /**
- * @inheritDoc
- */
- public function notEndLike(RawQuery|array|string $column, mixed $value = null, string $logical = 'AND'): self
- {
- return $this->notLike($column, $value, 'after', $logical);
- }
-
- /**
- * @inheritDoc
- */
- public function orEndNotLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->notLike($column, $value, 'after', 'OR');
- }
-
- /**
- * @inheritDoc
- */
- public function andEndNotLike(RawQuery|array|string $column, mixed $value = null): self
- {
- return $this->notLike($column, $value, 'after');
- }
-
- /**
- * @inheritDoc
- */
- public function subQuery(Closure $closure, ?string $alias = null, bool $isIntervalQuery = true): RawQuery
- {
- $builder = $this->clone()->resetStructure();
-
- call_user_func_array($closure, [&$builder]);
- if ($alias !== null && $isIntervalQuery !== TRUE) {
- throw new QueryBuilderException('To define alias to a subquery, it must be an inner query.');
- }
-
- $rawQuery = ($isIntervalQuery ? '(' : '')
- . $builder->generateSelectQuery()
- . ($isIntervalQuery ? ')' : '')
- . ($alias !== null ? ' AS ' . $alias : '');
-
- return $this->raw($rawQuery);
- }
-
- /**
- * @inheritDoc
- */
- public function group(Closure $closure, string $logical = 'AND'): self
- {
- $logical = str_replace(['&&', '||'], ['AND', 'OR'], strtoupper($logical));
- if(!in_array($logical, ['AND', 'OR'], true)){
- throw new QueryBuilderException('Logical operator OR, AND, && or || it could be.');
- }
-
- $builder = $this->clone();
- call_user_func_array($closure, [$builder->resetStructure()]);
-
-
-
- foreach (['where', 'on', 'having'] as $stmt) {
- $statement = $builder->__generateStructure($stmt);
- !empty($statement) && $this->structure[$stmt][$logical][] = '(' . $statement . ')';
- }
-
- return $this;
- }
-
- /**
- * @inheritDoc
- */
- public function raw(mixed $rawQuery): RawQuery
- {
- return new RawQuery($rawQuery);
- }
-
- /**
- * @return string
- * @throws QueryGeneratorException
- */
- public function generateInsertQuery(): string
- {
- $columns = [];
- $values = [];
- $set = array_merge(...$this->structure['set']);
- foreach ($set as $column => $value) {
- $columns[] = $column;
- $values[] = $value;
- }
- if (empty($columns)) {
- throw new QueryGeneratorException('The data set for the insert could not be found.');
- }
-
- return 'INSERT INTO'
- . ' ' . $this->__generateSchemaName() . ' '
- . '(' . implode(', ', $columns) . ')'
- . ' VALUES '
- . '(' . implode(', ', $values) . ');';
- }
-
- /**
- * @return string
- * @throws QueryGeneratorException
- */
- public function generateBatchInsertQuery(): string
- {
- $columns = array_keys(array_merge(...$this->structure['set']));
- if (empty($columns)) {
- throw new QueryGeneratorException('The data set for the insert could not be found.');
- }
- $values = [];
- foreach ($this->structure['set'] as $set) {
- $value = [];
- foreach ($columns as $column) {
- $value[$column] = $set[$column] ?? 'NULL';
- }
- $values[] = '(' . implode(', ', $value) . ')';
- }
-
- return 'INSERT INTO'
- . ' ' . $this->__generateSchemaName() . ' '
- . '(' . implode(', ', $columns) . ')'
- . ' VALUES '
- . implode(', ', $values) . ';';
- }
-
- /**
- * @return string
- * @throws QueryGeneratorException
- */
- public function generateDeleteQuery(): string
- {
- return 'DELETE FROM'
- . ' '
- . $this->__generateSchemaName()
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) !== null ? $where : '1')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- /**
- * @param array $selector
- * @param array $conditions
- * @return string
- */
- public function generateSelectQuery(array $selector = [], array $conditions = []): string
- {
- !empty($selector) && $this->select(...$selector);
- if (!empty($conditions)) {
- foreach ($conditions as $column => $value) {
- if (is_string($column)) {
- $this->where($column, $value);
- } else {
- $this->where($value);
- }
- }
- }
-
- return 'SELECT '
- . (empty($this->structure['select']) ? '*' : implode(', ', $this->structure['select']))
- . ' FROM '
- . implode(', ', $this->structure['table'])
- . (!empty($this->structure['join']) ? ' ' . implode(' ', $this->structure['join']) : '')
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) ? $where : '1')
- . (!empty($this->structure['group_by']) ? ' GROUP BY ' . implode(', ', $this->structure['group_by']) : '')
- . ($this->__generateHavingQuery() ?? '')
- . (!empty($this->structure['order_by']) ? ' ORDER BY ' . implode(', ', $this->structure['order_by']) : '')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- /**
- * @return string
- * @throws QueryGeneratorException
- */
- public function generateUpdateQuery(): string
- {
- $set = array_merge(...$this->structure['set']);
- $updateSet = [];
- foreach ($set as $column => $value) {
- $updateSet[] = $column . ' = ' . $value;
- }
- if (empty($updateSet)) {
- throw new QueryGeneratorException('The data set for the insert could not be found.');
- }
-
- return 'UPDATE ' . $this->__generateSchemaName()
- . ' SET ' . implode(', ', $updateSet)
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) ? $where : '1')
- . ($this->__generateHavingQuery() ?? '')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- /**
- * @param string $referenceColumn
- * @return string
- * @throws QueryGeneratorException
- */
- public function generateUpdateBatchQuery(string $referenceColumn): string
- {
- $update = [];
- $data = $this->structure['set'];
- $updateData = $columns = $where = [];
- foreach ($data as $set) {
- if (!isset($set[$referenceColumn])) {
- throw new QueryGeneratorException('The reference column does not exist in one or more of the set arrays.');
- }
- $setData = [];
- $where[] = $set[$referenceColumn];
- unset($set[$referenceColumn]);
- foreach ($set as $key => $value) {
- $setData[$key] = $value;
- (!in_array($key, $columns)) && $columns[] = $key;
- }
- $updateData[] = $setData;
- }
- foreach ($columns as $column) {
- $syntax = $column . ' = CASE';
- foreach ($updateData as $key => $values) {
- if (!array_key_exists($column, $values)) {
- continue;
- }
- $syntax .= ' WHEN ' . $referenceColumn . ' = '
- . ($this->isSQLParameterOrFunction($where[$key]) ? $where[$key] : $this->parameters->add($referenceColumn, $where[$key]))
- . ' THEN '
- . $values[$column];
- }
- $update[] = $syntax . ' ELSE ' . $column .' END';
- }
-
- $this->whereIn($referenceColumn, $where);
-
- return 'UPDATE ' . $this->__generateSchemaName()
- . ' SET '
- . implode(', ', $update)
- . ' WHERE '
- . (($where = $this->__generateWhereQuery()) ? $where : '1')
- . ($this->__generateHavingQuery() ?? '')
- . ($this->__generateLimitQuery() ?? '');
- }
-
- protected function isSQLParameter($value): bool
- {
- return (is_string($value)) && ($value === '?' || preg_match('/^:[(\w)]+$/', $value));
- }
-
- protected function isSQLParameterOrFunction($value): bool
- {
- return ((is_string($value)) && (
- $value === '?'
- || preg_match('/^:[(\w)]+$/', $value)
- || preg_match('/^[a-zA-Z_]+[.]+[a-zA-Z_]+$/', $value)
- || preg_match('/^[a-zA-Z_]+\(\)$/', $value)
- )) || ($value instanceof RawQuery) || is_int($value);
- }
-
- public function isBatch(): bool
- {
- foreach ($this->structure['set'] as $set) {
- if (is_array($set) && count($set) > 1) {
- return true;
- }
- }
-
- return false;
- }
-
- private function whereOrHavingStatementPrepare($column, $operator, $value): string
- {
- $operator = trim($operator);
- $column = (string)$column;
-
- if ($value !== null && in_array($operator, [
- '=', '!=', '>', '<', '>=', '<=', '<>',
- '+', '-', '*', '/', '%',
- '+=', '-=', '*=', '/=', '%=', '&=', '^-=', '|*='
- ], true)) {
- return $column . ' ' . $operator . ' '
- . ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value));
- }
- $upperCaseOperator = strtoupper($operator);
- $searchOperator = str_replace([' ', '_'], '', $upperCaseOperator);
- if ($value === null && !in_array($searchOperator, ['IS', 'ISNOT'])) {
- return $column;
- }
-
- switch ($searchOperator) {
- case 'IS':
- return $column . ' IS '
- . ((($value === null) ? 'NULL' : ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value))));
- case 'ISNOT':
- return $column . ' IS NOT '
- . ((($value === null) ? 'NULL' : ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value))));
- case 'LIKE':
- case 'NOTLIKE':
- case 'STARTLIKE':
- case 'NOTSTARTLIKE':
- case 'ENDLIKE':
- case 'NOTENDLIKE':
- if (!$this->isSQLParameter($value)) {
- $value = (in_array($searchOperator, ['LIKE', 'NOTLIKE', 'STARTLIKE', 'NOTSTARTLIKE']) ? '%' : '')
- . $value
- . (in_array($searchOperator, ['LIKE', 'NOTLIKE', 'ENDLIKE', 'NOTENDLIKE']) ? '%' : '');
-
- $value = $this->parameters->add($column, $value);
- }
-
- return $column
- . (in_array($searchOperator, ['NOTSTARTLIKE', 'NOTLIKE', 'NOTENDLIKE']) ? ' NOT' : '')
- . ' LIKE ' . $value;
- case 'BETWEEN':
- case 'NOTBETWEEN':
- return $column . ' '
- . ($searchOperator === 'NOTBETWEEN' ? 'NOT ' : '')
- . 'BETWEEN '
- . ($this->isSQLParameterOrFunction($value[0]) ? $value[0] : $this->parameters->add($column, $value[0]))
- . ' AND '
- . ($this->isSQLParameterOrFunction($value[1]) ? $value[1] : $this->parameters->add($column, $value[1]));
- case 'IN':
- case 'NOTIN':
- if (is_array($value)) {
- $values = [];
- array_map(function ($item) use (&$values, $column) {
- if (is_numeric($item)) {
- $values[] = $item;
- } else {
- $values[] = $this->isSQLParameterOrFunction($item) ? $item : $this->parameters->add($column, $item);
- }
- }, array_unique($value));
- $value = '(' . implode(', ', $values) . ')';
- }
- return $column
- . ($searchOperator === 'NOTIN' ? ' NOT' : '')
- . ' IN ' . $value;
- case 'REGEXP':
- return $column . ' REGEXP '
- . ($this->isSQLParameterOrFunction($value) ? $value : $this->parameters->add($column, $value));
- case 'FINDINSET':
- case 'NOTFINDINSET':
- if (is_array($value)) {
- $value = implode(', ', $value);
- } elseif ($this->isSQLParameterOrFunction($value)) {
- $value = $this->parameters->add($column, $value);
- }
- return ($searchOperator === 'NOTFINDINSET' ? 'NOT ' : '')
- . 'FIND_IN_SET(' . $value . ', ' . $column . ')';
- case 'SOUNDEX':
- if (!$this->isSQLParameterOrFunction($value)) {
- $value = $this->parameters->add($column, $value);
- }
- return "SOUNDEX(" . $column . ") LIKE CONCAT('%', TRIM(TRAILING '0' FROM SOUNDEX(" . $value . ")), '%')";
- default:
- if ($value === null && preg_match('/([\w_]+)\((.+)\)$/iu', $column, $matches) !== FALSE) {
- return strtoupper($matches[1]) . '(' . $matches[2] . ')';
- }
- return $column . ' ' . $operator . ' ' . $this->parameters->add($column, $value);
- }
- }
-
- /**
- * @return string
- * @throws QueryGeneratorException
- */
- private function __generateSchemaName(): string
- {
- if (!empty($this->structure['table'])) {
- $table = end($this->structure['table']);
- } else {
- throw new QueryGeneratorException('Table name not found when query.');
- }
-
- return $table;
- }
-
- private function __generateLimitQuery(): ?string
- {
- if ($this->structure['limit'] === null && $this->structure['offset'] === null) {
- return null;
- }
- $statement = ' ';
- if ($this->structure['limit'] === null) {
- $statement .= 'OFFSET ' . $this->structure['offset'];
- } else {
- $statement .= 'LIMIT '
- . ($this->structure['offset'] !== null ? $this->structure['offset'] . ', ' : '')
- . $this->structure['limit'];
- }
-
- return $statement;
- }
-
- private function __generateOnQuery(): ?string
- {
- return $this->__generateStructure('on');
- }
-
- private function __generateHavingQuery(): ?string
- {
- $stmt = $this->__generateStructure('having');
-
- return $stmt === null ? null : ' HAVING ' . $stmt;
- }
-
- private function __generateWhereQuery(): ?string
- {
- return $this->__generateStructure('where');
- }
-
- private function __generateStructure(string $key): ?string
- {
- $isAndEmpty = empty($this->structure[$key]['AND']);
- $isOrEmpty = empty($this->structure[$key]['OR']);
- if ($isOrEmpty && $isAndEmpty) {
- return null;
- }
-
- return (!$isAndEmpty ? implode(' AND ', $this->structure[$key]['AND']) : '')
- . (!$isAndEmpty && !$isOrEmpty ? ' AND ' : '')
- . (!$isOrEmpty ? implode(' OR ', $this->structure[$key]['OR']) : '');
- }
-
- private function whereOrHavingPrepare(&$operator, &$value, &$logical): void
- {
- $logical = strtoupper(strtr($logical, [
- '&&' => 'AND',
- '||' => 'OR',
- ]));
- if (!in_array($logical, ['AND', 'OR'], true)) {
- throw new InvalidArgumentException('Logical operator OR, AND, && or || it could be.');
- }
-
- if ($value === null && !in_array($operator, [
- 'IS', 'IS NOT',
- '=', '!=', '>', '<', '>=', '<=', '<>',
- '+', '-', '*', '/', '%',
- '+=', '-=', '*=', '/=', '%=', '&=', '^-=', '|*='
- ])) {
- $value = $operator;
- $operator = '=';
- }
- }
-
-}
diff --git a/src/QueryBuilder/RawQuery.php b/src/QueryBuilder/RawQuery.php
deleted file mode 100644
index 85b41f3..0000000
--- a/src/QueryBuilder/RawQuery.php
+++ /dev/null
@@ -1,53 +0,0 @@
-set($rawQuery);
- }
-
- public function __toString(): string
- {
- return $this->get();
- }
-
- public function set(mixed $rawQuery): self
- {
- if (is_string($rawQuery)) {
- $this->raw = $rawQuery;
- } else if ($rawQuery instanceof Closure) {
- $builder = new QueryBuilder();
- $res = call_user_func_array($rawQuery, [&$builder]);
- if (is_string($res)) {
- $this->raw = $res;
- } else if (is_object($res) && method_exists($res, '__toString')) {
- $this->raw = $res->__toString();
- } else {
- $this->raw = $builder->__toString();
- }
- } else {
- $this->raw = (string)$rawQuery;
- }
-
- return $this;
- }
-
- public function get(): string
- {
- return $this->raw ?? '';
- }
-
- public static function raw($rawQuery): RawQuery
- {
- return new self($rawQuery);
- }
-
-}
diff --git a/src/Utils/Datatables.php b/src/Utils/Datatables.php
index 32eb8ed..17f92b8 100644
--- a/src/Utils/Datatables.php
+++ b/src/Utils/Datatables.php
@@ -7,17 +7,18 @@
* @author Muhammet ŞAFAK
* @copyright Copyright © 2022 Muhammet ŞAFAK
* @license ./LICENSE MIT
- * @version 2.0.7
+ * @version 3.0
* @link https://www.muhammetsafak.com.tr
*/
namespace InitPHP\Database\Utils;
-use \InitPHP\Database\DBAL\Interfaces\{DatabaseInterface, CRUDInterface};
-use InitPHP\Database\DBAL\Exceptions\SQLQueryExecuteException;
-use InitPHP\Database\ORM\Interfaces\ModelInterface;
-use InitPHP\Database\QueryBuilder\Interfaces\QueryBuilderInterface;
use Closure;
+use Throwable;
+use InitORM\Database\Interfaces\DatabaseInterface;
+use InitORM\QueryBuilder\Exceptions\QueryBuilderException;
+use InitORM\QueryBuilder\QueryBuilderInterface;
+use InitORM\ORM\Interfaces\ModelInterface;
/**
* @mixin QueryBuilderInterface
@@ -25,7 +26,7 @@
class Datatables
{
- private DatabaseInterface|CRUDInterface|ModelInterface $db;
+ private DatabaseInterface|ModelInterface $db;
private array $request;
@@ -47,7 +48,7 @@ class Datatables
private array $permanentSelect = [];
- public function __construct(DatabaseInterface|ModelInterface|CRUDInterface $db)
+ public function __construct(DatabaseInterface|ModelInterface $db)
{
$this->request = array_merge($_GET ?? [], $_POST ?? []);
@@ -71,7 +72,7 @@ public function __call(string $name, array $arguments)
/**
* @return string
- * @throws SQLQueryExecuteException
+ * @throws Throwable
*/
public function __toString(): string
{
@@ -80,7 +81,7 @@ public function __toString(): string
/**
* @return array
- * @throws SQLQueryExecuteException
+ * @throws Throwable
*/
public function toArray(): array
{
@@ -90,7 +91,7 @@ public function toArray(): array
/**
* @return $this
- * @throws SQLQueryExecuteException
+ * @throws Throwable
*/
public function handle(): self
{
@@ -179,7 +180,7 @@ public function orderBySave(): self
/**
* @return int
- * @throws SQLQueryExecuteException
+ * @throws Throwable
*/
private function getCount(): int
{
@@ -203,21 +204,21 @@ private function getCount(): int
$this->db->{$process['method']}(...$process['arguments']);
}
$isGroupBy === false && $this->db->selectCount('*', 'data_length');
- $res = $this->db->get();
+ $res = $this->db->read();
return $res->numRows() > 0 ? $res->asAssoc()->row()['data_length'] : 0;
}
/**
* @return array
- * @throws SQLQueryExecuteException
+ * @throws Throwable
*/
private function getResults(): array
{
foreach ($this->builder as $process) {
$this->db->{$process['method']}(...$process['arguments']);
}
- $res = $this->db->get();
+ $res = $this->db->read();
return $res->numRows() > 0 ? $res->asAssoc()->rows() : [];
}
@@ -254,6 +255,7 @@ private function orderQuery(): self
/**
* @return void
+ * @throws QueryBuilderException
*/
private function filterQuery(): void
{
diff --git a/src/Utils/Helper.php b/src/Utils/Helper.php
deleted file mode 100644
index 42076b9..0000000
--- a/src/Utils/Helper.php
+++ /dev/null
@@ -1,37 +0,0 @@
-db = new QueryBuilder();
- parent::setUp();
- }
-
- public function testSelectBuilder()
- {
- $this->db->select('id', 'name');
- $this->db->table('user');
-
- $expected = "SELECT id, name FROM user WHERE 1";
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testBlankBuild()
- {
- $this->db->from('post');
-
- $expected = 'SELECT * FROM post WHERE 1';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testSelfJoinBuild()
- {
- $this->db->select('post.id', 'post.title', 'user.name AS authorName')
- ->table('post')
- ->selfJoin('user', 'user.id = post.user');
-
- $expected = "SELECT post.id, post.title, user.name AS authorName FROM post, user WHERE user.id = post.user";
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testInnerJoinBuild()
- {
- $this->db->select('post.id, post.title', 'user.name as authorName')
- ->from('post')
- ->innerJoin('user', 'user.id = post.user');
-
- $expected = "SELECT post.id, post.title, user.name as authorName FROM post INNER JOIN user ON user.id = post.user WHERE 1";
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testLeftJoinBuild()
- {
- $this->db->select('post.id', 'post.title', 'user.name as authorName');
- $this->db->from('post');
- $this->db->leftJoin('user', 'user.id=post.user');
-
- $expected = "SELECT post.id, post.title, user.name as authorName FROM post LEFT JOIN user ON user.id=post.user WHERE 1";
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testRightJoinBuild()
- {
- $this->db->select('post.id', 'post.title', 'user.name as authorName');
- $this->db->from('post');
- $this->db->rightJoin('user', 'user.id=post.user');
-
- $expected = "SELECT post.id, post.title, user.name as authorName FROM post RIGHT JOIN user ON user.id=post.user WHERE 1";
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testLeftOuterJoinBuild()
- {
- $this->db->select('post.id', 'post.title', 'user.name as authorName');
- $this->db->from('post');
- $this->db->leftOuterJoin('user', 'user.id=post.user');
-
- $expected = "SELECT post.id, post.title, user.name as authorName FROM post LEFT OUTER JOIN user ON user.id=post.user WHERE 1";
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testRightOuterJoinBuild()
- {
- $this->db->select('post.id', 'post.title', 'user.name as authorName');
- $this->db->from('post');
- $this->db->rightOuterJoin('user', 'user.id=post.user');
-
- $expected = "SELECT post.id, post.title, user.name as authorName FROM post RIGHT OUTER JOIN user ON user.id=post.user WHERE 1";
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testLimitStatement()
- {
- $this->db->select('id')
- ->from('book')
- ->limit(5);
-
- $expected = 'SELECT id FROM book WHERE 1 LIMIT 5';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testOffsetStatement()
- {
- $this->db->select('id')
- ->from('book')
- ->offset(5);
-
- $expected = 'SELECT id FROM book WHERE 1 OFFSET 5';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testOffsetLimitStatement()
- {
- $this->db->select('id')
- ->from('book')
- ->offset(50)
- ->limit(25);
-
- $expected = 'SELECT id FROM book WHERE 1 LIMIT 50, 25';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testNegativeOffsetLimitStatement()
- {
- $this->db->select('id')
- ->from('book')
- ->offset(-25)
- ->limit(-20);
-
- // If limit and offset are negative integers, their absolute values are taken.
- $expected = 'SELECT id FROM book WHERE 1 LIMIT 25, 20';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testSelectDistinctStatement()
- {
- $this->db->selectDistinct('name')
- ->from('book');
- $expected = 'SELECT DISTINCT(name) FROM book WHERE 1';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testSelectDistinctJoinStatement()
- {
- $this->db->selectDistinct('author.name')
- ->from('book')
- ->innerJoin('author', 'author.id=book.author');
- $expected = 'SELECT DISTINCT(author.name) FROM book INNER JOIN author ON author.id=book.author WHERE 1';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testOrderByStatement()
- {
- $this->db->select('name')
- ->from('book')
- ->orderBy('authorId', 'ASC')
- ->orderBy('id', 'DESC')
- ->limit(10);
-
- $expected = 'SELECT name FROM book WHERE 1 ORDER BY authorId ASC, id DESC LIMIT 10';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testInsertStatementBuild()
- {
- $this->db->from('post');
-
- $data = [
- 'title' => 'Post Title',
- 'content' => 'Post Content',
- 'author' => 5,
- 'status' => true,
- ];
- $this->db->set($data);
-
-
- $expected = 'INSERT INTO post (title, content, author, status) VALUES (:title, :content, 5, :status);';
- $this->assertEquals($expected, $this->db->generateInsertQuery());
- $this->db->resetStructure();
- }
-
- public function testInsertBatchStatementBuild()
- {
-
- $this->db->from('post');
-
- $this->db->set([
- 'title' => 'Post Title #1',
- 'content' => 'Post Content #1',
- 'author' => 5,
- 'status' => true,
- ])
- ->set([
- 'title' => 'Post Title #2',
- 'content' => 'Post Content #2',
- 'status' => false,
- ]);
-
- $expected = 'INSERT INTO post (title, content, author, status) VALUES (:title, :content, 5, :status), (:title_1, :content_1, NULL, :status_1);';
- $this->assertEquals($expected, $this->db->generateBatchInsertQuery());
- $this->db->resetStructure();
- }
-
- public function testUpdateStatementBuild()
- {
-
- $this->db->from('post')
- ->where('status', '=', true)
- ->limit(5);
-
- $data = [
- 'title' => 'New Title',
- 'status' => false,
- ];
- $this->db->set($data);
-
- $expected = 'UPDATE post SET title = :title, status = :status_1 WHERE status = :status LIMIT 5';
-
- $this->assertEquals($expected, $this->db->generateUpdateQuery());
- $this->db->resetStructure();
- }
-
- public function testUpdateBatchStatementBuild()
- {
-
- $this->db->from('post')
- ->where('status', '=', true);
-
- $this->db->set([
- 'id' => 5,
- 'title' => 'New Title #5',
- 'content' => 'New Content #5',
- ])->set([
- 'id' => 10,
- 'title' => 'New Title #10',
- ]);
-
- $expected = 'UPDATE post SET title = CASE WHEN id = 5 THEN :title WHEN id = 10 THEN :title_1 ELSE title END, content = CASE WHEN id = 5 THEN :content ELSE content END WHERE status = :status AND id IN (5, 10)';
-
- $this->assertEquals($expected, $this->db->generateUpdateBatchQuery('id'));
- $this->db->resetStructure();
- }
-
- public function testDeleteStatementBuild()
- {
-
- $this->db->from('post')
- ->where('authorId', '=', 5)
- ->limit(100);
-
- $expected = 'DELETE FROM post WHERE authorId = 5 LIMIT 100';
-
- $this->assertEquals($expected, $this->db->generateDeleteQuery());
- $this->db->resetStructure();
- }
-
- public function testWhereSQLFunctionStatementBuild()
- {
- $this->db->from('post')
- ->andBetween('date', ['2022-05-07', 'CURDATE()']);
-
- $expected = 'SELECT * FROM post WHERE date BETWEEN :date AND CURDATE()';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testWhereRegexpSQLStatementBuild()
- {
- $this->db->from('post')
- ->regexp('title', '^M[a-z]K$');
-
- $expected = 'SELECT * FROM post WHERE title REGEXP :title';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testSelectCoalesceSQLStatementBuild()
- {
- $this->db->select('post.title')
- ->selectCoalesce('stat.view', 0)
- ->from('post')
- ->leftJoin('stat', 'stat.id=post.id')
- ->where('post.id', 5);
-
- $expected = 'SELECT post.title, COALESCE(stat.view, 0) FROM post LEFT JOIN stat ON stat.id=post.id WHERE post.id = 5';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testSelectCoalesceDefaultValue()
- {
- $this->db->select('post.title')
- ->selectCoalesce('stat.view', 'post.view', 'views')
- ->from('post')
- ->leftJoin('stat', 'stat.id=post.id')
- ->where('post.id', 5);
-
- $expected = 'SELECT post.title, COALESCE(stat.view, post.view) AS views FROM post LEFT JOIN stat ON stat.id=post.id WHERE post.id = 5';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
-
- public function testTableAliasSQLStatementBuild()
- {
- $this->db->select('p.title')
- ->select('s.view as s_view')
- ->from('post as p')
- ->leftJoin('stat as s', 's.id=p.id')
- ->where('p.id', 5);
-
- $expected = 'SELECT p.title, s.view as s_view FROM post as p LEFT JOIN stat as s ON s.id=p.id WHERE p.id = 5';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testTableJoinAliasSQLStatementBuild()
- {
- $this->db->select('p.title')
- ->select('s.view as s_view')
- ->from('post p')
- ->leftJoin('stat s', 's.id=p.id')
- ->where('p.id', 5);
-
- $expected = 'SELECT p.title, s.view as s_view FROM post p LEFT JOIN stat s ON s.id=p.id WHERE p.id = 5';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testWhereGroupStatement()
- {
- $this->db->select('id')
- ->from('users')
- ->where('status', 1)
- ->group(function (QueryBuilder $builder) {
- $builder->where('type', 3)
- ->where('type', 4);
- });
-
- $expected = 'SELECT id FROM users WHERE status = 1 AND (type = 3 AND type = 4)';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testWhereGroupMultipleStatement()
- {
- $this->db->select('id, title, content, url')
- ->from('posts')
- ->where('status', 1)
- ->group(function (QueryBuilder $db) {
- $db->where('user_id', 1)
- ->where('datetime', '>=', date("Y-m-d"));
- }, 'or')
- ->group(function (QueryBuilder $db) {
- $db->group(function (QueryBuilder $db) {
- $db->where('id', 2)
- ->where('status', 3);
- }, 'or')
- ->group(function (QueryBuilder $db) {
- $db->where('id', 4)
- ->where('status', 5);
- }, 'or');
- }, 'or');
-
- $expected = 'SELECT id, title, content, url FROM posts WHERE status = 1 AND (user_id = 1 AND datetime >= :datetime) OR ((id = 2 AND status = 3) OR (id = 4 AND status = 5))';
-
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
-
- public function testJoinClosureGive()
- {
-
- $this->db->select('u.id', 'u.name', 'u.status, p.title')
- ->from('users AS u')
- ->where('u.status', 1)
- ->join('posts AS p', function (QueryBuilder $builder) {
- $builder->on('p.user_id', 'u.id')
- ->where('p.publisher_time', '>=', $builder->raw('NOW()'));
- })
- ->join('categories AS c', function (QueryBuilder $builder) {
- $builder->on('c.id', 'p.category_id')
- ->on('c.blog_id', 'u.blog_id')
- ->where('c.status', 1)
- ->having($builder->raw('COUNT(p.category_id) > 1'));
- })->limit(5);
-
- $expected = 'SELECT u.id, u.name, u.status, p.title FROM users AS u INNER JOIN posts AS p ON p.user_id = u.id INNER JOIN categories AS c ON c.id = p.category_id AND c.blog_id = u.blog_id WHERE u.status = 1 AND p.publisher_time >= NOW() AND c.status = 1 HAVING COUNT(p.category_id) > 1 LIMIT 5';
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testSubQuery()
- {
-
- $this->db->select('u.name')
- ->from('users AS u')
- ->whereIn('u.id', $this->db->subQuery(function (QueryBuilder $builder) {
- $builder->select('id')
- ->from('roles')
- ->where('name', 'admin');
- }));
- $expected = 'SELECT u.name FROM users AS u WHERE u.id IN (SELECT id FROM roles WHERE name = :name)';
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
- public function testSubQueryJoinTable()
- {
- $this->db->select('u.name, p.title')
- ->from('users AS u')
- ->join($this->db->subQuery(function (QueryBuilder $builder) {
- $builder->select('id, title, user_id')
- ->from('posts')
- ->where('user_id', 5);
- }, 'p'), 'p.user_id = u.id', '');
-
- $expected = 'SELECT u.name, p.title FROM users AS u JOIN (SELECT id, title, user_id FROM posts WHERE user_id = 5) AS p ON p.user_id = u.id WHERE 1';
- $this->assertEquals($expected, $this->db->generateSelectQuery());
- $this->db->resetStructure();
- }
-
-}