diff --git a/lib/ActiveRecord/RelationCollection.php b/lib/ActiveRecord/RelationCollection.php index 780064e..5aa7746 100644 --- a/lib/ActiveRecord/RelationCollection.php +++ b/lib/ActiveRecord/RelationCollection.php @@ -1,156 +1,81 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - namespace ICanBoogie\ActiveRecord; -use ArrayAccess; use Closure; -use ICanBoogie\ActiveRecord; -use ICanBoogie\OffsetNotWritable; +use ICanBoogie\ActiveRecord\Config\Association; use function is_string; /** * Relation collection of a model. - * - * @implements ArrayAccess - * Where _key_ is a getter name e.g. 'comments' */ -class RelationCollection implements ArrayAccess +class RelationCollection { /** - * Relations. - * * @var array - * Where _key_ is a getter name e.g. 'comments' + * Where _key_ is a getter name; for example, 'comments' */ - private array $relations; + private readonly array $relations; public function __construct( public readonly Model $model, - ?ActiveRecord\Config\Association $association, + ?Association $association ) { - $this->apply_association($association); - } + $relations = []; + + if ($association) { + foreach ($association->belongs_to as $r) { + $as = $r->as; + $relations[$as] = new BelongsToRelation( + owner: $this->model, + related: $r->associate, + local_key: $r->local_key, + foreign_key: $r->foreign_key, + as: $as, + ); + } - private function apply_association(?ActiveRecord\Config\Association $association): void - { - if (!$association) { - return; - } + if ($association->has_many) { + assert(is_string($this->model->primary)); + } - foreach ($association->belongs_to as $r) { - $this->belongs_to( - related: $r->associate, - local_key: $r->local_key, - foreign_key: $r->foreign_key, - as: $r->as, - ); + foreach ($association->has_many as $r) { + $as = $r->as; + $relations[$as] = new HasManyRelation( + owner: $this->model, + related: $r->associate, + foreign_key: $r->foreign_key, + as: $as, + through: $r->through, + ); + } } - foreach ($association->has_many as $r) { - $this->has_many( - related: $r->associate, - foreign_key: $r->foreign_key, - as: $r->as, - through: $r->through, - ); - } + $this->relations = $relations; } /** - * Checks if a relation exists. + * Whether a relation exists. * - * @param string $offset Relation name. + * @param non-empty-string $relation_name */ - public function offsetExists(mixed $offset): bool + public function has(string $relation_name): bool { - return isset($this->relations[$offset]); + return isset($this->relations[$relation_name]); } /** - * Returns a relation. + * Returns an existing relation. * - * @param string $offset Relation name. + * @param non-empty-string $relation_name * - * @throws RelationNotDefined if the relation is not defined. - */ - public function offsetGet(mixed $offset): Relation - { - return $this->relations[$offset] - ?? throw new RelationNotDefined($offset, $this); - } - - /** - * @throws OffsetNotWritable because relations cannot be set. + * @throws RelationNotDefined if the relation doesn't exist. */ - public function offsetSet(mixed $offset, mixed $value): void + public function get(string $relation_name): ?Relation { - throw new OffsetNotWritable($offset, $this); - } - - /** - * @throws OffsetNotWritable because relations cannot be unset. - */ - public function offsetUnset(mixed $offset): void - { - throw new OffsetNotWritable($offset, $this); - } - - /** - * Adds a {@link BelongsToRelation} relation. - * - * @param class-string $related - * @param non-empty-string $local_key - * @param non-empty-string $foreign_key - * @param non-empty-string $as - */ - public function belongs_to( - string $related, - string $local_key, - string $foreign_key, - string $as, - ): void { - $this->relations[$as] = new BelongsToRelation( - owner: $this->model, - related: $related, - local_key: $local_key, - foreign_key: $foreign_key, - as: $as, - ); - } - - /** - * Adds a {@link HasManyRelation} relation. - * - * @param class-string $related - * @param non-empty-string $foreign_key - * @param non-empty-string $as - * @param class-string|null $through - */ - public function has_many( - string $related, - string $foreign_key, - string $as, - ?string $through = null, - ): void { - assert(is_string($this->model->primary)); - - $this->relations[$as] = new HasManyRelation( - owner: $this->model, - related: $related, - foreign_key: $foreign_key, - as: $as, - through: $through, - ); + return $this->relations[$relation_name] + ?? throw new RelationNotDefined($relation_name, $this); } /** diff --git a/tests/ActiveRecord/HasManyRelationTest.php b/tests/ActiveRecord/HasManyRelationTest.php index fb9d752..735fe5e 100644 --- a/tests/ActiveRecord/HasManyRelationTest.php +++ b/tests/ActiveRecord/HasManyRelationTest.php @@ -59,7 +59,7 @@ public function test_relations(): void $relations = $this->articles->relations; $this->assertInstanceOf(RelationCollection::class, $relations); - $relation = $relations['comments']; + $relation = $relations->get('comments'); $this->assertInstanceOf(HasManyRelation::class, $relation); $this->assertSame('comments', $relation->as); $this->assertSame($this->articles, $relation->owner); @@ -71,7 +71,7 @@ public function test_relations(): void public function test_undefined_relation(): void { $this->expectException(RelationNotDefined::class); - $this->articles->relations['undefined_relation']; + $this->articles->relations->get('undefined_relation'); } public function test_getter(): void diff --git a/tests/ActiveRecord/HasManyRelationThroughTest.php b/tests/ActiveRecord/HasManyRelationThroughTest.php index 5768c19..34676b4 100644 --- a/tests/ActiveRecord/HasManyRelationThroughTest.php +++ b/tests/ActiveRecord/HasManyRelationThroughTest.php @@ -57,10 +57,10 @@ public function test_through_is_set(): void { $r = $this->physicians->relations; - $this->assertArrayHasKey('appointments', $r); - $this->assertArrayHasKey('patients', $r); + $this->assertTrue($r->has('appointments')); + $this->assertTrue($r->has('patients')); - $ra = $r['appointments']; + $ra = $r->get('appointments'); assert($ra instanceof HasManyRelation); $this->assertInstanceOf(HasManyRelation::class, $ra); $this->assertEquals('appointments', $ra->as); @@ -68,7 +68,7 @@ public function test_through_is_set(): void $this->assertEquals('physician_id', $ra->foreign_key); $this->assertNull($ra->through); - $rp = $r['patients']; + $rp = $r->get('patients'); assert($rp instanceof HasManyRelation); $this->assertInstanceOf(HasManyRelation::class, $rp); $this->assertEquals('patients', $rp->as); diff --git a/tests/ActiveRecord/ModelBelongsToTest.php b/tests/ActiveRecord/ModelBelongsToTest.php index 4538067..a106a38 100644 --- a/tests/ActiveRecord/ModelBelongsToTest.php +++ b/tests/ActiveRecord/ModelBelongsToTest.php @@ -99,9 +99,9 @@ public function getter_is_created_from_the_column_name_without_id_suffix(): void $people = $models->model_for_record(Person::class); - $this->assertArrayHasKey('dance_session', $people->relations); - $this->assertArrayHasKey('hire_skill', $people->relations); - $this->assertArrayHasKey('summon_skill', $people->relations); - $this->assertArrayHasKey('teach_skill', $people->relations); + $this->assertTrue($people->relations->has('dance_session')); + $this->assertTrue($people->relations->has('hire_skill')); + $this->assertTrue($people->relations->has('summon_skill')); + $this->assertTrue($people->relations->has('teach_skill')); } } diff --git a/tests/ActiveRecord/RelationNotDefinedTest.php b/tests/ActiveRecord/RelationNotDefinedTest.php index 86068ee..5e7df54 100644 --- a/tests/ActiveRecord/RelationNotDefinedTest.php +++ b/tests/ActiveRecord/RelationNotDefinedTest.php @@ -20,16 +20,14 @@ final class RelationNotDefinedTest extends TestCase public function test_exception(): void { $relation_name = uniqid(); - $collection = $this - ->getMockBuilder(RelationCollection::class) - ->disableOriginalConstructor() - ->getMock(); + $relations = new class() extends RelationCollection { + public function __construct() { + } + }; - /* @var $collection RelationCollection */ - - $exception = new RelationNotDefined($relation_name, $collection); + $exception = new RelationNotDefined($relation_name, $relations); $this->assertSame($relation_name, $exception->relation_name); - $this->assertSame($collection, $exception->collection); + $this->assertSame($relations, $exception->collection); } }