1: <?php
2:
3: namespace Baril\Bonsai\Relations\Concerns;
4:
5: use Illuminate\Database\Eloquent\Collection as EloquentCollection;
6: use Illuminate\Database\Eloquent\Relations\Relation;
7:
8: /**
9: * @mixin \Illuminate\Database\Eloquent\Relations\Relation
10: */
11: trait AutoloadsOtherRelations
12: {
13: protected $otherRelationsAutoloads;
14:
15: public function autoloads($relation, $callback = null)
16: {
17: $this->otherRelationsAutoloads[$relation] = $callback ?? function ($models, $results) {
18: return [$models, $results];
19: };
20:
21: return $this;
22: }
23:
24: /**
25: * @see \Illuminate\Database\Eloquent\Relations\BelongsToMany::getResults()
26: *
27: * @return \Illuminate\Database\Eloquent\Collection
28: */
29: public function getResults()
30: {
31: $results = parent::getResults();
32:
33: $this->matchOtherRelations([$this->parent], $results);
34: $this->matchOtherRelations($results->all(), $results);
35:
36: return $results;
37: }
38:
39: /**
40: * Match the eagerly loaded results to their parents.
41: *
42: * @see \Illuminate\Database\Eloquent\Relations\BelongsToMany::match()
43: *
44: * @param array<int, TDeclaringModel> $models
45: * @param \Illuminate\Database\Eloquent\Collection<int, TRelatedModel> $results
46: * @param string $relation
47: * @return array<int, TDeclaringModel>
48: */
49: public function match(array $models, EloquentCollection $results, $relation)
50: {
51: parent::match($models, $results, $relation);
52:
53: $this->matchOtherRelations($models, $results);
54: $this->matchOtherRelations($results->all(), $results);
55:
56: return $models;
57: }
58:
59: protected function matchOtherRelations(array $models, EloquentCollection $results)
60: {
61: foreach ($this->otherRelationsAutoloads as $relation => $callback) {
62: list($models, $results) = $callback($models, $results);
63: $this->matchOtherRelation($models, $results, $relation);
64: }
65: }
66:
67: /**
68: * On $this, and on each of the related models that were loaded by
69: * a "through-closures" relation (eg. "ancestors"), load the corresponding
70: * "closed" relation (eg. "parent").
71: *
72: * @param array<int, TDeclaringModel> $models
73: * @param \Illuminate\Database\Eloquent\Collection<int, TRelatedModel> $results
74: * @param string $relation
75: * @return array
76: */
77: protected function matchOtherRelation(array $models, EloquentCollection $results, $relation)
78: {
79: $model = $models[0] ?? null;
80: if (!$model) {
81: return $models;
82: }
83:
84: /**
85: * @var \Illuminate\Database\Eloquent\Relations\Relation
86: */
87: $relationObject = Relation::noConstraints(function () use ($model, $relation) {
88: return $model->$relation();
89: });
90:
91: // Prevents an unneeded query in case we try to access the relation on a leaf/root:
92: $relationObject->initRelation($models, $relation);
93:
94: // Set relation for all related models and parent model:
95: return $relationObject->match(
96: $models,
97: $results,
98: $relation
99: );
100: }
101: }
102: