I have a fairly common setup - 2 main models and 1 pivot with additional information regarding their relationship. I'm using Laravel API Resources and they work great, but I can't figure out a way how to display the data without the need of also displaying the pivot data.
Let's ilustrate with simplified situation:
Models:
class Report extends BaseModel
{
public function reportCountries(): HasMany
{
return $this->hasMany(ReportCountry::class, 'report', 'id');
}
}
class ReportCountry extends BaseModel
{
public function report(): BelongsTo
{
return $this->belongsTo(Report::class, 'report', 'id');
}
public function countryEntity(): BelongsTo
{
return $this->belongsTo(Country::class, 'country', 'id');
}
}
class Country extends BaseModel
{
protected $table = 'countries';
}
The Controller:
class ReportsController
{
public function show(int $id): ReportResource
{
$report = Report::with(['reportCountries.countryEntity'])->findOrFail($id);
return new ReportResource($report);
}
}
And finally the Resources:
/**
* @mixin Report
*/
class ReportResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'countries' => ReportCountryResource::collection($this->whenLoaded('reportCountries')), // I'd love to use CountryResource here instead
// bunch of other data
];
}
}
/**
* @mixin ReportCountry
*/
class ReportCountryResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id, // I don't really need to show this, I'm interested only in CountryResource below:
'country' => new CountryResource($this->whenLoaded('countryEntity')),
];
}
}
/**
* @mixin Country
*/
class CountryResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
];
}
}
What I would love to do is ignore ReportCountryResource
and load CountryResource
directly in ReportResource
, something like this:
/**
* @mixin Report
*/
class ReportResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'countries' => CountryResource::collection($this->whenLoaded('reportCountries.countryEntity')),
// bunch of other data
];
}
}
But when I tried to load this way, the CountryResource
was displaying data from the pivot (ReportCountryResource
) and not from the main model (CountryResource
).
Is there a way how to do this? I'm now creating bunch of Resources for pivot models, which I don't really need :/
So after more failing and tweaking I got it working by using another HasManyThrough
relationship.
So in my model I now have this:
class Report extends BaseModel
{
public function reportCountries(): HasMany
{
return $this->hasMany(ReportCountry::class, 'report', 'id');
}
public function countries(): HasManyThrough
{
return $this->hasManyThrough(Country::class, ReportCountry::class, 'report', 'id', 'id', 'country');
}
}
which allows me to ditch the pivot resources
class ReportsController
{
public function show(int $id): ReportResource
{
$report = Report::with(['countries'])->findOrFail($id);
return new ReportResource($report);
}
}
class ReportResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'countries' => CountryResource::collection($this->whenLoaded('countries')),
// bunch of other data
];
}
}
So I'm using this approach unless someone points something better