How to do complicated query SQL in Laravel? - laravel

I use three entities: user, categories, user_categories, articles, articeles_category
Each user can be subscribe on some categories, so table user_categories will be:
user_category
id user_id category_id
1 1 1
articles_category
id article_id category_id
1 1 1
articles
id name
1 Bla
So, I need to select all articles where articeles_category that correspond to the categories on user subsribed in table user_categories.
I tried this:
Articles::with("categories")->with("publisher.categories")->where("user_id", 1);
public function categories()
{
return $this->hasMany("App\ArticlesCategory", "article_id", "id");
}
public function publisher()
{
return $this->hasOne("App\User", "id", "user_id");
}
I have some ideas.
At first join tables:
user_categories with articeles_category by conditions:
user_categories.category_id = articeles_category.id AND user_categories.user_id = 4
Second is:
Select articles from Articles join with previous query.
Third is
To make global where by user_id

If your models are linked from users to categories to articles, you can do:
$categories = $user->categories()->with('articles')->get();
Then group them under the same collection (if you want):
$articles = $categories->map(function($category){
return $category->articles;
});
Alternatively, if you do not have the user instance:
$user = User::find($user_id)->with('categories.articles').->get();
See eager loading: https://laravel.com/docs/5.4/eloquent-relationships#eager-loading

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.

Treeview with Laravel and Database

I found this function in Internet and it works.
In my App
public function childs(){
return $this->hasMany('App\Account','p_id');
}
In my route:
Route::get('tests', function(){
return App\Account::with('childs')
->where('p_id',0)
->get();
});
So if you have in the dabase a raw with p_id = 0 and id = 1 it return a category and if you have a raw with p_id = 1 and id = 2 that mean that the child of the categorie with id = 1.
Another example::
If you have a raw with p_id = 6 and id = 16 that mean that the child of the category with id = 6
Please who can explain to mean why exactly it work like that ?
Laravel Relationship are not used to make relations between 1 model. You cannot make relation between Account and Account. An account does not has "hasMany" accounts.
You can have hasMany users for example.
public function childs(){
return $this->hasMany('App\User','p_id');
}

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

How to compact two relationship in one relationship

I have some issue when I wanna compact two relationships in Laravel.
There were 3 tables with model.
table1: episodes --> model: Episode
relations -- hasMany --> posts
-- belongsToMany --> users
pivot-table2: posts --> model: Post
columns -- episode_id
-- user_id
relations -- belongsTo --> user
-- belongsTo --> episode
table3: users --> model: User
Yes, It was a many-to-many relationship. We could get posts or users relationships on Episode model.
If I have a single model collection
$episode = new App\Episode
How could it eager load like
$episodes = new App\Episode::with('posts.user').
Maybe I could do like below
$episode = new App\Episode::with('posts.user')->where('id','=','targetId')
But It seems like would first join three tables then make a query to get target episode.
Is there any way works like
$episode = new App\Episode::find(targetId)-> with('posts.user')
If you are using posts table just as pivot table, you are doing it wrong.
Episode Model:
public function users()
{
return $this->belongsToMany('App\User', 'posts');
}
// I would name my pivot table different than posts, if you don't pass the second parameter Laravel will look for episode_user table
User Model:
public function episodes()
{
return $this->belongsToMany('App\Episode', 'posts');
}
In your controller:
$user = User::find(1);
$user->episodes();
$episodes = Episode::find(1);
$episodes->users();

Laravel Eloquent - many to many with where for the pivot table

I'm trying to see what the best way to do the following.
I have 3 tables: users, items and item_user.
users and items table are pretty generic, id and a few columns to hold whatever data.
item_user table has the following structure
id
item_id
user_id
user_type [ 1 - Owner | 2 - Follower | 3 - Something else ]
Relationships:
Each Item has 1 Owner (user type)
Each Item has many followers
Each User can own many Items
Each User can follow many Items
I would like to have the Owner and Followers be the Users table so I don't need to replicate user data. I created a pivot table of item_id, user_id and user_type to hold these relationships.
So the question is how to I do this in Laravel Eloquent?
Item Model looks like:
public function user() {
return $this->belongsToMany('Users');
// This isn't actually correct since it belongs to only one User but not sure how to specify a where user_type = 1;
// return $this->belongsTo('User');
}
User Model looks like:
public function item() {
return $this->hasMany('Item');
}
Thanks!
You can just append the condition to your belongsToMany declaration:
public function user() {
return $this->belongsToMany('Users')->where('user_type', 1);
}
This will return only the User entries that have user_type = 1 in your pivot table. And just to make it more clear you could name the method owner() instead of user() to reflect the added condition.

Resources