Eloquent hasManyThrough also get middle table information - laravel

I have same table structure as mentioned in laravel document for HasManyThrough relationship hasManyThrough
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
and define a relationship like same as in doc.
public function posts()
{
return $this->hasManyThrough(
'App\Post', 'App\User',
'country_id', 'user_id', 'id'
);
}
Now when I List posts of specific country. I need the information of user of the post too. I mean information from pivot table(users)
$posts = Country::find(2)->posts();
The above returns post data only..

What you need is to eager load the users alongside the posts, can be achieved via the with() method on the Builder:
$posts = Country::find(2)->posts()->with('user')->get();
If you're loading huge amounts of data and don't want the whole User instance loaded, you can even specify which fields to only be retrieved from the users table:
$posts = Country::find(2)->posts()->with('user:id,name')->get();
Then you can simply use $post->user->name or whatever you need when iterating your collection.
Refer to the documentation for more information.

Try this one:
$posts = Country::find(2)->posts()->with('user')->get();

Related

How can I return an array of Laravel Eloquent model objects from a query that requires left join?

There are two tables, "users" and "posts".
Table "posts" has "user_id" as a foreign key.
How to write this equivalent in Eloquent, in a way that returns an array called $posts which is not simple PHP array but the Post class model objects?
SELECT posts.*
FROM posts
LEFT JOIN users
ON posts.user_id = users.id
ORDER BY users.registered_at, posts.created_at;
In other words, I want to return an array of model objects and not just flat data that comes from raw SQL.
if you have setup proper relationships as suggested above, you can get your user model then get posts through that user.
Your user model should have a function like:
public function users(){
return $this->hasMany(Post::class);
}
You could probably do something like this:
$posts = Posts::load('users')
->orderBy('users.reregistered_at')
->orderBy('users.some_column')
->get();
This should return a collection of posts with their relations and orderBy what you need

Invalid argument supplied for foreach() in laravel when use Eloquent: Relationships

I wanna get the class name of a user via the user ID. When I input the ID of a user so I will wanna get the class name. I have three tables such as users table, classes table, and class_users table. The class_users table is born from two users table and classes table.
A users table has an id, name, email, password.
A classes table has an id, class_code, class_name.
A class_users table has an id, class_id, user_id
And this problem relates to Eloquent Relationships.
Thank you for help.
My Route:
Route::get('/find_classes/{id}','StudentController#find_classes');
My Controller function:
public function find_classes($id)
{
$users = User::find($id);
foreach($users->classes as $class)
{
echo $class->name . '<br';
dd($class);
}
}
My User Model:
public function classes()
{
return $this->belongsTo('App\Classes','class_users','user_id','class_id');
}
Looks like you might have the wrong relationship set up on your User model. You have a one to many set up, but your DB is setup to handle a many to many. I suggest you change your User model relationship:
public function classes()
{
return $this->belongsToMany('App\Classes');
}
Note, you may need to name the FK on that relation, as I see you have class_id on the table, but your actual class is named 'Classes'. Check through your relationships to ensure this is explicit on the FK where it doesn't follow Laravel convention exactly.
With this relationship, your foreach loop should work. It would be a good idea for efficiency, as mare96 noted, to eager load the classes on the $users collection when you query:
$users = User::with('classes')->find($id);

Laravel: return multiple relationships

