blob: 6d14c8574975dc9ecc64ab8ad1d76c3b5b56e5cc [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace LdapRecord\Models\Relations;
4
5use LdapRecord\Models\Model;
6use LdapRecord\Query\Collection;
7use LdapRecord\Query\Model\Builder;
8
9abstract class OneToMany extends Relation
10{
11 /**
12 * The relation to merge results with.
13 *
14 * @var OneToMany|null
15 */
16 protected $with;
17
18 /**
19 * The name of the relationship.
20 *
21 * @var string
22 */
23 protected $relationName;
24
25 /**
26 * Whether to include recursive results.
27 *
28 * @var bool
29 */
30 protected $recursive = false;
31
32 /**
33 * Constructor.
34 *
35 * @param Builder $query
36 * @param Model $parent
37 * @param string $related
38 * @param string $relationKey
39 * @param string $foreignKey
40 * @param string $relationName
41 */
42 public function __construct(Builder $query, Model $parent, $related, $relationKey, $foreignKey, $relationName)
43 {
44 $this->relationName = $relationName;
45
46 parent::__construct($query, $parent, $related, $relationKey, $foreignKey);
47 }
48
49 /**
50 * Set the relation to load with its parent.
51 *
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010052 * @param Relation $relation
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020053 *
54 * @return $this
55 */
56 public function with(Relation $relation)
57 {
58 $this->with = $relation;
59
60 return $this;
61 }
62
63 /**
64 * Whether to include recursive results.
65 *
66 * @param bool $enable
67 *
68 * @return $this
69 */
70 public function recursive($enable = true)
71 {
72 $this->recursive = $enable;
73
74 return $this;
75 }
76
77 /**
78 * Get the immediate relationships results.
79 *
80 * @return Collection
81 */
82 abstract public function getRelationResults();
83
84 /**
85 * Get the results of the relationship.
86 *
87 * @return Collection
88 */
89 public function getResults()
90 {
91 $results = $this->recursive
92 ? $this->getRecursiveResults()
93 : $this->getRelationResults();
94
95 return $results->merge(
96 $this->getMergingRelationResults()
97 );
98 }
99
100 /**
101 * Execute the callback excluding the merged query result.
102 *
103 * @param callable $callback
104 *
105 * @return mixed
106 */
107 protected function onceWithoutMerging($callback)
108 {
109 $merging = $this->with;
110
111 $this->with = null;
112
113 $result = $callback();
114
115 $this->with = $merging;
116
117 return $result;
118 }
119
120 /**
121 * Get the relation name.
122 *
123 * @return string
124 */
125 public function getRelationName()
126 {
127 return $this->relationName;
128 }
129
130 /**
131 * Get the results of the merging 'with' relation.
132 *
133 * @return Collection
134 */
135 protected function getMergingRelationResults()
136 {
137 return $this->with
138 ? $this->with->recursive($this->recursive)->get()
139 : $this->parent->newCollection();
140 }
141
142 /**
143 * Get the results for the models relation recursively.
144 *
145 * @param string[] $loaded The distinguished names of models already loaded
146 *
147 * @return Collection
148 */
149 protected function getRecursiveResults(array $loaded = [])
150 {
151 $results = $this->getRelationResults()->reject(function (Model $model) use ($loaded) {
152 // Here we will exclude the models that we have already
153 // loaded the recursive results for so we don't run
154 // into issues with circular relations in LDAP.
155 return in_array($model->getDn(), $loaded);
156 });
157
158 foreach ($results as $model) {
159 $loaded[] = $model->getDn();
160
161 // Finally, we will fetch the related models relations,
162 // passing along our loaded models, to ensure we do
163 // not attempt fetching already loaded relations.
164 $results = $results->merge(
165 $this->getRecursiveRelationResults($model, $loaded)
166 );
167 }
168
169 return $results;
170 }
171
172 /**
173 * Get the recursive relation results for given model.
174 *
175 * @param Model $model
176 * @param array $loaded
177 *
178 * @return Collection
179 */
180 protected function getRecursiveRelationResults(Model $model, array $loaded)
181 {
182 return method_exists($model, $this->relationName)
183 ? $model->{$this->relationName}()->getRecursiveResults($loaded)
184 : $model->newCollection();
185 }
186}