How to use eager loading for related models in Laravel - php

I have the following models:
Appointment [ belongsTo Timeslot | belongsTo User ]
Timeslot [ hasMany Appointment ]
User [ hasOne Appointment | HasOne Details ]
Details [ belongsTo User ]
I am trying to eager load the Details data starting from the Appointment model ( Appointment has a user, which has a Details record) using the following query:
$apps = Appointment::with('timeslot')->with('user')->with('user.details')->get();
This throws the following error in Builder.php
Call to a member function addEagerConstraints() on a non-object
Why am I calling on a non-object here and why is my query not working?
EDIT:
This is the relation on my User model:
public function details() {
dd($this->role_id);
switch($this->role_id) {
case 3:
return $this->hasOne('App\CandidateDetails', 'user_id');
break;
case 2:
return $this->hasOne('App\EmployerDetails', 'user_id');
break;
}
}
i'm aware that this would be better implemented using a pivot table, it's been a learning process. the dd() returns a null when called by my query, yet it works fine in other calls. What's going on here?

Make sure you have return in all your relation methods. It seems that one of them is not returning a Relation definition.
You can't use $this in relation definitions - attributes of the model will be uninitiated when query is built, so $this->role_id will give null and no relation will be returned.
In order to make it work you should define 2 separate relations:
// User.php
public function candidateDetails() {
return $this->hasOne('App\CandidateDetails', 'user_id');
}
public function cemployerDetails() {
return $this->hasOne('App\EmployerDetails', 'user_id');
}
public function getDetailsAttribute() {
switch($this->role_id) {
case 3:
return $this->candidateDetails;
case 2:
return $this->employerDetails;
}
}
// ...and then...
$user = User::with('candidateDetails', 'employerDetails')->findOrFail($userId);
// depending on user's role you'll get either candidate or employer details here
$details = $user->details;

Related

1 to 1 and 0 to many relationship Laravel not working

