44
55use Illuminate \Contracts \Support \Arrayable ;
66use Illuminate \Database \Eloquent \Model ;
7+ use Illuminate \Database \Eloquent \Relations \AsPivot ;
8+ use Illuminate \Database \Eloquent \Relations \BelongsToMany ;
9+ use Illuminate \Database \Eloquent \Relations \Pivot ;
710use Illuminate \Database \Eloquent \Relations \Relation ;
8- use Illuminate \Http \Request ;
911use Illuminate \Http \Resources \Json \JsonResource ;
1012use Illuminate \Http \Resources \JsonApi \Exceptions \ResourceIdentificationException ;
13+ use Illuminate \Http \Resources \JsonApi \JsonApiRequest ;
1114use Illuminate \Http \Resources \JsonApi \JsonApiResource ;
15+ use Illuminate \Http \Resources \MissingValue ;
1216use Illuminate \Support \Arr ;
1317use Illuminate \Support \Collection ;
1418use Illuminate \Support \Str ;
@@ -32,13 +36,15 @@ trait ResolvesJsonApiElements
3236 /**
3337 * Resolves `data` for the resource.
3438 */
35- public function resolveResourceData (Request $ request ): array
39+ public function resolveResourceData (JsonApiRequest $ request ): array
3640 {
41+ $ resourceType = $ this ->resolveResourceType ($ request );
42+
3743 return [
3844 'id ' => $ this ->resolveResourceIdentifier ($ request ),
39- 'type ' => $ this -> resolveResourceType ( $ request ) ,
45+ 'type ' => $ resourceType ,
4046 ...(new Collection ([
41- 'attributes ' => $ this ->resolveResourceAttributes ($ request ),
47+ 'attributes ' => $ this ->resolveResourceAttributes ($ request, $ resourceType ),
4248 'relationships ' => $ this ->resolveResourceRelationshipIdentifiers ($ request ),
4349 'links ' => $ this ->resolveResourceLinks ($ request ),
4450 'meta ' => $ this ->resolveResourceMetaInformation ($ request ),
@@ -53,7 +59,7 @@ public function resolveResourceData(Request $request): array
5359 *
5460 * @throws ResourceIdentificationException
5561 */
56- protected function resolveResourceIdentifier (Request $ request ): string
62+ protected function resolveResourceIdentifier (JsonApiRequest $ request ): string
5763 {
5864 if (! is_null ($ resourceId = $ this ->toId ($ request ))) {
5965 return $ resourceId ;
@@ -72,7 +78,7 @@ protected function resolveResourceIdentifier(Request $request): string
7278 *
7379 * @throws ResourceIdentificationException
7480 */
75- protected function resolveResourceType (Request $ request ): string
81+ protected function resolveResourceType (JsonApiRequest $ request ): string
7682 {
7783 if (! is_null ($ resourceType = $ this ->toType ($ request ))) {
7884 return $ resourceType ;
@@ -91,7 +97,7 @@ protected function resolveResourceType(Request $request): string
9197 *
9298 * @throws \RuntimeException
9399 */
94- protected function resolveResourceAttributes (Request $ request ): array
100+ protected function resolveResourceAttributes (JsonApiRequest $ request, string $ resourceType ): array
95101 {
96102 $ data = $ this ->toAttributes ($ request );
97103
@@ -101,8 +107,11 @@ protected function resolveResourceAttributes(Request $request): array
101107 $ data = $ data ->jsonSerialize ();
102108 }
103109
110+ $ sparseFieldset = $ request ->sparseFields ($ resourceType );
111+
104112 $ data = (new Collection ($ data ))
105113 ->mapWithKeys (fn ($ value , $ key ) => is_int ($ key ) ? [$ value => $ this ->resource ->{$ value }] : [$ key => $ value ])
114+ ->when (! empty ($ sparseFieldset ), fn ($ attributes ) => $ attributes ->only ($ sparseFieldset ))
106115 ->transform (fn ($ value ) => value ($ value , $ request ))
107116 ->all ();
108117
@@ -116,7 +125,7 @@ protected function resolveResourceAttributes(Request $request): array
116125 *
117126 * @throws \RuntimeException
118127 */
119- protected function resolveResourceRelationshipIdentifiers (Request $ request ): array
128+ protected function resolveResourceRelationshipIdentifiers (JsonApiRequest $ request ): array
120129 {
121130 if (! $ this ->resource instanceof Model) {
122131 return [];
@@ -125,85 +134,123 @@ protected function resolveResourceRelationshipIdentifiers(Request $request): arr
125134 $ this ->compileResourceRelationships ($ request );
126135
127136 return [
128- ...$ this ->loadedRelationshipIdentifiers ,
137+ ...(new Collection ($ this ->filter ($ this ->loadedRelationshipIdentifiers )))
138+ ->map (function ($ relation ) {
139+ return ! is_null ($ relation ) ? $ relation : ['data ' => []];
140+ })->all (),
129141 ];
130142 }
131143
132144 /**
133145 * Compile resource relationships.
134146 */
135- protected function compileResourceRelationships (Request $ request ): void
147+ protected function compileResourceRelationships (JsonApiRequest $ request ): void
136148 {
137149 if ($ this ->loadedRelationshipsMap instanceof WeakMap) {
138150 return ;
139151 }
140152
141- $ this ->loadedRelationshipsMap = new WeakMap ;
153+ $ sparseIncluded = $ request ->sparseIncluded ();
154+
155+ $ resourceRelationships = (new Collection ($ this ->toRelationships ($ request )))
156+ ->mapWithKeys (fn ($ value , $ key ) => is_int ($ key ) ? [$ value => fn () => $ this ->resource ->{$ value }] : [$ key => $ value ])
157+ ->filter (fn ($ value , $ key ) => in_array ($ key , $ sparseIncluded ));
158+
159+ $ resourceRelationshipKeys = $ resourceRelationships ->keys ();
160+
161+ $ this ->resource ->loadMissing ($ resourceRelationshipKeys ->all ());
142162
143- $ this ->loadedRelationshipIdentifiers = (new Collection ($ this ->resource ->getRelations ()))
144- ->mapWithKeys (function ($ relations , $ key ) {
145- if ($ relations instanceof Collection) {
146- if ($ relations ->isEmpty ()) {
147- return [$ key => ['data ' => $ relations ]];
148- }
163+ $ this ->loadedRelationshipsMap = new WeakMap ;
149164
150- $ key = static ::resourceTypeFromModel ($ relations ->first ());
165+ $ this ->loadedRelationshipIdentifiers = $ resourceRelationships ->mapWithKeys (function ($ relationResolver , $ key ) {
166+ $ relatedModels = value ($ relationResolver );
151167
152- return [ $ key => [ ' data ' => $ relations -> map ( function ( $ relation ) use ( $ key ) {
153- return transform ([ $ key , static :: resourceIdFromModel ( $ relation )], function ( $ uniqueKey ) use ( $ relation ) {
154- $ this -> loadedRelationshipsMap [ $ relation ] = $ uniqueKey ;
168+ // Relationship is a collection of models...
169+ if ( $ relatedModels instanceof Collection ) {
170+ $ relatedModels = $ relatedModels -> values () ;
155171
156- return ['id ' => $ uniqueKey [1 ], 'type ' => $ uniqueKey [0 ]];
157- });
158- })]];
172+ if ($ relatedModels ->isEmpty ()) {
173+ return [$ key => ['data ' => $ relatedModels ]];
159174 }
160175
161- return [$ key => ['data ' => transform (
162- [static ::resourceTypeFromModel ($ relations ), static ::resourceIdFromModel ($ relations )],
163- function ($ uniqueKey ) use ($ relations ) {
164- $ this ->loadedRelationshipsMap [$ relations ] = $ uniqueKey ;
176+ $ relationship = $ this ->resource ->{$ key }();
177+
178+ $ isUnique = ! $ relationship instanceof BelongsToMany;
179+
180+ $ key = static ::resourceTypeFromModel ($ relatedModels ->first ());
181+
182+ return [$ key => ['data ' => $ relatedModels ->map (function ($ relation ) use ($ key , $ isUnique ) {
183+ return transform ([$ key , static ::resourceIdFromModel ($ relation )], function ($ uniqueKey ) use ($ relation , $ isUnique ) {
184+ $ this ->loadedRelationshipsMap [$ relation ] = [...$ uniqueKey , $ isUnique ];
165185
166186 return ['id ' => $ uniqueKey [1 ], 'type ' => $ uniqueKey [0 ]];
167- }
168- )]];
169- })->all ();
187+ });
188+ })]];
189+ }
190+
191+ // Relationship is a single model...
192+ $ relatedModel = $ relatedModels ;
193+
194+ if (is_null ($ relatedModel )) {
195+ return [$ key => null ];
196+ } elseif ($ relatedModel instanceof Pivot ||
197+ in_array (AsPivot::class, class_uses_recursive ($ relatedModel ), true )) {
198+ return [$ key => new MissingValue ];
199+ }
200+
201+ return [$ key => ['data ' => [transform (
202+ [static ::resourceTypeFromModel ($ relatedModel ), static ::resourceIdFromModel ($ relatedModel )],
203+ function ($ uniqueKey ) use ($ relatedModel ) {
204+ $ this ->loadedRelationshipsMap [$ relatedModel ] = [...$ uniqueKey , true ];
205+
206+ return ['id ' => $ uniqueKey [1 ], 'type ' => $ uniqueKey [0 ]];
207+ }
208+ )]]];
209+ })->all ();
170210 }
171211
172212 /**
173213 * Resolves `included` for the resource.
174214 */
175- public function resolveIncludedResources (Request $ request ): array
215+ public function resolveIncludedResources (JsonApiRequest $ request ): array
176216 {
217+ if (! $ this ->resource instanceof Model) {
218+ return [];
219+ }
220+
177221 $ this ->compileResourceRelationships ($ request );
178222
179223 $ relations = new Collection ;
180224
181- foreach ($ this ->loadedRelationshipsMap as $ relation => $ uniqueKey ) {
225+ foreach ($ this ->loadedRelationshipsMap as $ relation => $ value ) {
182226 $ resourceInstance = rescue (fn () => $ relation ->toResource (), new JsonApiResource ($ relation ), false );
183227
184228 if (! $ resourceInstance instanceof JsonApiResource &&
185229 $ resourceInstance instanceof JsonResource) {
186230 $ resourceInstance = new JsonApiResource ($ resourceInstance ->resource );
187231 }
188232
233+ [$ type , $ id , $ isUnique ] = $ value ;
234+
189235 $ relations ->push ([
190- 'id ' => $ uniqueKey [1 ],
191- 'type ' => $ uniqueKey [0 ],
236+ 'id ' => $ id ,
237+ 'type ' => $ type ,
238+ '_uniqueKey ' => $ isUnique === true ? [$ id , $ type ] : [$ id , $ type , (string ) Str::random ()],
192239 'attributes ' => Arr::get ($ resourceInstance ->resolve ($ request ), 'data.attributes ' , []),
193240 ]);
194241 }
195242
196- return $ relations ->uniqueStrict (
197- fn ($ relation ): array => [ $ relation[ ' id ' ], $ relation [ ' type ' ]]
198- ) ->all ();
243+ return $ relations ->uniqueStrict (fn ( $ relation ) => $ relation [ ' _uniqueKey ' ])
244+ -> map ( fn ($ relation ) => Arr:: except ( $ relation, [ ' _uniqueKey ' ]))
245+ ->all ();
199246 }
200247
201248 /**
202249 * Resolve the links for the resource.
203250 *
204251 * @return array<string, mixed>
205252 */
206- protected function resolveResourceLinks (Request $ request ): array
253+ protected function resolveResourceLinks (JsonApiRequest $ request ): array
207254 {
208255 return $ this ->toLinks ($ request );
209256 }
@@ -213,7 +260,7 @@ protected function resolveResourceLinks(Request $request): array
213260 *
214261 * @return array<string, mixed>
215262 */
216- protected function resolveResourceMetaInformation (Request $ request ): array
263+ protected function resolveResourceMetaInformation (JsonApiRequest $ request ): array
217264 {
218265 return $ this ->toMeta ($ request );
219266 }
0 commit comments