We are currently developing a feature in codotto.com where a user can comment on an IT meetup. Each comment can have an answer to it. We are only allowing for one-level deep answers, so something like:
- 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 the answers
as a 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 that do not have 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 App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
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 the answers
to it?
As @matialauriti pointed out, you cant use resource collections inside 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 formation to a private method and re-use 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,
];
}
}