Lines 100.00% 8 / 8
Functions and Methods 100.00% 2 / 2
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
MorphMany 100.00% 8 / 8 100.00% 2 / 2 4 100.00% 1 / 1
 executeResults 100.00% 1 / 1 100.00% 1 / 1 1
 match 100.00% 7 / 7 100.00% 1 / 1 3
1<?php
2
3declare(strict_types=1);
4
5namespace BlueprintAU\Radiant\Relations;
6
7use BlueprintAU\Radiant\Collection;
8use BlueprintAU\Radiant\Model;
9
10/**
11 * One-to-many polymorphic: the parent's key + morph alias are referenced
12 * by the related table's FK + type columns.
13 *
14 * `Post::comments()` → `Comment::newQuery()->where(commentable_id,
15 * $post->id)->where(commentable_type, Post::class)`. The type filter is
16 * what makes the relation polymorphic — a Video with the same id shares
17 * the comment pool without ever seeing Post's comments.
18 *
19 * @template TRelated of Model
20 * @extends MorphOneOrMany<TRelated>
21 */
22final class MorphMany extends MorphOneOrMany
23{
24    /**
25     * Run the constrained query.
26     *
27     * @return Collection<int, TRelated>
28     */
29    #[\Override]
30    protected function executeResults(): Collection
31    {
32        return $this->query->get();
33    }
34
35    /**
36     * Distribute eager results onto parents, keyed by the FK value.
37     *
38     * Parents with no matching children get an empty collection — the
39     * relation is loaded either way.
40     *
41     * @param  list<Model>  $parents
42     * @param  Collection<int, TRelated>  $results
43     * @param  string  $name
44     * @param  list<int|string|null|list<int|string|null>>|null  $eagerParentKeys  Unused.
45     * @return void
46     */
47    #[\Override]
48    public function match(array $parents, Collection $results, string $name, ?array $eagerParentKeys = null): void
49    {
50        $grouped = [];
51
52        foreach ($results as $related) {
53            $grouped[self::serializeKey($related->attribute($this->getForeignKey()))][] = $related;
54        }
55
56        foreach ($parents as $parent) {
57            $key = self::serializeKey($parent->attribute($this->getLocalKey()));
58            // The bag's items came off $results (TRelated) — every one IS
59            // a Model; setRelation accepts Collection<int, Model> and the item
60            // template is not covariant.
61            /** @var Collection<int, Model> $bag */
62            $bag = Collection::make($grouped[$key] ?? []);
63            $parent->setRelation($name, $bag);
64        }
65    }
66}