@@ -16,43 +16,57 @@ abstract class Relation
1616 * @var bool
1717 */
1818 protected static bool $ has_constraints = true ;
19+
1920 /**
2021 * Indicate whether the relationships use a pivot table.*.
2122 *
2223 * @var bool
2324 */
2425 protected static bool $ has_pivot = false ;
26+
2527 /**
2628 * The foreign key of the parent model.
2729 *
2830 * @var string
2931 */
3032 protected string $ foreign_key ;
33+
3134 /**
3235 * The associated key on the parent model.
3336 *
3437 * @var string
3538 */
3639 protected string $ local_key ;
40+
3741 /**
3842 * The parent model instance
3943 *
4044 * @var Model
4145 */
4246 protected Model $ parent ;
47+
4348 /**
4449 * The related model instance
4550 *
4651 * @var Model
4752 */
4853 protected Model $ related ;
54+
4955 /**
5056 * The Bow Query builder
5157 *
5258 * @var QueryBuilder
5359 */
5460 protected QueryBuilder $ query ;
5561
62+ /**
63+ * Whether no parent exposed a key during eager loading, in which case the
64+ * relation resolves to nothing without querying the database.
65+ *
66+ * @var bool
67+ */
68+ protected bool $ eager_has_no_keys = false ;
69+
5670 /**
5771 * Relation Contractor
5872 *
@@ -84,6 +98,34 @@ public function __construct(Model $related, Model $parent)
8498 */
8599 abstract public function addConstraints (): void ;
86100
101+ /**
102+ * Get the results of the relationship.
103+ *
104+ * @return mixed
105+ */
106+ abstract public function getResults (): mixed ;
107+
108+ /**
109+ * The parent attribute whose value is matched against the related models.
110+ *
111+ * @return string
112+ */
113+ abstract protected function eagerParentKey (): string ;
114+
115+ /**
116+ * The related column queried when eager loading the relation.
117+ *
118+ * @return string
119+ */
120+ abstract protected function eagerRelatedKey (): string ;
121+
122+ /**
123+ * Whether the relation resolves to many related models.
124+ *
125+ * @return bool
126+ */
127+ abstract protected function eagerIsMany (): bool ;
128+
87129 /**
88130 * Get the parent model.
89131 *
@@ -104,24 +146,6 @@ public function getRelated(): Model
104146 return $ this ->related ;
105147 }
106148
107- /**
108- * _Call
109- *
110- * @param string $method
111- * @param array $args
112- * @return mixed
113- */
114- public function __call (string $ method , array $ args = [])
115- {
116- $ result = call_user_func_array ([$ this ->query , $ method ], (array )$ args );
117-
118- if ($ result === $ this ->query ) {
119- return $ this ;
120- }
121-
122- return $ result ;
123- }
124-
125149 /**
126150 * Create a new row of the related
127151 *
@@ -135,34 +159,6 @@ public function create(array $attributes): Model
135159 return $ this ->related ->create ($ attributes );
136160 }
137161
138- /**
139- * Get the results of the relationship.
140- *
141- * @return mixed
142- */
143- abstract public function getResults (): mixed ;
144-
145- /**
146- * The parent attribute whose value is matched against the related models.
147- *
148- * @return string
149- */
150- abstract protected function eagerParentKey (): string ;
151-
152- /**
153- * The related column queried when eager loading the relation.
154- *
155- * @return string
156- */
157- abstract protected function eagerRelatedKey (): string ;
158-
159- /**
160- * Whether the relation resolves to many related models.
161- *
162- * @return bool
163- */
164- abstract protected function eagerIsMany (): bool ;
165-
166162 /**
167163 * Run the given callback with relation constraints disabled.
168164 *
@@ -197,9 +193,17 @@ public function addEagerConstraints(array $parents): void
197193 fn ($ value ) => !is_null ($ value )
198194 )));
199195
200- // Fall back to an impossible match when no parent exposes a key so the
201- // query stays well-formed and returns nothing.
202- $ this ->query ->whereIn ($ this ->eagerRelatedKey (), count ($ keys ) > 0 ? $ keys : [0 ]);
196+ // With no keys to match, skip the value-based constraint entirely.
197+ // Injecting a placeholder value (such as 0) is rejected by strongly
198+ // typed columns — e.g. a PostgreSQL uuid primary key raises
199+ // "invalid input syntax for type uuid: 0". getEager() short-circuits.
200+ if (count ($ keys ) === 0 ) {
201+ $ this ->eager_has_no_keys = true ;
202+
203+ return ;
204+ }
205+
206+ $ this ->query ->whereIn ($ this ->eagerRelatedKey (), $ keys );
203207 }
204208
205209 /**
@@ -209,6 +213,11 @@ public function addEagerConstraints(array $parents): void
209213 */
210214 public function getEager (): Collection
211215 {
216+ // No parent exposed a key, so there is nothing to fetch.
217+ if ($ this ->eager_has_no_keys ) {
218+ return new Collection ([]);
219+ }
220+
212221 $ results = $ this ->query ->get ();
213222
214223 return $ results instanceof Collection ? $ results : new Collection ([]);
@@ -240,4 +249,22 @@ public function match(array $parents, Collection $results, string $name): void
240249 );
241250 }
242251 }
252+
253+ /**
254+ * _Call
255+ *
256+ * @param string $method
257+ * @param array $args
258+ * @return mixed
259+ */
260+ public function __call (string $ method , array $ args = [])
261+ {
262+ $ result = call_user_func_array ([$ this ->query , $ method ], (array )$ args );
263+
264+ if ($ result === $ this ->query ) {
265+ return $ this ;
266+ }
267+
268+ return $ result ;
269+ }
243270}
0 commit comments