@@ -488,4 +488,93 @@ public function testDenormalizeRelationIsNotResourceLinkage(): void
488488
489489 $ normalizer ->denormalize ($ data , Dummy::class, ItemNormalizer::FORMAT );
490490 }
491+
492+ public function testNormalizeWithNullToOneAndEmptyToManyRelationships (): void
493+ {
494+ $ dummy = new Dummy ();
495+ $ dummy ->setId (1 );
496+ $ dummy ->setName ('Dummy with relationships ' );
497+
498+ $ propertyNameCollectionFactory = $ this ->createMock (PropertyNameCollectionFactoryInterface::class);
499+ $ propertyNameCollectionFactory ->method ('create ' )->willReturn (
500+ new PropertyNameCollection (['id ' , 'name ' , 'relatedDummy ' , 'relatedDummies ' ])
501+ );
502+
503+ $ propertyMetadataFactory = $ this ->createMock (PropertyMetadataFactoryInterface::class);
504+ $ propertyMetadataFactory ->method ('create ' )->willReturnCallback (function ($ class , $ property ) {
505+ return match ($ property ) {
506+ 'id ' => (new ApiProperty ())->withReadable (true )->withIdentifier (true ),
507+ 'name ' => (new ApiProperty ())->withReadable (true ),
508+ 'relatedDummy ' => (new ApiProperty ())
509+ ->withReadable (true )
510+ ->withReadableLink (true )
511+ ->withNativeType (Type::nullable (Type::object (RelatedDummy::class))),
512+ 'relatedDummies ' => (new ApiProperty ())
513+ ->withReadable (true )
514+ ->withReadableLink (true )
515+ ->withNativeType (Type::collection (Type::object (ArrayCollection::class), Type::object (RelatedDummy::class))),
516+ default => new ApiProperty (),
517+ };
518+ });
519+
520+ $ iriConverter = $ this ->createMock (IriConverterInterface::class);
521+ $ iriConverter ->method ('getIriFromResource ' )->willReturn ('/dummies/1 ' );
522+
523+ $ resourceClassResolver = $ this ->createMock (ResourceClassResolverInterface::class);
524+ $ resourceClassResolver ->method ('getResourceClass ' )->willReturn (Dummy::class);
525+ $ resourceClassResolver ->method ('isResourceClass ' )->willReturnCallback (fn ($ class ) => \in_array ($ class , [Dummy::class, RelatedDummy::class], true ));
526+
527+ $ propertyAccessor = $ this ->createMock (PropertyAccessorInterface::class);
528+ $ propertyAccessor ->method ('getValue ' )->willReturnCallback (function ($ object , $ property ) {
529+ return match ($ property ) {
530+ 'id ' => 1 ,
531+ 'name ' => 'Dummy with relationships ' ,
532+ 'relatedDummy ' => null ,
533+ 'relatedDummies ' => [],
534+ default => null ,
535+ };
536+ });
537+
538+ $ resourceMetadataCollectionFactory = $ this ->createMock (ResourceMetadataCollectionFactoryInterface::class);
539+ $ resourceMetadataCollectionFactory ->method ('create ' )->willReturn (
540+ new ResourceMetadataCollection ('Dummy ' , [
541+ (new ApiResource ())
542+ ->withShortName ('Dummy ' )
543+ ->withOperations (new Operations (['get ' => (new Get ())->withShortName ('Dummy ' )])),
544+ ])
545+ );
546+
547+ $ serializer = $ this ->createStub (Serializer::class);
548+
549+ $ normalizer = new ItemNormalizer (
550+ $ propertyNameCollectionFactory ,
551+ $ propertyMetadataFactory ,
552+ $ iriConverter ,
553+ $ resourceClassResolver ,
554+ $ propertyAccessor ,
555+ null ,
556+ null ,
557+ [],
558+ $ resourceMetadataCollectionFactory ,
559+ );
560+
561+ $ normalizer ->setSerializer ($ serializer );
562+
563+ $ result = $ normalizer ->normalize ($ dummy , ItemNormalizer::FORMAT , [
564+ 'resources ' => [],
565+ 'resource_class ' => Dummy::class,
566+ ]);
567+
568+ $ this ->assertIsArray ($ result );
569+ $ this ->assertArrayHasKey ('data ' , $ result );
570+ $ this ->assertArrayHasKey ('relationships ' , $ result ['data ' ]);
571+
572+ // Verify to-one relationship with null value returns {"data": null}
573+ $ this ->assertArrayHasKey ('relatedDummy ' , $ result ['data ' ]['relationships ' ]);
574+ $ this ->assertSame (['data ' => null ], $ result ['data ' ]['relationships ' ]['relatedDummy ' ]);
575+
576+ // Verify to-many relationship with empty array returns {"data": []}
577+ $ this ->assertArrayHasKey ('relatedDummies ' , $ result ['data ' ]['relationships ' ]);
578+ $ this ->assertSame (['data ' => []], $ result ['data ' ]['relationships ' ]['relatedDummies ' ]);
579+ }
491580}
0 commit comments