I have the following table:
The table is called user_eggs and it stores the user eggs.
eggs are items with additional data (hatch_time)
As you can see, user 2 has 2 eggs, which are items 46 and 47.
My items table stores the item general information such as name, image, description, etc...
How I can return the user eggs using $user->eggs() including the item data in my items table of the egg item_id?
I tried:
User Model:
/**
* Get the eggs
*/
public function eggs()
{
return $this->belongsToMany(Egg::Class, 'user_eggs','user_id','item_id')
->withPivot('id','hatch_time');
}
but $user->eggs() returns an empty array.
Any ideas?
A simple approach will be:
in your UserEgg model define:
/**
* Get the user associated with egg.
*/
public function _user()
{
return $this->belongsTo('App\User','user_id');
}
/**
* Get the item associated with egg.
*/
public function item()
{
return $this->belongsTo('App\Item','item_id');
}
then in your controller:
use the model to extract everything like this:
$userEggs = UserEgg::where('user_id',2)->get();
foreach($userEggs as $userEgg){
$associateduser = $userEgg->_user;
$associatedItem = $userEgg->item;
}
Short answer
If you loop through the user's eggs:
foreach($user->eggs as $egg){
$item = Item::find($egg->pivot->item_id);
}
If you want to query:
$user->eggs()->wherePivot('item_id', 1)->get();
Long answer
From the Laravel Documentation
Retrieving Intermediate Table Columns
As you have already learned, working with many-to-many relations requires the presence of an intermediate table. Eloquent provides some very helpful ways of interacting with this table. For example, let's assume our User object has many Role objects that it is related to. After accessing this relationship, we may access the intermediate table using the pivot attribute on the models:
$user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
Notice that each Role model we retrieve is automatically assigned a pivot attribute. This attribute contains a model representing the intermediate table, and may be used like any other Eloquent model.
By default, only the model keys will be present on the pivot object. If your pivot table contains extra attributes, you must specify them when defining the relationship:
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
If you want your pivot table to have automatically maintained created_at and updated_at timestamps, use the withTimestamps method on the relationship definition:
return $this->belongsToMany('App\Role')->withTimestamps();
Filtering Relationships Via Intermediate Table Columns
You can also filter the results returned by belongsToMany using the wherePivot and wherePivotIn methods when defining the relationship:
return $this->belongsToMany('App\Role')->wherePivot('approved', 1);
return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]);

How to use 'hasManyThrough' with more than 3 tables in Laravel?

as stated in the docs the current hasManyThrough can be used when u have something like country > users > posts
which result in something like Country::whereName('xx')->posts;
which is great but what if i have more than that like
country > cities > users > posts
or even
country > cities > towns > users > posts
how would you then implement something like that so you can write the same as above
Country::whereName('xx')->posts; or Town::whereName('xx')->posts;
I created a HasManyThrough relationship with unlimited levels: Repository on GitHub
After the installation, you can use it like this:
class Country extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function posts() {
return $this->hasManyDeep(Post::class, [City::class, Town::class, User::class]);
}
}
Here's what I've done. I did some modification to #ctfo's answer, so it will return as collection.
public function posts()
{
$posts = collect();
foreach ($this->towns->get() as $town) {
$post = $town->posts();
if ( $post->count() ) $posts = $posts->merge( $post );
}
return $posts;
}
I hope this can help anyone who came through.
sadly there is no one easy solution, so heres what i found
1- add the other table foreign id into the post table and stick to the hasMany & belongsTo, ex.
posts
id - integer
country_id - foreign
// etc...
title - string
2- go with the hasManyThrough on each table except user & post as there is no need unless you want to go deeper, ex.
countries
id - integer
name - string
cities
id - integer
country_id - foreign
name - string
towns
id - integer
city_id - foreign
name - string
users
id - integer
town_id - foreign
name - string
posts
id - integer
user_id - foreign
title - string
- so lets try with the 2nd option
1- set up your hasMany & belongsTo relations as usual
2- setup the hasManyThrough on the models.
3- to achieve the above example of Country::whereName('xx')->posts we add another method to the country model
// because Country::find($id)->towns, return an array
// so to get all the posts we have to loop over that
// and return a new array with all the country posts
public function posts()
{
$posts = [];
foreach ($this->towns as $town) {
$posts[] = $town->posts;
}
return $posts;
}
4- as the whole thing is working through the foreign_id so we search by the id instead of the name like Country::find($id)->posts()

Laravel 4.2 Eloquent query by relationship column value

Good day to you all...
I'm trying to access a collection based on a column in a related table within Eloquent (Laravel 4.2).
I have the following tables:
tags:
(int) id
(string) name
tag_usage:
(int) id
(string) model (the name of the model that is allowed to use the tag)
tag_tag_usage: (pivot)
(int) id
(int) tag_id
(int) tag_usage_id
I also have a taggables (polymorphic to store tags for multiple models) table which I believe is out of scope here as I only want to retrieve the tags that am allowed to use for each model.
My tag model has the relationship
public function usage()
{
return $this->belongsToMany('TagUsage');
}
and the TagUsage model has
public function tags() {
return $this->belongsToMany('Tag');
}
Now, what I want to do is return the tags that ONLY have a specific usage, some pseudo code would be
get_tags->where(tag_usage.model = modelname)
which would return only a subset of the tags.
Tried a few things with no success so over to the many fine brains available here.
Many thanks.
You need to use whereHas in the following way:
$tags = Tag::whereHas('usage', function($q)
{
$q->whereModel('modelname');
})->get();

Resources