diff --git a/src/Libraries/Database/Adapters/Idiorm/Statements/Join.php b/src/Libraries/Database/Adapters/Idiorm/Statements/Join.php index c467c36ea..0835c5ed1 100644 --- a/src/Libraries/Database/Adapters/Idiorm/Statements/Join.php +++ b/src/Libraries/Database/Adapters/Idiorm/Statements/Join.php @@ -9,15 +9,16 @@ * @author Arman Ag. * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) * @link http://quantum.softberg.org/ - * @since 2.9.8 + * @since 2.9.9 */ namespace Quantum\Libraries\Database\Adapters\Idiorm\Statements; -use Quantum\Libraries\Database\Exceptions\DatabaseException; use Quantum\Libraries\Database\Adapters\Idiorm\IdiormPatch; use Quantum\Libraries\Database\Contracts\DbalInterface; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\Exceptions\ModelException; +use Quantum\App\Exceptions\BaseException; use Quantum\Model\QtModel; /** @@ -29,7 +30,7 @@ trait Join /** * @inheritDoc - * @throws DatabaseException + * @throws BaseException */ public function join(string $table, array $constraint, ?string $tableAlias = null): DbalInterface { @@ -39,7 +40,7 @@ public function join(string $table, array $constraint, ?string $tableAlias = nul /** * @inheritDoc - * @throws DatabaseException + * @throws BaseException */ public function innerJoin(string $table, array $constraint, ?string $tableAlias = null): DbalInterface { @@ -49,7 +50,7 @@ public function innerJoin(string $table, array $constraint, ?string $tableAlias /** * @inheritDoc - * @throws DatabaseException + * @throws BaseException */ public function leftJoin(string $table, array $constraint, ?string $tableAlias = null): DbalInterface { @@ -59,7 +60,7 @@ public function leftJoin(string $table, array $constraint, ?string $tableAlias = /** * @inheritDoc - * @throws DatabaseException + * @throws BaseException */ public function rightJoin(string $table, array $constraint, ?string $tableAlias = null): DbalInterface { @@ -69,65 +70,97 @@ public function rightJoin(string $table, array $constraint, ?string $tableAlias /** * @inheritDoc - * @throws DatabaseException + * @throws BaseException * @throws ModelException */ - public function joinTo(QtModel $modelToJoin, bool $switch = true): DbalInterface + public function joinTo(QtModel $relatedModel, bool $switch = true): DbalInterface { - $foreignKeys = $modelToJoin->relations(); - $relatedModelName = $this->getModelName(); + $relation = $this->getValidatedRelation($relatedModel); - if (!isset($foreignKeys[$relatedModelName])) { - throw ModelException::wrongRelation(get_class($modelToJoin), $relatedModelName); + switch ($relation['type']) { + case Relation::HAS_ONE: + case Relation::HAS_MANY: + $this->applyHasRelation($relatedModel, $relation); + break; + + case Relation::BELONGS_TO: + $this->applyBelongsTo($relatedModel, $relation); + break; + + default: + throw ModelException::unsupportedRelationType($relation['type']); + } + + if ($switch) { + $this->modelName = get_class($relatedModel); + $this->table = $relatedModel->table; + $this->idColumn = $relatedModel->idColumn; + $this->foreignKeys = $relatedModel->relations(); } - $this->getOrmModel()->join($modelToJoin->table, + return $this; + } + + /** + * @param QtModel $relatedModel + * @param array $relation + * @return void + * @throws BaseException + */ + protected function applyHasRelation(QtModel $relatedModel, array $relation): void + { + $this->getOrmModel()->join( + $relatedModel->table, [ - $modelToJoin->table . '.' . $foreignKeys[$relatedModelName]['foreign_key'], + $relatedModel->table . '.' . $relation['foreign_key'], '=', - $this->table . '.' . $foreignKeys[$relatedModelName]['local_key'] + $this->table . '.' . $relation['local_key'] ] ); + } - if ($switch) { - $this->modelName = get_class($modelToJoin); - $this->table = $modelToJoin->table; - $this->idColumn = $modelToJoin->idColumn; - $this->foreignKeys = $foreignKeys; - } - - return $this; + /** + * @param QtModel $relatedModel + * @param array $relation + * @return void + * @throws BaseException + */ + protected function applyBelongsTo(QtModel $relatedModel, array $relation): void + { + $this->getOrmModel()->join( + $relatedModel->table, + [ + $relatedModel->table . '.' . $relation['local_key'], + '=', + $this->table . '.' . $relation['foreign_key'] + ] + ); } /** - * @inheritDoc - * @throws DatabaseException + * @param QtModel $modelToJoin + * @return array * @throws ModelException */ - public function joinThrough(QtModel $modelToJoin, bool $switch = true): DbalInterface + private function getValidatedRelation(QtModel $modelToJoin): array { - $foreignKeys = $this->getForeignKeys(); + $relations = $this->getForeignKeys(); $relatedModelName = get_class($modelToJoin); - if (!isset($foreignKeys[$relatedModelName])) { - throw ModelException::wrongRelation($relatedModelName, $this->getModelName()); + if (!isset($relations[$relatedModelName])) { + throw ModelException::wrongRelation($this->getModelName(), $relatedModelName); } - $this->getOrmModel()->join($modelToJoin->table, - [ - $modelToJoin->table . '.' . $foreignKeys[$relatedModelName]['local_key'], - '=', - $this->table . '.' . $foreignKeys[$relatedModelName]['foreign_key'], - ] - ); + $relation = $relations[$relatedModelName]; - if ($switch) { - $this->modelName = get_class($modelToJoin); - $this->table = $modelToJoin->table; - $this->idColumn = $modelToJoin->idColumn; - $this->foreignKeys = $modelToJoin->relations(); + if (empty($relation['type'])) { + throw ModelException::relationTypeMissing($this->getModelName(), $relatedModelName); } - return $this; + if (empty($relation['foreign_key']) || empty($relation['local_key'])) { + throw ModelException::missingRelationKeys($this->getModelName(), $relatedModelName); + } + + return $relation; } } \ No newline at end of file diff --git a/src/Libraries/Database/Adapters/Sleekdb/SleekDbal.php b/src/Libraries/Database/Adapters/Sleekdb/SleekDbal.php index 10387e98e..4f0464fab 100644 --- a/src/Libraries/Database/Adapters/Sleekdb/SleekDbal.php +++ b/src/Libraries/Database/Adapters/Sleekdb/SleekDbal.php @@ -291,6 +291,7 @@ public function updateOrmModel(?array $modelData) /** * Deletes the table and the data + * @throws BaseException * @throws DatabaseException * @throws IOException * @throws InvalidArgumentException @@ -304,11 +305,12 @@ public function deleteTable() /** * Gets the query builder object * @return QueryBuilder + * @throws BaseException * @throws DatabaseException - * @throws ModelException * @throws IOException * @throws InvalidArgumentException * @throws InvalidConfigurationException + * @throws ModelException */ public function getBuilder(): QueryBuilder { diff --git a/src/Libraries/Database/Adapters/Sleekdb/Statements/Join.php b/src/Libraries/Database/Adapters/Sleekdb/Statements/Join.php index 1689c9347..fce6f03e2 100644 --- a/src/Libraries/Database/Adapters/Sleekdb/Statements/Join.php +++ b/src/Libraries/Database/Adapters/Sleekdb/Statements/Join.php @@ -9,7 +9,7 @@ * @author Arman Ag. * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) * @link http://quantum.softberg.org/ - * @since 2.9.8 + * @since 2.9.9 */ namespace Quantum\Libraries\Database\Adapters\Sleekdb\Statements; @@ -17,6 +17,7 @@ use Quantum\Libraries\Database\Adapters\Sleekdb\SleekDbal; use Quantum\Libraries\Database\Contracts\DbalInterface; use SleekDB\Exceptions\InvalidArgumentException; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\Exceptions\ModelException; use Quantum\Model\QtModel; use SleekDB\QueryBuilder; @@ -32,33 +33,13 @@ trait Join * @inheritDoc */ public function joinTo(QtModel $model, bool $switch = true): DbalInterface - { - $this->addJoin(__FUNCTION__, $model, $switch); - return $this; - } - - /** - * @inheritDoc - */ - public function joinThrough(QtModel $model, bool $switch = true): DbalInterface - { - $this->addJoin(__FUNCTION__, $model, $switch); - return $this; - } - - /** - * Adds join - * @param string $type - * @param QtModel $model - * @param bool $switch - */ - private function addJoin(string $type, QtModel $model, bool $switch = true) { $this->joins[] = [ - 'type' => $type, 'model' => serialize($model), 'switch' => $switch, ]; + + return $this; } /** @@ -67,7 +48,7 @@ private function addJoin(string $type, QtModel $model, bool $switch = true) */ private function applyJoins() { - if (isset($this->joins[0])) { + if (!empty($this->joins)) { $this->applyJoin($this->queryBuilder, $this, $this->joins[0]); } } @@ -85,19 +66,19 @@ private function applyJoin(QueryBuilder $queryBuilder, SleekDbal $currentItem, a { $modelToJoin = unserialize($nextItem['model']); $switch = $nextItem['switch']; - $joinType = $nextItem['type']; - $queryBuilder->join(function ($item) use ($currentItem, $modelToJoin, $switch, $joinType, $level) { + $queryBuilder->join(function ($item) use ($currentItem, $modelToJoin, $switch, $level) { - $sleekModel = new self($modelToJoin->table, get_class($modelToJoin), $modelToJoin->idColumn, $modelToJoin->relations()); + $sleekModel = new self( + $modelToJoin->table, + get_class($modelToJoin), + $modelToJoin->idColumn, + $modelToJoin->relations() + ); $newQueryBuilder = $sleekModel->getOrmModel()->createQueryBuilder(); - if ($joinType == self::JOINTO) { - $this->applyJoinTo($newQueryBuilder, $modelToJoin, $currentItem, $item); - } else if ($joinType == self::JOINTHROUGH) { - $this->applyJoinThrough($newQueryBuilder, $modelToJoin, $currentItem, $item); - } + $this->applyJoinTo($newQueryBuilder, $modelToJoin, $currentItem, $item); if ($switch && isset($this->joins[$level])) { $this->applyJoin($newQueryBuilder, $sleekModel, $this->joins[$level], $level + 1); @@ -117,52 +98,95 @@ private function applyJoin(QueryBuilder $queryBuilder, SleekDbal $currentItem, a /** * Apply join condition for JOINTO type * @param QueryBuilder $queryBuilder - * @param QtModel $modelToJoin - * @param SleekDbal $currentItem - * @param array $item + * @param QtModel $relatedModel + * @param SleekDbal $currentModel + * @param array $currentItem * @return void * @throws InvalidArgumentException * @throws ModelException */ - private function applyJoinTo(QueryBuilder $queryBuilder, QtModel $modelToJoin, SleekDbal $currentItem, array $item): void + private function applyJoinTo(QueryBuilder $queryBuilder, QtModel $relatedModel, SleekDbal $currentModel, array $currentItem): void { - $foreignKeys = $modelToJoin->relations(); - $relatedModelName = $currentItem->getModelName(); + $relation = $this->getValidatedRelation($currentModel, $relatedModel); - if (!isset($foreignKeys[$relatedModelName])) { - throw ModelException::wrongRelation(get_class($modelToJoin), $relatedModelName); + switch ($relation['type']) { + case Relation::HAS_ONE: + case Relation::HAS_MANY: + $this->applyHasRelation($queryBuilder, $currentItem, $relation); + break; + + case Relation::BELONGS_TO: + $this->applyBelongsTo($queryBuilder, $currentItem, $relation, $currentModel); + break; + + default: + throw ModelException::unsupportedRelationType($relation['type']); } + } + /** + * @param QueryBuilder $queryBuilder + * @param array $currentItem + * @param array $relation + * @return void + * @throws InvalidArgumentException + */ + private function applyHasRelation(QueryBuilder $queryBuilder, array $currentItem, array $relation): void + { $queryBuilder->where([ - $foreignKeys[$relatedModelName]['foreign_key'], + $relation['foreign_key'], '=', - $item[$foreignKeys[$relatedModelName]['local_key']] + $currentItem[$relation['local_key']] ]); } /** - * Apply join condition for JOINTHROUGH type * @param QueryBuilder $queryBuilder - * @param QtModel $modelToJoin - * @param SleekDbal $currentItem - * @param array $item + * @param array $currentItem + * @param array $relation + * @param SleekDbal $currentModel * @return void - * @throws ModelException * @throws InvalidArgumentException + * @throws ModelException */ - private function applyJoinThrough(QueryBuilder $queryBuilder, QtModel $modelToJoin, SleekDbal $currentItem, array $item): void + private function applyBelongsTo(QueryBuilder $queryBuilder, array $currentItem, array $relation, SleekDbal $currentModel): void { - $foreignKeys = $currentItem->getForeignKeys(); - $relatedModelName = get_class($modelToJoin); - - if (!isset($foreignKeys[$relatedModelName])) { - throw ModelException::wrongRelation($relatedModelName, $currentItem->getModelName()); + if (!isset($currentItem[$relation['foreign_key']])) { + throw ModelException::missingForeignKeyValue($currentModel->getModelName(), $relation['foreign_key']); } $queryBuilder->where([ - $foreignKeys[$relatedModelName]['local_key'], + $relation['local_key'], '=', - $item[$foreignKeys[$relatedModelName]['foreign_key']] + $currentItem[$relation['foreign_key']] ]); } + + /** + * @param SleekDbal $currentModel + * @param QtModel $relatedModel + * @return array + * @throws ModelException + */ + private function getValidatedRelation(SleekDbal $currentModel, QtModel $relatedModel): array + { + $relations = $currentModel->getForeignKeys(); + $relatedModelName = get_class($relatedModel); + + if (!isset($relations[$relatedModelName])) { + throw ModelException::wrongRelation($currentModel->getModelName(), $relatedModelName); + } + + $relation = $relations[$relatedModelName]; + + if (empty($relation['type'])) { + throw ModelException::relationTypeMissing($currentModel->getModelName(), $relatedModelName); + } + + if (empty($relation['foreign_key']) || empty($relation['local_key'])) { + throw ModelException::missingRelationKeys($currentModel->getModelName(), $relatedModelName); + } + + return $relation; + } } \ No newline at end of file diff --git a/src/Libraries/Database/Adapters/Sleekdb/Statements/Result.php b/src/Libraries/Database/Adapters/Sleekdb/Statements/Result.php index fd6930d87..c093870c8 100644 --- a/src/Libraries/Database/Adapters/Sleekdb/Statements/Result.php +++ b/src/Libraries/Database/Adapters/Sleekdb/Statements/Result.php @@ -9,7 +9,7 @@ * @author Arman Ag. * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) * @link http://quantum.softberg.org/ - * @since 2.9.7 + * @since 2.9.9 */ namespace Quantum\Libraries\Database\Adapters\Sleekdb\Statements; @@ -19,6 +19,7 @@ use SleekDB\Exceptions\InvalidConfigurationException; use SleekDB\Exceptions\InvalidArgumentException; use Quantum\Model\Exceptions\ModelException; +use Quantum\App\Exceptions\BaseException; use SleekDB\Exceptions\IOException; /** @@ -38,9 +39,7 @@ public function get(): array try { return array_map(function ($element) { $item = clone $this; - $item->data = $element; - $item->modifiedFields = $element; - $item->isNew = false; + $item->updateOrmModel($element); return $item; }, $this->getBuilder()->getQuery()->fetch()); } finally { @@ -49,18 +48,14 @@ public function get(): array } /** - * - * @return DbalInterface + * @inheritDoc + * @throws BaseException * @throws DatabaseException * @throws IOException * @throws InvalidArgumentException * @throws InvalidConfigurationException * @throws ModelException */ - - /** - * @inheritDoc - */ public function findOne(int $id): DbalInterface { try { @@ -75,6 +70,12 @@ public function findOne(int $id): DbalInterface /** * @inheritDoc + * @throws BaseException + * @throws DatabaseException + * @throws IOException + * @throws InvalidArgumentException + * @throws InvalidConfigurationException + * @throws ModelException */ public function findOneBy(string $column, $value): DbalInterface { @@ -90,6 +91,12 @@ public function findOneBy(string $column, $value): DbalInterface /** * @inheritDoc + * @throws BaseException + * @throws DatabaseException + * @throws IOException + * @throws InvalidArgumentException + * @throws InvalidConfigurationException + * @throws ModelException */ public function first(): DbalInterface { @@ -106,10 +113,11 @@ public function first(): DbalInterface /** * @inheritDoc * @throws DatabaseException - * @throws ModelException * @throws IOException * @throws InvalidArgumentException * @throws InvalidConfigurationException + * @throws ModelException + * @throws BaseException */ public function count(): int { diff --git a/src/Libraries/Database/Contracts/DbalInterface.php b/src/Libraries/Database/Contracts/DbalInterface.php index 1ef2239ee..20beb8180 100644 --- a/src/Libraries/Database/Contracts/DbalInterface.php +++ b/src/Libraries/Database/Contracts/DbalInterface.php @@ -9,7 +9,7 @@ * @author Arman Ag. * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) * @link http://quantum.softberg.org/ - * @since 2.9.7 + * @since 2.9.9 */ namespace Quantum\Libraries\Database\Contracts; @@ -186,14 +186,6 @@ public function deleteMany(): bool; */ public function joinTo(QtModel $model, bool $switch = true): DbalInterface; - /** - * Joins through connector model - * @param QtModel $model - * @param bool $switch - * @return DbalInterface - */ - public function joinThrough(QtModel $model, bool $switch = true): DbalInterface; - /** * Checks if the given column is NULL * @param string $column diff --git a/src/Libraries/Database/Enums/Relation.php b/src/Libraries/Database/Enums/Relation.php new file mode 100644 index 000000000..014231180 --- /dev/null +++ b/src/Libraries/Database/Enums/Relation.php @@ -0,0 +1,26 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.9 + */ + +namespace Quantum\Libraries\Database\Enums; + +/** + * Class Relation + * @package Quantum\Libraries\Database + */ +class Relation { + const HAS_ONE = 'hasOne'; + const HAS_MANY = 'hasMany'; + const BELONGS_TO = 'belongsTo'; + const BELONGS_TO_MANY = 'belongsToMany'; +} \ No newline at end of file diff --git a/src/Model/Enums/ExceptionMessages.php b/src/Model/Enums/ExceptionMessages.php index 1a176670f..d15460b3c 100644 --- a/src/Model/Enums/ExceptionMessages.php +++ b/src/Model/Enums/ExceptionMessages.php @@ -25,4 +25,12 @@ final class ExceptionMessages extends BaseExceptionMessages const INAPPROPRIATE_MODEL_PROPERTY = 'Inappropriate property `{%1}` for fillable object'; const WRONG_RELATION = 'The model `{%1}` does not define relation with `{%2}`'; + + const RELATION_TYPE_MISSING = 'Relation type is missing for model `{%1}` with related model `{%2}`'; + + const MISSING_RELATION_KEYS = 'Relation keys `foreign_key` or `local_key` are missing for model `{%1}` with related model `{%2}`'; + + const MISSING_FOREIGN_KEY = 'Foreign key `{%1}` is missing in model `{%2}`'; + + const UNSUPPORTED_RELATION = 'Relation type `{%1}` is not supported'; } \ No newline at end of file diff --git a/src/Model/Exceptions/ModelException.php b/src/Model/Exceptions/ModelException.php index e3e275b2e..054d0268f 100644 --- a/src/Model/Exceptions/ModelException.php +++ b/src/Model/Exceptions/ModelException.php @@ -30,7 +30,10 @@ class ModelException extends BaseException */ public static function inappropriateProperty(string $name): ModelException { - return new static(_message(ExceptionMessages::INAPPROPRIATE_MODEL_PROPERTY, $name), E_WARNING); + return new static( + _message(ExceptionMessages::INAPPROPRIATE_MODEL_PROPERTY, $name), + E_WARNING + ); } /** @@ -40,6 +43,60 @@ public static function inappropriateProperty(string $name): ModelException */ public static function wrongRelation(string $modelName, string $tableName): ModelException { - return new static(_message(ExceptionMessages::WRONG_RELATION, [$modelName, $tableName]), E_ERROR); + return new static( + _message(ExceptionMessages::WRONG_RELATION, [$modelName, $tableName]), + E_ERROR + ); } -} + + /** + * @param string $modelName + * @param string $relatedModelName + * @return ModelException + */ + public static function relationTypeMissing(string $modelName, string $relatedModelName): ModelException + { + return new static( + _message(ExceptionMessages::RELATION_TYPE_MISSING, [$modelName, $relatedModelName]), + E_ERROR + ); + } + + /** + * @param string $modelName + * @param string $relatedModelName + * @return ModelException + */ + public static function missingRelationKeys(string $modelName, string $relatedModelName): ModelException + { + return new static( + _message(ExceptionMessages::MISSING_RELATION_KEYS, [$modelName, $relatedModelName]), + E_ERROR + ); + } + + /** + * @param string $modelName + * @param string $foreignKey + * @return ModelException + */ + public static function missingForeignKeyValue(string $modelName, string $foreignKey): ModelException + { + return new static( + _message(ExceptionMessages::MISSING_FOREIGN_KEY, [$foreignKey, $modelName]), + E_ERROR + ); + } + + /** + * @param string $relationType + * @return ModelException + */ + public static function unsupportedRelationType(string $relationType): ModelException + { + return new static( + _message(ExceptionMessages::UNSUPPORTED_RELATION, [$relationType]), + E_ERROR + ); + } +} \ No newline at end of file diff --git a/src/Model/Helpers/model.php b/src/Model/Helpers/model.php index dbd7b9f73..d3c36a0fe 100644 --- a/src/Model/Helpers/model.php +++ b/src/Model/Helpers/model.php @@ -9,11 +9,12 @@ * @author Arman Ag. * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) * @link http://quantum.softberg.org/ - * @since 2.9.8 + * @since 2.9.9 */ use Quantum\Libraries\Database\Contracts\DbalInterface; use Quantum\Model\Exceptions\ModelException; +use Quantum\App\Exceptions\BaseException; use Quantum\Model\Factories\ModelFactory; use Quantum\Model\QtModel; @@ -22,6 +23,7 @@ * @param string $modelClass * @return QtModel * @throws ModelException + * @throws BaseException */ function model(string $modelClass): QtModel { @@ -41,8 +43,8 @@ function dynamicModel( string $table, string $modelName = '@anonymous', string $idColumn = 'id', - array $foreignKeys = [], - array $hidden = [] + array $foreignKeys = [], + array $hidden = [] ): QtModel { return ModelFactory::createDynamicModel($table, $modelName, $idColumn, $foreignKeys, $hidden); @@ -53,17 +55,18 @@ function dynamicModel( * @param DbalInterface $ormInstance * @param string $modelClass * @return QtModel + * @throws BaseException */ function wrapToModel(DbalInterface $ormInstance, string $modelClass): QtModel { if (!class_exists($modelClass)) { - throw new InvalidArgumentException("Model class '$modelClass' does not exist."); + throw ModelException::notFound('Model class', $modelClass); } $model = new $modelClass(); if (!$model instanceof QtModel) { - throw new InvalidArgumentException("Model class '$modelClass' must extend QtModel."); + throw ModelException::notInstanceOf($modelClass, QtModel::class); } $model->setOrmInstance($ormInstance); diff --git a/src/Model/ModelCollection.php b/src/Model/ModelCollection.php index 86b019b2b..908cc7490 100644 --- a/src/Model/ModelCollection.php +++ b/src/Model/ModelCollection.php @@ -9,12 +9,13 @@ * @author Arman Ag. * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) * @link http://quantum.softberg.org/ - * @since 2.9.6 + * @since 2.9.9 */ namespace Quantum\Model; -use InvalidArgumentException; +use Quantum\Model\Exceptions\ModelException; +use Quantum\App\Exceptions\BaseException; use IteratorAggregate; use Countable; use Generator; @@ -43,6 +44,7 @@ class ModelCollection implements Countable, IteratorAggregate /** * @param iterable $models + * @throws BaseException */ public function __construct(iterable $models = []) { @@ -57,6 +59,7 @@ public function __construct(iterable $models = []) * Add a model to the collection * @param QtModel $model * @return self + * @throws BaseException */ public function add(QtModel $model): self { @@ -77,6 +80,7 @@ public function add(QtModel $model): self * Remove a model from the collection * @param QtModel $model * @return self + * @throws BaseException */ public function remove(QtModel $model): self { @@ -94,6 +98,7 @@ public function remove(QtModel $model): self /** * Get all models as an array * @return QtModel[] + * @throws BaseException */ public function all(): array { @@ -104,6 +109,7 @@ public function all(): array /** * Get the count of models in the collection * @return int + * @throws BaseException */ public function count(): int { @@ -114,6 +120,7 @@ public function count(): int /** * Get the first model in the collection * @return QtModel|null + * @throws BaseException */ public function first(): ?QtModel { @@ -127,6 +134,7 @@ public function first(): ?QtModel /** * Get the last model in the collection * @return QtModel|null + * @throws BaseException */ public function last(): ?QtModel { @@ -146,7 +154,7 @@ public function isEmpty(): bool /** * Get an iterator for the collection * @return Generator - * @throws InvalidArgumentException + * @throws BaseException */ public function getIterator(): Generator { @@ -164,7 +172,7 @@ public function getIterator(): Generator /** * Process models from original source into the internal array - * @throws InvalidArgumentException + * @throws BaseException */ private function processModels() { @@ -184,13 +192,14 @@ private function processModels() /** * Validate that an item is a QtModel instance - * @param mixed $model - * @throws InvalidArgumentException + * @param $model + * @return void + * @throws BaseException */ private function validateModel($model): void { if (!$model instanceof QtModel) { - throw new InvalidArgumentException('All items must be instances of QtModel.'); + throw ModelException::notInstanceOf(get_class($model), QtModel::class); } } } \ No newline at end of file diff --git a/tests/Unit/Libraries/Database/Adapters/DatabaseSeeder.php b/tests/Unit/Libraries/Database/Adapters/DatabaseSeeder.php index a06cbf78a..1c5d547fb 100644 --- a/tests/Unit/Libraries/Database/Adapters/DatabaseSeeder.php +++ b/tests/Unit/Libraries/Database/Adapters/DatabaseSeeder.php @@ -2,9 +2,19 @@ namespace Quantum\Tests\Unit\Libraries\Database\Adapters; +use Quantum\Tests\_root\shared\Models\TestUserProfessionModel; +use Quantum\Tests\_root\shared\Models\TestUserMeetingModel; +use Quantum\Tests\_root\shared\Models\TestUserEventModel; +use Quantum\Tests\_root\shared\Models\TestProfileModel; +use Quantum\Tests\_root\shared\Models\TestTicketModel; +use Quantum\Tests\_root\shared\Models\TestEventModel; +use Quantum\Tests\_root\shared\Models\TestNotesModel; +use Quantum\Tests\_root\shared\Models\TestUserModel; + class DatabaseSeeder { private $userModel; + private $profileModel; private $userProfessionModel; private $eventModel; private $userEventModel; @@ -12,20 +22,22 @@ class DatabaseSeeder private $ticketModel; private $noteModel; - public function __construct(string $dbalClass) + public function __construct() { - $this->userModel = new $dbalClass('users'); - $this->userProfessionModel = new $dbalClass('user_professions'); - $this->eventModel = new $dbalClass('events'); - $this->userEventModel = new $dbalClass('user_events'); - $this->userMeetingModel = new $dbalClass('user_meetings'); - $this->ticketModel = new $dbalClass('tickets'); - $this->noteModel = new $dbalClass('notes'); + $this->userModel = model(TestUserModel::class); + $this->profileModel = model(TestProfileModel::class); + $this->userProfessionModel = model(TestUserProfessionModel::class); + $this->eventModel = model(TestEventModel::class); + $this->userEventModel = model(TestUserEventModel::class); + $this->userMeetingModel = model(TestUserMeetingModel::class); + $this->ticketModel = model(TestTicketModel::class); + $this->noteModel = model(TestNotesModel::class); } public function seed(): void { $this->seedUsers(); + $this->seedProfiles(); $this->seedUserProfessions(); $this->seedEvents(); $this->seedUserEvents(); @@ -36,91 +48,73 @@ public function seed(): void private function seedUsers(): void { - foreach (TestData::users() as $user) { - $this->userModel->create(); - - foreach ($user as $field => $value) { - $this->userModel->prop($field, $value); - } + foreach (TestData::users() as $userData) { + $this->userModel->create(); + $this->userModel->fillObjectProps($userData); $this->userModel->save(); } } + private function seedProfiles(): void + { + foreach (TestData::profiles() as $profileData) { + $this->profileModel->create(); + $this->profileModel->fillObjectProps($profileData); + $this->profileModel->save(); + } + } + private function seedUserProfessions(): void { - foreach (TestData::userProfessions() as $profession) { + foreach (TestData::userProfessions() as $professionData) { $this->userProfessionModel->create(); - - foreach ($profession as $field => $value) { - $this->userProfessionModel->prop($field, $value); - } - + $this->userProfessionModel->fillObjectProps($professionData); $this->userProfessionModel->save(); } } private function seedEvents(): void { - foreach (TestData::events() as $event) { + foreach (TestData::events() as $eventData) { $this->eventModel->create(); - - foreach ($event as $field => $value) { - $this->eventModel->prop($field, $value); - } - + $this->eventModel->fillObjectProps($eventData); $this->eventModel->save(); } } private function seedUserEvents(): void { - foreach (TestData::userEvents() as $userEvent) { + foreach (TestData::userEvents() as $userEventData) { $this->userEventModel->create(); - - foreach ($userEvent as $field => $value) { - $this->userEventModel->prop($field, $value); - } - + $this->userEventModel->fillObjectProps($userEventData); $this->userEventModel->save(); } } private function seedUserMeetings(): void { - foreach (TestData::userMeetings() as $meeting) { + foreach (TestData::userMeetings() as $meetingData) { $this->userMeetingModel->create(); - - foreach ($meeting as $field => $value) { - $this->userMeetingModel->prop($field, $value); - } - + $this->userMeetingModel->fillObjectProps($meetingData); $this->userMeetingModel->save(); } } private function seedTickets(): void { - foreach (TestData::tickets() as $ticket) { + foreach (TestData::tickets() as $ticketData) { $this->ticketModel->create(); - - foreach ($ticket as $field => $value) { - $this->ticketModel->prop($field, $value); - } - + $this->ticketModel->fillObjectProps($ticketData); $this->ticketModel->save(); } } private function seedNotes(): void { - foreach (TestData::notes() as $note) { + foreach (TestData::notes() as $noteData) { $this->noteModel->create(); - - foreach ($note as $field => $value) { - $this->noteModel->prop($field, $value); - } - + $this->noteModel->fillObjectProps($noteData); $this->noteModel->save(); } } diff --git a/tests/Unit/Libraries/Database/Adapters/Idiorm/IdiormDbalTestCase.php b/tests/Unit/Libraries/Database/Adapters/Idiorm/IdiormDbalTestCase.php index 2a6dae7f8..855ebce1e 100644 --- a/tests/Unit/Libraries/Database/Adapters/Idiorm/IdiormDbalTestCase.php +++ b/tests/Unit/Libraries/Database/Adapters/Idiorm/IdiormDbalTestCase.php @@ -32,7 +32,7 @@ public function setUp(): void $this->createTables(); - $seeder = new DatabaseSeeder(IdiormDbal::class); + $seeder = new DatabaseSeeder(); $seeder->seed(); } @@ -46,6 +46,8 @@ public function tearDown(): void private function createTables() { + $this->createUserTable(); + $this->createProfileTable(); $this->createUserTable(); $this->createUserProfessionTable(); $this->createEventsTable(); @@ -66,6 +68,16 @@ private function createUserTable() { IdiormDbal::execute("CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, + email VARCHAR(255), + password VARCHAR(255) + )"); + } + + private function createProfileTable() + { + IdiormDbal::execute("CREATE TABLE IF NOT EXISTS profiles ( + id INTEGER PRIMARY KEY, + user_id INTEGER(11), firstname VARCHAR(255), lastname VARCHAR(255), age INTEGER(11), diff --git a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/CriteriaIdiormTest.php b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/CriteriaIdiormTest.php index 8f9a03e62..9ea41d890 100644 --- a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/CriteriaIdiormTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/CriteriaIdiormTest.php @@ -10,9 +10,9 @@ class CriteriaIdiormTest extends IdiormDbalTestCase public function testIdiormCriteriaEquals() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->criteria('firstname', '=', 'John')->first(); + $user = $userProfileModel->criteria('firstname', '=', 'John')->first(); $this->assertEquals('John', $user->prop('firstname')); @@ -21,48 +21,48 @@ public function testIdiormCriteriaEquals() public function testIdiormCriteriaNotEquals() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->criteria('firstname', '!=', 'John')->first(); + $user = $userProfileModel->criteria('firstname', '!=', 'John')->first(); $this->assertEquals('Jane', $user->prop('firstname')); } public function testIdiormCriteriaGreaterAndGreaterOrEqual() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('age', '>', 35)->get(); + $users = $userProfileModel->criteria('age', '>', 35)->get(); $this->assertCount(1, $users); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('age', '>=', 35)->get(); + $users = $userProfileModel->criteria('age', '>=', 35)->get(); $this->assertCount(2, $users); } public function testIdiormCriteriaSmallerAndSmallerOrEqual() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('age', '<', 35)->get(); + $users = $userProfileModel->criteria('age', '<', 35)->get(); $this->assertCount(0, $users); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('age', '<=', 35)->get(); + $users = $userProfileModel->criteria('age', '<=', 35)->get(); $this->assertCount(1, $users); } public function testIdiormCriteriaInAndNotIn() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('age', 'IN', [35, 40, 45])->get(); + $users = $userProfileModel->criteria('age', 'IN', [35, 40, 45])->get(); $this->assertCount(2, $users); @@ -70,9 +70,9 @@ public function testIdiormCriteriaInAndNotIn() $this->assertEquals('Jane', $users[1]->prop('firstname')); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('age', 'NOT IN', [30, 40, 45])->get(); + $users = $userProfileModel->criteria('age', 'NOT IN', [30, 40, 45])->get(); $this->assertCount(1, $users); @@ -81,25 +81,25 @@ public function testIdiormCriteriaInAndNotIn() public function testIdiormCriteriaLikeAndNotLike() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('firstname', 'LIKE', '%Jo%')->get(); + $users = $userProfileModel->criteria('firstname', 'LIKE', '%Jo%')->get(); $this->assertCount(1, $users); $this->assertEquals('John', $users[0]->prop('firstname')); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('firstname', 'LIKE', '%J%')->get(); + $users = $userProfileModel->criteria('firstname', 'LIKE', '%J%')->get(); $this->assertCount(2, $users); $this->assertEquals('Jane', $users[1]->prop('firstname')); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('firstname', 'NOT LIKE', '%Jo%')->get(); + $users = $userProfileModel->criteria('firstname', 'NOT LIKE', '%Jo%')->get(); $this->assertCount(1, $users); @@ -108,15 +108,15 @@ public function testIdiormCriteriaLikeAndNotLike() public function testIdiormCriteriaNullAndNotNull() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('firstname', 'NULL', '')->get(); + $users = $userProfileModel->criteria('firstname', 'NULL', '')->get(); $this->assertCount(0, $users); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->criteria('firstname', 'NOT NULL', '')->get(); + $users = $userProfileModel->criteria('firstname', 'NOT NULL', '')->get(); $this->assertCount(2, $users); @@ -140,14 +140,14 @@ public function testIdiormCriteriaWithFunction() public function testIdiormCriteriaColumnsEqual() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $userModel->join('user_events', ['user_events.user_id', '=', 'users.id']) + $userProfileModel->join('user_events', ['user_events.user_id', '=', 'profiles.user_id']) ->join('events', ['user_events.event_id', '=', 'events.id']) - ->criteria('users.country', '#=#', 'events.country') + ->criteria('profiles.country', '#=#', 'events.country') ->get(); - $expectedQuery = "SELECT * FROM `users` JOIN `user_events` ON `user_events`.`user_id` = `users`.`id` JOIN `events` ON `user_events`.`event_id` = `events`.`id` WHERE users.country = events.country"; + $expectedQuery = "SELECT * FROM `profiles` JOIN `user_events` ON `user_events`.`user_id` = `profiles`.`user_id` JOIN `events` ON `user_events`.`event_id` = `events`.`id` WHERE profiles.country = events.country"; $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); } diff --git a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/JoinIdiormTest.php b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/JoinIdiormTest.php index 1f97acdfe..782a0c9dc 100644 --- a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/JoinIdiormTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/JoinIdiormTest.php @@ -7,12 +7,48 @@ use Quantum\Tests\_root\shared\Models\TestUserMeetingModel; use Quantum\Libraries\Database\Adapters\Idiorm\IdiormDbal; use Quantum\Tests\_root\shared\Models\TestUserEventModel; +use Quantum\Tests\_root\shared\Models\TestProfileModel; use Quantum\Tests\_root\shared\Models\TestTicketModel; +use Quantum\Tests\_root\shared\Models\TestNotesModel; use Quantum\Tests\_root\shared\Models\TestEventModel; use Quantum\Tests\_root\shared\Models\TestUserModel; use Quantum\Model\Exceptions\ModelException; use Quantum\Model\Factories\ModelFactory; use Quantum\Model\ModelCollection; +use Quantum\Model\QtModel; + + +class BrokenProfileMissingTypeModel extends QtModel +{ + public $table = 'profiles'; + + public function relations(): array + { + return [ + TestUserModel::class => [ + // 'type' intentionally missing + 'foreign_key' => 'user_id', + 'local_key' => 'id', + ], + ]; + } +} + +class BrokenProfileUnsupportedRelationModel extends QtModel +{ + public $table = 'profiles'; + + public function relations(): array + { + return [ + TestUserModel::class => [ + 'type' => 'SIDEWAYS', + 'foreign_key' => 'user_id', + 'local_key' => 'id', + ], + ]; + } +} class JoinIdiormTest extends IdiormDbalTestCase { @@ -81,215 +117,305 @@ public function testIdiormLeftJoinAndRightJoin() $this->assertNull($events[count($events) - 1]->prop('id')); } - public function testIdiormJoinTo() + public function testIdiormJoinToHasOne() { $userModel = ModelFactory::get(TestUserModel::class); + $profileModel = ModelFactory::get(TestProfileModel::class); - $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); - - $users = $userModel->select(['users.id' => 'user_id'], - 'firstname', 'user_professions.title') - ->joinTo($userProfessionModel) + $users = $userModel + ->joinTo($profileModel) ->get(); $this->assertInstanceOf(ModelCollection::class, $users); $this->assertCount(2, $users); - $this->assertEquals('Writer', $users->first()->prop('title')); + $user = $users->first()->asArray(); + + $this->assertArrayHasKey('email', $user); - $expectedQuery = "SELECT `users`.`id` AS `user_id`, `firstname`, `user_professions`.`title` + $this->assertArrayHasKey('firstname', $user); + + $this->assertArrayHasKey('lastname', $user); + + $this->assertArrayHasKey('age', $user); + + $this->assertArrayHasKey('country', $user); + + $expectedQuery = "SELECT * FROM `users` - JOIN `user_professions` ON `user_professions`.`user_id` = `users`.`id`"; + JOIN `profiles` ON `profiles`.`user_id` = `users`.`id`"; $expectedQuery = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $expectedQuery)); $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); } - public function testIdiormJoinToFromSameTable() + public function testIdiormJoinToBelongsTo() { $userModel = ModelFactory::get(TestUserModel::class); + $profileModel = ModelFactory::get(TestProfileModel::class); - $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); + $profiles = $profileModel + ->joinTo($userModel) + ->get(); - $userEventModel = ModelFactory::get(TestUserEventModel::class); + $this->assertInstanceOf(ModelCollection::class, $profiles); + + $user = $profiles->first()->asArray(); + + $this->assertArrayHasKey('email', $user); + + $this->assertArrayHasKey('firstname', $user); + + $this->assertArrayHasKey('lastname', $user); + + $this->assertArrayHasKey('age', $user); + + $this->assertArrayHasKey('country', $user); + + $expectedQuery = "SELECT * + FROM `profiles` + JOIN `users` ON `users`.`id` = `profiles`.`user_id`"; + + $expectedQuery = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $expectedQuery)); + + $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); + + $this->assertIsArray($user); + } + + public function testIdiormJoinToFromSameTable() + { + $userModel = ModelFactory::get(TestUserModel::class); + $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); + $userMeetings = ModelFactory::get(TestUserMeetingModel::class); - $users = $userModel->select('users.*', 'user_professions.title', 'user_events.event_id') + $users = $userModel ->joinTo($userProfessionModel, false) - ->joinTo($userEventModel, false) + ->joinTo($userMeetings) ->get(); $this->assertInstanceOf(ModelCollection::class, $users); - $this->assertEquals('Writer', $users->first()->prop('title')); + $user = $users->first()->asArray(); - $this->assertEquals(1, $users->first()->prop('event_id')); + $this->assertArrayHasKey('email', $user); - $query = "SELECT `users`.*, `user_professions`.`title`, `user_events`.`event_id` - FROM `users` - JOIN `user_professions` ON `user_professions`.`user_id` = `users`.`id` - JOIN `user_events` ON `user_events`.`user_id` = `users`.`id`"; + $this->assertArrayHasKey('user_id', $user); + + $this->assertArrayHasKey('title', $user); + + $this->assertArrayHasKey('start_date', $user); + + $this->assertInstanceOf(ModelCollection::class, $users); + + $expectedQuery = "SELECT * + FROM `users` + JOIN `user_professions` ON `user_professions`.`user_id` = `users`.`id` + JOIN `user_meetings` ON `user_meetings`.`user_id` = `users`.`id`"; - $query = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $query)); + $expectedQuery = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $expectedQuery)); - $this->assertEquals($query, IdiormDbal::lastQuery()); + $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); } public function testIdiormJoinToWithTableSwitch() { $userModel = ModelFactory::get(TestUserModel::class); - $meetingModel = ModelFactory::get(TestUserMeetingModel::class); - $ticketModel = ModelFactory::get(TestTicketModel::class); + $noteModel = ModelFactory::get(TestNotesModel::class); $users = $userModel ->joinTo($meetingModel) ->joinTo($ticketModel) + ->joinTo($noteModel) + ->groupBy('users.id') ->get(); $this->assertInstanceOf(ModelCollection::class, $users); - $this->assertEquals('Business planning', $users->first()->prop('title')); + $this->assertCount(2, $users); + + $user = $users->first()->asArray(); + + $this->assertArrayHasKey('email', $user); + + $this->assertArrayHasKey('user_id', $user); + + $this->assertArrayHasKey('title', $user); + + $this->assertArrayHasKey('start_date', $user); + + $this->assertArrayHasKey('meeting_id', $user); + + $this->assertArrayHasKey('type', $user); + + $this->assertArrayHasKey('ticket_id', $user); - $this->assertNull($users->first()->prop('event_id')); + $this->assertArrayHasKey('note', $user); - $query = "SELECT * FROM `users` + $expectedQuery = "SELECT * + FROM `users` JOIN `user_meetings` ON `user_meetings`.`user_id` = `users`.`id` - JOIN `tickets` ON `tickets`.`meeting_id` = `user_meetings`.`id`"; + JOIN `tickets` ON `tickets`.`meeting_id` = `user_meetings`.`id` + JOIN `notes` ON `notes`.`ticket_id` = `tickets`.`id` + GROUP BY `users`.`id`"; - $query = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $query)); + $expectedQuery = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $expectedQuery)); - $this->assertEquals($query, IdiormDbal::lastQuery()); + $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); } - public function testIdiormJoinToAndThrough() + public function testIdiormJoiningViaPivotModel() { $userModel = ModelFactory::get(TestUserModel::class); - $userEventModel = ModelFactory::get(TestUserEventModel::class); - $eventModel = ModelFactory::get(TestEventModel::class); - $users = $userModel->select( - ['users.id' => 'user_id'], - ['events.id' => 'event_id'], - 'firstname', - 'confirmed', - ['events.title' => 'event_title']) + $users = $userModel ->joinTo($userEventModel) - ->joinThrough($eventModel) - ->criteria('user_events.confirmed', '=', 'Yes') - ->orderBy('user_events.created_at', 'desc') + ->joinTo($eventModel) ->get(); - $this->assertInstanceOf(ModelCollection::class, $users); + $this->assertNotEmpty($users); - $this->assertEquals('Yes', $users->first()->prop('confirmed')); + $user = $users->first(); - $this->assertEquals('Music', $users->first()->prop('event_title')); + $this->assertInstanceOf(TestUserModel::class, $user); - $query = "SELECT `users`.`id` AS `user_id`, - `events`.`id` AS `event_id`, - `firstname`, - `confirmed`, - `events`.`title` AS `event_title` - FROM `users` - JOIN `user_events` ON `user_events`.`user_id` = `users`.`id` - JOIN `events` ON `events`.`id` = `user_events`.`event_id` - WHERE `user_events`.`confirmed` = 'Yes' - ORDER BY `user_events`.`created_at` DESC"; + $userRecord = $users->first()->asArray(); - $query = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $query)); + $this->assertArrayHasKey('email', $userRecord); - $this->assertEquals($query, IdiormDbal::lastQuery()); - } + $this->assertArrayHasKey('user_id', $userRecord); - public function testIdiormJoinThroughInverse() - { - $meetingModel = ModelFactory::get(TestUserMeetingModel::class); + $this->assertArrayHasKey('event_id', $userRecord); - $ticketModel = ModelFactory::get(TestTicketModel::class); + $this->assertArrayHasKey('confirmed', $userRecord); + + $this->assertArrayHasKey('title', $userRecord); - $tickets = $ticketModel->joinThrough($meetingModel)->get(); + $this->assertArrayHasKey('country', $userRecord); - $this->assertInstanceOf(ModelCollection::class, $tickets); + $this->assertArrayHasKey('started_at', $userRecord); - $query = "SELECT * FROM `tickets` JOIN `user_meetings` ON `user_meetings`.`id` = `tickets`.`meeting_id`"; + $expectedQuery = "SELECT * + FROM `users` + JOIN `user_events` ON `user_events`.`user_id` = `users`.`id` + JOIN `events` ON `events`.`id` = `user_events`.`event_id`"; - $query = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $query)); + $expectedQuery = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $expectedQuery)); - $this->assertEquals($query, IdiormDbal::lastQuery()); + $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); } - public function testIdiormJoinToAndJoinThrough() + public function testIdiormJoinToWithCriteria() { $userModel = ModelFactory::get(TestUserModel::class); - + $profileModel = ModelFactory::get(TestProfileModel::class); $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); + $meetingModel = ModelFactory::get(TestUserMeetingModel::class); + $ticketModel = ModelFactory::get(TestTicketModel::class); - $userEventModel = ModelFactory::get(TestUserEventModel::class); + $users = $userModel + ->joinTo($profileModel, false) + ->joinTo($userProfessionModel, false) + ->joinTo($meetingModel) + ->joinTo($ticketModel) + ->criteria('email', 'LIKE', '%jane%') + ->groupBy('users.id') + ->get(); - $eventModel = ModelFactory::get(TestEventModel::class); + $this->assertInstanceOf(ModelCollection::class, $users); - $user = $userModel->select( - ['users.id' => 'user_id'], - 'firstname', - ['user_professions.title' => 'profession_title'], - ['events.title' => 'event_title']) - ->joinTo($userProfessionModel, false) - ->joinTo($userEventModel) - ->joinThrough($eventModel) - ->first(); + $this->assertCount(1, $users); + + $expectedQuery = "SELECT * + FROM `users` + JOIN `profiles` ON `profiles`.`user_id` = `users`.`id` + JOIN `user_professions` ON `user_professions`.`user_id` = `users`.`id` + JOIN `user_meetings` ON `user_meetings`.`user_id` = `users`.`id` + JOIN `tickets` ON `tickets`.`meeting_id` = `user_meetings`.`id` + WHERE `users`.`email` + LIKE '%jane%' + GROUP BY `users`.`id`"; + + $expectedQuery = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $expectedQuery)); - $this->assertEquals('John', $user->firstname); + $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); + } + + public function testIdiormSelectFieldsAtJoin() + { + $userModel = ModelFactory::get(TestUserModel::class); + $profileModel = ModelFactory::get(TestProfileModel::class); + $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); + + $users = $userModel + ->joinTo($profileModel, false) + ->joinTo($userProfessionModel) + ->select( + 'firstname', + 'lastname', + 'age', + 'country', + ['user_professions.title' => 'profession'] + ) + ->orderBy('age', 'desc') + ->get(); - $this->assertEquals('Writer', $user->profession_title); + $this->assertInstanceOf(ModelCollection::class, $users); - $this->assertEquals('Dance', $user->event_title); + $this->assertEquals('Writer', $users->first()->prop('profession')); - $query = "SELECT `users`.`id` AS `user_id`, - `firstname`, `user_professions`.`title` AS `profession_title`, - `events`.`title` AS `event_title` - FROM `users` - JOIN `user_professions` ON `user_professions`.`user_id` = `users`.`id` - JOIN `user_events` ON `user_events`.`user_id` = `users`.`id` - JOIN `events` ON `events`.`id` = `user_events`.`event_id` - LIMIT 1"; + $expectedQuery = "SELECT + `firstname`, + `lastname`, + `age`, + `country`, + `user_professions`.`title` AS `profession` + FROM `users` + JOIN `profiles` ON `profiles`.`user_id` = `users`.`id` + JOIN `user_professions` ON `user_professions`.`user_id` = `users`.`id` + ORDER BY `age` DESC"; - $query = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $query)); + $expectedQuery = preg_replace('/[\s\t]+/', ' ', preg_replace('/' . PHP_EOL . '+/', '', $expectedQuery)); - $this->assertEquals($query, IdiormDbal::lastQuery()); + $this->assertEquals($expectedQuery, IdiormDbal::lastQuery()); } - public function testIdiormWrongRelation() + public function testIdiormThrowsExceptionForWrongRelation() { $this->expectException(ModelException::class); - $this->expectExceptionMessage('The model `' . TestTicketModel::class . '` does not define relation with `' . TestEventModel::class . '`'); + $this->expectExceptionMessage('The model `' . TestEventModel::class . '` does not define relation with `' . TestNotesModel::class . '`'); $eventModel = ModelFactory::get(TestEventModel::class); - $ticketModel = ModelFactory::get(TestTicketModel::class); + $ticketModel = ModelFactory::get(TestNotesModel::class); $eventModel->joinTo($ticketModel)->get(); } - public function testIdiormSelectFieldsAtJoin() + public function testIdiormJoinThrowsExceptionWhenRelationKeysMissing() { - $userModel = ModelFactory::get(TestUserModel::class); - - $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); + $this->expectException(ModelException::class); + $this->expectExceptionMessage('Relation type is missing for model `' . BrokenProfileMissingTypeModel::class .'`'); - $users = $userModel - ->joinTo($userProfessionModel, false) - ->select('firstname', 'lastname', 'age', 'country', ['user_professions.title' => 'profession']) - ->orderBy('age', 'desc') + ModelFactory::get(BrokenProfileMissingTypeModel::class) + ->joinTo(ModelFactory::get(TestUserModel::class)) ->get(); + } - $this->assertInstanceOf(ModelCollection::class, $users); + public function testIdiormJoinThrowsExceptionForUnsupportedRelationType() + { + $this->expectException(ModelException::class); + $this->expectExceptionMessage('Relation type `SIDEWAYS` is not supported'); - $this->assertEquals('Writer', $users->first()->prop('profession')); + ModelFactory::get(BrokenProfileUnsupportedRelationModel::class) + ->joinTo(ModelFactory::get(TestUserModel::class)) + ->get(); } } \ No newline at end of file diff --git a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ReducerIdiormTest.php b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ReducerIdiormTest.php index ee8c215bd..309b9e1d8 100644 --- a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ReducerIdiormTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ReducerIdiormTest.php @@ -10,15 +10,15 @@ class ReducerIdiormTest extends IdiormDbalTestCase public function testIdiormSelect() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->select('id', 'age')->first(); + $user = $userProfileModel->select('id', 'age')->first(); $this->assertCount(2, $user->asArray()); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->select('id', ['firstname' => 'name'], ['lastname' => 'surname'])->first(); + $user = $userProfileModel->select('id', ['firstname' => 'name'], ['lastname' => 'surname'])->first(); $this->assertEquals('John', $user->prop('name')); diff --git a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ResultIdiormTest.php b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ResultIdiormTest.php index 54c1bca47..3893810ef 100644 --- a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ResultIdiormTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/ResultIdiormTest.php @@ -10,9 +10,9 @@ class ResultIdiormTest extends IdiormDbalTestCase public function testIdiormGet() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $users = $userModel->get(); + $users = $userProfileModel->get(); $this->assertIsArray($users); @@ -23,9 +23,9 @@ public function testIdiormGet() public function testIdiormFindOne() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->findOne(1); + $user = $userProfileModel->findOne(1); $this->assertEquals('John', $user->prop('firstname')); @@ -34,9 +34,9 @@ public function testIdiormFindOne() public function testIdiormFindOneBy() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->findOneBy('firstname', 'John'); + $user = $userProfileModel->findOneBy('firstname', 'John'); $this->assertEquals('Doe', $user->prop('lastname')); @@ -45,17 +45,17 @@ public function testIdiormFindOneBy() public function testIdiormFirst() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->first(); + $user = $userProfileModel->first(); $this->assertEquals('Doe', $user->prop('lastname')); $this->assertEquals('45', $user->prop('age')); - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->criteria('age', '<', 50)->first(); + $user = $userProfileModel->criteria('age', '<', 50)->first(); $this->assertEquals('John', $user->prop('firstname')); @@ -66,9 +66,9 @@ public function testIdiormFirst() public function testIdiormCount() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $userCount = $userModel->count(); + $userCount = $userProfileModel->count(); $this->assertIsInt($userCount); @@ -77,9 +77,9 @@ public function testIdiormCount() public function testIdiormAsArray() { - $userModel = new IdiormDbal('users'); + $userProfileModel = new IdiormDbal('profiles'); - $user = $userModel->first(); + $user = $userProfileModel->first(); $this->assertIsObject($user); diff --git a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/TransactionIdiormTest.php b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/TransactionIdiormTest.php index 853bcdeb6..03b18d6ba 100644 --- a/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/TransactionIdiormTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Idiorm/Statements/TransactionIdiormTest.php @@ -16,7 +16,7 @@ public function testBeginTransactionAndRollback() { IdiormDbal::beginTransaction(); - IdiormDbal::execute('INSERT INTO users (firstname, lastname, age, country) + IdiormDbal::execute('INSERT INTO profiles (firstname, lastname, age, country) VALUES (:firstname, :lastname, :age, :country)', [ 'firstname' => 'Alice', 'lastname' => 'Smith', @@ -26,7 +26,7 @@ public function testBeginTransactionAndRollback() IdiormDbal::rollback(); - $result = IdiormDbal::query('SELECT * FROM users WHERE firstname = :firstname', ['firstname' => 'Alice']); + $result = IdiormDbal::query('SELECT * FROM profiles WHERE firstname = :firstname', ['firstname' => 'Alice']); $this->assertEmpty($result); } @@ -35,7 +35,7 @@ public function testBeginTransactionAndCommit() { IdiormDbal::beginTransaction(); - IdiormDbal::execute('INSERT INTO users (firstname, lastname, age, country) + IdiormDbal::execute('INSERT INTO profiles (firstname, lastname, age, country) VALUES (:firstname, :lastname, :age, :country)', [ 'firstname' => 'Bob', 'lastname' => 'Marley', @@ -45,7 +45,7 @@ public function testBeginTransactionAndCommit() IdiormDbal::commit(); - $result = IdiormDbal::query('SELECT * FROM users WHERE firstname = :firstname', ['firstname' => 'Bob']); + $result = IdiormDbal::query('SELECT * FROM profiles WHERE firstname = :firstname', ['firstname' => 'Bob']); $this->assertNotEmpty($result); diff --git a/tests/Unit/Libraries/Database/Adapters/Sleekdb/SleekDbalTestCase.php b/tests/Unit/Libraries/Database/Adapters/Sleekdb/SleekDbalTestCase.php index 1f3dbae7a..a0976d78b 100644 --- a/tests/Unit/Libraries/Database/Adapters/Sleekdb/SleekDbalTestCase.php +++ b/tests/Unit/Libraries/Database/Adapters/Sleekdb/SleekDbalTestCase.php @@ -13,6 +13,7 @@ abstract class SleekDbalTestCase extends AppTestCase private $tables = [ 'users', + 'profiles', 'user_professions', 'events', 'user_events', @@ -35,7 +36,7 @@ public function setUp(): void SleekDbal::connect(config()->get('database.sleekdb')); - $seeder = new DatabaseSeeder(SleekDbal::class); + $seeder = new DatabaseSeeder(); $seeder->seed(); } diff --git a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/CriteriaSleekTest.php b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/CriteriaSleekTest.php index b180fd5a6..166bd593c 100644 --- a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/CriteriaSleekTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/CriteriaSleekTest.php @@ -10,9 +10,9 @@ class CriteriaSleekTest extends SleekDbalTestCase public function testSleekCriteriaEquals() { - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $user = $this->userModel->criteria('firstname', '=', 'John')->first(); + $user = $this->userProfileModel->criteria('firstname', '=', 'John')->first(); $this->assertEquals('John', $user->prop('firstname')); @@ -21,9 +21,9 @@ public function testSleekCriteriaEquals() public function testSleekCriteriaNotEquals() { - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $user = $this->userModel->criteria('firstname', '!=', 'John')->first(); + $user = $this->userProfileModel->criteria('firstname', '!=', 'John')->first(); $this->assertEquals('Jane', $user->prop('firstname')); @@ -32,39 +32,39 @@ public function testSleekCriteriaNotEquals() public function testSleekCriteriaGreaterAndGreaterOrEqual() { - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', '>', 35)->get(); + $users = $this->userProfileModel->criteria('age', '>', 35)->get(); $this->assertCount(1, $users); - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', '>=', 35)->get(); + $users = $this->userProfileModel->criteria('age', '>=', 35)->get(); $this->assertCount(2, $users); } public function testSleekCriteriaSmallerAndSmallerOrEqual() { - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', '<', 35)->get(); + $users = $this->userProfileModel->criteria('age', '<', 35)->get(); $this->assertCount(0, $users); - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', '<=', 35)->get(); + $users = $this->userProfileModel->criteria('age', '<=', 35)->get(); $this->assertCount(1, $users); } public function testSleekCriteriaInAndNotIn() { - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', 'IN', [35, 40, 45])->orderBy('age', 'desc')->get(); + $users = $this->userProfileModel->criteria('age', 'IN', [35, 40, 45])->orderBy('age', 'desc')->get(); $this->assertCount(2, $users); @@ -72,9 +72,9 @@ public function testSleekCriteriaInAndNotIn() $this->assertEquals('Jane', $users[1]->firstname); - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', 'NOT IN', [30, 40, 45])->get(); + $users = $this->userProfileModel->criteria('age', 'NOT IN', [30, 40, 45])->get(); $this->assertCount(1, $users); @@ -83,25 +83,25 @@ public function testSleekCriteriaInAndNotIn() public function testSleekCriteriaLikeAndNotLike() { - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('firstname', 'LIKE', '%Jo%')->get(); + $users = $this->userProfileModel->criteria('firstname', 'LIKE', '%Jo%')->get(); $this->assertCount(1, $users); $this->assertEquals('John', $users[0]->firstname); - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('firstname', 'LIKE', '%J%')->orderBy('firstname', 'desc')->get(); + $users = $this->userProfileModel->criteria('firstname', 'LIKE', '%J%')->orderBy('firstname', 'desc')->get(); $this->assertCount(2, $users); $this->assertEquals('Jane', $users[1]->firstname); - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('firstname', 'NOT LIKE', '%Jo%')->get(); + $users = $this->userProfileModel->criteria('firstname', 'NOT LIKE', '%Jo%')->get(); $this->assertCount(1, $users); @@ -110,17 +110,17 @@ public function testSleekCriteriaLikeAndNotLike() public function testSleekCriteriaBetweenAndNotBetween() { - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', 'BETWEEN', [20, 40])->get(); + $users = $this->userProfileModel->criteria('age', 'BETWEEN', [20, 40])->get(); $this->assertCount(1, $users); $this->assertEquals('Jane', $users[0]->firstname); - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); - $users = $this->userModel->criteria('age', 'NOT BETWEEN', [0, 40])->get(); + $users = $this->userProfileModel->criteria('age', 'NOT BETWEEN', [0, 40])->get(); $this->assertCount(1, $users); diff --git a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/JoinSleekTest.php b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/JoinSleekTest.php index e744930f9..3d1ff938e 100644 --- a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/JoinSleekTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/JoinSleekTest.php @@ -6,6 +6,7 @@ use Quantum\Tests\_root\shared\Models\TestUserProfessionModel; use Quantum\Tests\_root\shared\Models\TestUserMeetingModel; use Quantum\Tests\_root\shared\Models\TestUserEventModel; +use Quantum\Tests\_root\shared\Models\TestProfileModel; use Quantum\Tests\_root\shared\Models\TestTicketModel; use Quantum\Tests\_root\shared\Models\TestEventModel; use Quantum\Tests\_root\shared\Models\TestNotesModel; @@ -13,12 +14,93 @@ use Quantum\Model\Exceptions\ModelException; use Quantum\Model\Factories\ModelFactory; use Quantum\Model\ModelCollection; +use Quantum\Model\QtModel; +class BrokenProfileMissingTypeModel extends QtModel +{ + public $table = 'profiles'; + + public function relations(): array + { + return [ + TestUserModel::class => [ + // 'type' intentionally missing + 'foreign_key' => 'user_id', + 'local_key' => 'id', + ], + ]; + } +} + +class BrokenProfileUnsupportedRelationModel extends QtModel +{ + public $table = 'profiles'; + + public function relations(): array + { + return [ + TestUserModel::class => [ + 'type' => 'SIDEWAYS', + 'foreign_key' => 'user_id', + 'local_key' => 'id', + ], + ]; + } +} class JoinSleekTest extends SleekDbalTestCase { - public function testSleekSameLevelJoinTo() + public function testSleekJoinToHasOne() + { + $userModel = ModelFactory::get(TestUserModel::class); + $profileModel = ModelFactory::get(TestProfileModel::class); + + $users = $userModel + ->joinTo($profileModel) + ->get(); + + $this->assertInstanceOf(ModelCollection::class, $users); + + $profile = $users->first()->prop('profiles')[0]; + + $this->assertIsArray($profile); + + $this->assertArrayHasKey('firstname', $profile); + + $this->assertArrayHasKey('lastname', $profile); + + $this->assertArrayHasKey('age', $profile); + + $this->assertArrayHasKey('country', $profile); + } + + public function testSleekJoinToBelongsTo() + { + $userModel = ModelFactory::get(TestUserModel::class); + $profileModel = ModelFactory::get(TestProfileModel::class); + + $profiles = $profileModel + ->joinTo($userModel) + ->get(); + + $this->assertInstanceOf(ModelCollection::class, $profiles); + + $user = $profiles->first()->prop('users')[0]; + + $this->assertIsArray($user); + + $this->assertArrayHasKey('email', $user); + + $this->assertArrayHasKey('password', $user); + + $this->assertEquals( + $profiles->first()->user_id, + $user['id'] + ); + } + + public function testSleekSameLevelJoinToHasMany() { $userModel = ModelFactory::get(TestUserModel::class); @@ -38,7 +120,7 @@ public function testSleekSameLevelJoinTo() $this->assertIsArray($users->first()->prop('user_meetings')); } - public function testSleekNestedLevelJoinTo() + public function testSleekNestedLevelJoinToHasMany() { $userModel = ModelFactory::get(TestUserModel::class); @@ -67,119 +149,103 @@ public function testSleekNestedLevelJoinTo() $this->assertArrayHasKey('notes', $users->first()->prop('user_meetings')[0]['tickets'][0]); } - public function testSleekMixedLevelJoinToWithCriteria() + public function testSleekJoiningViaPivotModel() { $userModel = ModelFactory::get(TestUserModel::class); - - $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); - - $meetingModel = ModelFactory::get(TestUserMeetingModel::class); - - $ticketModel = ModelFactory::get(TestTicketModel::class); + $userEventModel = ModelFactory::get(TestUserEventModel::class); + $eventModel = ModelFactory::get(TestEventModel::class); $users = $userModel - ->joinTo($userProfessionModel, false) - ->joinTo($meetingModel) - ->joinTo($ticketModel) - ->criteria('age', '=', 35) + ->joinTo($userEventModel) + ->joinTo($eventModel) ->get(); - $this->assertInstanceOf(ModelCollection::class, $users); + $this->assertNotEmpty($users); - $this->assertCount(1, $users); + $user = $users->first(); - $this->assertEquals('Jane', $users->first()->prop('firstname')); + $this->assertInstanceOf(TestUserModel::class, $user); - $this->assertIsArray($users->first()->prop('user_professions')); + $userRecord = $users->first()->asArray(); - $this->assertIsArray($users->first()->prop('user_meetings')); + $this->assertArrayHasKey('email', $userRecord); - $this->assertEquals('Marketing', $users->first()->prop('user_meetings')[0]['title']); + $this->assertArrayHasKey('user_events', $userRecord); - $this->assertArrayHasKey('tickets', $users->first()->prop('user_meetings')[0]); + $pivotRecord = $user->prop('user_events')[0]; - $this->assertIsArray($users->first()->prop('user_meetings')[0]['tickets']); - } + $this->assertIsArray($pivotRecord); - public function testSleekJoinToAndThrough() - { - $userModel = ModelFactory::get(TestUserModel::class); + $this->assertArrayHasKey('user_id', $pivotRecord); - $userEventModel = ModelFactory::get(TestUserEventModel::class); + $this->assertArrayHasKey('event_id', $pivotRecord); - $eventModel = ModelFactory::get(TestEventModel::class); - - $users = $userModel - ->joinTo($userEventModel) - ->joinThrough($eventModel) - ->orderBy('title', 'asc') - ->get(); + $this->assertArrayHasKey('confirmed', $pivotRecord); - $this->assertInstanceOf(ModelCollection::class, $users); + $this->assertArrayHasKey('events', $pivotRecord); - $this->assertIsArray($users->first()->user_events); + $eventRecord = $pivotRecord['events'][0]; - $this->assertArrayHasKey('confirmed', $users->first()->user_events[0]); + $this->assertIsArray($eventRecord); - $this->assertArrayHasKey('events', $users->first()->user_events[0]); + $this->assertArrayHasKey('title', $eventRecord); - $this->assertIsArray($users->first()->user_events[0]['events']); + $this->assertArrayHasKey('country', $eventRecord); - $this->assertArrayHasKey('title', $users->first()->user_events[0]['events'][0]); + $this->assertArrayHasKey('started_at', $eventRecord); } - public function testSleekJoinThroughInverse() + public function testSleekMixedLevelJoinToWithCriteria() { + $userModel = ModelFactory::get(TestUserModel::class); + $profileModel = ModelFactory::get(TestProfileModel::class); + $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); + $meetingModel = ModelFactory::get(TestUserMeetingModel::class); $ticketModel = ModelFactory::get(TestTicketModel::class); - $noteModel = ModelFactory::get(TestNotesModel::class); + $users = $userModel + ->joinTo($profileModel, false) + ->joinTo($userProfessionModel, false) + ->joinTo($meetingModel) + ->joinTo($ticketModel) + ->criteria('email', 'LIKE', '%jane%') + ->get(); + + $this->assertInstanceOf(ModelCollection::class, $users); - $notes = $noteModel->joinThrough($ticketModel)->criteria('id', '=', 3)->first()->asArray(); + $this->assertCount(1, $users); - $this->assertArrayHasKey('note', $notes); + $this->assertEquals('Jane', $users->first()->prop('profiles')[0]['firstname']); - $this->assertEquals('note three', $notes['note']); + $this->assertIsArray($users->first()->prop('user_professions')); - $this->assertArrayHasKey('tickets', $notes); + $this->assertIsArray($users->first()->prop('user_meetings')); - $this->assertIsArray($notes['tickets']); + $this->assertEquals('Marketing', $users->first()->prop('user_meetings')[0]['title']); - $this->assertArrayHasKey('number', $notes['tickets'][0]); + $this->assertArrayHasKey('tickets', $users->first()->prop('user_meetings')[0]); - $this->assertEquals('R4563', $notes['tickets'][0]['number']); + $this->assertIsArray($users->first()->prop('user_meetings')[0]['tickets']); } - public function testSleekJoinToAndJoinThroughUsingSwitch() + public function testSleekSelectFieldsAtJoin() { $userModel = ModelFactory::get(TestUserModel::class); - $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); - $userEventModel = ModelFactory::get(TestUserEventModel::class); - - $eventModel = ModelFactory::get(TestEventModel::class); - - $users = $userModel - ->joinTo($userProfessionModel, false) - ->joinTo($userEventModel) - ->joinThrough($eventModel) - ->orderBy('title', 'asc') - ->get(); - - $this->assertInstanceOf(ModelCollection::class, $users); - - $this->assertIsArray($users->first()->user_professions); - - $this->assertArrayHasKey('title', $users->first()->user_professions[0]); + $users = $userModel->joinTo($userProfessionModel) + ->select('firstname', 'lastname', 'age', 'country', ['user_professions.title' => 'profession']) + ->orderBy('age', 'desc') + ->get(); - $this->assertEquals('Singer', $users->first()->user_professions[0]['title']); + $this->assertEquals('Writer', $users->first()->profession); } - public function testSleekWrongRelation() + public function testSleekJoinThrowsExceptionForWrongRelation() { $this->expectException(ModelException::class); - $this->expectExceptionMessage('The model `' . TestNotesModel::class . '` does not define relation with `' . TestEventModel::class . '`'); + $this->expectExceptionMessage('The model `' . TestEventModel::class . '` does not define relation with `' . TestNotesModel::class . '`'); $eventModel = ModelFactory::get(TestEventModel::class); @@ -188,17 +254,43 @@ public function testSleekWrongRelation() $eventModel->joinTo($noteModel)->get(); } - public function testSleekSelectFieldsAtJoin() + public function testSleekJoinThrowsExceptionForMissingForeignKey() { - $userModel = ModelFactory::get(TestUserModel::class); + $this->expectException(ModelException::class); + $this->expectExceptionMessage('Foreign key `user_id` is missing in model `' . TestProfileModel::class . '`'); - $userProfessionModel = ModelFactory::get(TestUserProfessionModel::class); + $profileModel = ModelFactory::get(TestProfileModel::class); - $users = $userModel->joinTo($userProfessionModel) - ->select('firstname', 'lastname', 'age', 'country', ['user_professions.title' => 'profession']) - ->orderBy('age', 'desc') + $profileModel->create(); + $profileModel->firstname = 'Test'; + $profileModel->lastname = 'User'; + $profileModel->age = 30; + $profileModel->country = 'Testland'; + $profileModel->user_id = null; + $profileModel->save(); + + $profileModel->criteria('firstname', '=', 'Test') + ->joinTo(ModelFactory::get(TestUserModel::class)) ->get(); + } - $this->assertEquals('Writer', $users->first()->profession); + public function testSleekJoinThrowsExceptionWhenRelationKeysMissing() + { + $this->expectException(ModelException::class); + $this->expectExceptionMessage('Relation type is missing for model `' . BrokenProfileMissingTypeModel::class .'`'); + + ModelFactory::get(BrokenProfileMissingTypeModel::class) + ->joinTo(ModelFactory::get(TestUserModel::class)) + ->get(); + } + + public function testSleekJoinThrowsExceptionForUnsupportedRelationType() + { + $this->expectException(ModelException::class); + $this->expectExceptionMessage('Relation type `SIDEWAYS` is not supported'); + + ModelFactory::get(BrokenProfileUnsupportedRelationModel::class) + ->joinTo(ModelFactory::get(TestUserModel::class)) + ->get(); } } \ No newline at end of file diff --git a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ReducerSleekTest.php b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ReducerSleekTest.php index f11a7cc14..e987a7d5a 100644 --- a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ReducerSleekTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ReducerSleekTest.php @@ -10,15 +10,15 @@ class ReducerSleekTest extends SleekDbalTestCase public function testSleekSelect() { - $userModel = new SleekDbal('users'); + $userProfileModel = new SleekDbal('profiles'); - $user = $userModel->select('age')->first(); + $user = $userProfileModel->select('age')->first(); $this->assertCount(2, $user->asArray()); $userModel = new SleekDbal('users'); - $user = $userModel + $user = $userProfileModel ->select('id', ['firstname' => 'name'], ['lastname' => 'surname'], 'age') ->orderBy('id', 'asc') ->get(); diff --git a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ResultSleekTest.php b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ResultSleekTest.php index 4327c127c..9674d6402 100644 --- a/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ResultSleekTest.php +++ b/tests/Unit/Libraries/Database/Adapters/Sleekdb/Statements/ResultSleekTest.php @@ -8,18 +8,18 @@ class ResultSleekTest extends SleekDbalTestCase { - private $userModel; + private $userProfileModel; public function setUp(): void { parent::setUp(); - $this->userModel = new SleekDbal('users'); + $this->userProfileModel = new SleekDbal('profiles'); } public function testSleekGet() { - $users = $this->userModel->get(); + $users = $this->userProfileModel->get(); $this->assertIsArray($users); @@ -32,7 +32,7 @@ public function testSleekGet() public function testSleekFindOne() { - $user = $this->userModel->findOne(1); + $user = $this->userProfileModel->findOne(1); $this->assertEquals('John', $user->prop('firstname')); @@ -41,7 +41,7 @@ public function testSleekFindOne() public function testSleekFindOneBy() { - $user = $this->userModel->findOneBy('firstname', 'John'); + $user = $this->userProfileModel->findOneBy('firstname', 'John'); $this->assertEquals('Doe', $user->prop('lastname')); @@ -50,13 +50,13 @@ public function testSleekFindOneBy() public function testSleekFirst() { - $user = $this->userModel->criteria('age', '>', 40)->first(); + $user = $this->userProfileModel->criteria('age', '>', 40)->first(); $this->assertEquals('Doe', $user->prop('lastname')); $this->assertEquals('45', $user->prop('age')); - $userModel = new SleekDbal('users'); + $userModel = new SleekDbal('profiles'); $user = $userModel->criteria('age', '<', 40)->first(); @@ -69,18 +69,18 @@ public function testSleekFirst() public function testSleekCount() { - $this->assertIsInt($this->userModel->count()); + $this->assertIsInt($this->userProfileModel->count()); - $this->assertEquals(2, $this->userModel->count()); + $this->assertEquals(2, $this->userProfileModel->count()); - $this->userModel->criteria('age', '>', 40); + $this->userProfileModel->criteria('age', '>', 40); - $this->assertEquals(1, $this->userModel->count()); + $this->assertEquals(1, $this->userProfileModel->count()); } public function testSleekAsArray() { - $user = $this->userModel->first(); + $user = $this->userProfileModel->first(); $this->assertIsObject($user); diff --git a/tests/Unit/Libraries/Database/Adapters/TestData.php b/tests/Unit/Libraries/Database/Adapters/TestData.php index b250ebcb2..ad57d5b76 100644 --- a/tests/Unit/Libraries/Database/Adapters/TestData.php +++ b/tests/Unit/Libraries/Database/Adapters/TestData.php @@ -7,8 +7,16 @@ class TestData public static function users(): array { return [ - ['firstname' => 'John', 'lastname' => 'Doe', 'age' => 45, 'country' => 'Ireland', 'created_at' => date('Y-m-d H:i:s')], - ['firstname' => 'Jane', 'lastname' => 'Du', 'age' => 35, 'country' => 'England', 'created_at' => date('Y-m-d H:i:s')], + ['email' => 'john@test.com', 'password' => 'hashed1'], + ['email' => 'jane@test.com', 'password' => 'hashed2'], + ]; + } + + public static function profiles(): array + { + return [ + ['user_id' => 1, 'firstname' => 'John', 'lastname' => 'Doe', 'age' => 45, 'country' => 'Ireland', 'created_at' => date('Y-m-d H:i:s')], + ['user_id' => 2, 'firstname' => 'Jane', 'lastname' => 'Du', 'age' => 35, 'country' => 'England', 'created_at' => date('Y-m-d H:i:s')], ]; } diff --git a/tests/Unit/Libraries/Database/DatabaseTest.php b/tests/Unit/Libraries/Database/DatabaseTest.php index ffe717067..dc9daf493 100644 --- a/tests/Unit/Libraries/Database/DatabaseTest.php +++ b/tests/Unit/Libraries/Database/DatabaseTest.php @@ -22,7 +22,9 @@ public function setUp(): void config()->set('debug', true); - Database::execute("CREATE TABLE IF NOT EXISTS users ( + $this->setPrivateProperty(Database::class, 'instance', null); + + Database::execute("CREATE TABLE IF NOT EXISTS profiles ( id INTEGER PRIMARY KEY, firstname VARCHAR(255), lastname VARCHAR(255), @@ -34,7 +36,7 @@ public function setUp(): void public function tearDown(): void { - Database::execute("DROP TABLE IF EXISTS users"); + Database::execute("DROP TABLE IF EXISTS profiles"); } public function testDatabaseInstance() @@ -60,17 +62,17 @@ public function testDatabaseGetOrmClass() public function testDatabaseRawQueries() { - $result = Database::query('SELECT * FROM users WHERE id=:id', ['id' => 1]); + $result = Database::query('SELECT * FROM profiles WHERE id=:id', ['id' => 1]); $this->assertIsArray($result); $this->assertEmpty($result); - Database::execute('INSERT INTO users (firstname, lastname, age, country) + Database::execute('INSERT INTO profiles (firstname, lastname, age, country) VALUES (:firstname, :lastname, :age, :country)', ['firstname' => 'John', 'lastname' => 'Doe', 'age' => '56', 'country' => 'Spain']); - $result = Database::query('SELECT * FROM users WHERE id=:id', ['id' => 1]); + $result = Database::query('SELECT * FROM profiles WHERE id=:id', ['id' => 1]); $this->assertNotEmpty($result); @@ -78,7 +80,7 @@ public function testDatabaseRawQueries() $this->assertEquals('Doe', $result[0]['lastname']); - $this->assertEquals("SELECT * FROM users WHERE '1'=:'1'", Database::lastQuery()); + $this->assertEquals("SELECT * FROM profiles WHERE '1'=:'1'", Database::lastQuery()); $this->assertIsArray(Database::queryLog()); } @@ -87,7 +89,7 @@ public function testDatabaseTransactionMethods() { Database::beginTransaction(); - Database::execute('INSERT INTO users (firstname, lastname, age, country) + Database::execute('INSERT INTO profiles (firstname, lastname, age, country) VALUES (:firstname, :lastname, :age, :country)', [ 'firstname' => 'Alice', 'lastname' => 'Smith', @@ -97,13 +99,13 @@ public function testDatabaseTransactionMethods() Database::rollback(); - $result = Database::query('SELECT * FROM users WHERE firstname = :firstname', ['firstname' => 'Alice']); + $result = Database::query('SELECT * FROM profiles WHERE firstname = :firstname', ['firstname' => 'Alice']); $this->assertEmpty($result); Database::beginTransaction(); - Database::execute('INSERT INTO users (firstname, lastname, age, country) + Database::execute('INSERT INTO profiles (firstname, lastname, age, country) VALUES (:firstname, :lastname, :age, :country)', [ 'firstname' => 'Bob', 'lastname' => 'Marley', @@ -113,7 +115,7 @@ public function testDatabaseTransactionMethods() Database::commit(); - $result = Database::query('SELECT * FROM users WHERE firstname = :firstname', ['firstname' => 'Bob']); + $result = Database::query('SELECT * FROM profiles WHERE firstname = :firstname', ['firstname' => 'Bob']); $this->assertNotEmpty($result); @@ -123,7 +125,7 @@ public function testDatabaseTransactionMethods() public function testDatabaseTransactionClosureSuccess() { $result = Database::transaction(function () { - Database::execute('INSERT INTO users (firstname, lastname, age, country) + Database::execute('INSERT INTO profiles (firstname, lastname, age, country) VALUES (:firstname, :lastname, :age, :country)', [ 'firstname' => 'Charlie', 'lastname' => 'Brown', @@ -136,7 +138,7 @@ public function testDatabaseTransactionClosureSuccess() $this->assertEquals('committed', $result); - $rows = Database::query('SELECT * FROM users WHERE firstname = :firstname', ['firstname' => 'Charlie']); + $rows = Database::query('SELECT * FROM profiles WHERE firstname = :firstname', ['firstname' => 'Charlie']); $this->assertNotEmpty($rows); @@ -147,7 +149,7 @@ public function testDatabaseTransactionClosureFailure() { try { Database::transaction(function () { - Database::execute('INSERT INTO users (firstname, lastname, age, country) + Database::execute('INSERT INTO profiles (firstname, lastname, age, country) VALUES (:firstname, :lastname, :age, :country)', [ 'firstname' => 'Eve', 'lastname' => 'Hacker', @@ -161,7 +163,7 @@ public function testDatabaseTransactionClosureFailure() $this->assertEquals('Something went wrong!', $e->getMessage()); } - $rows = Database::query('SELECT * FROM users WHERE firstname = :firstname', ['firstname' => 'Eve']); + $rows = Database::query('SELECT * FROM profiles WHERE firstname = :firstname', ['firstname' => 'Eve']); $this->assertEmpty($rows); } diff --git a/tests/Unit/Libraries/Database/Factories/TableFactoryTest.php b/tests/Unit/Libraries/Database/Factories/TableFactoryTest.php index 986c2680f..4d47ad173 100644 --- a/tests/Unit/Libraries/Database/Factories/TableFactoryTest.php +++ b/tests/Unit/Libraries/Database/Factories/TableFactoryTest.php @@ -54,19 +54,19 @@ public function setUp(): void public function tearDown(): void { - Database::execute("DROP TABLE IF EXISTS users"); + Database::execute("DROP TABLE IF EXISTS profiles"); } public function testCreateTable() { - $table = $this->tableFactory->create('users'); + $table = $this->tableFactory->create('profiles'); $this->assertInstanceOf(Table::class, $table); } public function testAttemptToCreateTableWhichAlreadyExists() { - Database::execute("CREATE TABLE users ( + Database::execute("CREATE TABLE profiles ( id INTEGER PRIMARY KEY, firstname VARCHAR(255), lastname VARCHAR(255), @@ -77,14 +77,14 @@ public function testAttemptToCreateTableWhichAlreadyExists() $this->expectException(DatabaseException::class); - $this->expectExceptionMessage('The table `users` is already exists'); + $this->expectExceptionMessage('The table `profiles` is already exists'); - $this->tableFactory->create('users'); + $this->tableFactory->create('profiles'); } public function testGetTable() { - Database::execute("CREATE TABLE IF NOT EXISTS users ( + Database::execute("CREATE TABLE IF NOT EXISTS profiles ( id INTEGER PRIMARY KEY, firstname VARCHAR(255), lastname VARCHAR(255), @@ -93,7 +93,7 @@ public function testGetTable() created_at DATETIME )"); - $table = $this->tableFactory->get('users'); + $table = $this->tableFactory->get('profiles'); $this->assertInstanceOf(Table::class, $table); } @@ -102,14 +102,14 @@ public function testAttemptToGetNonExistingTable() { $this->expectException(DatabaseException::class); - $this->expectExceptionMessage('The table `users` does not exists'); + $this->expectExceptionMessage('The table `profiles` does not exists'); - $table = $this->tableFactory->get('users'); + $table = $this->tableFactory->get('profiles'); } public function testRenameTable() { - Database::execute("CREATE TABLE IF NOT EXISTS users ( + Database::execute("CREATE TABLE IF NOT EXISTS profiles ( id INTEGER PRIMARY KEY, firstname VARCHAR(255), lastname VARCHAR(255), @@ -118,21 +118,21 @@ public function testRenameTable() created_at DATETIME )"); - $this->assertTrue($this->tableFactory->rename('users', 'system_users')); + $this->assertTrue($this->tableFactory->rename('profiles', 'system_users')); } public function testAttemptToRenameNonExistingTable() { $this->expectException(DatabaseException::class); - $this->expectExceptionMessage('The table `users` does not exists'); + $this->expectExceptionMessage('The table `profiles` does not exists'); - $this->tableFactory->rename('users', 'system_users'); + $this->tableFactory->rename('profiles', 'system_users'); } public function testDropTable() { - Database::execute("CREATE TABLE IF NOT EXISTS users ( + Database::execute("CREATE TABLE IF NOT EXISTS profiles ( id INTEGER PRIMARY KEY, firstname VARCHAR(255), lastname VARCHAR(255), @@ -141,15 +141,15 @@ public function testDropTable() created_at DATETIME )"); - $this->assertTrue($this->tableFactory->drop('users')); + $this->assertTrue($this->tableFactory->drop('profiles')); } public function testAttemptToDropNonExistingTable() { $this->expectException(DatabaseException::class); - $this->expectExceptionMessage('The table `users` does not exists'); + $this->expectExceptionMessage('The table `profiles` does not exists'); - $this->tableFactory->drop('users', 'system_users'); + $this->tableFactory->drop('profiles'); } } diff --git a/tests/Unit/Model/Helpers/ModelHelperFunctionsTest.php b/tests/Unit/Model/Helpers/ModelHelperFunctionsTest.php index 7d2148bd1..3e6cddf82 100644 --- a/tests/Unit/Model/Helpers/ModelHelperFunctionsTest.php +++ b/tests/Unit/Model/Helpers/ModelHelperFunctionsTest.php @@ -57,7 +57,7 @@ public function testWrapToModelThrowsOnInvalidClass() { $dbal = Mockery::mock(DbalInterface::class); - $this->expectException(\InvalidArgumentException::class); + $this->expectException(ModelException::class); wrapToModel($dbal, 'NonExistentModelClass'); } @@ -66,7 +66,7 @@ public function testWrapToModelThrowsIfNotQtModel() { $dbal = Mockery::mock(DbalInterface::class); - $this->expectException(\InvalidArgumentException::class); + $this->expectException(ModelException::class); wrapToModel($dbal, \stdClass::class); } diff --git a/tests/Unit/Model/ModelCollectionTest.php b/tests/Unit/Model/ModelCollectionTest.php index 7c12f0988..a55b889da 100644 --- a/tests/Unit/Model/ModelCollectionTest.php +++ b/tests/Unit/Model/ModelCollectionTest.php @@ -3,9 +3,9 @@ namespace Quantum\Tests\Unit\Model; use Quantum\Tests\_root\shared\Models\TestUserModel; +use Quantum\Model\Exceptions\ModelException; use Quantum\Tests\Unit\AppTestCase; use Quantum\Model\ModelCollection; -use InvalidArgumentException; use stdClass; class ModelCollectionTest extends AppTestCase @@ -32,7 +32,7 @@ public function testModelCollectionConstructorWithValidModels() public function testModelCollectionConstructorWithInvalidModels() { - $this->expectException(InvalidArgumentException::class); + $this->expectException(ModelException::class); new ModelCollection([new stdClass(), $this->model1, $this->model2]); } diff --git a/tests/Unit/Model/QtModelTest.php b/tests/Unit/Model/QtModelTest.php index 34a80d66e..6f243bd7a 100644 --- a/tests/Unit/Model/QtModelTest.php +++ b/tests/Unit/Model/QtModelTest.php @@ -117,9 +117,9 @@ public function testQtModelFillObjectPropsWithUndefinedFillable() { $this->expectException(ModelException::class); - $this->expectExceptionMessage('Inappropriate property `country` for fillable object'); + $this->expectExceptionMessage('Inappropriate property `currency` for fillable object'); - $this->model->fillObjectProps(['country' => 'Ireland']); + $this->model->fillObjectProps(['currency' => 'Ireland']); } public function testQtModelSetterAndGetter() @@ -196,7 +196,7 @@ public function testQtModelCallingModelWithCriterias() { $profileModel = ModelFactory::get(TestProfileModel::class); - $user = $profileModel->criteria('age', '<', '50')->orderBy('age', 'asc')->first(); + $user = $profileModel->criteria('age', '<', '40')->orderBy('age', 'asc')->first(); $this->assertIsObject($user); @@ -215,16 +215,19 @@ public function testQtModelGetModelProperties() { $expected = [ "id" => "1", + "user_id" => "1", "firstname" => "John", "lastname" => "Doe", - "age" => "45" + "age" => "45", + "country" => "Ireland", + "created_at" => "2025-12-17 19:27:46" ]; - $actual = $this->model->orderBy('id', 'asc')->first()->asArray(); + $profileModel = ModelFactory::get(TestProfileModel::class); - $this->assertIsArray($actual); + $actual = $profileModel->orderBy('id', 'asc')->first()->asArray(); - $this->assertArrayNotHasKey('password', $actual); + $this->assertIsArray($actual); $this->assertEquals($expected, $actual); } @@ -233,18 +236,20 @@ private function _createProfileTableWithData() { IdiormDbal::execute("CREATE TABLE IF NOT EXISTS profiles ( id INTEGER PRIMARY KEY, - password VAARCHAR(255), + user_id INTEGER(11), firstname VARCHAR(255), lastname VARCHAR(255), - age int(11) + age INTEGER(11), + country VARCHAR(255), + created_at DATETIME )"); - IdiormDbal::execute("INSERT INTO - profiles - (password, firstname, lastname, age) + IdiormDbal::execute(" + INSERT INTO profiles + (user_id, firstname, lastname, age, country, created_at) VALUES - ('@R45sdfFD7dsf&', 'John', 'Doe', 45), - ('@RaTRdfF9dsa*', 'Jane', 'Dous', 35) - "); + (1, 'John', 'Doe', 45, 'Ireland', '2025-12-17 19:27:46'), + (2, 'Jane', 'Due', 35, 'England', '2025-12-17 19:27:47') + "); } } diff --git a/tests/_root/shared/Models/TestEventModel.php b/tests/_root/shared/Models/TestEventModel.php index 6c609607c..9f590508d 100644 --- a/tests/_root/shared/Models/TestEventModel.php +++ b/tests/_root/shared/Models/TestEventModel.php @@ -2,16 +2,26 @@ namespace Quantum\Tests\_root\shared\Models; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\QtModel; class TestEventModel extends QtModel { public $table = 'events'; + public $idColumn = 'id'; + + public $fillable = [ + 'title', + 'country', + 'started_at' + ]; + public function relations(): array { return [ TestUserEventModel::class => [ + 'type' => Relation::HAS_MANY, 'foreign_key' => 'event_id', 'local_key' => 'id' ] diff --git a/tests/_root/shared/Models/TestNotesModel.php b/tests/_root/shared/Models/TestNotesModel.php index 113d10793..52b3a315e 100644 --- a/tests/_root/shared/Models/TestNotesModel.php +++ b/tests/_root/shared/Models/TestNotesModel.php @@ -8,13 +8,10 @@ class TestNotesModel extends QtModel { public $table = 'notes'; - public function relations(): array - { - return [ - TestTicketModel::class => [ - 'foreign_key' => 'ticket_id', - 'local_key' => 'id' - ] - ]; - } + public $idColumn = 'id'; + + public $fillable = [ + 'ticket_id', + 'note', + ]; } \ No newline at end of file diff --git a/tests/_root/shared/Models/TestProfileModel.php b/tests/_root/shared/Models/TestProfileModel.php index dbb4d9b21..906aa7f4b 100644 --- a/tests/_root/shared/Models/TestProfileModel.php +++ b/tests/_root/shared/Models/TestProfileModel.php @@ -2,23 +2,32 @@ namespace Quantum\Tests\_root\shared\Models; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\QtModel; class TestProfileModel extends QtModel { + public $table = 'profiles'; public $idColumn = 'id'; - public $table = 'profiles'; - protected $fillable = [ - 'password', + 'user_id', 'firstname', 'lastname', - 'age' + 'age', + 'country', + 'created_at', ]; - public $hidden = [ - 'password' - ]; + public function relations(): array + { + return [ + TestUserModel::class => [ + 'type' => Relation::BELONGS_TO, + 'foreign_key' => 'user_id', + 'local_key' => 'id' + ], + ]; + } } diff --git a/tests/_root/shared/Models/TestTicketModel.php b/tests/_root/shared/Models/TestTicketModel.php index 92d49c237..494ae5707 100644 --- a/tests/_root/shared/Models/TestTicketModel.php +++ b/tests/_root/shared/Models/TestTicketModel.php @@ -2,17 +2,27 @@ namespace Quantum\Tests\_root\shared\Models; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\QtModel; class TestTicketModel extends QtModel { public $table = 'tickets'; + public $idColumn = 'id'; + + public $fillable = [ + 'meeting_id', + 'type', + 'number', + ]; + public function relations(): array { return [ - TestUserMeetingModel::class => [ - 'foreign_key' => 'meeting_id', + TestNotesModel::class => [ + 'type' => Relation::HAS_MANY, + 'foreign_key' => 'ticket_id', 'local_key' => 'id' ] ]; diff --git a/tests/_root/shared/Models/TestUserEventModel.php b/tests/_root/shared/Models/TestUserEventModel.php index 37ff1fb3a..ec338bbb1 100644 --- a/tests/_root/shared/Models/TestUserEventModel.php +++ b/tests/_root/shared/Models/TestUserEventModel.php @@ -2,21 +2,27 @@ namespace Quantum\Tests\_root\shared\Models; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\QtModel; class TestUserEventModel extends QtModel { - public $table = 'user_events'; + public $idColumn = 'id'; + + public $fillable = [ + 'user_id', + 'event_id', + 'confirmed', + 'created_at', + ]; + public function relations(): array { return [ - TestUserModel::class => [ - 'foreign_key' => 'user_id', - 'local_key' => 'id' - ], TestEventModel::class => [ + 'type' => Relation::BELONGS_TO, 'foreign_key' => 'event_id', 'local_key' => 'id' ] diff --git a/tests/_root/shared/Models/TestUserMeetingModel.php b/tests/_root/shared/Models/TestUserMeetingModel.php index 80831b6f0..5b9335dca 100644 --- a/tests/_root/shared/Models/TestUserMeetingModel.php +++ b/tests/_root/shared/Models/TestUserMeetingModel.php @@ -2,19 +2,30 @@ namespace Quantum\Tests\_root\shared\Models; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\QtModel; class TestUserMeetingModel extends QtModel { public $table = 'user_meetings'; + public $idColumn = 'id'; + + public $fillable = [ + 'user_id', + 'title', + 'start_date' + ]; + public function relations(): array { return [ - TestUserModel::class => [ - 'foreign_key' => 'user_id', + TestTicketModel::class => [ + 'type' => Relation::HAS_MANY, + 'foreign_key' => 'meeting_id', 'local_key' => 'id' ] ]; + } } diff --git a/tests/_root/shared/Models/TestUserModel.php b/tests/_root/shared/Models/TestUserModel.php index 9223fb6aa..fb4f569df 100644 --- a/tests/_root/shared/Models/TestUserModel.php +++ b/tests/_root/shared/Models/TestUserModel.php @@ -2,6 +2,7 @@ namespace Quantum\Tests\_root\shared\Models; +use Quantum\Libraries\Database\Enums\Relation; use Quantum\Model\QtModel; class TestUserModel extends QtModel @@ -12,19 +13,40 @@ class TestUserModel extends QtModel public $table = 'users'; public $fillable = [ - 'uuid', - 'firstname', - 'lastname', - 'role', 'email', 'password', - 'activation_token', - 'remember_token', - 'reset_token', - 'access_token', - 'refresh_token', - 'otp', - 'otp_expires', - 'otp_token', ]; + + public $hidden = [ + 'password' + ]; + + public function relations(): array + { + return [ + TestProfileModel::class => [ + 'type' => Relation::HAS_ONE, + 'foreign_key' => 'user_id', + 'local_key' => 'id' + ], + + TestUserProfessionModel::class => [ + 'type' => Relation::HAS_MANY, + 'foreign_key' => 'user_id', + 'local_key' => 'id' + ], + + TestUserMeetingModel::class => [ + 'type' => Relation::HAS_MANY, + 'foreign_key' => 'user_id', + 'local_key' => 'id' + ], + + TestUserEventModel::class => [ + 'type' => Relation::HAS_MANY, + 'foreign_key' => 'user_id', + 'local_key' => 'id' + ], + ]; + } } diff --git a/tests/_root/shared/Models/TestUserProfessionModel.php b/tests/_root/shared/Models/TestUserProfessionModel.php index 4e29bdaff..e46022aed 100644 --- a/tests/_root/shared/Models/TestUserProfessionModel.php +++ b/tests/_root/shared/Models/TestUserProfessionModel.php @@ -9,13 +9,10 @@ class TestUserProfessionModel extends QtModel public $table = 'user_professions'; - public function relations(): array - { - return [ - TestUserModel::class => [ - 'foreign_key' => 'user_id', - 'local_key' => 'id' - ] - ]; - } + public $primaryKey = 'id'; + + public $fillable = [ + 'user_id', + 'title' + ]; }