We are currently developing a feature in codotto.com where users can leave comments on IT Meetups. Every comment can have an answer. We only allow one level of deep answers, for example:
- Comment 1 - Answer to comment 1 - Answer to comment 1 - Comment 2 - Answer to comment 2 - Answer to comment 2
I have the following database structure:
// meetup_messages - id - user_id - meetup_id - meetup_message_id (nullable) -> comments that do not answer will have this set to nullable
In my model I define answers
as HasMany
relationship:
class MeetupMessage extends Model { // ... public function answers(): HasMany { return $this->hasMany(self::class, 'meetup_message_id'); } }
Then on my controller I get all comments without answers:
public function index( IndexMeetupMessageRequest $request, Meetup $meetup, MeetupMessageService $meetupMessageService ): MeetupMessageCollection { $meetupMessages = MeetupMessage::with([ 'user', // 'answers' => function ($query) { // $query->limit(3); // } 'answers' ]) ->whereNull('meetup_message_id') ->whereMeetupId($meetup->id) ->paginate(); return new MeetupMessageCollection($meetupMessages); }
Then on my MeetupMessageCollection
:
class MeetupMessageCollection extends ResourceCollection { public function toArray($request) { return parent::toArray($request); } }
Then on my MeetupMessageResource
:
<?php namespace AppHttpResources; use IlluminateHttpResourcesJsonJsonResource; use IlluminateSupportCollection; class MeetupMessageResource extends JsonResource { public function toArray($request) { return collect([ // 'answers' => new MeetupMessageCollection($this->whenLoaded('answers')), ]) ->when( is_null($this->meetup_message_id) && $this->relationLoaded('answers'), function (Collection $collection) { $collection->put('answers', MeetupMessageCollection::collection($this->answers)); } ); } }
But I get the following error: Call to undefined method App\Models\Meetup\MeetupMessage::mapInto()
. How can I still use MeetupMessageCollection
by passing answers
to it?
P粉6961462052024-01-03 09:05:21
As @matialauriti pointed out, you cannot use resource collections within collections in Laravel
class MeetupMessageResource extends JsonResource { public function toArray() { return [ 'answers' => new MeetupMessageCollction($this->answers) // ❌ You can't do this ] } }
My solution was to pull my resource form into a private method and reuse it if answers
is present:
class MeetupMessageResource extends JsonResource { public function toArray($request) { return collect($this->messageToArray($this->resource)) ->when($this->relationLoaded('user'), function (Collection $collection) { $collection->put('user', $this->userToArray($this->user)); }) // ✅ Now I don't need to use Resources inside my API Resource class ->when( is_null($this->meetup_message_id) && $this->relationLoaded('answers'), function (Collection $collection) { $answers = $this ->answers ->map(function (MeetupMessage $answer) { return array_merge( $this->messageToArray($answer), ['user' => $this->userToArray($answer->user)] ); }); $collection->put('answers', $answers); } ); } private function messageToArray(MeetupMessage $meetupMessage): array { return [ 'id' => $meetupMessage->id, 'message' => Purify::config(MeetupMessageService::CONFIG_PURIFY)->clean($meetupMessage->message), 'answersCount' => $this->whenCounted('answers'), 'createdAt' => $meetupMessage->created_at, ]; } }