How to use 'hasManyThrough' with more than 3 tables in Laravel? - 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()

Related

Get only one column from relation

I have found this: Get Specific Columns Using “With()” Function in Laravel Eloquent
but nothing from there did not help.
I have users table, columns: id , name , supplier_id. Table suppliers with columns: id, name.
When I call relation from Model or use eager constraints, relation is empty. When I comment(remove) constraint select(['id']) - results are present, but with all users fields.
$query = Supplier::with(['test_staff_id_only' => function ($query) {
//$query->where('id',8); // works only for testing https://laravel.com/docs/6.x/eloquent-relationships#constraining-eager-loads
// option 1
$query->select(['id']); // not working , no results in // "test_staff_id_only": []
// option 2
//$query->raw('select id from users'); // results with all fields from users table
}])->first();
return $query;
In Supplier model:
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id')
//option 3 - if enabled, no results in this relation
->select(['id']);// also tried: ->selectRaw('users.id as uid from users') and ->select('users.id')
}
How can I select only id from users?
in you relation remove select(['id'])
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id');
}
now in your code:
$query = Supplier::with(['test_staff_id_only:id,supplier_id'])->first();
There's a pretty simple answer actually. Define your relationship as:
public function users(){
return $this->hasMany(User::class, 'supplier_id', 'id');
}
Now, if you call Supplier::with('users')->get(), you'll get a list of all suppliers with their users, which is close, but a bit bloated. To limit the columns returned in the relationship, use the : modifier:
$suppliersWithUserIds = Supplier::with('users:id')->get();
Now, you will have a list of Supplier models, and each $supplier->users value will only contain the ID.

Eloquent hasManyThrough also get middle table information

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();

Laravel eloquent use id from one table to search junction table?

So I have 3 tables
-Business
- id
-Address
- id
-business_address
- business_id
- address_id
And now at the moment when I go into a view business page I pass business->id from business table as $id:
public function displayBusiness($id) {
$business = Business::find($id);
$address = Address::find($id);
Which works absolutely fine at this moment but what if address has a different id?
so:
-Business
- id = 1
-Address
- id = 2
-business_address
- business_id = 1
- address_id = 2
So how can I modify that so that when id in business table = 1 it goes into business_address and find matching address id and bring back records that match it
What you should have is a relationship. So in your Business model you'd add the following.
public function addresses() {
return $this->belongsToMany(Address::class, 'business_address', 'address_id', 'business_id');
}
Then in your Address model you'd have the following.
public function addresses() {
return $this->belongsToMany(Business::class, 'business_address', 'business_id', 'address_id');
}
With this, you can now do the following.
public function displayBusiness($id) {
$business = Business::with('addresses')->find($id);
}
Then you access addresses by doing $business->addresses.
This is all based on what you currently have, so I'm assuming that one Address can belong to several Business. If this should not be the case, you'll need to refactor your relationship and database, as the pivot table isn't needed.

How do I store values into a table along with join query in laravel?

I have three tables:->
-- Student(PK userid)
-- Teacher(PK teacherid, FK userid ref...)
-- Rating(PK rateid, FK userid ref....., FK teacherid ref....)
I want to insert into rating table by joining with other tables. How do I achieve that in Laravel 5.2? Please help me out guys!
This can be done in Larvel using Polymorphic relationships
To make it simpler, here's a sample db structure:
student
id - integer
user_id - integer
teacher
id - integer
user_id - integer
ratings
id - integer
value - integer
rateable_id - integer
rateable_type - string
The rateable_id corresponds to either the id on teacher or student
The rateable_type is a string of the class name of the model that is rateable. This is added automatically for you, as explained below.
Your models should look like this:
Rating.php
class Rating extends Model
{
public function rateable()
{
return $this->morphTo();
}
}
Teacher.php
class Teacher extends Model
{
public function rating()
{
return $this->morphOne(Rating::class, 'rateable');
}
}
Student.php
class Student extends Model
{
public function rating()
{
return $this->morphOne(Rating::class, 'rateable');
}
}
Now, here's a small example on how to use this.
$student = Student::find(1);
$rating = new Rating();
$rating->value = 10;
$student->rating()->save($rating);
First you find a student. Then you create a new rating and add a value.
When you do $student->rating()->save($rating); the rateable_id and rateable_type are filled in for you, with the correct value for your Student.php model through the relationship. Simple with Laravel.

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