@@ -167,7 +167,7 @@ protected function isMorphRelation($relation)
167167 */
168168 protected function isHasManyDeep ($ model ): bool
169169 {
170- return class_exists (' Staudenmeir\EloquentHasManyDeep\HasManyDeep ' )
170+ return class_exists (\ Staudenmeir \EloquentHasManyDeep \HasManyDeep::class )
171171 && $ model instanceof \Staudenmeir \EloquentHasManyDeep \HasManyDeep;
172172 }
173173
@@ -182,33 +182,21 @@ protected function getHasManyDeepForeignKey($model): string
182182 // Try to get from relationship definition using reflection
183183 $ foreignKeys = $ this ->getForeignKeys ($ model );
184184 if (! empty ($ foreignKeys )) {
185- // Get the last foreign key (for the final join)
186- $ lastFK = end ($ foreignKeys );
187-
188- return $ this ->extractColumnFromQualified ($ lastFK );
185+ return $ this ->extractColumnFromQualified (end ($ foreignKeys ));
189186 }
190187
191188 // Try to get the foreign key using common HasManyDeep methods
192189 if (method_exists ($ model , 'getForeignKeyName ' )) {
193190 return $ model ->getForeignKeyName ();
194191 }
195192
196- // HasManyDeep may use getQualifiedForeignKeyName() and extract the column
197- if (method_exists ($ model , 'getQualifiedForeignKeyName ' )) {
198- $ qualified = $ model ->getQualifiedForeignKeyName ();
199-
200- return $ this ->extractColumnFromQualified ($ qualified );
201- }
202-
203- // Fallback: try to infer from intermediate model
193+ // Fallback: try to infer from intermediate model or use related model's key
204194 $ intermediateTable = $ this ->getHasManyDeepIntermediateTable ($ model , '' );
205- if ($ intermediateTable ) {
206- // Assume the related table has a foreign key named {intermediate_table}_id
207- return \Illuminate \Support \Str::singular ($ intermediateTable ).'_id ' ;
208- }
195+ $ fallbackKey = $ intermediateTable
196+ ? \Illuminate \Support \Str::singular ($ intermediateTable ).'_id '
197+ : $ model ->getRelated ()->getKeyName ();
209198
210- // Final fallback: use the related model's key name
211- return $ model ->getRelated ()->getKeyName ();
199+ return $ fallbackKey ;
212200 }
213201
214202 /**
@@ -220,54 +208,28 @@ protected function getHasManyDeepForeignKey($model): string
220208 protected function getHasManyDeepLocalKey ($ model ): string
221209 {
222210 // Try to get from relationship definition using reflection
223- $ localKeys = [];
224- try {
225- $ reflection = new \ReflectionClass ($ model );
226- if ($ reflection ->hasProperty ('localKeys ' )) {
227- $ property = $ reflection ->getProperty ('localKeys ' );
228- $ property ->setAccessible (true );
229- $ localKeys = $ property ->getValue ($ model );
230- }
231- } catch (\Exception $ e ) {
232- // Reflection failed - proceed to other methods
233- // This is safe because we have multiple fallback strategies
234- }
235-
236- if (is_array ($ localKeys ) && ! empty ($ localKeys )) {
237- // Get the last local key (for the final join)
238- $ lastLK = end ($ localKeys );
239-
240- return $ this ->extractColumnFromQualified ($ lastLK );
211+ $ localKeys = $ this ->getLocalKeys ($ model );
212+ if (! empty ($ localKeys )) {
213+ return $ this ->extractColumnFromQualified (end ($ localKeys ));
241214 }
242215
243216 // Try to get the local key using common HasManyDeep methods
244217 if (method_exists ($ model , 'getLocalKeyName ' )) {
245218 return $ model ->getLocalKeyName ();
246219 }
247220
248- // HasManyDeep may use getQualifiedLocalKeyName() and extract the column
249- if (method_exists ($ model , 'getQualifiedLocalKeyName ' )) {
250- $ qualified = $ model ->getQualifiedLocalKeyName ();
251-
252- return $ this ->extractColumnFromQualified ($ qualified );
253- }
254-
255221 // Fallback: use the intermediate model's key name, or parent if no intermediate
256222 $ intermediateTable = $ this ->getHasManyDeepIntermediateTable ($ model , '' );
257- if ($ intermediateTable ) {
258- $ through = $ this ->getThroughModels ($ model );
259- if (! empty ($ through )) {
260- $ firstThrough = is_string ($ through [0 ]) ? $ through [0 ] : get_class ($ through [0 ]);
261- if (class_exists ($ firstThrough )) {
262- $ throughModel = app ($ firstThrough );
263-
264- return $ throughModel ->getKeyName ();
265- }
223+ $ through = $ this ->getThroughModels ($ model );
224+ $ fallbackKey = $ model ->getParent ()->getKeyName ();
225+ if ($ intermediateTable && ! empty ($ through )) {
226+ $ firstThrough = is_string ($ through [0 ]) ? $ through [0 ] : $ through [0 ]::class;
227+ if (class_exists ($ firstThrough )) {
228+ $ fallbackKey = app ($ firstThrough )->getKeyName ();
266229 }
267230 }
268231
269- // Final fallback: use the parent model's key name
270- return $ model ->getParent ()->getKeyName ();
232+ return $ fallbackKey ;
271233 }
272234
273235 /**
@@ -283,7 +245,7 @@ protected function getHasManyDeepIntermediateTable($model, $lastAlias): ?string
283245 $ through = $ this ->getThroughModels ($ model );
284246 if (! empty ($ through )) {
285247 // Get the first intermediate model
286- $ firstThrough = is_string ($ through [0 ]) ? $ through [0 ] : get_class ( $ through [0 ]) ;
248+ $ firstThrough = is_string ($ through [0 ]) ? $ through [0 ] : $ through [0 ]::class ;
287249 if (class_exists ($ firstThrough )) {
288250 $ throughModel = app ($ firstThrough );
289251
@@ -545,7 +507,32 @@ private function getForeignKeys($model): array
545507 return $ foreignKeys ;
546508 }
547509 }
548- } catch (\Exception $ e ) {
510+ } catch (\Exception ) {
511+ // Reflection failed - fall back to empty array
512+ // This is safe because callers handle empty arrays appropriately
513+ }
514+
515+ return [];
516+ }
517+
518+ /**
519+ * Extract the array of local keys from a HasManyDeep relationship using reflection.
520+ *
521+ * @param \Staudenmeir\EloquentHasManyDeep\HasManyDeep $model
522+ */
523+ private function getLocalKeys ($ model ): array
524+ {
525+ try {
526+ $ reflection = new \ReflectionClass ($ model );
527+ if ($ reflection ->hasProperty ('localKeys ' )) {
528+ $ property = $ reflection ->getProperty ('localKeys ' );
529+ $ property ->setAccessible (true );
530+ $ localKeys = $ property ->getValue ($ model );
531+ if (is_array ($ localKeys ) && ! empty ($ localKeys )) {
532+ return $ localKeys ;
533+ }
534+ }
535+ } catch (\Exception ) {
549536 // Reflection failed - fall back to empty array
550537 // This is safe because callers handle empty arrays appropriately
551538 }
@@ -570,7 +557,7 @@ private function getThroughModels($model): array
570557 return $ through ;
571558 }
572559 }
573- } catch (\Exception $ e ) {
560+ } catch (\Exception ) {
574561 // Reflection failed - fall back to empty array
575562 // This is safe because callers handle empty arrays appropriately
576563 }
0 commit comments