0

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 :/

3
  • why do you need this ReportCountry model? This seems to be just a pivot table connecting Reports and Countries in a many-to-many relation Commented Sep 26, 2024 at 12:58
  • @krisgjika I don't, that was the question... Commented Sep 27, 2024 at 13:29
  • no your question is about displaying nested relations, but this is not a nested relation at all! laravel.com/docs/11.x/eloquent-relationships#many-to-many Commented Sep 30, 2024 at 7:39

1 Answer 1

0

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.