Suppose I have a Conversation model like this :
class Conversation extends Model
{
public function questions (){
return $this->hasMany('App\Question','conversation_id','conversation_id');
}
public function category ()
{
return $this->belongsTo('App\Category', 'cat', 'cat_id');
}
}
And a Question model like this:
class Question extends Model
{
public function conversation ()
{
return $this->belongsTo('App\Conversation', 'conversation_id', 'conversation_id');
}
}
As you can see there is a hasMany relation between those two.
In the other hand there is a Category like below that has a relation with Conversation model :
class Category extends Node
{
public function conversations (){
return $this->hasMany('App\Conversation','cat','cat_id');
}
}
Now I want to append an attribute named question_count to Category that counts all questions of conversations of each category. for that I added this :
public function getQuestionsCountAttribute ()
{
return $this->conversations->questions->count();
}
But when fetch a category I got this error :
ErrorException in Category.php line 59:
Undefined property: Illuminate\Database\Eloquent\Collection::$questions
What did I do? how can I count relations of a relation with minimum server overloading?
I am using laravel 5.3.4.
I think that you need a has many through relationship here.
What you do wrong:
When you write $this->conversations->questions, this can't work, because the questions are a relation of a single conversation and not of a collection of conversations (here, $this->conversations is a Collection)
The solution:
Using hasManyThrough relation:
You can find the documentation for this relation on this page, if my explanation is bad
The basics are, you need to define a relation on your Category model:
class Category extends Node
{
public function conversations ()
{
return $this->hasMany('App\Conversation');
}
public function questions ()
{
return $this->hasManyThrough('App\Question', 'App\Conversation');
}
}
(I will let your look into the documentation for your non standards foreign keys)
You should then be able to use: $category->questions->count()
You can count related model with 'withCount' method.
https://laravel.com/docs/5.3/eloquent-relationships#counting-related-models
$this->conversations contains the collection of conversations. Each of the conversations has its own questions property that contains all related questions for that conversation, however there is no questions attribute on the collection itself that would contain all related questions for that category.
In order to be able to get the count of all the questions in the category, you need to define a hasManyThrough relation that would link questions to the category directly.
First, add the relation to your Category model:
class Category extends Model {
public function questions() {
return $this->hasManyThrough('App\Question', 'App\Conversation', 'category_id', 'conversation_id', 'id');
}
}
Once you have it, you'll be able to get the count of all questions for a category with:
public function getQuestionsCountAttribute ()
{
return $this->questions()->count(); // if you only need the counter
}
Related
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);
}
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);
I have four tables which is departments, users, items, items_inventories
The relationship is like this:
A user has a assigned department.
An item has a assigned department. item_inventories has many items.
Structure:
users
->id
->name
->password
->access_type (department_id)
departments
->id
->name
items
->id
->name
->department_id
items_inventories
->id
->item_id
->qty
My models:
class Item extends Model
{
public function department()
{
return $this->hasOne('App\Http\Models\Department', "id", "department_id");
}
}
class ItemsInventory extends Model
{
public function item()
{
return $this->hasOne('App\Http\Models\Item', "id", "item_id");
}
}
In my items_inventories how do I query all items that belongs to a specific department? Since items has already a relationship to departments, How do I query like: select all items in items_inventories where item department_id is equal to 3?
My goal is, I have a user who is logged in, and I can access the assigned department to him/her via access_type (department_id) when my page loads, I want to list only items in the items_inventories that is assigned to his/her department. I already checked: https://laravel.com/docs/5.4/eloquent-relationships#relationship-methods-vs-dynamic-properties but can't seem to find something that matches my requirement. Thanks
Your relationships are a bit confusing. The table structure says that items belong to departments and item inventories belong to items. I've based the relationship on your table structure and how you can achieve your desired result. You might want to check on your relationship once more to verify how exactly you want it to pan out. As for the current relationship, my models should give you an idea.
class User extends Model
{
public function department()
{
return $this->belongsTo('App\Http\Models\Department', 'access_type');
}
}
class Department extends Model
{
public function items()
{
return $this->hasMany('App\Http\Models\Item');
}
public function itemInventory()
{
return $this->hasManyThrough('App\Http\Models\ItemsInventory', 'App\Http\Models\Item');
}
}
class Item extends Model
{
public function department()
{
return $this->belongsTo('App\Http\Models\Department');
}
public function itemInventory()
{
return $this->hasMany('App\Http\Models\ItemsInventory');
}
}
class ItemsInventory extends Model
{
public function item()
{
return $this->belongsTo('App\Http\Models\Item');
}
}
Controller logic
$department_id = 3;
$itemInventory = ItemsInventory::whereHas('item', function ($query) use ($department_id) {
$query->where('department_id', $department_id);
})->get();
// With user:department relation and department:iteminventory 'hasManyThrough' relation.
$itemInventory = $user->department()->itemInventory;
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.
Using Laravel, I'm having some trouble accessing my other tables, which are all many to many.
So basically, I start out with the user id and want to list the customers that user has.
public function customers()
{
return $this->belongsToMany('Customer', 'user_to_customer');
}
This works, assume my user id is 1:
User::find(1)->customers;
However now I want to say, for each of these customers, list their products. However this needs to be within the same result.
I guess I would need something within the Customer model, such as:
public function products()
{
return $this->belongsToMany('Product', 'user_to_customer');
}
I can't seem to work out how to access this within the same query? Something like:
User::find(1)->customers->products;
Not sure.. any suggestions?
You can look into eager loading to accomplish this behavior. Given the following model relationships:
class User extends Eloquent {
public function customers()
{
return $this->has_many( 'Customer' );
}
}
class Customer extends Eloquent {
public function products()
{
return $this->has_many( 'Product' );
}
}
class Product extends Eloquent {}
The following query will return all products belonging to customers belonging to a specific (in this case, first) user:
User::with(array('customers', 'customers.products'))->first();