diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
index b65df91..7a8e244 100644
--- a/.github/workflows/phpunit.yml
+++ b/.github/workflows/phpunit.yml
@@ -20,6 +20,23 @@ jobs:
- "8.2"
- "8.1"
+ services:
+ mysql:
+ image: mysql:8.0.20
+ env:
+ MYSQL_ROOT_PASSWORD: password
+ MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
+ ports:
+ - "3306:3306"
+ options: >-
+ --health-cmd="mysqladmin ping"
+ --health-interval=10s
+ --health-timeout=20s
+ --health-retries=10
+
+ env:
+ MYSQL_TEST_HOST: mysql
+
steps:
- uses: actions/checkout@v4
- run: composer install
diff --git a/.idea/runConfigurations/MySQL_Server_Test.xml b/.idea/runConfigurations/MySQL_Server_Test.xml
new file mode 100644
index 0000000..04f7a1e
--- /dev/null
+++ b/.idea/runConfigurations/MySQL_Server_Test.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/PHPUnit.xml b/.idea/runConfigurations/PHPUnit.xml
index a5a683e..f81c245 100644
--- a/.idea/runConfigurations/PHPUnit.xml
+++ b/.idea/runConfigurations/PHPUnit.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
index d29bccf..f834130 100644
--- a/README.md
+++ b/README.md
@@ -166,6 +166,7 @@ $result = $repository->getByQuery($query);
* [Using FieldAlias](docs/using-fieldalias.md)
* [Tables without auto increments fields](docs/tables-without-auto-increment-fields.md)
* [Using With Recursive SQL Command](docs/using-with-recursive-sql-command.md)
+* [QueryRaw (raw SQL)](docs/query-raw.md)
## Install
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..072f693
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,13 @@
+services:
+ mysql:
+ image: mysql:8.0.20
+ environment:
+ - MYSQL_ROOT_PASSWORD=password
+ - MYSQL_AUTHENTICATION_PLUGIN=mysql_native_password
+ ports:
+ - "3306:3306"
+ healthcheck:
+ test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
+ timeout: 20s
+ interval: 10s
+ retries: 10
diff --git a/docs/query-raw.md b/docs/query-raw.md
new file mode 100644
index 0000000..9f22ee5
--- /dev/null
+++ b/docs/query-raw.md
@@ -0,0 +1,83 @@
+# QueryRaw
+
+QueryRaw lets you execute a raw SQL statement while still benefiting from the repository pipeline (parameter binding,
+connection handling, iterators, and optional caching when used via SqlStatement).
+
+Important: QueryRaw does NOT generate or adapt SQL based on the DbDriver/DB dialect. It passes through exactly the SQL
+string you provide. That means the SQL you write must already be valid for the target database engine. Because of this,
+QueryRaw should be used only for very specific use cases where the high-level Query/Update/Insert/Delete builders cannot
+express what you need.
+
+When to use QueryRaw
+
+- Vendor-specific features that are not covered by the query builders (e.g., dialect functions, specialized hints).
+- One-off statements where you accept tight coupling to a single database dialect.
+- Chaining within bulk operations when you need to run a raw select right after a write.
+
+When NOT to use QueryRaw
+
+- Everyday selects/joins/filters/limits that can be expressed with Query or Union.
+- Inserts/updates/deletes that can be handled by InsertQuery/UpdateQuery/DeleteQuery.
+- Anywhere you want portability across databases or automatic dialect handling.
+
+Behavior
+
+- build(): returns a SqlObject with your SQL and parameters unchanged. The optional DbDriver parameter is ignored for
+ SQL generation.
+- buildAndGetIterator($dbDriver): executes your SQL against the provided driver and returns a GenericIterator.
+- Parameter binding: pass parameters as an array (named or positional) and the underlying driver will bind them.
+
+Examples
+
+1) Basic raw select returning rows as arrays
+
+```php
+use ByJG\MicroOrm\QueryRaw;
+
+$query = QueryRaw::getInstance(
+ 'select id, name from users where name like :part',
+ ['part' => 'A%']
+);
+
+$rows = $repository->getByQueryRaw($query); // array>
+```
+
+2) Using dialect-specific functions (DB-specific SQL)
+
+```php
+// Example for SQLite using julianday(); not portable to other databases
+$query = QueryRaw::getInstance(
+ "select name, julianday('2020-06-28') - julianday(createdate) as days from users limit 1"
+);
+
+$rows = $repository->getByQueryRaw($query);
+// e.g., [ [ 'name' => 'Jane Doe', 'days' => 1271.0 ] ]
+```
+
+3) Bulk execution: insert followed by selecting last inserted id
+
+```php
+use ByJG\MicroOrm\InsertQuery;
+use ByJG\MicroOrm\QueryRaw;
+
+$insert = InsertQuery::getInstance('users', [
+ 'name' => 'Charlie',
+ 'createdate' => '2025-01-01',
+]);
+
+// Use DB helper to get the correct SQL for your driver
+$selectLastId = QueryRaw::getInstance(
+ $repository->getDbDriver()->getDbHelper()->getSqlLastInsertId()
+);
+
+$it = $repository->bulkExecute([$insert, $selectLastId]);
+$result = $it->toArray();
+// $result is the rows from the last statement (the select)
+```
+
+Notes
+
+- QueryRaw ties your code to the specific SQL dialect you write. If you switch databases, you may need to rewrite the
+ raw SQL.
+- Always use bound parameters to avoid SQL injection; pass them in the second argument to getInstance().
+- For portable queries, prefer Query/Union and the provided Insert/Update/Delete builders.
diff --git a/docs/updating-the-database.md b/docs/updating-the-database.md
index 130e12a..dd9730d 100644
--- a/docs/updating-the-database.md
+++ b/docs/updating-the-database.md
@@ -136,4 +136,94 @@ You can delete records using the `DeleteQuery` object. See an example:
$deleteQuery = new \ByJG\MicroOrm\DeleteQuery();
$deleteQuery->table('test');
$deleteQuery->where('fld1 = :value', ['value' => 'A']);
-```
\ No newline at end of file
+```
+
+## Execute multiple write queries in bulk
+
+You can execute multiple write queries (insert, update, delete) sequentially within a single transaction using
+`Repository::bulkExecute`.
+This is useful when you need to perform a set of changes atomically: either all of them succeed, or none of them are
+applied.
+
+Signature:
+
+```php
+public function Repository::bulkExecute(
+ array $queries,
+ ?\ByJG\AnyDataset\Db\IsolationLevelEnum $isolationLevel = null
+): ?\ByJG\AnyDataset\Core\GenericIterator
+```
+
+Rules and behavior:
+
+- Accepts an array of QueryBuilderInterface or Updatable instances (e.g., InsertQuery, UpdateQuery, DeleteQuery). Each
+ item is built with the repository write driver. Non-SELECT statements are batched and executed together; a trailing
+ SELECT (if present) is executed separately.
+- All queries are executed inside a transaction. If any query throws an exception, the transaction is rolled back and
+ the exception is rethrown.
+- If the last command is a SELECT, bulkExecute returns an iterator (GenericIterator) with its results. Intermediate
+ SELECT statements are allowed but do not return iterators; their results are not yielded.
+- If there is no trailing SELECT, bulkExecute returns an empty iterator.
+- Passing an empty array throws InvalidArgumentException.
+- Passing an item that is not a QueryBuilderInterface or Updatable throws InvalidArgumentException.
+- You can optionally pass a transaction isolation level using IsolationLevelEnum. The transaction allows joining an
+ existing transaction if present.
+
+Example (writes only):
+
+```php
+ 'Alice',
+ 'createdate' => '2020-01-01'
+]);
+
+$update = UpdateQuery::getInstance()
+ ->table('users')
+ ->set('name', 'Bob')
+ ->where('id = :id', ['id' => 1]);
+
+$delete = DeleteQuery::getInstance()
+ ->table('users')
+ ->where('name = :name', ['name' => 'OldName']);
+
+$it = $repository->bulkExecute([$insert, $update, $delete]);
+// $it is an empty iterator because there is no trailing SELECT
+```
+
+Example (returning results when the last command is a SELECT):
+
+```php
+ 'Charlie',
+ 'createdate' => '2025-01-01'
+]);
+
+// Example of a SELECT as the last command (driver-specific last insert id)
+$selectLastId = QueryRaw::getInstance($repository->getDbDriver()->getDbHelper()->getSqlLastInsertId());
+
+$it = $repository->bulkExecute([$insert, $selectLastId]);
+foreach ($it as $row) {
+ // process results (e.g., $row['id'])
+}
+```
+
+Notes:
+
+- Parameter names can overlap between queries (e.g., multiple queries using :name). bulkExecute internally batches
+ non-SELECT statements and safely renames parameters to avoid collisions; the trailing SELECT (if present) is executed
+ separately.
+- If you need a specific transaction isolation level, pass it as the second argument, e.g.,
+ `IsolationLevelEnum::SERIALIZABLE`.
\ No newline at end of file
diff --git a/src/DeleteQuery.php b/src/DeleteQuery.php
index 9a4baa7..e1c548c 100644
--- a/src/DeleteQuery.php
+++ b/src/DeleteQuery.php
@@ -2,6 +2,7 @@
namespace ByJG\MicroOrm;
+use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\DbFunctionsInterface;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
use ByJG\MicroOrm\Interface\QueryBuilderInterface;
@@ -13,7 +14,7 @@ public static function getInstance(): DeleteQuery
return new DeleteQuery();
}
- public function build(DbFunctionsInterface $dbHelper = null): SqlObject
+ public function build(DbFunctionsInterface|DbDriverInterface|null $dbDriverOrHelper = null): SqlObject
{
$whereStr = $this->getWhere();
if (is_null($whereStr)) {
diff --git a/src/InsertBulkQuery.php b/src/InsertBulkQuery.php
index 4f931bf..a41247e 100644
--- a/src/InsertBulkQuery.php
+++ b/src/InsertBulkQuery.php
@@ -2,6 +2,7 @@
namespace ByJG\MicroOrm;
+use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\DbFunctionsInterface;
use ByJG\MicroOrm\Exception\OrmInvalidFieldsException;
use ByJG\MicroOrm\Interface\QueryBuilderInterface;
@@ -60,19 +61,23 @@ public function values(array $values, bool $allowNonMatchFields = true): static
}
/**
- * @param DbFunctionsInterface|null $dbHelper
+ * @param DbDriverInterface|DbFunctionsInterface|null $dbDriverOrHelper
* @return SqlObject
* @throws OrmInvalidFieldsException
*/
- public function build(DbFunctionsInterface $dbHelper = null): SqlObject
+ public function build(DbFunctionsInterface|DbDriverInterface|null $dbDriverOrHelper = null): SqlObject
{
if (empty($this->fields)) {
throw new OrmInvalidFieldsException('You must specify the fields for insert');
}
+ if ($dbDriverOrHelper instanceof DbDriverInterface) {
+ $dbDriverOrHelper = $dbDriverOrHelper->getDbHelper();
+ }
+
$tableStr = $this->table;
- if (!is_null($dbHelper)) {
- $tableStr = $dbHelper->delimiterTable($tableStr);
+ if (!is_null($dbDriverOrHelper)) {
+ $tableStr = $dbDriverOrHelper->delimiterTable($tableStr);
}
// Extract column names
@@ -96,7 +101,7 @@ public function build(DbFunctionsInterface $dbHelper = null): SqlObject
} else {
$value = str_replace("'", "''", $this->fields[$col][$i]);
if (!is_numeric($value)) {
- $value = $dbHelper?->delimiterField($value) ?? "'{$value}'";
+ $value = $dbDriverOrHelper?->delimiterField($value) ?? "'{$value}'";
}
$params[$paramKey] = new Literal($value); // Map parameter key to value
}
@@ -104,8 +109,8 @@ public function build(DbFunctionsInterface $dbHelper = null): SqlObject
$placeholders[] = '(' . implode(', ', $rowPlaceholders) . ')'; // Add row placeholders to query
}
- if (!is_null($dbHelper)) {
- $columns = $dbHelper->delimiterField($columns);
+ if (!is_null($dbDriverOrHelper)) {
+ $columns = $dbDriverOrHelper->delimiterField($columns);
}
// Construct the final SQL query
diff --git a/src/InsertQuery.php b/src/InsertQuery.php
index 908a771..08b305b 100644
--- a/src/InsertQuery.php
+++ b/src/InsertQuery.php
@@ -2,6 +2,7 @@
namespace ByJG\MicroOrm;
+use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\DbFunctionsInterface;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
use ByJG\MicroOrm\Exception\OrmInvalidFieldsException;
@@ -65,24 +66,28 @@ public function defineFields(array $fields): static
}
/**
- * @param DbFunctionsInterface|null $dbHelper
+ * @param DbDriverInterface|DbFunctionsInterface|null $dbDriverOrHelper
* @return SqlObject
* @throws OrmInvalidFieldsException
*/
- public function build(DbFunctionsInterface $dbHelper = null): SqlObject
+ public function build(DbFunctionsInterface|DbDriverInterface|null $dbDriverOrHelper = null): SqlObject
{
if (empty($this->values)) {
throw new OrmInvalidFieldsException('You must specify the fields for insert');
}
+ if ($dbDriverOrHelper instanceof DbDriverInterface) {
+ $dbDriverOrHelper = $dbDriverOrHelper->getDbHelper();
+ }
+
$fieldsStr = array_keys($this->values); // get the fields from the first element only
- if (!is_null($dbHelper)) {
- $fieldsStr = $dbHelper->delimiterField($fieldsStr);
+ if (!is_null($dbDriverOrHelper)) {
+ $fieldsStr = $dbDriverOrHelper->delimiterField($fieldsStr);
}
$tableStr = $this->table;
- if (!is_null($dbHelper)) {
- $tableStr = $dbHelper->delimiterTable($tableStr);
+ if (!is_null($dbDriverOrHelper)) {
+ $tableStr = $dbDriverOrHelper->delimiterTable($tableStr);
}
$sql = 'INSERT INTO '
diff --git a/src/InsertSelectQuery.php b/src/InsertSelectQuery.php
index c5c0fba..b8f3270 100644
--- a/src/InsertSelectQuery.php
+++ b/src/InsertSelectQuery.php
@@ -2,6 +2,7 @@
namespace ByJG\MicroOrm;
+use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\DbFunctionsInterface;
use ByJG\MicroOrm\Exception\OrmInvalidFieldsException;
use ByJG\MicroOrm\Interface\QueryBuilderInterface;
@@ -49,16 +50,20 @@ public function fromSqlObject(SqlObject $sqlObject): static
}
/**
- * @param DbFunctionsInterface|null $dbHelper
+ * @param DbDriverInterface|DbFunctionsInterface|null $dbDriverOrHelper
* @return SqlObject
* @throws OrmInvalidFieldsException
*/
- public function build(DbFunctionsInterface $dbHelper = null): SqlObject
+ public function build(DbFunctionsInterface|DbDriverInterface|null $dbDriverOrHelper = null): SqlObject
{
if (empty($this->fields)) {
throw new OrmInvalidFieldsException('You must specify the fields for insert');
}
+ if ($dbDriverOrHelper instanceof DbDriverInterface) {
+ $dbDriverOrHelper = $dbDriverOrHelper->getDbHelper();
+ }
+
if (empty($this->query) && empty($this->sqlObject)) {
throw new OrmInvalidFieldsException('You must specify the query for insert');
} elseif (!empty($this->query) && !empty($this->sqlObject)) {
@@ -66,13 +71,13 @@ public function build(DbFunctionsInterface $dbHelper = null): SqlObject
}
$fieldsStr = $this->fields;
- if (!is_null($dbHelper)) {
- $fieldsStr = $dbHelper->delimiterField($fieldsStr);
+ if (!is_null($dbDriverOrHelper)) {
+ $fieldsStr = $dbDriverOrHelper->delimiterField($fieldsStr);
}
$tableStr = $this->table;
- if (!is_null($dbHelper)) {
- $tableStr = $dbHelper->delimiterTable($tableStr);
+ if (!is_null($dbDriverOrHelper)) {
+ $tableStr = $dbDriverOrHelper->delimiterTable($tableStr);
}
$sql = 'INSERT INTO '
diff --git a/src/Interface/UpdateBuilderInterface.php b/src/Interface/UpdateBuilderInterface.php
index 56c76a8..699abbd 100644
--- a/src/Interface/UpdateBuilderInterface.php
+++ b/src/Interface/UpdateBuilderInterface.php
@@ -8,7 +8,7 @@
interface UpdateBuilderInterface
{
- public function build(?DbFunctionsInterface $dbHelper = null): SqlObject;
+ public function build(DbFunctionsInterface|DbDriverInterface|null $dbDriverOrHelper = null): SqlObject;
public function buildAndExecute(DbDriverInterface $dbDriver, $params = [], ?DbFunctionsInterface $dbHelper = null);
diff --git a/src/QueryBasic.php b/src/QueryBasic.php
index 8c458df..97ff974 100644
--- a/src/QueryBasic.php
+++ b/src/QueryBasic.php
@@ -272,7 +272,7 @@ public function build(?DbDriverInterface $dbDriver = null): SqlObject
$sql .= "SELECT " .
($this->distinct ? "DISTINCT " : "") .
$fieldList .
- "FROM " . $tableList;
+ (!empty($tableList) ? "FROM " . $tableList : "");
$whereStr = $this->getWhere();
if (!is_null($whereStr)) {
diff --git a/src/QueryRaw.php b/src/QueryRaw.php
new file mode 100644
index 0000000..2a3adf8
--- /dev/null
+++ b/src/QueryRaw.php
@@ -0,0 +1,29 @@
+sql, $this->parameters);
+ }
+
+ public function buildAndGetIterator(?DbDriverInterface $dbDriver = null, ?CacheQueryResult $cache = null): GenericIterator
+ {
+ return $dbDriver->getIterator($this->sql, $this->parameters);
+ }
+}
\ No newline at end of file
diff --git a/src/Repository.php b/src/Repository.php
index 909530b..5f85f5f 100644
--- a/src/Repository.php
+++ b/src/Repository.php
@@ -3,10 +3,13 @@
namespace ByJG\MicroOrm;
use ByJG\AnyDataset\Core\Enum\Relation;
+use ByJG\AnyDataset\Core\GenericIterator;
use ByJG\AnyDataset\Core\IteratorFilter;
use ByJG\AnyDataset\Db\DbDriverInterface;
+use ByJG\AnyDataset\Db\IsolationLevelEnum;
use ByJG\AnyDataset\Db\IteratorFilterSqlFormatter;
use ByJG\AnyDataset\Db\SqlStatement;
+use ByJG\AnyDataset\Lists\ArrayDataset;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
use ByJG\MicroOrm\Exception\OrmBeforeInvalidException;
use ByJG\MicroOrm\Exception\OrmInvalidFieldsException;
@@ -20,8 +23,10 @@
use ByJG\Serializer\ObjectCopy;
use ByJG\Serializer\Serialize;
use Closure;
+use Exception;
use ReflectionException;
use stdClass;
+use Throwable;
class Repository
{
@@ -188,6 +193,91 @@ public function delete(array|string|int|LiteralInterface $pkId): bool
return $this->deleteByQuery($updatable);
}
+ /**
+ * Execute multiple write queries (insert/update/delete) sequentially within a transaction.
+ * Invalid entries are ignored silently. If any execution fails, the transaction is rolled back.
+ *
+ * @param array $queries List of queries to be executed in bulk
+ * @param IsolationLevelEnum|null $isolationLevel
+ * @return GenericIterator|null
+ * @throws InvalidArgumentException
+ * @throws RepositoryReadOnlyException
+ * @throws Throwable
+ */
+ public function bulkExecute(array $queries, ?IsolationLevelEnum $isolationLevel = null): ?GenericIterator
+ {
+ if (empty($queries)) {
+ throw new InvalidArgumentException('You pass an empty array to bulk');
+ }
+
+ $dbDriver = $this->getDbDriverWrite();
+
+ $bigSqlWrites = '';
+ $selectSql = null;
+ $selectParams = [];
+ $bigParams = [];
+
+ foreach ($queries as $i => $query) {
+ if (!($query instanceof QueryBuilderInterface) && !($query instanceof Updatable)) {
+ throw new InvalidArgumentException('Invalid query type. Expected QueryBuilderInterface or Updatable.');
+ }
+
+ // Build SQL object using the write driver to ensure correct helper/dialect
+ $sqlObject = $query->build($dbDriver);
+ $sql = $sqlObject->getSql();
+ $params = $sqlObject->getParameters();
+ $isSelect = str_starts_with(strtoupper(ltrim($sql)), 'SELECT');
+
+ if ($isSelect && $i === array_key_last($queries)) {
+ // Trailing SELECT: keep it separate with its own params
+ $selectSql = rtrim($sql, "; \t\n\r\0\x0B");
+ $selectParams = $params;
+ continue;
+ }
+
+ // For write statements, avoid parameter name collisions by uniquifying named params
+ foreach ($params as $key => $value) {
+ // Only process named parameters (string keys)
+ if (isset($bigParams[$key])) {
+ $uniqueKey = $key . '__b' . $i;
+ // Replace ":key" with ":key__b{i}" using a safe regex that avoids partial matches
+ $pattern = '/(?beginTransaction($isolationLevel, allowJoin: true);
+ try {
+ // First execute all writes (if any) in a single batch using direct PDO exec
+ if (trim($bigSqlWrites) !== '') {
+ // Use direct PDO to ensure multi-statement execution across drivers like SQLite
+ $dbDriver->execute($bigSqlWrites, $bigParams);
+ }
+
+ // If there is a trailing SELECT, fetch it and return its iterator. Otherwise return an empty iterator
+ if (!empty($selectSql)) {
+ $it = $dbDriver->getIterator($selectSql, $selectParams);
+ } else {
+ $it = (new ArrayDataset([]))->getIterator();
+ }
+
+ $dbDriver->commitTransaction();
+
+ return $it;
+ } catch (Exception $ex) {
+ $dbDriver->rollbackTransaction();
+ throw $ex;
+ }
+ }
+
/**
* @param DeleteQuery $updatable
* @return bool
@@ -314,14 +404,80 @@ public function getByQueryRaw(QueryBuilderInterface $query): array
* @param mixed $instance
* @param UpdateConstraint|null $updateConstraint
* @return mixed
- * @throws Exception\InvalidArgumentException
+ * @throws InvalidArgumentException
* @throws OrmBeforeInvalidException
* @throws OrmInvalidFieldsException
* @throws RepositoryReadOnlyException
* @throws UpdateConstraintException
- * @throws \ByJG\Serializer\Exception\InvalidArgumentException
*/
public function save(mixed $instance, UpdateConstraint $updateConstraint = null): mixed
+ {
+ // Build the updatable without executing
+ [$updatable, $array, $fieldToProperty, $isInsert, $oldInstance, $pkList] = $this->saveUpdatableInternal($instance);
+
+ // Execute the Insert or Update
+ if ($isInsert) {
+ $keyGen = $this->getMapper()->generateKey($this->getDbDriver(), $instance) ?? [];
+ if (!empty($keyGen) && !is_array($keyGen)) {
+ $keyGen = [$keyGen];
+ }
+ $position = 0;
+ foreach ($keyGen as $value) {
+ $array[$pkList[$position]] = $value;
+ $updatable->set($this->mapper->getPrimaryKey()[$position++], $value);
+ }
+ $keyReturned = $this->insert($updatable, $keyGen);
+ if (count($pkList) == 1 && !empty($keyReturned)) {
+ $array[$pkList[0]] = $keyReturned;
+ }
+ } else {
+ if (!empty($updateConstraint)) {
+ $updateConstraint->check($oldInstance, $this->getMapper()->getEntity($array));
+ }
+ $this->update($updatable);
+ }
+
+ ObjectCopy::copy($array, $instance, function ($sourcePropertyName) use ($fieldToProperty) {
+ return $fieldToProperty[$sourcePropertyName] ?? $sourcePropertyName;
+ });
+
+ ORMSubject::getInstance()->notify(
+ $this->mapper->getTable(),
+ $isInsert ? ORMSubject::EVENT_INSERT : ORMSubject::EVENT_UPDATE,
+ $instance, $oldInstance
+ );
+
+ return $instance;
+ }
+
+ /**
+ * Build and return the updatable (InsertQuery or UpdateQuery) without executing it.
+ * This method mirrors the preparatory stage of save() and can be used to inspect or
+ * bulk-compose updates prior to execution.
+ *
+ * @param mixed $instance
+ * @return Updatable
+ * @throws InvalidArgumentException
+ * @throws OrmBeforeInvalidException
+ * @throws RepositoryReadOnlyException
+ */
+ public function saveUpdatable(mixed $instance): Updatable
+ {
+ [$updatable] = $this->saveUpdatableInternal($instance);
+ return $updatable;
+ }
+
+ /**
+ * Internal helper that prepares the updatable and returns additional context
+ * needed by save().
+ *
+ * @param mixed $instance
+ * @return array [Updatable $updatable, array $array, array $fieldToProperty, bool $isInsert, mixed $oldInstance, array $pkList]
+ * @throws InvalidArgumentException
+ * @throws OrmBeforeInvalidException
+ * @throws RepositoryReadOnlyException
+ */
+ protected function saveUpdatableInternal(mixed $instance): array
{
// Get all fields
$array = Serialize::from($instance)
@@ -358,9 +514,11 @@ function ($propName, $targetName, $value) use ($mapper, $instance) {
}
} else {
$fields = array_map(function ($item) use ($array) {
- return $array[$item];
+ return $array[$item] ?? null;
}, $pkList);
- $oldInstance = $this->get($fields);
+ if (!in_array(null, $fields, true)) {
+ $oldInstance = $this->get($fields);
+ }
}
$isInsert = empty($oldInstance);
@@ -368,10 +526,10 @@ function ($propName, $targetName, $value) use ($mapper, $instance) {
if ($isInsert) {
$closure = $this->beforeInsert;
$array = $closure($array);
- foreach ($this->getMapper()->getFieldMap() as $mapper) {
- $fieldValue = $mapper->getInsertFunctionValue($array[$mapper->getFieldName()] ?? null, $instance, $this->getDbDriverWrite()->getDbHelper());
+ foreach ($this->getMapper()->getFieldMap() as $mapItem) {
+ $fieldValue = $mapItem->getInsertFunctionValue($array[$mapItem->getFieldName()] ?? null, $instance, $this->getDbDriverWrite()->getDbHelper());
if ($fieldValue !== false) {
- $array[$mapper->getFieldName()] = $fieldValue;
+ $array[$mapItem->getFieldName()] = $fieldValue;
}
}
$updatable = InsertQuery::getInstance($this->mapper->getTable(), $array);
@@ -386,42 +544,9 @@ function ($propName, $targetName, $value) use ($mapper, $instance) {
throw new OrmBeforeInvalidException('Invalid Before Insert Closure');
}
- // Execute the Insert or Update
- if ($isInsert) {
- $keyGen = $this->getMapper()->generateKey($this->getDbDriver(), $instance) ?? [];
- if (!empty($keyGen) && !is_array($keyGen)) {
- $keyGen = [$keyGen];
- }
- $position = 0;
- foreach ($keyGen as $value) {
- $array[$pkList[$position]] = $value;
- $updatable->set($this->mapper->getPrimaryKey()[$position++], $value);
- }
- $keyReturned = $this->insert($updatable, $keyGen);
- if (count($pkList) == 1 && !empty($keyReturned)) {
- $array[$pkList[0]] = $keyReturned;
- }
- } else {
- if (!empty($updateConstraint)) {
- $updateConstraint->check($oldInstance, $this->getMapper()->getEntity($array));
- }
- $this->update($updatable);
- }
-
- ObjectCopy::copy($array, $instance, function ($sourcePropertyName) use ($fieldToProperty) {
- return $fieldToProperty[$sourcePropertyName] ?? $sourcePropertyName;
- });
-
- ORMSubject::getInstance()->notify(
- $this->mapper->getTable(),
- $isInsert ? ORMSubject::EVENT_INSERT : ORMSubject::EVENT_UPDATE,
- $instance, $oldInstance
- );
-
- return $instance;
+ return [$updatable, $array, $fieldToProperty, $isInsert, $oldInstance, $pkList];
}
-
/**
* @throws InvalidArgumentException
*/
@@ -440,7 +565,7 @@ public function addObserver(ObserverProcessorInterface $observerProcessor): void
protected function insert(InsertQuery $updatable, mixed $keyGen): mixed
{
if (empty($keyGen)) {
- return $this->insertWithAutoInc($updatable);
+ return $this->insertWithAutoinc($updatable);
} else {
$this->insertWithKeyGen($updatable);
return null;
diff --git a/src/UpdateQuery.php b/src/UpdateQuery.php
index 24f0a0e..d68172f 100644
--- a/src/UpdateQuery.php
+++ b/src/UpdateQuery.php
@@ -2,7 +2,9 @@
namespace ByJG\MicroOrm;
+use ByJG\AnyDataset\Db\DbDriverInterface;
use ByJG\AnyDataset\Db\DbFunctionsInterface;
+use ByJG\AnyDataset\Db\SqlStatement;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
use ByJG\MicroOrm\Interface\QueryBuilderInterface;
use ByJG\MicroOrm\Literal\Literal;
@@ -65,34 +67,58 @@ public function setLiteral(string $field, mixed $value): UpdateQuery
return $this;
}
- protected function getJoinTables(DbFunctionsInterface $dbHelper = null): array
+ protected function getJoinTables(DbFunctionsInterface|DbDriverInterface $dbDriverOrHelper = null): array
{
+ $dbDriver = null;
+ if ($dbDriverOrHelper instanceof DbDriverInterface) {
+ $dbDriver = $dbDriverOrHelper;
+ $dbHelper = $dbDriverOrHelper->getDbHelper();
+ } else {
+ $dbHelper = $dbDriverOrHelper;
+ }
+
if (is_null($dbHelper)) {
if (!empty($this->joinTables)) {
throw new InvalidArgumentException('You must specify a DbFunctionsInterface to use join tables');
}
return ['sql' => '', 'position' => 'before_set'];
}
+ foreach ($this->joinTables as $key => $joinTable) {
+ if ($this->joinTables[$key]['table'] instanceof QueryBasic) {
+ if (is_null($dbDriver)) {
+ Throw new InvalidArgumentException('You must specify a DbDriverInterface to use Query join tables');
+ }
+ $this->joinTables[$key]['table'] = new SqlStatement($this->joinTables[$key]['table']->build($dbDriver)->getSql());
+ }
+ }
return $dbHelper->getJoinTablesUpdate($this->joinTables);
}
- public function join(string $table, string $joinCondition): UpdateQuery
+ public function join(QueryBasic|string $table, string $joinCondition, ?string $alias = null): UpdateQuery
{
- $this->joinTables[] = ["table" => $table, "condition" => $joinCondition];
+ $this->joinTables[] = ["table" => $table, "condition" => $joinCondition, "alias" => $alias];
return $this;
}
/**
- * @param DbFunctionsInterface|null $dbHelper
+ * @param DbDriverInterface|DbFunctionsInterface|null $dbDriverOrHelper
* @return SqlObject
* @throws InvalidArgumentException
*/
- public function build(DbFunctionsInterface $dbHelper = null): SqlObject
+ public function build(DbFunctionsInterface|DbDriverInterface|null $dbDriverOrHelper = null): SqlObject
{
if (empty($this->set)) {
throw new InvalidArgumentException('You must specify the fields for update');
}
+
+ $dbDriver = null;
+ if ($dbDriverOrHelper instanceof DbDriverInterface) {
+ $dbHelper = $dbDriverOrHelper->getDbHelper();
+ $dbDriver = $dbDriverOrHelper;
+ } else {
+ $dbHelper = $dbDriverOrHelper;
+ }
$fieldsStr = [];
$params = [];
@@ -120,7 +146,7 @@ public function build(DbFunctionsInterface $dbHelper = null): SqlObject
$tableName = $dbHelper->delimiterTable($tableName);
}
- $joinTables = $this->getJoinTables($dbHelper);
+ $joinTables = $this->getJoinTables($dbDriverOrHelper);
$joinBeforeSet = $joinTables['position'] === 'before_set' ? $joinTables['sql'] : '';
$joinAfterSet = $joinTables['position'] === 'after_set' ? $joinTables['sql'] : '';
diff --git a/tests/BulkTest.php b/tests/BulkTest.php
new file mode 100644
index 0000000..4b838a9
--- /dev/null
+++ b/tests/BulkTest.php
@@ -0,0 +1,187 @@
+dbDriver = ConnectionUtil::getConnection('testmicroorm');
+
+ // Create table and seed data similar to RepositoryTest
+ $this->dbDriver->execute('create table users (
+ id integer primary key auto_increment,
+ name varchar(45),
+ createdate datetime);'
+ );
+ $insertBulk = InsertBulkQuery::getInstance('users', ['name', 'createdate']);
+ $insertBulk->values(['name' => 'John Doe', 'createdate' => '2017-01-02']);
+ $insertBulk->values(['name' => 'Jane Doe', 'createdate' => '2017-01-04']);
+ $insertBulk->values(['name' => 'JG', 'createdate' => '1974-01-26']);
+ $insertBulk->buildAndExecute($this->dbDriver);
+
+ $mapper = new Mapper(Users::class, 'users', 'Id');
+ $this->repository = new Repository($this->dbDriver, $mapper);
+ }
+
+ protected function tearDown(): void
+ {
+ $this->dbDriver->execute('drop table users');
+ }
+
+ public function testBulkMixedQueriesWithParamCollision(): void
+ {
+ // Sanity check: count, specific rows
+ // 3 initial rows
+ $count = Query::getInstance()->fields(['count(*) as cnt'])->table('users');
+ $res = $this->repository->getByQueryRaw($count);
+ $this->assertEquals(3, (int)$res[0]['cnt']);
+
+ // id=1 should have name 'John Doe'
+ $row = $this->repository->get(1);
+ $this->assertEquals('John Doe', $row->getName());
+
+ // The 'Alice' should not exist
+ $rows = $this->repository->getByFilter('name = :name', ['name' => 'Alice']);
+ $this->assertCount(0, $rows);
+
+ // The 'JG' should exist before bulk
+ $rows = $this->repository->getByFilter('name = :name', ['name' => 'JG']);
+ $this->assertCount(1, $rows);
+
+ // Prepare queries with overlapping parameter names (:name used in multiple queries)
+ $insert = InsertQuery::getInstance('users', [
+ 'name' => 'Alice',
+ 'createdate' => '2020-01-01'
+ ]);
+
+ $update = UpdateQuery::getInstance()
+ ->table('users')
+ ->set('name', 'Bob')
+ ->where('id = :id', ['id' => 1]);
+
+ $delete = DeleteQuery::getInstance()
+ ->table('users')
+ ->where('name = :name', ['name' => 'JG']);
+
+ // Execute bulk
+ $it = $this->repository->bulkExecute([$insert, $update, $delete], null);
+ $this->assertEquals([], $it->toArray());
+
+ // Validate results: count, specific rows
+ // 3 initial + 1 insert - 1 delete = 3 rows
+ $count = Query::getInstance()->fields(['count(*) as cnt'])->table('users');
+ $res = $this->repository->getByQueryRaw($count);
+ $this->assertEquals(3, (int)$res[0]['cnt']);
+
+ // id=1 should now have name 'Bob'
+ $row = $this->repository->get(1);
+ $this->assertEquals('Bob', $row->getName());
+
+ // The newly inserted 'Alice' should exist
+ $rows = $this->repository->getByFilter('name = :name', ['name' => 'Alice']);
+ $this->assertCount(1, $rows);
+
+ // The deleted 'JG' should be gone
+ $rows = $this->repository->getByFilter('name = :name', ['name' => 'JG']);
+ $this->assertCount(0, $rows);
+ }
+
+ public function testBulkEmptyArray(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('You pass an empty array to bulk');
+
+ // Should not throw and not change anything
+ $this->repository->bulkExecute([], null);
+ }
+
+ public function testBulkMixedQueriesWithInvalidQuery(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid query type. Expected QueryBuilderInterface or Updatable.');
+
+ // Prepare queries with overlapping parameter names (:name used in multiple queries)
+ $insert = InsertQuery::getInstance('users', [
+ 'name' => 'Alice',
+ 'createdate' => '2020-01-01'
+ ]);
+
+ $update = UpdateQuery::getInstance()
+ ->table('users')
+ ->set('name', 'Bob')
+ ->where('id = :id', ['id' => 1]);
+
+ $delete = DeleteQuery::getInstance()
+ ->table('users')
+ ->where('name = :name', ['name' => 'JG']);
+
+ $invalid = "invalid";
+
+ // Execute bulk
+ /** @psalm-suppress InvalidArgument */
+ $this->repository->bulkExecute([$insert, $invalid, $update, $delete], null);
+ }
+
+ public function testBulkInsertAndSelectLastInsertedId(): void
+ {
+ // initial rows are 3; next autoincrement id should be 4 after insert
+ $insert = InsertQuery::getInstance('users', [
+ 'name' => 'Charlie',
+ 'createdate' => '2025-01-01'
+ ]);
+
+ // Outer query selects last_insert_rowid() from the single-row subquery
+ $selectLastId = QueryRaw::getInstance($this->repository->getDbDriver()->getDbHelper()->getSqlLastInsertId());
+
+ $it = $this->repository->bulkExecute([$insert, $selectLastId], null);
+ $result = $it->toArray();
+
+ $this->assertCount(1, $result);
+ // SQLite returns integer for last_insert_rowid(); expect 4 here
+ $this->assertEquals(4, (int)$result[0]['id']);
+
+ // Also ensure the inserted row exists
+ $rows = $this->repository->getByFilter('name = :name', ['name' => 'Charlie']);
+ $this->assertCount(1, $rows);
+ $this->assertEquals('Charlie', $rows[0]->getName());
+ }
+
+ public function testBulkInsertNoParams(): void
+ {
+ // initial rows are 3; next autoincrement id should be 4 after insert
+ $insert = QueryRaw::getInstance("insert into users (name, createdate) values ('Charlie', '2025-01-01')");
+
+ // Outer query selects last_insert_rowid() from the single-row subquery
+ $selectLastId = QueryRaw::getInstance($this->repository->getDbDriver()->getDbHelper()->getSqlLastInsertId());
+
+ $it = $this->repository->bulkExecute([$insert, $selectLastId], null);
+ $result = $it->toArray();
+
+ $this->assertCount(1, $result);
+ // SQLite returns integer for last_insert_rowid(); expect 4 here
+ $this->assertEquals(4, (int)$result[0]['id']);
+
+ // Also ensure the inserted row exists
+ $rows = $this->repository->getByFilter('name = :name', ['name' => 'Charlie']);
+ $this->assertCount(1, $rows);
+ $this->assertEquals('Charlie', $rows[0]->getName());
+ }
+
+}
diff --git a/tests/ConnectionUtil.php b/tests/ConnectionUtil.php
new file mode 100644
index 0000000..08847de
--- /dev/null
+++ b/tests/ConnectionUtil.php
@@ -0,0 +1,29 @@
+execute("create database if not exists $database;");
+ return Factory::getDbInstance(ConnectionUtil::getUri($database));
+ }
+
+ public static function getUri(?string $database = null): Uri
+ {
+ $host = getenv('MYSQL_TEST_HOST') ? getenv('MYSQL_TEST_HOST') : '127.0.0.1';
+ $uri = new Uri("mysql://root:password@$host");
+
+ if (empty($database)) {
+ return $uri;
+ }
+
+ return $uri->withPath("/$database");
+ }
+}
\ No newline at end of file
diff --git a/tests/Model/UsersWithUuidKey.php b/tests/Model/UsersWithUuidKey.php
index 1ac1911..76097c5 100644
--- a/tests/Model/UsersWithUuidKey.php
+++ b/tests/Model/UsersWithUuidKey.php
@@ -3,10 +3,10 @@
namespace Tests\Model;
use ByJG\MicroOrm\Attributes\FieldAttribute;
-use ByJG\MicroOrm\Attributes\TableSqliteUuidPKAttribute;
+use ByJG\MicroOrm\Attributes\TableMySqlUuidPKAttribute;
use ByJG\MicroOrm\Literal\Literal;
-#[TableSqliteUuidPKAttribute(tableName: 'usersuuid')]
+#[TableMySqlUuidPKAttribute(tableName: 'usersuuid')]
class UsersWithUuidKey
{
#[FieldAttribute(primaryKey: true)]
diff --git a/tests/RepositoryAliasTest.php b/tests/RepositoryAliasTest.php
index 5fab58c..6b5552c 100644
--- a/tests/RepositoryAliasTest.php
+++ b/tests/RepositoryAliasTest.php
@@ -3,20 +3,15 @@
namespace Tests;
use ByJG\AnyDataset\Db\DbDriverInterface;
-use ByJG\AnyDataset\Db\Factory;
use ByJG\MicroOrm\FieldMapping;
use ByJG\MicroOrm\Mapper;
use ByJG\MicroOrm\Query;
use ByJG\MicroOrm\Repository;
-use ByJG\Util\Uri;
use PHPUnit\Framework\TestCase;
use Tests\Model\Customer;
class RepositoryAliasTest extends TestCase
{
-
- const URI='sqlite:///tmp/teste.db';
-
/**
* @var Mapper
*/
@@ -34,10 +29,10 @@ class RepositoryAliasTest extends TestCase
public function setUp(): void
{
- $this->dbDriver = Factory::getDbInstance(self::URI);
+ $this->dbDriver = ConnectionUtil::getConnection("testmicroorm");
$this->dbDriver->execute('create table customers (
- id integer primary key autoincrement,
+ id integer primary key auto_increment,
customer_name varchar(45),
customer_age int);'
);
@@ -58,8 +53,7 @@ public function setUp(): void
public function tearDown(): void
{
- $uri = new Uri(self::URI);
- unlink($uri->getPath());
+ $this->dbDriver->execute('drop table if exists customers;');
}
public function testGet()
diff --git a/tests/RepositoryPkListTest.php b/tests/RepositoryPkListTest.php
index 9a3410d..40f5e11 100644
--- a/tests/RepositoryPkListTest.php
+++ b/tests/RepositoryPkListTest.php
@@ -3,18 +3,13 @@
namespace Tests;
use ByJG\AnyDataset\Db\DbDriverInterface;
-use ByJG\AnyDataset\Db\Factory;
use ByJG\MicroOrm\Mapper;
use ByJG\MicroOrm\Repository;
-use ByJG\Util\Uri;
use PHPUnit\Framework\TestCase;
use Tests\Model\Items;
class RepositoryPkListTest extends TestCase
{
-
- const URI='sqlite:///tmp/teste.db';
-
/**
* @var Mapper
*/
@@ -32,7 +27,7 @@ class RepositoryPkListTest extends TestCase
public function setUp(): void
{
- $this->dbDriver = Factory::getDbInstance(self::URI);
+ $this->dbDriver = ConnectionUtil::getConnection("testmicroorm");
$this->dbDriver->execute('CREATE TABLE items (
storeid INTEGER,
@@ -52,8 +47,7 @@ public function setUp(): void
public function tearDown(): void
{
- $uri = new Uri(self::URI);
- unlink($uri->getPath());
+ $this->dbDriver->execute('drop table if exists items;');
}
public function testGet()
diff --git a/tests/RepositoryTest.php b/tests/RepositoryTest.php
index 627d825..3fb912c 100644
--- a/tests/RepositoryTest.php
+++ b/tests/RepositoryTest.php
@@ -5,7 +5,6 @@
use ByJG\AnyDataset\Core\Enum\Relation;
use ByJG\AnyDataset\Core\IteratorFilter;
use ByJG\AnyDataset\Db\DbDriverInterface;
-use ByJG\AnyDataset\Db\Factory;
use ByJG\Cache\Psr16\ArrayCacheEngine;
use ByJG\MicroOrm\CacheQueryResult;
use ByJG\MicroOrm\DeleteQuery;
@@ -30,7 +29,6 @@
use ByJG\MicroOrm\Union;
use ByJG\MicroOrm\UpdateConstraint;
use ByJG\MicroOrm\UpdateQuery;
-use ByJG\Util\Uri;
use DateTime;
use Exception;
use PHPUnit\Framework\ExpectationFailedException;
@@ -45,9 +43,6 @@
class RepositoryTest extends TestCase
{
-
- const URI='sqlite:///tmp/test.db';
-
/**
* @var Mapper
*/
@@ -70,10 +65,10 @@ class RepositoryTest extends TestCase
public function setUp(): void
{
- $this->dbDriver = Factory::getDbInstance(self::URI);
+ $this->dbDriver = ConnectionUtil::getConnection("testmicroorm");
$this->dbDriver->execute('create table users (
- id integer primary key autoincrement,
+ id integer primary key auto_increment,
name varchar(45),
createdate datetime);'
);
@@ -86,11 +81,11 @@ public function setUp(): void
$this->dbDriver->execute('create table info (
- id integer primary key autoincrement,
+ id integer primary key auto_increment,
iduser INTEGER,
- property number(10,2),
- created_at datetime,
- updated_at datetime,
+ property decimal(10, 2),
+ created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at datetime);'
);
$insertMultiple = InsertBulkQuery::getInstance('info', ['iduser', 'property']);
@@ -107,8 +102,8 @@ public function setUp(): void
public function tearDown(): void
{
- $uri = new Uri(self::URI);
- unlink($uri->getPath());
+ $this->dbDriver->execute('drop table if exists users;');
+ $this->dbDriver->execute('drop table if exists info;');
ORM::clearRelationships();
}
@@ -117,17 +112,17 @@ public function testGet()
$users = $this->repository->get(1);
$this->assertEquals(1, $users->getId());
$this->assertEquals('John Doe', $users->getName());
- $this->assertEquals('2017-01-02', $users->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $users->getCreatedate());
$users = $this->repository->get("2");
$this->assertEquals(2, $users->getId());
$this->assertEquals('Jane Doe', $users->getName());
- $this->assertEquals('2017-01-04', $users->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $users->getCreatedate());
$users = $this->repository->get(new Literal(1));
$this->assertEquals(1, $users->getId());
$this->assertEquals('John Doe', $users->getName());
- $this->assertEquals('2017-01-02', $users->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $users->getCreatedate());
}
public function testGetByFilter()
@@ -136,7 +131,7 @@ public function testGetByFilter()
$this->assertCount(1, $users);
$this->assertEquals(1, $users[0]->getId());
$this->assertEquals('John Doe', $users[0]->getName());
- $this->assertEquals('2017-01-02', $users[0]->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $users[0]->getCreatedate());
$filter = new IteratorFilter();
$filter->and('id', Relation::EQUAL, 2);
@@ -144,7 +139,7 @@ public function testGetByFilter()
$this->assertCount(1, $users);
$this->assertEquals(2, $users[0]->getId());
$this->assertEquals('Jane Doe', $users[0]->getName());
- $this->assertEquals('2017-01-04', $users[0]->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $users[0]->getCreatedate());
}
public function testGetSelectFunction()
@@ -168,14 +163,14 @@ public function testGetSelectFunction()
$users = $this->repository->get(1);
$this->assertEquals(1, $users->getId());
- $this->assertEquals('[JOHN DOE] - 2017-01-02', $users->getName());
- $this->assertEquals('2017-01-02', $users->getCreatedate());
+ $this->assertEquals('[JOHN DOE] - 2017-01-02 00:00:00', $users->getName());
+ $this->assertEquals('2017-01-02 00:00:00', $users->getCreatedate());
$this->assertEquals(2017, $users->getYear());
$users = $this->repository->get(2);
$this->assertEquals(2, $users->getId());
- $this->assertEquals('[JANE DOE] - 2017-01-04', $users->getName());
- $this->assertEquals('2017-01-04', $users->getCreatedate());
+ $this->assertEquals('[JANE DOE] - 2017-01-04 00:00:00', $users->getName());
+ $this->assertEquals('2017-01-04 00:00:00', $users->getCreatedate());
$this->assertEquals(2017, $users->getYear());
}
@@ -237,7 +232,7 @@ public function testInsert()
$this->assertEquals(4, $users2->getId());
$this->assertEquals('Bla99991919', $users2->getName());
- $this->assertEquals('2015-08-09', $users2->getCreatedate());
+ $this->assertEquals('2015-08-09 00:00:00', $users2->getCreatedate());
}
public function testInsertReadOnly()
@@ -272,7 +267,7 @@ public function testInsert_beforeInsert()
$this->assertEquals(4, $users2->getId());
$this->assertEquals('Bla-add', $users2->getName());
- $this->assertEquals('2017-12-21', $users2->getCreatedate());
+ $this->assertEquals('2017-12-21 00:00:00', $users2->getCreatedate());
}
public function testInsertLiteral()
@@ -280,7 +275,7 @@ public function testInsertLiteral()
/** @var Users $users */
$users = $this->repository->entity([
"name" => new Literal("X'6565'"),
- "createdate" => '2015-08-09'
+ "createdate" => '2015-08-09 12:13:14'
]);
$this->assertEquals(null, $users->getId());
@@ -290,7 +285,7 @@ public function testInsertLiteral()
$this->assertEquals(4, $users2->getId());
$this->assertEquals('ee', $users2->getName());
- $this->assertEquals('2015-08-09', $users2->getCreatedate());
+ $this->assertEquals('2015-08-09 12:13:14', $users2->getCreatedate());
$this->assertEquals($users2->getId(), $users->getId());
$this->assertEquals($users2->getCreatedate(), $users->getCreatedate());
@@ -319,7 +314,7 @@ public function testInsertFromObject()
$this->assertEquals(4, $users2->getId());
$this->assertEquals('ee', $users2->getName());
- $this->assertEquals('2015-08-09', $users2->getCreatedate());
+ $this->assertEquals('2015-08-09 00:00:00', $users2->getCreatedate());
}
public function testInsertKeyGen()
@@ -345,7 +340,7 @@ public function testInsertKeyGen()
$this->assertEquals(50, $users2->getId());
$this->assertEquals('Bla99991919', $users2->getName());
- $this->assertEquals('2015-08-09', $users2->getCreatedate());
+ $this->assertEquals('2015-08-09 00:00:00', $users2->getCreatedate());
}
public function testInsertUpdateFunction()
@@ -379,7 +374,7 @@ public function testInsertUpdateFunction()
$users2 = $this->repository->get(4);
$this->assertEquals(4, $users2->getId());
- $this->assertEquals('2015-08-09', $users2->getCreatedate());
+ $this->assertEquals('2015-08-09 00:00:00', $users2->getCreatedate());
$this->assertEquals(2015, $users2->getYear());
$this->assertEquals('Sr. John Doe - 2015-08-09', $users2->getName());
}
@@ -395,12 +390,12 @@ public function testUpdate()
$users2 = $this->repository->get(1);
$this->assertEquals(1, $users2->getId());
$this->assertEquals('New Name', $users2->getName());
- $this->assertEquals('2016-01-09', $users2->getCreatedate());
+ $this->assertEquals('2016-01-09 00:00:00', $users2->getCreatedate());
$users2 = $this->repository->get(2);
$this->assertEquals(2, $users2->getId());
$this->assertEquals('Jane Doe', $users2->getName());
- $this->assertEquals('2017-01-04', $users2->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $users2->getCreatedate());
}
public function testUpdateReadOnly()
@@ -446,7 +441,7 @@ public function testInsertBuildAndExecute()
$users = $this->repository->get(4);
$this->assertEquals('inserted name', $users->getName());
- $this->assertEquals('2024-09-03', $users->getCreatedate());
+ $this->assertEquals('2024-09-03 00:00:00', $users->getCreatedate());
}
public function testInsertBuildAndExecute2()
@@ -463,7 +458,7 @@ public function testInsertBuildAndExecute2()
$users = $this->repository->get(4);
$this->assertEquals('inserted name', $users->getName());
- $this->assertEquals('2024-09-03', $users->getCreatedate());
+ $this->assertEquals('2024-09-03 00:00:00', $users->getCreatedate());
}
public function testDeleteBuildAndExecute()
@@ -497,12 +492,12 @@ public function testUpdate_beforeUpdate()
$users2 = $this->repository->get(1);
$this->assertEquals(1, $users2->getId());
$this->assertEquals('New Name-upd', $users2->getName());
- $this->assertEquals('2017-12-21', $users2->getCreatedate());
+ $this->assertEquals('2017-12-21 00:00:00', $users2->getCreatedate());
$users2 = $this->repository->get(2);
$this->assertEquals(2, $users2->getId());
$this->assertEquals('Jane Doe', $users2->getName());
- $this->assertEquals('2017-01-04', $users2->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $users2->getCreatedate());
}
public function testUpdateLiteral()
@@ -515,7 +510,7 @@ public function testUpdateLiteral()
$this->assertEquals(1, $users2->getId());
$this->assertEquals('ee', $users2->getName());
- $this->assertEquals('2017-01-02', $users2->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $users2->getCreatedate());
}
public function testUpdateObject()
@@ -541,7 +536,7 @@ public function testUpdateObject()
$this->assertEquals(1, $users2->getId());
$this->assertEquals('ee', $users2->getName());
- $this->assertEquals('2020-01-02', $users2->getCreatedate());
+ $this->assertEquals('2020-01-02 00:00:00', $users2->getCreatedate());
}
@@ -573,13 +568,13 @@ public function testUpdateFunction()
$users2 = $this->repository->get(1);
$this->assertEquals(1, $users2->getId());
$this->assertEquals('Sr. New Name', $users2->getName());
- $this->assertEquals('2016-01-09', $users2->getCreatedate());
+ $this->assertEquals('2016-01-09 00:00:00', $users2->getCreatedate());
$this->assertEquals(2016, $users2->getYear());
$users2 = $this->repository->get(2);
$this->assertEquals(2, $users2->getId());
$this->assertEquals('Jane Doe', $users2->getName());
- $this->assertEquals('2017-01-04', $users2->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $users2->getCreatedate());
$this->assertEquals(2017, $users2->getYear());
}
@@ -592,7 +587,7 @@ public function testDelete()
$users = $this->repository->get(2);
$this->assertEquals(2, $users->getId());
$this->assertEquals('Jane Doe', $users->getName());
- $this->assertEquals('2017-01-04', $users->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $users->getCreatedate());
}
public function testDeleteReadOnly()
@@ -610,7 +605,7 @@ public function testDeleteLiteral()
$users = $this->repository->get(2);
$this->assertEquals(2, $users->getId());
$this->assertEquals('Jane Doe', $users->getName());
- $this->assertEquals('2017-01-04', $users->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $users->getCreatedate());
}
public function testDelete2()
@@ -624,7 +619,7 @@ public function testDelete2()
$users = $this->repository->get(1);
$this->assertEquals(1, $users->getId());
$this->assertEquals('John Doe', $users->getName());
- $this->assertEquals('2017-01-02', $users->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $users->getCreatedate());
$users = $this->repository->get(2);
$this->assertEmpty($users);
@@ -662,7 +657,7 @@ public function testGetByQueryOne()
$infoRepository->save($result[0]);
$result = $infoRepository->getByQuery($query);
- $this->assertSame('0', (string)$result[0]->getValue());
+ $this->assertSame('0.00', (string)$result[0]->getValue());
// Set Null
$result[0]->setValue(null);
@@ -687,7 +682,7 @@ public function testFilterInOne()
$this->assertEquals(2, $result[0]->getId());
$this->assertEquals('Jane Doe', $result[0]->getName());
- $this->assertEquals('2017-01-04', $result[0]->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $result[0]->getCreatedate());
}
public function testFilterInTwo()
@@ -698,11 +693,11 @@ public function testFilterInTwo()
$this->assertEquals(2, $result[0]->getId());
$this->assertEquals('Jane Doe', $result[0]->getName());
- $this->assertEquals('2017-01-04', $result[0]->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $result[0]->getCreatedate());
$this->assertEquals(3, $result[1]->getId());
$this->assertEquals('JG', $result[1]->getName());
- $this->assertEquals('1974-01-26', $result[1]->getCreatedate());
+ $this->assertEquals('1974-01-26 00:00:00', $result[1]->getCreatedate());
}
/**
@@ -755,11 +750,11 @@ public function testJoin()
$this->assertEquals(1, $result[0][0]->getId());
$this->assertEquals('John Doe', $result[0][0]->getName());
- $this->assertEquals('2017-01-02', $result[0][0]->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $result[0][0]->getCreatedate());
$this->assertEquals(1, $result[1][0]->getId());
$this->assertEquals('John Doe', $result[1][0]->getName());
- $this->assertEquals('2017-01-02', $result[1][0]->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $result[1][0]->getCreatedate());
// - ------------------
@@ -788,7 +783,7 @@ public function testLeftJoin()
$this->assertEquals(2, $result[0][0]->getId());
$this->assertEquals('Jane Doe', $result[0][0]->getName());
- $this->assertEquals('2017-01-04', $result[0][0]->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $result[0][0]->getCreatedate());
// - ------------------
@@ -805,7 +800,7 @@ public function testTop()
$this->assertEquals(1, $result[0]->getId());
$this->assertEquals('John Doe', $result[0]->getName());
- $this->assertEquals('2017-01-02', $result[0]->getCreatedate());
+ $this->assertEquals('2017-01-02 00:00:00', $result[0]->getCreatedate());
$this->assertEquals(1, count($result));
}
@@ -819,7 +814,7 @@ public function testLimit()
$this->assertEquals(2, $result[0]->getId());
$this->assertEquals('Jane Doe', $result[0]->getName());
- $this->assertEquals('2017-01-04', $result[0]->getCreatedate());
+ $this->assertEquals('2017-01-04 00:00:00', $result[0]->getCreatedate());
$this->assertEquals(1, count($result));
}
@@ -829,7 +824,7 @@ public function testQueryRaw()
$query = $this->repository->queryInstance()
->fields([
"name",
- "julianday('2020-06-28') - julianday(createdate) as days"
+ "DATEDIFF('2020-06-28', createdate) AS days"
])
->limit(1, 1);
@@ -1234,15 +1229,17 @@ public function testMappingAttribute()
$this->assertEquals(3, $result[0]->getPk());
$this->assertEquals(3, $result[0]->iduser);
$this->assertEquals(3.5, $result[0]->value);
- $this->assertNull($result[0]->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($result[0]->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($result[0]->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($result[0]->getCreatedAt());
+ $this->assertNotNull($result[0]->getUpdatedAt());
+ $this->assertNull($result[0]->getDeletedAt());
+ $this->assertEquals($result[0]->getCreatedAt(), $result[0]->getUpdatedAt());
}
public function testMappingAttributeInsert()
{
$infoRepository = new Repository($this->dbDriver, ModelWithAttributes::class);
+ // Sanity check
$query = new Query();
$query->table($this->infoMapper->getTable())
->where('iduser = :id', ['id' => 123])
@@ -1250,11 +1247,13 @@ public function testMappingAttributeInsert()
$result = $infoRepository->getByQuery($query);
$this->assertEmpty($result);
+ // Add a new record
$info = new ModelWithAttributes();
$info->iduser = 123;
$info->value = 98.5;
$infoRepository->save($info);
+ // Get the Record and assert the values
/** @var ModelWithAttributes[] $result */
$result = $infoRepository->getByQuery($query);
$this->assertEquals(count($result), 1);
@@ -1266,10 +1265,12 @@ public function testMappingAttributeInsert()
$this->assertNotNull($result[0]->getUpdatedAt());
$this->assertNull($result[0]->getDeletedAt());
- // Check if the updated_at works
+ // save the record again and assert the values
sleep(1);
$info->value = 99.5;
$infoRepository->save($info);
+
+ // Get directly from the databae
/** @var ModelWithAttributes[] $result2 */
$result2 = $infoRepository->getByQuery($query);
$this->assertEquals(count($result2), 1);
@@ -1296,14 +1297,19 @@ public function testMappingAttributeSoftDeleteAndGetByQuery()
$this->assertEquals(3, $result[0]->getPk());
$this->assertEquals(3, $result[0]->iduser);
$this->assertEquals(3.5, $result[0]->value);
- $this->assertNull($result[0]->getCreatedAt());
- $this->assertNull($result[0]->getUpdatedAt());
+ $this->assertNotNull($result[0]->getCreatedAt());
+ $this->assertNotNull($result[0]->getUpdatedAt());
$this->assertNull($result[0]->getDeletedAt());
$infoRepository->delete(3);
$result = $infoRepository->getByQuery($query);
$this->assertCount(0, $result);
+
+ $query->unsafe();
+ $result = $infoRepository->getByQuery($query);
+ $this->assertCount(1, $result);
+ $this->assertNotNull($result[0]->getDeletedAt());
}
public function testMappingAttributeSoftDeleteAndGetByFilter()
@@ -1317,8 +1323,8 @@ public function testMappingAttributeSoftDeleteAndGetByFilter()
$this->assertEquals(3, $result[0]->getPk());
$this->assertEquals(3, $result[0]->iduser);
$this->assertEquals(3.5, $result[0]->value);
- $this->assertNull($result[0]->getCreatedAt());
- $this->assertNull($result[0]->getUpdatedAt());
+ $this->assertNotNull($result[0]->getCreatedAt());
+ $this->assertNotNull($result[0]->getUpdatedAt());
$this->assertNull($result[0]->getDeletedAt());
$infoRepository->delete(3);
@@ -1336,8 +1342,8 @@ public function testMappingAttributeSoftDeleteAndGetByPK()
$this->assertEquals(3, $result->getPk());
$this->assertEquals(3, $result->iduser);
$this->assertEquals(3.5, $result->value);
- $this->assertNull($result->getCreatedAt());
- $this->assertNull($result->getUpdatedAt());
+ $this->assertNotNull($result->getCreatedAt());
+ $this->assertNotNull($result->getUpdatedAt());
$this->assertNull($result->getDeletedAt());
$infoRepository->delete(3);
@@ -1360,7 +1366,7 @@ public function testQueryInstanceWithModel()
$this->assertEquals(3, $result[0]->getId());
$this->assertEquals('JG', $result[0]->getName());
- $this->assertEquals('1974-01-26', $result[0]->getCreatedate());
+ $this->assertEquals('1974-01-26 00:00:00', $result[0]->getCreatedate());
}
@@ -1449,9 +1455,9 @@ public function testActiveRecordGet()
$this->assertEquals(3, $model->getPk());
$this->assertEquals(3, $model->iduser);
$this->assertEquals(3.5, $model->value);
- $this->assertNull($model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model->getCreatedAt());
+ $this->assertNotNull($model->getUpdatedAt());
+ $this->assertNull($model->getDeletedAt());
}
public function testActiveRecordRefresh()
@@ -1472,9 +1478,9 @@ public function testActiveRecordRefresh()
$this->assertEquals(3, $model->getPk());
$this->assertEquals(3, $model->iduser);
$this->assertEquals(3.5, $model->value);
- $this->assertNull($createdAt); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($updatedAt); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($deletedAt); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($createdAt);
+ $this->assertNotNull($updatedAt);
+ $this->assertNull($deletedAt);
// Update the record OUTSIDE the Active Record
$this->dbDriver->execute("UPDATE info SET iduser = 4, property = 44.44 WHERE id = 3");
@@ -1483,9 +1489,9 @@ public function testActiveRecordRefresh()
$this->assertEquals(3, $model->getPk());
$this->assertEquals(3, $model->iduser);
$this->assertEquals(3.5, $model->value);
- $this->assertEquals($createdAt, $model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertEquals($updatedAt, $model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertEquals($deletedAt, $model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertEquals($createdAt, $model->getCreatedAt());
+ $this->assertEquals($updatedAt, $model->getUpdatedAt());
+ $this->assertEquals($deletedAt, $model->getDeletedAt());
// Refresh the model
$model->refresh();
@@ -1494,9 +1500,9 @@ public function testActiveRecordRefresh()
$this->assertEquals(3, $model->getPk());
$this->assertEquals(4, $model->iduser);
$this->assertEquals(44.44, $model->value);
- $this->assertEquals($createdAt, $model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertEquals($updatedAt, $model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertEquals($deletedAt, $model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertEquals($createdAt, $model->getCreatedAt());
+ $this->assertEquals($updatedAt, $model->getUpdatedAt());
+ $this->assertEquals($deletedAt, $model->getDeletedAt());
}
public function testActiveRecordRefreshError()
@@ -1519,9 +1525,9 @@ public function testActiveRecordFill()
$this->assertEquals(3, $model->getPk());
$this->assertEquals(3, $model->iduser);
$this->assertEquals(3.5, $model->value);
- $this->assertNull($model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model->getCreatedAt());
+ $this->assertNotNull($model->getUpdatedAt());
+ $this->assertNull($model->getDeletedAt());
$model->fill([
'iduser' => 4,
@@ -1531,9 +1537,9 @@ public function testActiveRecordFill()
$this->assertEquals(3, $model->getPk());
$this->assertEquals(4, $model->iduser);
$this->assertEquals(44.44, $model->value);
- $this->assertNull($model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model->getCreatedAt());
+ $this->assertNotNull($model->getUpdatedAt());
+ $this->assertNull($model->getDeletedAt());
}
@@ -1547,16 +1553,16 @@ public function testActiveRecordFilter()
$this->assertEquals(1, $model[0]->getPk());
$this->assertEquals(1, $model[0]->iduser);
$this->assertEquals(30.4, $model[0]->value);
- $this->assertNull($model[0]->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model[0]->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model[0]->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model[0]->getCreatedAt());
+ $this->assertNotNull($model[0]->getUpdatedAt());
+ $this->assertNull($model[0]->getDeletedAt());
$this->assertEquals(2, $model[1]->getPk());
$this->assertEquals(1, $model[1]->iduser);
$this->assertEquals(1250.96, $model[1]->value);
- $this->assertNull($model[1]->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model[1]->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model[1]->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model[1]->getCreatedAt());
+ $this->assertNotNull($model[1]->getUpdatedAt());
+ $this->assertNull($model[1]->getDeletedAt());
}
public function testActiveRecordEmptyFilter()
@@ -1594,17 +1600,17 @@ public function testActiveRecordNew()
$this->assertEquals(4, $model->getPk());
$this->assertEquals(5, $model->iduser);
$this->assertEquals(55.8, $model->value);
- $this->assertNotNull($model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNotNull($model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model->getCreatedAt());
+ $this->assertNotNull($model->getUpdatedAt());
+ $this->assertNull($model->getDeletedAt());
$model = ActiveRecordModel::get(4);
$this->assertEquals(4, $model->getPk());
$this->assertEquals(5, $model->iduser);
$this->assertEquals(55.8, $model->value);
- $this->assertNotNull($model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNotNull($model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model->getCreatedAt());
+ $this->assertNotNull($model->getUpdatedAt());
+ $this->assertNull($model->getDeletedAt());
}
public function testActiveRecordUpdate()
@@ -1620,17 +1626,17 @@ public function testActiveRecordUpdate()
$this->assertEquals(3, $model->getPk());
$this->assertEquals(3, $model->iduser);
$this->assertEquals(99.1, $model->value);
- $this->assertNull($model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNotNull($model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model->getCreatedAt());
+ $this->assertNotNull($model->getUpdatedAt());
+ $this->assertNull($model->getDeletedAt());
$model = ActiveRecordModel::get(3);
$this->assertEquals(3, $model->getPk());
$this->assertEquals(3, $model->iduser);
$this->assertEquals(99.1, $model->value);
- $this->assertNull($model->getCreatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNotNull($model->getUpdatedAt()); // Because it was not set in the initial insert outside the ORM
- $this->assertNull($model->getDeletedAt()); // Because it was not set in the initial insert outside the ORM
+ $this->assertNotNull($model->getCreatedAt());
+ $this->assertNotNull($model->getUpdatedAt());
+ $this->assertNull($model->getDeletedAt());
}
public function testActiveRecordDelete()
diff --git a/tests/RepositoryUuidTest.php b/tests/RepositoryUuidTest.php
index 93988a2..bf2bce2 100644
--- a/tests/RepositoryUuidTest.php
+++ b/tests/RepositoryUuidTest.php
@@ -3,18 +3,13 @@
namespace Tests;
use ByJG\AnyDataset\Db\DbDriverInterface;
-use ByJG\AnyDataset\Db\Factory;
use ByJG\MicroOrm\Literal\HexUuidLiteral;
use ByJG\MicroOrm\Repository;
-use ByJG\Util\Uri;
use PHPUnit\Framework\TestCase;
use Tests\Model\UsersWithUuidKey;
class RepositoryUuidTest extends TestCase
{
-
- const URI='sqlite:///tmp/test.db';
-
/**
* @var DbDriverInterface
*/
@@ -27,7 +22,7 @@ class RepositoryUuidTest extends TestCase
public function setUp(): void
{
- $this->dbDriver = Factory::getDbInstance(self::URI);
+ $this->dbDriver = ConnectionUtil::getConnection("testmicroorm");
$this->dbDriver->execute('create table usersuuid (
id binary(16) primary key,
@@ -38,8 +33,7 @@ public function setUp(): void
public function tearDown(): void
{
- $uri = new Uri(self::URI);
- unlink($uri->getPath());
+ $this->dbDriver->execute('drop table if exists usersuuid;');
}
public function testGet()
diff --git a/tests/TransactionManagerTest.php b/tests/TransactionManagerTest.php
index b95328a..fc5fa4e 100644
--- a/tests/TransactionManagerTest.php
+++ b/tests/TransactionManagerTest.php
@@ -7,6 +7,7 @@
use ByJG\MicroOrm\Mapper;
use ByJG\MicroOrm\Repository;
use ByJG\MicroOrm\TransactionManager;
+use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Tests\Model\Users;
@@ -20,41 +21,41 @@ class TransactionManagerTest extends TestCase
public function setUp(): void
{
$this->object = new TransactionManager();
+
+ ConnectionUtil::getConnection("a");
+ ConnectionUtil::getConnection("b");
+ ConnectionUtil::getConnection("c");
+ ConnectionUtil::getConnection("d");
}
public function tearDown(): void
{
$this->object->destroy();
$this->object = null;
- if (file_exists("/tmp/a.db")) {
- unlink("/tmp/a.db");
- }
- if (file_exists("/tmp/b.db")) {
- unlink("/tmp/b.db");
- }
- if (file_exists("/tmp/c.db")) {
- unlink("/tmp/c.db");
- }
- if (file_exists("/tmp/d.db")) {
- unlink("/tmp/d.db");
- }
+
+ $dbDriver = ConnectionUtil::getConnection("a");
+ $dbDriver->execute('drop table if exists users;');
+ $dbDriver->execute('drop table if exists users1;');
+
+ $dbDriver = ConnectionUtil::getConnection("b");
+ $dbDriver->execute('drop table if exists users2;');
}
public function testAddConnectionError()
{
- $this->expectException(\InvalidArgumentException::class);
+ $this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("The connection already exists with a different instance");
- $dbDrive1 = $this->object->addConnection("sqlite:///tmp/a.db");
- $dbDrive2 = $this->object->addConnection("sqlite:///tmp/b.db");
- $dbDrive3 = $this->object->addConnection("sqlite:///tmp/a.db");
- $dbDrive4 = $this->object->addConnection("sqlite:///tmp/b.db");
+ $dbDrive1 = $this->object->addConnection(ConnectionUtil::getUri("a")->__toString());
+ $dbDrive2 = $this->object->addConnection(ConnectionUtil::getUri("b")->__toString());
+ $dbDrive3 = $this->object->addConnection(ConnectionUtil::getUri("a")->__toString());
+ $dbDrive4 = $this->object->addConnection(ConnectionUtil::getUri("b")->__toString());
}
public function testAddConnection()
{
- $dbDrive1 = $this->object->addConnection("sqlite:///tmp/a.db");
- $dbDrive2 = $this->object->addConnection("sqlite:///tmp/b.db");
+ $dbDrive1 = $this->object->addConnection(ConnectionUtil::getUri("a")->__toString());
+ $dbDrive2 = $this->object->addConnection(ConnectionUtil::getUri("b")->__toString());
$this->object->addDbDriver($dbDrive1);
$this->object->addDbDriver($dbDrive2);
@@ -65,13 +66,13 @@ public function testAddConnection()
public function testAddDbDriverError()
{
- $this->expectException(\InvalidArgumentException::class);
+ $this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("The connection already exists with a different instance");
- $dbDrive1 = Factory::getDbInstance("sqlite:///tmp/a.db");
- $dbDrive2 = Factory::getDbInstance("sqlite:///tmp/b.db");
- $dbDrive3 = Factory::getDbInstance("sqlite:///tmp/a.db");
- $dbDrive4 = Factory::getDbInstance("sqlite:///tmp/b.db");
+ $dbDrive1 = Factory::getDbInstance(ConnectionUtil::getUri("a"));
+ $dbDrive2 = Factory::getDbInstance(ConnectionUtil::getUri("b"));
+ $dbDrive3 = Factory::getDbInstance(ConnectionUtil::getUri("a"));
+ $dbDrive4 = Factory::getDbInstance(ConnectionUtil::getUri("b"));
$this->object->addDbDriver($dbDrive1);
$this->object->addDbDriver($dbDrive2);
@@ -81,8 +82,8 @@ public function testAddDbDriverError()
public function testAddDbDriver()
{
- $dbDrive1 = Factory::getDbInstance("sqlite:///tmp/a.db");
- $dbDrive2 = Factory::getDbInstance("sqlite:///tmp/b.db");
+ $dbDrive1 = ConnectionUtil::getConnection("a");
+ $dbDrive2 = ConnectionUtil::getConnection("b");
$this->object->addDbDriver($dbDrive1);
$this->object->addDbDriver($dbDrive2);
@@ -96,10 +97,10 @@ public function testAddDbDriver()
public function testAddRepository()
{
- $dbDriver = Factory::getDbInstance("sqlite:///tmp/a.db");
+ $dbDriver = ConnectionUtil::getConnection("a");
$dbDriver->execute('create table users (
- id integer primary key autoincrement,
+ id integer primary key auto_increment,
name varchar(45),
createdate datetime);'
);
@@ -118,8 +119,8 @@ public function testAddRepository()
public function testBeginTransaction()
{
- $this->object->addConnection("sqlite:///tmp/c.db");
- $this->object->addConnection("sqlite:///tmp/d.db");
+ $this->object->addConnection(ConnectionUtil::getUri("c")->__toString());
+ $this->object->addConnection(ConnectionUtil::getUri("d")->__toString());
$this->object->beginTransaction();
$this->object->commitTransaction();
@@ -132,8 +133,8 @@ public function testBeginTransactionTwice()
$this->expectException(TransactionException::class);
$this->expectExceptionMessage("Transaction Already Started");
- $this->object->addConnection("sqlite:///tmp/a.db");
- $this->object->addConnection("sqlite:///tmp/d.db");
+ $this->object->addConnection(ConnectionUtil::getUri("a")->__toString());
+ $this->object->addConnection(ConnectionUtil::getUri("d")->__toString());
$this->assertEquals(2, $this->object->count());
@@ -146,7 +147,7 @@ public function testRollbackWithNoTransaction()
$this->expectException(TransactionException::class);
$this->expectExceptionMessage("There is no Active Transaction");
- $this->object->addConnection("sqlite:///tmp/c.db");
+ $this->object->addConnection(ConnectionUtil::getUri("c")->__toString());
$this->assertEquals(1, $this->object->count());
$this->object->rollbackTransaction();
}
@@ -156,22 +157,22 @@ public function testCommitWithNoTransaction()
$this->expectException(TransactionException::class);
$this->expectExceptionMessage("There is no Active Transaction");
- $this->object->addConnection("sqlite:///tmp/d.db");
+ $this->object->addConnection(ConnectionUtil::getUri("d")->__toString());
$this->assertEquals(1, $this->object->count());
$this->object->commitTransaction();
}
public function testTransaction()
{
- $dbDrive1 = Factory::getDbInstance("sqlite:///tmp/a.db");
- $dbDrive2 = Factory::getDbInstance("sqlite:///tmp/b.db");
+ $dbDrive1 = ConnectionUtil::getConnection("a");
+ $dbDrive2 = ConnectionUtil::getConnection("b");
$dbDrive1->execute('create table users1 (
- id integer primary key autoincrement,
+ id integer primary key auto_increment,
name varchar(45));'
);
$dbDrive2->execute('create table users2 (
- id integer primary key autoincrement,
+ id integer primary key auto_increment,
name varchar(45));'
);
diff --git a/tests/UpdateQueryTest.php b/tests/UpdateQueryTest.php
index 5e1be53..09a76c1 100644
--- a/tests/UpdateQueryTest.php
+++ b/tests/UpdateQueryTest.php
@@ -2,13 +2,18 @@
namespace Tests;
+use ByJG\AnyDataset\Db\Factory;
use ByJG\AnyDataset\Db\Helpers\DbMysqlFunctions;
use ByJG\AnyDataset\Db\Helpers\DbPgsqlFunctions;
use ByJG\AnyDataset\Db\Helpers\DbSqliteFunctions;
+use ByJG\AnyDataset\Db\PdoMysql;
+use ByJG\AnyDataset\Db\PdoObj;
use ByJG\MicroOrm\Exception\InvalidArgumentException;
+use ByJG\MicroOrm\Query;
use ByJG\MicroOrm\SqlObject;
use ByJG\MicroOrm\SqlObjectEnum;
use ByJG\MicroOrm\UpdateQuery;
+use ByJG\Util\Uri;
use PHPUnit\Framework\TestCase;
class UpdateQueryTest extends TestCase
@@ -144,6 +149,58 @@ public function testUpdateJoinMySQl()
);
}
+ public function testUpdateJoinMySQlAlias()
+ {
+ $this->object->table('test');
+ $this->object->join('table2', 't2.id = test.id', 't2');
+ $this->object->set('fld1', 'A');
+ $this->object->set('fld2', 'B');
+ $this->object->set('fld3', 'C');
+ $this->object->where('fld1 = :id', ['id' => 10]);
+
+ $sqlObject = $this->object->build(new DbMysqlFunctions());
+ $this->assertEquals(
+ new SqlObject(
+ 'UPDATE `test` INNER JOIN `table2` AS t2 ON t2.id = test.id SET `fld1` = :fld1 , `fld2` = :fld2 , `fld3` = :fld3 WHERE fld1 = :id',
+ ['id' => 10, 'fld1' => 'A', 'fld2' => 'B', 'fld3' => 'C'],
+ SqlObjectEnum::UPDATE
+ ),
+ $sqlObject
+ );
+ }
+
+ public function testUpdateJoinMySQlSubqueryAlias()
+ {
+ $query = Query::getInstance()
+ ->table('table2')
+ ->where('id = 10');
+ $this->object->table('test');
+ $this->object->join($query, 't2.id = test.id', 't2');
+ $this->object->set('fld1', 'A');
+ $this->object->set('fld2', 'B');
+ $this->object->set('fld3', 'C');
+ $this->object->where('fld1 = :id', ['id' => 10]);
+
+
+ $dbDriver = new class extends PdoMysql
+ {
+ public function __construct()
+ {
+ $this->pdoObj = new PdoObj(new Uri('mysql://root:root@localhost/test'));
+ }
+ };
+
+ $sqlObject = $this->object->build($dbDriver);
+ $this->assertEquals(
+ new SqlObject(
+ 'UPDATE `test` INNER JOIN (SELECT * FROM table2 WHERE id = 10) AS t2 ON t2.id = test.id SET `fld1` = :fld1 , `fld2` = :fld2 , `fld3` = :fld3 WHERE fld1 = :id',
+ ['id' => 10, 'fld1' => 'A', 'fld2' => 'B', 'fld3' => 'C'],
+ SqlObjectEnum::UPDATE
+ ),
+ $sqlObject
+ );
+ }
+
public function testUpdateJoinPostgres()
{
$this->object->table('test');