Patrick Simard
Patrick Simard

Reputation: 2375

Laravel resource routes with id's in the URL and model association

I have a laravel app with a bunch of CRUDS that has LIST, VIEW, EDIT, CREATE and DELETE. It's all working great. The problem I have thought is that one of those CRUDS will be filtered based on the company ID a user owns. The setup is that 1 user can own many companies and every company can have many offers. So the offer CRUD needs to be filtered based on the company id.

Before my changes the route looked like this

Route::resource('offers', 'OfferController');

So that ressource route takes care of generating the diffrent URLS

The resource seems to magically relate the offer ID with the model even if the ID is not specified in the route. And that's awesome. But it does not work anymore as soon as you add a new var in the URL.

My route now looks like this

Route::resource('{merch}/offers', 'OfferController');

Because there's a second ID involved the offer model can no longer "Auto relate"

After adding {merch} in the ressource I end up with a URL that looks like this:

Where 25 is the company ID and 10 is the offer ID

The controller before the add of the {merch} var in the URL looked like this

public function show(Offer $offer)
{
    abort_if(Gate::denies('offer_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');

    $offer->load('categories', 'tags');

    return view('admin.offers.show', compact('offer'));
}

So I changed that to:

public function show($merch, Offer $offer)
{
    abort_if(Gate::denies('offer_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');

    $offer->load('categories', 'tags');

    return view('admin.offers.show', compact('offer', 'merch'));
}

The problem I have is that $offer seems undefined now. It does not seem to find the actual offer ID anymore and can't seem to relate anything. The error I get talks about categories is not an instance of the collection $offer.

Note that before adding {merch} it was all working great. How can I benefit from the Laravel "magic" association when the URL has 2 ids?

When I saw that Offer $offer no longer relates I thought I might need to replace the route with something more specific. So I eded up doing something like this:

// Offers
Route::get('{merch}/offers', 'OfferController@index');
Route::get('{merch}/offers/create', 'OfferController@create');
Route::post('{merch}/offers/store', 'OfferController@store');
Route::get('{merch}/offers/{id}/edit', 'OfferController@edit');
Route::put('{merch}/offers/{id}/update', 'OfferController@update');
Route::delete('{merch}/offers/{id}/delete', 'OfferController@destroy');
Route::get('{merch}/offers/{id}', 'OfferController@show');

But still, Offer $offer does not relate to the {id}

Upvotes: 2

Views: 1334

Answers (1)

Patrick Simard
Patrick Simard

Reputation: 2375

Ok so after reading the laravel documentation theres a way called nested ressources to do exacly waht i needed. It goes like this:

Since the {merch} var i was looking for was the merchant ID, Theres a way to use resource to nest both of them in one call like this:

Route::resource('merchants.offers', 'OfferController');

The URL would then be

  • merchants/25/offers (List)
  • merchants/25/offers/10 (Show)
  • merchants/25/offers/10/edit (Edit)
  • merchants/25/offers/create (Create)
  • merchants/25/offers/10/update (Saving edit)
  • merchants/25/offers/10/store (Saving create)
  • merchants/25/offers/10/delete (Destroy offer)

The controller would then look like this

public function show(Merchant $merchant, Offer $offer)
{
    abort_if(Gate::denies('offer_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');
    $offer->load('categories', 'tags');
    return view('admin.offers.show', compact('offer', 'merchant'));
}

And just like that everything was solved and worked as expected!

Upvotes: 1

Related Questions