99use Illuminate \Database \Eloquent \Model ;
1010use Illuminate \Database \Eloquent \Relations \BelongsTo ;
1111use Illuminate \Database \Eloquent \Relations \BelongsToMany ;
12+ use Illuminate \Database \Eloquent \Relations \MorphTo ;
1213use Illuminate \Database \Eloquent \Relations \Relation ;
1314use Illuminate \Support \Arr ;
1415use Illuminate \Support \Collection ;
@@ -45,6 +46,8 @@ trait HasCellState
4546
4647 protected ?string $ attributeNameCache = null ;
4748
49+ protected ?bool $ hasNestedMorphToRelationshipCache = null ;
50+
4851 public function inverseRelationship (?string $ name ): static
4952 {
5053 $ this ->inverseRelationshipName = $ name ;
@@ -217,7 +220,9 @@ public function queriesRelationships(Model $record): bool
217220
218221 public function getRelationship (Model $ record , ?string $ relationshipName = null ): ?Relation
219222 {
220- if ($ this ->relationshipCache ) {
223+ $ hasNestedMorphToRelationship = $ this ->hasNestedMorphToRelationship ($ record );
224+
225+ if ($ this ->relationshipCache && (! $ hasNestedMorphToRelationship )) {
221226 return $ this ->relationshipCache ;
222227 }
223228
@@ -249,15 +254,23 @@ public function getRelationship(Model $record, ?string $relationshipName = null)
249254 $ record = $ relationship ->getRelated ();
250255 }
251256
257+ if ($ hasNestedMorphToRelationship ) {
258+ return $ relationship ;
259+ }
260+
252261 return $ this ->relationshipCache = $ relationship ;
253262 }
254263
255264 public function hasMultipleRelationship (Model $ record ): bool
256265 {
257- if (isset ($ this ->hasMultipleRelationshipCache )) {
266+ $ hasNestedMorphToRelationship = $ this ->hasNestedMorphToRelationship ($ record );
267+
268+ if (isset ($ this ->hasMultipleRelationshipCache ) && (! $ hasNestedMorphToRelationship )) {
258269 return $ this ->hasMultipleRelationshipCache ;
259270 }
260271
272+ $ hasMultipleRelationship = false ;
273+
261274 $ relationships = explode ('. ' , $ this ->getRelationshipName ($ record ));
262275
263276 while (count ($ relationships )) {
@@ -266,7 +279,9 @@ public function hasMultipleRelationship(Model $record): bool
266279 $ currentRelationshipValue = $ record ->getRelationValue ($ currentRelationshipName );
267280
268281 if ($ currentRelationshipValue instanceof Collection) {
269- return $ this ->hasMultipleRelationshipCache = true ;
282+ $ hasMultipleRelationship = true ;
283+
284+ break ;
270285 }
271286
272287 if (! $ currentRelationshipValue instanceof Model) {
@@ -280,7 +295,11 @@ public function hasMultipleRelationship(Model $record): bool
280295 $ record = $ currentRelationshipValue ;
281296 }
282297
283- return $ this ->hasMultipleRelationshipCache = false ;
298+ if ($ hasNestedMorphToRelationship ) {
299+ return $ hasMultipleRelationship ;
300+ }
301+
302+ return $ this ->hasMultipleRelationshipCache = $ hasMultipleRelationship ;
284303 }
285304
286305 /**
@@ -339,7 +358,9 @@ public function getRelationshipResults(Model $record, ?array $relationships = nu
339358
340359 public function getAttributeName (Model $ record ): string
341360 {
342- if ($ this ->attributeNameCache !== null ) {
361+ $ hasNestedMorphToRelationship = $ this ->hasNestedMorphToRelationship ($ record );
362+
363+ if (($ this ->attributeNameCache !== null ) && (! $ hasNestedMorphToRelationship )) {
343364 return $ this ->attributeNameCache ;
344365 }
345366
@@ -365,12 +386,20 @@ public function getAttributeName(Model $record): string
365386 $ record = $ record ->{$ namePart }()->getRelated ();
366387 }
367388
368- return $ this ->attributeNameCache = Arr::first ([...$ nameParts , $ lastPart ]);
389+ $ attributeName = Arr::first ([...$ nameParts , $ lastPart ]);
390+
391+ if ($ hasNestedMorphToRelationship ) {
392+ return $ attributeName ;
393+ }
394+
395+ return $ this ->attributeNameCache = $ attributeName ;
369396 }
370397
371398 public function getFullAttributeName (Model $ record ): string
372399 {
373- if ($ this ->fullAttributeNameCache !== null ) {
400+ $ hasNestedMorphToRelationship = $ this ->hasNestedMorphToRelationship ($ record );
401+
402+ if (($ this ->fullAttributeNameCache !== null ) && (! $ hasNestedMorphToRelationship )) {
374403 return $ this ->fullAttributeNameCache ;
375404 }
376405
@@ -396,7 +425,13 @@ public function getFullAttributeName(Model $record): string
396425 $ record = $ record ->{$ namePart }()->getRelated ();
397426 }
398427
399- return $ this ->fullAttributeNameCache = implode ('. ' , [...$ nameParts , $ lastPart ]);
428+ $ fullAttributeName = implode ('. ' , [...$ nameParts , $ lastPart ]);
429+
430+ if ($ hasNestedMorphToRelationship ) {
431+ return $ fullAttributeName ;
432+ }
433+
434+ return $ this ->fullAttributeNameCache = $ fullAttributeName ;
400435 }
401436
402437 public function getInverseRelationshipName (Model $ record ): string
@@ -452,7 +487,9 @@ public function getInverseRelationshipName(Model $record): string
452487
453488 public function getRelationshipName (Model $ record ): ?string
454489 {
455- if ($ this ->relationshipNameCache !== null ) {
490+ $ hasNestedMorphToRelationship = $ this ->hasNestedMorphToRelationship ($ record );
491+
492+ if (($ this ->relationshipNameCache !== null ) && (! $ hasNestedMorphToRelationship )) {
456493 return $ this ->relationshipNameCache ;
457494 }
458495
@@ -480,7 +517,58 @@ public function getRelationshipName(Model $record): ?string
480517 $ record = $ record ->{$ namePart }()->getRelated ();
481518 }
482519
483- return $ this ->relationshipNameCache = implode ('. ' , $ relationshipParts );
520+ $ relationshipName = implode ('. ' , $ relationshipParts );
521+
522+ if ($ hasNestedMorphToRelationship ) {
523+ return $ relationshipName ;
524+ }
525+
526+ return $ this ->relationshipNameCache = $ relationshipName ;
527+ }
528+
529+ /**
530+ * When the name of a cell traverses a `MorphTo` relationship and then continues deeper,
531+ * the remainder of the path resolves against the concrete related model of each record,
532+ * which may differ between records and is unknown while the query is being built. In
533+ * that case, the relationship and attribute names cannot be cached across records and
534+ * must be resolved fresh for each one.
535+ */
536+ public function hasNestedMorphToRelationship (Model $ record ): bool
537+ {
538+ if (isset ($ this ->hasNestedMorphToRelationshipCache )) {
539+ return $ this ->hasNestedMorphToRelationshipCache ;
540+ }
541+
542+ $ name = $ this ->getName ();
543+
544+ if (! str ($ name )->contains ('. ' )) {
545+ return $ this ->hasNestedMorphToRelationshipCache = false ;
546+ }
547+
548+ $ nameParts = explode ('. ' , $ name );
549+ array_pop ($ nameParts );
550+
551+ $ lastNamePartIndex = count ($ nameParts ) - 1 ;
552+
553+ foreach ($ nameParts as $ namePartIndex => $ namePart ) {
554+ if ($ record ->hasAttribute ($ namePart )) {
555+ break ;
556+ }
557+
558+ if (! $ record ->isRelation ($ namePart )) {
559+ break ;
560+ }
561+
562+ $ relationship = $ record ->{$ namePart }();
563+
564+ if ($ relationship instanceof MorphTo) {
565+ return $ this ->hasNestedMorphToRelationshipCache = ($ namePartIndex < $ lastNamePartIndex );
566+ }
567+
568+ $ record = $ relationship ->getRelated ();
569+ }
570+
571+ return $ this ->hasNestedMorphToRelationshipCache = false ;
484572 }
485573
486574 protected function cacheState (Closure $ state ): mixed
0 commit comments