2

I am trying to load a relationship for a model that is retrieved using route model binding via the load() method, but it doesn't seem to work. Any ideas on what I am doing wrong?

public function update(UpdateOrganisationFormRequest $request, Organisation $organisation)
{
    $organisation->load('contacts');

    dd(
        $organisation->contacts, // returns empty collection
        Organisation::first()->contacts // returns values I generated via factory
    );
}

These answers suggest that this is the correct way to do this:

Eager loading with route model binding

Laravel Route model binding with relaionship

https://laracasts.com/discuss/channels/laravel/laravel-model-binding-with-eager-loading

Here are my relationship definitions:

class Organisation extends Model
{
    public function contacts()
    {
        return $this->hasMany(OrganisationContact::class);
    }
}

class OrganisationContact extends Model
{
    public function organisation()
    {
        return $this->belongsTo(Organisation::class);
    }
}

I have considered Customizing The Resolution Logic but seems a little overkill as I only need some relationships loaded for this update process.

Any help would be appreciated! Thank you.

Update

Thanks to the comments I realised that the route model binding is not returning anything. For some further context... I am running a test where I generate an Organisation model, then hitting the update route for this resource, the code looks like this:

Test class:

/** @test */
public function existing_organisation_can_be_updated()
{
    // Arrange
    $organisation = factory(Organisation::class)->create();

    factory(OrganisationContact::class)->create([
        'organisation_id' => $organisation->id,
    ]);

    /*
        note:
        dd( route('admin.organisation.update', $organisation->id) )
        produces: "http://mydevurl.dev/admin/organisation/1"
    */

    // Act
    $response = $this->put(route('admin.organisation.update', $organisation->id), [
        'name' => 'New name',
    ]);

    // ... assertions and other stuff
}

Resource controller

public function update(UpdateOrganisationFormRequest $request, Organisation $organisation)
{
    // this return null ???
    dd(
        $organisation->id    
    );

    // update logic ...
}

Update 2 - schema

_create_organisations_table.php

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateOrganisationsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('organisations', function (Blueprint $table) {
            $table->increments('id');
            // some other fields
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('organisations');
    }
}

_create_organisation_contacts

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateOrganisationContacts extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('organisation_contacts', function (Blueprint $table) {
            $table->increments('id');
            // some other fields
            $table->timestamps();

            // the relationship
            $table->foreign('organisation_id')->references('id')->on('organisations')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('organisation_contacts');
    }
}
haakym
  • 12,050
  • 12
  • 70
  • 98
  • 1
    Are you sure that `$organisation` from parameter and `Organisation::first()` are the same? – Laerte Dec 12 '17 at 13:38
  • 2
    Show your schema, please. You may also want to check if that particular organization has any contacts. – aynber Dec 12 '17 at 13:40
  • try this `Organisation::find($organisation->id)->contacts` and see if there are some !! – Maraboc Dec 12 '17 at 13:50
  • @Laerte Yes, however the route model binding doesn't seem to work, see the update to the question I made – haakym Dec 12 '17 at 14:23
  • @aynber I added the schema, see update 2 on the question. The organisation has contacts as I'm generating them with a factory in a test. The issue seems to be with the route model binding not working (clearly I'm doing something wrong?) – haakym Dec 12 '17 at 14:28
  • @Maraboc see the updates to my question. `$organisation->id` returns null, so it seems the model is not being retrieved properly, any ideas why? – haakym Dec 12 '17 at 14:29
  • What happens if you do `dd($organisation);` ? – aynber Dec 12 '17 at 14:34
  • @aynber `dd($organisation)` returns an empty `Organisation` object... I just remembered this project I am working on went through an upgrade so it may be something to do with that! Currently 5.5 and was previously 5.3, time to check the docs upgrade guide! – haakym Dec 12 '17 at 14:39
  • My `app\Http\Kernel.php` has `\Illuminate\Routing\Middleware\SubstituteBindings::class,` in the `web` `$middlewaresGroup` and `'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,` in `$routeMiddleware` - so perhaps this isn't an issue? – haakym Dec 12 '17 at 14:50
  • When I run the code through the browser `dd($organisation)` in the update method returns the `Organisation` model with its current info in the DB. Why is this not working for the test? – haakym Dec 12 '17 at 15:07
  • 1
    Ahhh it's because I had the `WithoutMiddleware` trait being used which pulled out the binding which is managed via the middleware – haakym Dec 12 '17 at 15:11
  • 1
    You ffigureitout your self ;) nice ! – Maraboc Dec 12 '17 at 15:13
  • @Maraboc Thanks! And thanks a lot for your comments you definitely helped me get to the solution. Hopefully, this benefitted you too - lesson learnt: do not use `WithoutMiddleware` in tests with your eyes closed! – haakym Dec 12 '17 at 15:15
  • agree, but not all the time because there are some cases where you have to disable midlewares for test for example disable CSRF token midleware ... !! – Maraboc Dec 12 '17 at 15:31
  • Sure, it's not an absolute truth but the cause for why the route model binding wasn't working. How to get both working would be interesting! – haakym Dec 12 '17 at 18:55

1 Answers1

2

The Organisation model is not retrieved via route model bindings because the WithoutMiddleware trait is being used in the test!

As of Laravel 5.3 route model binding is accomplished via Middleware:

Binding Substitution Middleware

Route model binding is now accomplished using middleware. All applications should add the Illuminate\Routing\Middleware\SubstituteBindings to your web middleware group in your app/Http/Kernel.php file

...

https://laravel.com/docs/5.3/upgrade#upgrade-5.3.0

By using the WithoutMiddleware trait you are stopping the route model binding from being triggered.

Remove the WithoutMiddleware for it to function as expected.

See further discussion here: https://github.com/laravel/framework/issues/15163

haakym
  • 12,050
  • 12
  • 70
  • 98