Search code examples
phplaravel-5many-to-manypolymorphic-associations

Why is this polymorphic many to many relationship returning empty? Laravel 5.3


I have polymorphic many to many relationships set up between my user tables and some of their data. At the moment it's returning empty and I don't know why. The relationship looks a bit like this:

polymorphic

Since the users have exactly the same setup, and I'm testing the agency code at the moment.

I'm trying to call the description of the language in the languages table for each language the agency has tagged. Something like this:

public function compose(View $view) {

        $loggedinagency = Auth::user()->id;
        $agency = Agency::with('languages')->findOrFail($loggedinagency);

        $view->with(compact('agency', 'loggedinagency'));

    }

In the view blade: 

 <p> @foreach($agency->languages as $Lang)
          {{ $Lang->description }}<br>
      @endforeach</p> 

Languageable model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Languageable extends Model
{
    protected $table = 'languageables'; 



  protected $fillable = ['language_id','languageable_id', 'languageable_type']; 

}

Language model:

<?php

namespace App;

use App\Agency;

use Illuminate\Database\Eloquent\Model;

class Language extends Model
{
     protected $table = 'languages'; 

    public function agencyies() {

      return  $this->morphedByMany('Agency', 'Languageable');
   } 
}

Agency model:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use App\languageable;
use App\Language;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Agency extends Authenticatable

    public function languages() {

          return  $this->morphToMany(Language::class, 'Languageable');
        } 
}

Schema:

languageables:

Schema::create('languageables', function (Blueprint $table){

            $table->increments('id');
            $table->integer('language_id');
            $table->foreign('language_id')->references('id')->on('languages');
            $table->integer('languageable_id');
            $table->string('languageable_type', 31);
            $table->timestamps();
        });

languages:

Schema::create('languages', function (Blueprint $table){

                $table->increments('id');
                $table->string('subcategory', 31);
                $table->string('description', 127);
            });

the info is stored in the languageables table like so:

languageables table info

It will show nothing, and querying with tinker will return a null when I try to var_dump a model call with the relationship. Is there something I've missed or gotten wrong? There's no error so in theory it should be working, but perhaps laravel is looking for the wrong id or data point in the database?

Edit: The debugbar is showing the following query being performed:

select `languages`.*, `Languageables`.`Languageable_id` as `pivot_Languageable_id`, `Languageables`.`language_id` as `pivot_language_id` from `languages` inner join `Languageables` on `languages`.`id` = `Languageables`.`language_id` where `Languageables`.`Languageable_id` in ('196') and `Languageables`.`Languageable_type` = 'App\Agency'

Solution

  • Ok. So I've solved the problem. The problem would have been immediately apparent to those more experienced with SQL and databases but it took a while for me to spot it.

    The clue was in the SQL query being thrown up by my debugger which I definitely recommend: Laravel Debugbar.

    select `languages`.*, `Languageables`.`Languageable_id` as `pivot_Languageable_id`, `Languageables`.`language_id` as `pivot_language_id` from `languages` inner join `Languageables` on `languages`.`id` = `Languageables`.`language_id` where `Languageables`.`Languageable_id` in ('196') and `Languageables`.`Languageable_type` = 'App\Agency'
    

    If you read through that, you'll see it's a pretty standard pivot query but at the end there, notice how it says 'App\Agency'? Yeah. That's what it's looking for. It's checking in the Languageable_type for 'App\Agency'.

    Those of you familiar with laravel will know that's the namespaced name. The normal reference for the model and how I stored it is simply Agency. This might be a bug in how laravel does polymorphic's.

    So the problem is now clear. I've stored the model as Agency, and the query is looking for App\Agency. It finds nothing, and returns null.

    My solution was to change the way data is written to the database and reimport the data onto the table to include the namespaces. I have no idea how to fix this at the backend.

    My research stumbled onto this which might help some of you.

    For the newbies out there. A null return without an error generally means a correct SQL query, but it's looking for the wrong thing. This can be hard to figure out in relational setups, so your best bet is always to check what the SQL query that laravel has generated is looking for. For instance, in pivot tables, the only type of information laravel will look for is an id in lowercase. If you store a set of names in a pivot table and try to use that as your comparison, it will return empty.