From 38a8ae63865e9d08a3459c8fc23683a63e4ad7e7 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Tue, 17 Dec 2024 17:14:02 -0500 Subject: [PATCH 01/15] feat: save model --- src/Database/Concerns/Query/HasSentences.php | 13 ++++++ src/Database/Models/Attributes/DateTime.php | 17 ++++++++ src/Database/Models/DatabaseModel.php | 46 ++++++++++++++++++++ tests/Feature/Database/DatabaseModelTest.php | 41 +++++++++++++++++ tests/Feature/Database/Models/User.php | 5 ++- 5 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/Database/Models/Attributes/DateTime.php diff --git a/src/Database/Concerns/Query/HasSentences.php b/src/Database/Concerns/Query/HasSentences.php index d1bb6a89..0fb1edc5 100644 --- a/src/Database/Concerns/Query/HasSentences.php +++ b/src/Database/Concerns/Query/HasSentences.php @@ -64,6 +64,19 @@ public function insert(array $data): bool } } + public function insertRow(array $data): int|string|bool + { + if ($this->insert($data)) { + $result = $this->connection->prepare('SELECT LAST_INSERT_ID()') + ->execute() + ->fetchRow(); + + return array_values($result)[0]; + } + + return false; + } + public function exists(): bool { $this->action = Actions::EXISTS; diff --git a/src/Database/Models/Attributes/DateTime.php b/src/Database/Models/Attributes/DateTime.php new file mode 100644 index 00000000..92ddbf74 --- /dev/null +++ b/src/Database/Models/Attributes/DateTime.php @@ -0,0 +1,17 @@ +toArray()); } + public function save(): bool + { + $propertyBindings = $this->getPropertyBindings(); + $data = []; + foreach ($propertyBindings as $property) { + $propertyName = $property->getName(); + $attribute = $property->getAttribute(); + + if (isset($this->{$propertyName})) { + $data[$propertyName] = $this->{$propertyName}; + } + + if ($attribute instanceof DateTime && $attribute->autoInit && ! isset($this->{$propertyName})) { + $data[$propertyName] = new Date(); + + $this->{$propertyName} = $data[$propertyName]; + } + } + + $queryBuilder = static::newQueryBuilder(); + $queryBuilder->setModel($this); + + if ($this->keyIsInitialized()) { + unset($data[$this->getModelKeyName()]); + + return $queryBuilder->whereEqual($this->getModelKeyName(), $this->getKey()) + ->update($data); + } + + $result = $queryBuilder->insertRow($data); + + if ($result) { + $this->{$this->getModelKeyName()} = $result; + + return true; + } + + return false; + } + protected static function newQueryBuilder(): DatabaseQueryBuilder { return new DatabaseQueryBuilder(); @@ -220,4 +261,9 @@ protected function findModelKey(): ModelProperty return $property->getAttribute() instanceof Id; }); } + + protected function keyIsInitialized(): bool + { + return isset($this->{$this->getModelKeyName()}); + } } diff --git a/tests/Feature/Database/DatabaseModelTest.php b/tests/Feature/Database/DatabaseModelTest.php index 4259c38f..5b7b078c 100644 --- a/tests/Feature/Database/DatabaseModelTest.php +++ b/tests/Feature/Database/DatabaseModelTest.php @@ -711,3 +711,44 @@ "Undefined relationship company for " . Post::class, ); }); + +it('saves a new model', function () { + $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); + + $connection->expects($this->exactly(2)) + ->method('prepare') + ->willReturnOnConsecutiveCalls( + new Statement(new Result([['Query OK']])), + new Statement(new Result([['LAST_INSERT_ID()' => 1]])), + ); + + $this->app->swap(Connections::default(), $connection); + + $model = new User(); + $model->name = 'John Doe'; + $model->email = 'john.doe@mail.com'; + + expect($model->save())->toBeTrue(); + expect($model->id)->toBe(1); + expect($model->createdAt)->toBeInstanceOf(Date::class); +}); + +it('updates a model successfully', function () { + $model = new User(); + $model->id = 1; + $model->name = 'John Doe'; + $model->email = 'john.doe@mail.com'; + $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); + + $connection->expects($this->exactly(1)) + ->method('prepare') + ->willReturnOnConsecutiveCalls( + new Statement(new Result([['Query OK']])), + ); + + $this->app->swap(Connections::default(), $connection); + + $model->name = 'John Doe Jr.'; + + expect($model->save())->toBeTrue(); +}); diff --git a/tests/Feature/Database/Models/User.php b/tests/Feature/Database/Models/User.php index ab85847d..ebd36f34 100644 --- a/tests/Feature/Database/Models/User.php +++ b/tests/Feature/Database/Models/User.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Database\Models; use Phenix\Database\Models\Attributes\Column; +use Phenix\Database\Models\Attributes\DateTime; use Phenix\Database\Models\Attributes\HasMany; use Phenix\Database\Models\Attributes\Id; use Phenix\Database\Models\Collection; @@ -22,10 +23,10 @@ class User extends DatabaseModel #[Column] public string $email; - #[Column(name: 'created_at')] + #[DateTime(name: 'created_at', autoInit: true)] public Date $createdAt; - #[Column(name: 'updated_at')] + #[DateTime(name: 'updated_at')] public Date|null $updatedAt = null; #[HasMany(model: Product::class, foreignKey: 'user_id')] From 21a72b6cfa884f20583a16b6d57d10eaa29042b2 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Tue, 17 Dec 2024 17:33:09 -0500 Subject: [PATCH 02/15] fix: call last insert id from Amp MySql --- src/Database/Concerns/Query/HasSentences.php | 14 +++++++------- tests/Feature/Database/DatabaseModelTest.php | 3 +-- tests/Mocks/Database/Result.php | 5 +++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Database/Concerns/Query/HasSentences.php b/src/Database/Concerns/Query/HasSentences.php index 0fb1edc5..73c35cf1 100644 --- a/src/Database/Concerns/Query/HasSentences.php +++ b/src/Database/Concerns/Query/HasSentences.php @@ -66,15 +66,15 @@ public function insert(array $data): bool public function insertRow(array $data): int|string|bool { - if ($this->insert($data)) { - $result = $this->connection->prepare('SELECT LAST_INSERT_ID()') - ->execute() - ->fetchRow(); + [$dml, $params] = $this->insertRows($data)->toSql(); - return array_values($result)[0]; - } + try { + $result = $this->connection->prepare($dml)->execute($params); - return false; + return $result->getLastInsertId(); + } catch (SqlQueryError|SqlTransactionError) { + return false; + } } public function exists(): bool diff --git a/tests/Feature/Database/DatabaseModelTest.php b/tests/Feature/Database/DatabaseModelTest.php index 5b7b078c..1346ce8f 100644 --- a/tests/Feature/Database/DatabaseModelTest.php +++ b/tests/Feature/Database/DatabaseModelTest.php @@ -715,11 +715,10 @@ it('saves a new model', function () { $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); - $connection->expects($this->exactly(2)) + $connection->expects($this->exactly(1)) ->method('prepare') ->willReturnOnConsecutiveCalls( new Statement(new Result([['Query OK']])), - new Statement(new Result([['LAST_INSERT_ID()' => 1]])), ); $this->app->swap(Connections::default(), $connection); diff --git a/tests/Mocks/Database/Result.php b/tests/Mocks/Database/Result.php index 733e9d2c..707f99c3 100644 --- a/tests/Mocks/Database/Result.php +++ b/tests/Mocks/Database/Result.php @@ -44,4 +44,9 @@ public function getIterator(): Traversable { return $this->fakeResult; } + + public function getLastInsertId(): int + { + return 1; + } } From 3da32dc111bf5c5189abf8f1b4bdfd7eec366482 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Tue, 17 Dec 2024 17:52:58 -0500 Subject: [PATCH 03/15] feat: create model instance --- src/Database/Models/DatabaseModel.php | 16 +++++++++++++ tests/Feature/Database/DatabaseModelTest.php | 25 ++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index aa98e99d..b70c7108 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -69,6 +69,22 @@ public static function query(): DatabaseQueryBuilder return $queryBuilder; } + public static function create(array $attributes): static + { + $model = new static(); + $propertyBindings = $model->getPropertyBindings(); + + foreach ($attributes as $key => $value) { + $property = $propertyBindings[$key]; + + $model->{$property->getName()} = $value; + } + + $model->save(); + + return $model; + } + /** * @return array */ diff --git a/tests/Feature/Database/DatabaseModelTest.php b/tests/Feature/Database/DatabaseModelTest.php index 1346ce8f..75eb3fc8 100644 --- a/tests/Feature/Database/DatabaseModelTest.php +++ b/tests/Feature/Database/DatabaseModelTest.php @@ -725,7 +725,7 @@ $model = new User(); $model->name = 'John Doe'; - $model->email = 'john.doe@mail.com'; + $model->email = faker()->email(); expect($model->save())->toBeTrue(); expect($model->id)->toBe(1); @@ -736,7 +736,7 @@ $model = new User(); $model->id = 1; $model->name = 'John Doe'; - $model->email = 'john.doe@mail.com'; + $model->email = faker()->email(); $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); $connection->expects($this->exactly(1)) @@ -751,3 +751,24 @@ expect($model->save())->toBeTrue(); }); + +it('creates model instance successfully', function () { + $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); + + $connection->expects($this->exactly(1)) + ->method('prepare') + ->willReturnOnConsecutiveCalls( + new Statement(new Result([['Query OK']])), + ); + + $this->app->swap(Connections::default(), $connection); + + $model = User::create([ + 'name' => 'John Doe', + 'email' => faker()->email(), + 'created_at' => Date::now(), + ]); + + expect($model->id)->toBe(1); + expect($model->createdAt)->toBeInstanceOf(Date::class); +}); From bd56a74b56b520b03bb97cca22a5ee1f3828e204 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Tue, 17 Dec 2024 18:39:50 -0500 Subject: [PATCH 04/15] feat: find models --- src/Database/Models/DatabaseModel.php | 15 +++++++++++ tests/Feature/Database/DatabaseModelTest.php | 28 ++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index b70c7108..a361e50a 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -85,6 +85,21 @@ public static function create(array $attributes): static return $model; } + public static function find(string|int $id): static + { + $model = new static(); + $queryBuilder = static::newQueryBuilder(); + $queryBuilder->setModel($model); + + $result = $queryBuilder->whereEqual($model->getModelKeyName(), $id)->first(); + + if (!$result) { + throw new ModelException('Model not found.'); + } + + return $result; + } + /** * @return array */ diff --git a/tests/Feature/Database/DatabaseModelTest.php b/tests/Feature/Database/DatabaseModelTest.php index 75eb3fc8..83782507 100644 --- a/tests/Feature/Database/DatabaseModelTest.php +++ b/tests/Feature/Database/DatabaseModelTest.php @@ -772,3 +772,31 @@ expect($model->id)->toBe(1); expect($model->createdAt)->toBeInstanceOf(Date::class); }); + +it('finds a model successfully', function () { + $data = [ + 'id' => 1, + 'name' => 'John Doe', + 'email' => 'john.doe@email.com', + 'created_at' => Date::now()->toDateTimeString(), + ]; + + $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); + + $connection->expects($this->exactly(1)) + ->method('prepare') + ->willReturnOnConsecutiveCalls( + new Statement(new Result([$data])), + ); + + $this->app->swap(Connections::default(), $connection); + + /** @var User $user */ + $user = User::find(1); + + expect($user)->toBeInstanceOf(User::class); + expect($user->id)->toBe($data['id']); + expect($user->name)->toBe($data['name']); + expect($user->email)->toBe($data['email']); + expect($user->createdAt)->toBeInstanceOf(Date::class); +}); From c318dedcdac35e4bba6ec9572218a83b0fb3ad94 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Tue, 17 Dec 2024 18:40:04 -0500 Subject: [PATCH 05/15] fix: set data by column name --- src/Database/Models/DatabaseModel.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index a361e50a..91c25e9f 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -170,16 +170,17 @@ public function save(): bool { $propertyBindings = $this->getPropertyBindings(); $data = []; + foreach ($propertyBindings as $property) { $propertyName = $property->getName(); $attribute = $property->getAttribute(); if (isset($this->{$propertyName})) { - $data[$propertyName] = $this->{$propertyName}; + $data[$property->getColumnName()] = $this->{$propertyName}; } if ($attribute instanceof DateTime && $attribute->autoInit && ! isset($this->{$propertyName})) { - $data[$propertyName] = new Date(); + $data[$property->getColumnName()] = new Date(); $this->{$propertyName} = $data[$propertyName]; } From f048f5d88ae8eceba478e157c9202deac506fded Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Tue, 17 Dec 2024 21:31:26 -0500 Subject: [PATCH 06/15] refactor: pass columns to select --- src/Database/Models/DatabaseModel.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index 91c25e9f..3bd7a4f9 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -85,19 +85,16 @@ public static function create(array $attributes): static return $model; } - public static function find(string|int $id): static + public static function find(string|int $id, array $columns = ['*']): static|null { $model = new static(); $queryBuilder = static::newQueryBuilder(); $queryBuilder->setModel($model); - $result = $queryBuilder->whereEqual($model->getModelKeyName(), $id)->first(); - - if (!$result) { - throw new ModelException('Model not found.'); - } - - return $result; + return $queryBuilder + ->select($columns) + ->whereEqual($model->getModelKeyName(), $id) + ->first(); } /** From 133d29412ce40c7b847fb11b83f49c3ef854724b Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Tue, 17 Dec 2024 21:42:59 -0500 Subject: [PATCH 07/15] feat: throws error on invalid keys --- src/Database/Models/DatabaseModel.php | 6 +++++- .../QueryBuilders/DatabaseQueryBuilder.php | 2 +- tests/Feature/Database/DatabaseModelTest.php | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index 3bd7a4f9..e19d72ee 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -75,7 +75,11 @@ public static function create(array $attributes): static $propertyBindings = $model->getPropertyBindings(); foreach ($attributes as $key => $value) { - $property = $propertyBindings[$key]; + $property = $propertyBindings[$key] ?? null; + + if (! $property) { + throw new ModelException("Property {$key} not found for model " . static::class); + } $model->{$property->getName()} = $value; } diff --git a/src/Database/Models/QueryBuilders/DatabaseQueryBuilder.php b/src/Database/Models/QueryBuilders/DatabaseQueryBuilder.php index 199e571e..6b3db4dd 100644 --- a/src/Database/Models/QueryBuilders/DatabaseQueryBuilder.php +++ b/src/Database/Models/QueryBuilders/DatabaseQueryBuilder.php @@ -152,7 +152,7 @@ public function get(): Collection return $collection; } - public function first(): DatabaseModel + public function first(): DatabaseModel|null { $this->action = Actions::SELECT; diff --git a/tests/Feature/Database/DatabaseModelTest.php b/tests/Feature/Database/DatabaseModelTest.php index 83782507..9438dd9a 100644 --- a/tests/Feature/Database/DatabaseModelTest.php +++ b/tests/Feature/Database/DatabaseModelTest.php @@ -773,6 +773,23 @@ expect($model->createdAt)->toBeInstanceOf(Date::class); }); +it('throws an exception when column in invalid on create instance', function () { + expect(function () { + $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); + + $this->app->swap(Connections::default(), $connection); + + User::create([ + 'name' => 'John Doe', + 'email' => faker()->email(), + 'other_date' => Date::now(), + ]); + })->toThrow( + ModelException::class, + "Property other_date not found for model " . User::class, + ); +}); + it('finds a model successfully', function () { $data = [ 'id' => 1, From d3f9ee27af29764d83682196c1df9dea0a69afa6 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 08:24:58 -0500 Subject: [PATCH 08/15] feat: delete model --- src/Database/Models/DatabaseModel.php | 10 ++++++++++ tests/Feature/Database/DatabaseModelTest.php | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index e19d72ee..b6b532e8 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -208,6 +208,16 @@ public function save(): bool return false; } + public function delete(): bool + { + $queryBuilder = static::newQueryBuilder(); + $queryBuilder->setModel($this); + + return $queryBuilder + ->whereEqual($this->getModelKeyName(), $this->getKey()) + ->delete(); + } + protected static function newQueryBuilder(): DatabaseQueryBuilder { return new DatabaseQueryBuilder(); diff --git a/tests/Feature/Database/DatabaseModelTest.php b/tests/Feature/Database/DatabaseModelTest.php index 9438dd9a..5e28a5e7 100644 --- a/tests/Feature/Database/DatabaseModelTest.php +++ b/tests/Feature/Database/DatabaseModelTest.php @@ -817,3 +817,22 @@ expect($user->email)->toBe($data['email']); expect($user->createdAt)->toBeInstanceOf(Date::class); }); + +it('deletes a model successfully', function () { + $model = new User(); + $model->id = 1; + $model->name = 'John Doe'; + $model->email = faker()->email(); + + $connection = $this->getMockBuilder(MysqlConnectionPool::class)->getMock(); + + $connection->expects($this->exactly(1)) + ->method('prepare') + ->willReturnOnConsecutiveCalls( + new Statement(new Result([['Query OK']])), + ); + + $this->app->swap(Connections::default(), $connection); + + expect($model->delete())->toBeTrue(); +}); From de432482a6e7ad05aad4465baa4994bbb3a7f492 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 08:29:38 -0500 Subject: [PATCH 09/15] fix: set db columns in right way --- src/Database/Models/Attributes/DateTime.php | 3 ++- src/Database/Models/DatabaseModel.php | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Database/Models/Attributes/DateTime.php b/src/Database/Models/Attributes/DateTime.php index 92ddbf74..5e881ca0 100644 --- a/src/Database/Models/Attributes/DateTime.php +++ b/src/Database/Models/Attributes/DateTime.php @@ -11,7 +11,8 @@ { public function __construct( public string|null $name = null, - public bool $autoInit = false + public bool $autoInit = false, + public string $format = 'Y-m-d H:i:s', ) { } } diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index b6b532e8..dfa74d69 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -181,9 +181,11 @@ public function save(): bool } if ($attribute instanceof DateTime && $attribute->autoInit && ! isset($this->{$propertyName})) { - $data[$property->getColumnName()] = new Date(); + $now = Date::now(); - $this->{$propertyName} = $data[$propertyName]; + $data[$property->getColumnName()] = $now->format($attribute->format); + + $this->{$propertyName} = $now; } } From 459e68a0b2ef9fd3ec4c04418f6e82084559faa4 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 09:12:25 -0500 Subject: [PATCH 10/15] fix: add docblock to query result for MySQL --- src/Database/Concerns/Query/HasSentences.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Database/Concerns/Query/HasSentences.php b/src/Database/Concerns/Query/HasSentences.php index 73c35cf1..0f0b5f40 100644 --- a/src/Database/Concerns/Query/HasSentences.php +++ b/src/Database/Concerns/Query/HasSentences.php @@ -4,6 +4,7 @@ namespace Phenix\Database\Concerns\Query; +use Amp\Mysql\Internal\MysqlPooledResult; use Amp\Sql\SqlQueryError; use Amp\Sql\SqlTransactionError; use League\Uri\Components\Query; @@ -69,6 +70,7 @@ public function insertRow(array $data): int|string|bool [$dml, $params] = $this->insertRows($data)->toSql(); try { + /** @var MysqlPooledResult $result */ $result = $this->connection->prepare($dml)->execute($params); return $result->getLastInsertId(); From 882785509ac749b5200a38f9fb2156e6bfb143c9 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 10:44:58 -0500 Subject: [PATCH 11/15] fix: phpstan return type --- src/Database/Models/DatabaseModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index dfa74d69..13f0c711 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -89,7 +89,7 @@ public static function create(array $attributes): static return $model; } - public static function find(string|int $id, array $columns = ['*']): static|null + public static function find(string|int $id, array $columns = ['*']): self|null { $model = new static(); $queryBuilder = static::newQueryBuilder(); From 22b0695283f73228566bffb10cc3f90fbcbbb289 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 11:14:51 -0500 Subject: [PATCH 12/15] refactor: add docblocks and return type --- src/Database/Models/DatabaseModel.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index 13f0c711..7fe0369d 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -69,6 +69,11 @@ public static function query(): DatabaseQueryBuilder return $queryBuilder; } + /** + * @param array $attributes + * @throws ModelException + * @return DatabaseModel + */ public static function create(array $attributes): static { $model = new static(); @@ -89,6 +94,11 @@ public static function create(array $attributes): static return $model; } + /** + * @param string|int $id + * @param array $columns + * @return DatabaseModel|null + */ public static function find(string|int $id, array $columns = ['*']): self|null { $model = new static(); @@ -112,7 +122,7 @@ public function getPropertyBindings(): array /** * @return array> */ - public function getRelationshipBindings() + public function getRelationshipBindings(): array { return $this->relationshipBindings ??= $this->buildRelationshipBindings(); } From 2bf55c6eaa4d69277421cd516ce9a27f842395aa Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 11:16:44 -0500 Subject: [PATCH 13/15] refactor: reduce complexity --- src/Database/Models/DatabaseModel.php | 59 ++++++++++++++++----------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index 7fe0369d..371215b5 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -159,11 +159,11 @@ public function toArray(): array $value = isset($this->{$propertyName}) ? $this->{$propertyName} : null; if ($value || $property->isNullable()) { - if ($value instanceof Arrayable) { - $value = $value->toArray(); - } elseif ($value instanceof Date) { - $value = $value->toIso8601String(); - } + $value = match (true) { + $value instanceof Arrayable => $value->toArray(), + $value instanceof Date => $value->toIso8601String(), + default => $value, + }; $data[$propertyName] = $value; } @@ -179,25 +179,7 @@ public function toJson(): string public function save(): bool { - $propertyBindings = $this->getPropertyBindings(); - $data = []; - - foreach ($propertyBindings as $property) { - $propertyName = $property->getName(); - $attribute = $property->getAttribute(); - - if (isset($this->{$propertyName})) { - $data[$property->getColumnName()] = $this->{$propertyName}; - } - - if ($attribute instanceof DateTime && $attribute->autoInit && ! isset($this->{$propertyName})) { - $now = Date::now(); - - $data[$property->getColumnName()] = $now->format($attribute->format); - - $this->{$propertyName} = $now; - } - } + $data = $this->buildSavingData(); $queryBuilder = static::newQueryBuilder(); $queryBuilder->setModel($this); @@ -321,4 +303,33 @@ protected function keyIsInitialized(): bool { return isset($this->{$this->getModelKeyName()}); } + + + /** + * @return array + */ + protected function buildSavingData(): array + { + $data = []; + + foreach ($this->getPropertyBindings() as $property) { + $propertyName = $property->getName(); + $attribute = $property->getAttribute(); + + if (isset($this->{$propertyName})) { + $data[$property->getColumnName()] = $this->{$propertyName}; + } + + if ($attribute instanceof DateTime && $attribute->autoInit && ! isset($this->{$propertyName})) { + $now = Date::now(); + + $data[$property->getColumnName()] = $now->format($attribute->format); + + $this->{$propertyName} = $now; + } + } + + + return $data; + } } From 7fcb0c8221bd76a2c61e1e7b5208e4f67061144f Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 11:42:16 -0500 Subject: [PATCH 14/15] style: phpcs --- src/Database/Models/DatabaseModel.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index 371215b5..fa6a19bc 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -304,7 +304,6 @@ protected function keyIsInitialized(): bool return isset($this->{$this->getModelKeyName()}); } - /** * @return array */ From bfc2884b60dc5eeb3f9f354430cf33ac63470451 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Wed, 18 Dec 2024 11:49:28 -0500 Subject: [PATCH 15/15] fix: update docblock --- src/Database/Models/DatabaseModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Models/DatabaseModel.php b/src/Database/Models/DatabaseModel.php index fa6a19bc..42889d90 100644 --- a/src/Database/Models/DatabaseModel.php +++ b/src/Database/Models/DatabaseModel.php @@ -72,7 +72,7 @@ public static function query(): DatabaseQueryBuilder /** * @param array $attributes * @throws ModelException - * @return DatabaseModel + * @return static */ public static function create(array $attributes): static {