Leandro Leandro - 4 months ago 8
PHP Question

Eager loaded model returns empty set for parent model property

I am trying to eager load two models with their parent model. I get a null set for the relationship function though.

Here is how I am loading the models:

Master_Key controller (show method):

$master_keys = Version2015_Master_Key::with('version2015_question')->paginate(50);
return view('masterkey/show',compact('master_keys'));


Response controller (show method):

$responses = Version2015_Response::with('version2015_question')->paginate(50);
return view('response/show',compact('responses'));


I want to display master keys and responses alongside with the matching questions.
Here is as snippet from my
show.blade.php
for the master key controller:

<table class="table">
<tr>
<th>Ref Number</th>
<th>Question</th>
<th>Expected Response</th>
<th>Additional Information</th>
</tr>

@foreach ($master_keys as $master_key)
<tr>
<td>{{ $master_key->question_num }} </td>
<td>{{ $master_key->version2015_question }} </td>
<td>{{ $master_key->expected_response }} </td>
<td>{{ $master_key->additional_information }} </td>
</tr>
@endforeach
</table>


The line
<td>{{ $master_key->version2015_question }} </td>
is the only one that tries to show data from the parent table. It actually should be
<td>{{ $master_key->version2015_question->question_text }} </td>
-> but when I try to do this it throws an error saying that I am trying to access a property from a non-object. And
<td>{{ $master_key->version2015_question }} </td>
returns an empty set. All the other properties return the expected data (since they belong to the master key model itself).

There are three models:
Version2015_Question
,
Version2015_Response
and
Version2015_Master_Key
. They are all in the name space
app\version2015
. I am gonna post only two of them (since Response and Master Key have the same structure and same behavior):

Question Model

use Illuminate\Database\Eloquent\Model;

class Version2015_Question extends Model
{
protected $table = 'Version2015_questions';
protected $primaryKey = 'question_num';
public $timestamps = false;

public function version(){
return $this->belongsTo('App\Version');
}

public function Version2015_responses(){
return $this->hasMany('App\Version2015\Version2015_Response','question_num','question_num');
}

public function version2015_master_keys(){
return $this->hasMany('App\Version2015\Version2015_Master_Key','question_num','question_num');
}

protected $fillable = ['question_num','question_text','version_id'];
}


Response Model:

namespace App\Version2015;
use Illuminate\Database\Eloquent\Model;
use App\Encryptable;

class Version2015_Response extends Model
{
use Encryptable;
protected $fillable = ['response','additional_information', 'question_num', 'vendor_id', 'label_id', 'group_id'];

protected $encryptable = ['response','additonal_information'];

protected $table = 'version2015_responses';

public $timestamps = false;

public function version2015_question(){

return $this->belongsTo('App\Version2015\Version2015_Question','question_num','question_num');
}

public function vendor(){
return $this->belongsTo('App\Vendor');
}

public function group(){
return $this->belongsTo('App\Group');
}

public function response_label(){
return $this->belongsTo('App\Response_Label');
}
}


I am not going to post the migrations because there's too much code in this post already. When I run
php artisan migrate
it works fine. When I create
responses
and
master keys
there's no issue with the foreign keys referenced in the
questions
table. So I assume there's no problem in the database side. I believe it has something to do with the models and relationships. I've spent several hours reviewing the whole code and looking up other threads. I didn't come to any conclusion for why it doesn't pull the question data from the
questions
model. I am doing this kind of query with many other models in my application, but it only happens with those three - which happen to be the only ones in their own name space.



    • Update - -




Version2015_question model as @MarcoAurélioDeleu requested

namespace App\Version2015;

use Illuminate\Database\Eloquent\Model;

use App\Encryptable;

class Version2015_Master_Key extends Model
{
use Encryptable;

public $timestamps = false;

protected $table = 'version2015_master_keys';

protected $fillable = ['question_num','expected_response','group_id','label_id','additional_information'];

protected $encryptable = ['expected_response','additional_information'];

public function version2015_question(){
//return $this->belongsTo('App\version2015\version2015_Question','question_num','question_num');
return $this->belongsTo('App\Version2015\Version2015_Question');
}

public function master_key_label(){
return $this->belongsTo('App\Master_Key_Label');
}

public function group(){
return $this->belongsTo('App\Group');
}
}

Answer

I found a solution! All I had to do was to explicitly set which primary key Laravel should look for in the parent model since it is not following the convention "id" but "question_number". I was doing this before but with an extra argument return $this->belongsTo('App\version2015\version2015_Question','question_num','question_num');, then I tried without any argument return $this->belongsTo('App\version2015\version2015_Question');. However, I should simply point what is the primary key in the parent table: return $this->belongsTo('App\version2015\version2015_Question','question_num');. Same thing for the other parent table return $this->belongsTo('App\Master_Key_Label','label_id');.

Besides that, the parent table doesn't need this explicit definition for their children as I was doing: return $this->hasMany('App\version2015\version2015_Response','question_num','question_num');. It only needs the child model name.

So, this is how my models look like now:

Question Model

use Illuminate\Database\Eloquent\Model;

class Version2015_Question extends Model
{
    protected $table = 'Version2015_questions';
    protected $primaryKey = 'question_num';
    public $timestamps = false;

    public function version(){
        return $this->belongsTo('App\Version');
    }

    public function Version2015_responses(){
        return $this->hasMany('App\Version2015\Version2015_Response');
    }

    public function version2015_master_keys(){
        return $this->hasMany('App\Version2015\Version2015_Master_Key');
   }

    protected $fillable = ['question_num','question_text','version_id'];
}

Response Model:

namespace App\Version2015;
use Illuminate\Database\Eloquent\Model;
use App\Encryptable;

class Version2015_Response extends Model
    {
        use Encryptable;
        protected $fillable = ['response','additional_information', 'question_num', 'vendor_id', 'label_id', 'group_id'];

        protected $encryptable = ['response','additonal_information'];

        protected $table = 'version2015_responses';

        public $timestamps = false;

        public function version2015_question(){

        return $this->belongsTo('App\Version2015\Version2015_Question','question_num');
     }

    public function vendor(){
        return $this->belongsTo('App\Vendor');
    }

    public function group(){
        return $this->belongsTo('App\Group');
    }

    public function response_label(){
        return $this->belongsTo('App\Response_Label', 'label_id');
    }
}

Same goes for Master Key model as in Response Key.

I also noticed another problem. For some reason I could not eager load the related models even with the right settings. My controllers had this:

  $responses = version2015_Response::with('version2015_question')
  ->where('label_id','=',$label->id)
  ->paginate(50);

Now I have to do this:

  $responses = version2015_Response::where('label_id','=',$label->id)
  ->paginate(50);

Well, with it all done everything works fine. I hope it helps other people with similar problems.

Comments