I am struggling with an eloquent request. Let me explain what I want to do:
I have two models: User and Item
One User can have many Item and one Item belongs to One user.
I wrote the two method for this relation in my models as followed:
class Item extends Model
{
public function user() {
return $this->belongsTo(User::class);
}
}
class User extends Model {
public function items() {
return $this->hasMany(Item::class, 'items', 'user_id', 'user_people_id');
}
}
I try to access to the items from my controller its user's relation with:
public function index()
{
$items = Item::with('user')->get();
dd($items);
FYI: I seeded my items table with 10 items and my user table with 4 users:
items table:
users table:
My problem is that in the when I check my query with dd() here is what I get: Only the 4 first items get the relation, the others 6 return a null value
Relation working:
Relation returning null:
Thank you for helping me!
According to Laravel doc, hasMany relationship parameters are the following:
return $this->hasMany(Myclass::class, 'foreign_key', 'local_key');
So try to change your relationship in your User class like that
// change this
return $this->hasMany(Item::class, 'items', 'user_id', 'user_people_id');
// to this
return $this->hasMany(Item::class, 'user_people_id', 'id');
The easiest solution would be to rename your foreign key to user_id. That is what Laravel expects, so you won't need to deal with extra arguments in your hasMany() functions.
If you can't do that I think this'll work: return $this->hasMany(Item::class, 'user_people_id');.
Please try to add all in your query to see if it will work:
Update
public function index()
{
$items = Item::all();
dd($items);
}

Laravel relationships: How do I retrieve all courses to which a collection of assessment tests belong?

Am not a pro in neither PHP nor Laravel and I think I am cornered. In my assessment app, I have courses, lessons and assessment tests. An assessment_test has many to one relationship with lesson. lesson also has many to one relationship with course.
Now here is the problem, How do I retrieve all courses to which a collection of assessment_tests belong?
As shown in the code snippets provides, I tried to get around this by manually looping through the collection and saving the courses to an array but I got some weird error.
Trying to get property 'course' of non-object
Here is my problematic function
public function index()
{
$attempts=AssessmentAttempt::all();
$i=0;
$courses=array();
foreach ($attempts as $attempt) {
// dd($attempt->lesson->course);
$courses[$i++]=$attempt->lesson->course;
}
dd(array_unique($courses));
return view('achievements.index', ['attempts'=>$attempts, 'courses'=>$courses]);
}
AssessmentAttempt model
public function lesson()
{
return $this->belongsTo(Lesson::class);
}
Lesson model
public function course()
{
return $this->belongsTo(Course::class);
}
public function assessmentAttempts()
{
return $this->hasMany(AssessmentAttempt::class);
}
in the assesment_modal define the relation ship with course and make the course id column as a foreign_key in the assesment_model table
return $this->hasMany(Course::class, 'foreign_key');
also design the relation in course model
return $this->belongsTo(Assesment_Model::class);

How to load a nested relationship in Laravel

I have a model called CallbackRequest the model has a relationship with Loan model and that is the only relationship for CallbackRequest model.
CallbackModel:
public function loan() {
return $this->belongsTo(Loan::class);
}
Now Loan model itself has a relationship with a third model called Applicant.
Loan Model:
public function applicant() {
return $this->belongsTo(Applicant::class);
}
My point:
When I load CallbackRequest I eagerload loan model with it, all fine! But now I am wondering if there is a way to eagerload applicant model when I do:
Right now I access it like:
$modelResults = PublicCallback::with('loan')->get();
I get all callbacks with loan eagerloaded, but my point is I want when I eagerload loans in this case I want applicant to be loaded also !
Is there any way how to do this, is it possible ?
You can do this with:
$modelResults = PublicCallback::with(['loan', 'loan.applicant'])->get();
Ref: https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
Just for posterity, there's also another way of loading nested relationships that can be done against a returned model, provided you have set up the relationships correctly:
Posts model:
public function comments() {
return $this->hasMany('App\Comment', 'quote_id', 'id');
}
Comments model:
public function user() {
return $this->belongsTo('App\User');
}
Then you can actually infer the user via relationship to a comment by drawing the post but loading an array of relations, eg:
$post = \App\Post::find($post_id);
return $post->load(['comments','comments.user']);

use relationship in model accessor in laravel

Suppose I have a Course model like this :
class Course extends Model
{
public $primaryKey = 'course_id';
protected $appends = ['teacher_name'];
public function getTeacherNameAttribute ()
{
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
}
public function teacher ()
{
return $this->belongsTo('App\User', 'teacher', 'user_id');
}
}
And in the other hand there is a User model like this :
class User extends Authenticatable
{
public $primaryKey = 'user_id';
protected $appends = ['full_name'];
public function getFullNameAttribute ()
{
return $this->name . ' ' . $this->family;
}
public function course ()
{
return $this->hasMany('App\Course', 'teacher', 'user_id');
}
}
As you can see there is a hasMany relationship between those.
There is an full_name accessor in User model.
Now I want to add a teacher_name accessor to Course model that uses it's teacher relations and gets full_name of teacher and appends to Course always.
In fact I want whenever call a Course model, it's related teacher name included like other properties.
But every time , when call a Course model , I got this error :
exception 'ErrorException' with message 'Trying to get property of non-object' in D:\wamp\www\lms-api\app\Course.php:166
That refers to this line of Course model :
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
I do not know how can I solve that and what is problem exactly.
Yikes some interesting answers here.
FYI to those coming after me- getFooAttribute() should return the data, and not modify the internal attributes array.
If you set a new value in the attributes array (that doesnt exist in this model's db schema) and then attempt to save the model, you'll hit a query exception.
It's worth reading up the laravel docs on attribute accessors/mutators for more info.
Furthermore, if you need to access a related object from within the model (like in an accessor) you ought to call $related = $this->getRelation('foo'); - note that if the relation isnt loaded (e.g., if you didnt fetch this object/collection with eager loaded relations) then $this->getRelation() could return null, but crucially if it is loaded, it won't run the same query(ies) to fetch the data again. So couple that with if (!$this->relationLoaded('foo')) { $this->loadRelation('foo'); }. You can then interact with the related object/collection as normal.
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
Should be
$this->attributes['teacher_name'] = $this->teacher->full_name;
First thing is that you want to reference the relationship, so loose the brackets (), and because the relationship is belongsTo, you will have one user / teacher returned. So you don't need the first().
We haven't seen your fields but probably you will have to change:
return $this->belongsTo('App\User', 'teacher', 'user_id');
to
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
where foreign_key and other_key are the primary keys that you need to make the join on.
Check this link from the documentation for reference:
https://laravel.com/docs/5.4/eloquent-relationships#one-to-many-inverse
the right way to do this is:
COURSE
public function setTeacherNameAttribute ()
{
$this->attributes['teacher_name'] = $this->teacher->full_name;
}
100% working for me.
I have one to one relationship between Order and Shipment. I have to add the accessor of shipments table column from orders table.
function getOrderNoAttribute()
{
$appendText = "OR100";
if($this->orderShipment()->first()) {
$appendText = $this->orderShipment()->first()->is_shipping === 1 ? "ORE100" : "OR100";
}
return $appendText . $this->attributes['id'];
}
This error is only object data to array use or array data to object data use.
example::
$var->feild insted of $var[feild]
$var[feild] insted of $var->feild
You should use return for accessors . something like this :
public function getTeacherNameAttribute ()
{
return $this->teacher()->first()->full_name ?? '';
}
maybe a course hasn't teacher.

Laravel 5.2 hasManyThrough relationship issue

I have 3 tables: orders, codes, events
I want to be able to pull all events that an order has, but there's an intermediary table that acts as a pivot table. I've been trying to use hasManyThrough and belongsToMany (along with withPivot) without any luck.
Examples:
public function events()
{
return $this->belongsToMany('events'); // tried this, fails
return $this->hasManyThrough('events', 'codes'); // tried this, fails
return $this->hasManyThrough('events', 'codes', 'event_id', 'id'); // tried this, fails
}
Any pointers would be great!
That's a belongsToMany setup. First, the first parameter is the name of the related class. Second, since your pivot table doesn't follow the Laravel naming conventions, you need to specify the name of the pivot table in your relationship definition:
public function events()
{
// first parameter is the name of the related class
// second parameter is pivot table name
return $this->belongsToMany(Event::class, 'codes');
}
With this setup, you can do:
// get an order
$order = Order::first();
// has all the events related to an order
$events = $order->events;
There are many ways to do this. I will show a one you can get it done.
In Order.php model
public function codes(){
return $this->has('App\Http\Code');
}
In Code.php model
public function orders(){
return $this->belongsTo('App\Http\Order');
}
public function events(){
return $this->hasMany('App\Http\Event');
}
In Event.php model
public function codes(){
return $this->belongsTo('App\Http\Code');
}
Then in you Controller, call them to get required data.
In your case you can do it like below:
$orders = Order::with(['codes' => function($q){
$q->with('events');
})->get();
May be you can get them with nested manner(not sure about this because i didn't tried before posting):
$orders = Order::with('codes.events')->get();
put return $orders; in your controller to see the query.
Enjoy!

Categories