Filtering eloquent relationship many to many query - laravel

I have
Users, Departments, Positions, table.
User belongs to many department
Department has many positions
User belongs to many positions
I want a query that would give a single department with list of users and their position in that department.
$department = DepartmentView::with(['users.positions' => function($query) {
// do something?
}])->where('tag', $tag)
->get();
I also get the department through tag, and not ID. Position table does not have tag column, only department ID.
--
I could make it work by defining static Id, however the problem is I get the department via tag.
public function getDepartmentByTag($tag) {
$departmentId = 1; FOR TESTING PURPOSE, IT WORKS
$department = DepartmentView::with(['users.positions' => function($query) use($departmentId) {
$query->whereHas('department', function($query) use($departmentId) {
$query->where('departmentId', $departmentId);
});
}])->where('tag', $tag)
->get();
}

I want to only get the user position that is in the department
DepartmentView::with(['users.positions' => function($q) use($tag) {
$q->whereHas('department', function($q) use($tag) {
$q->where('tag', $tag);
});
}])
->get();

Related

Laravel 9. Why does specifying a column limit for a related table not work?

I need get specify columns of the relationship. But both recomended in documentation way don't work for me.
I can do this by specifying the desired columns in the model, but I don't want to do it: maybe in the future I will need this relationship elsewhere with more (or fewer) columns.
Data base: table Author (id, name) and Article (id, title, author_id)
The author_id column is foreign key for id column of Author table.
Controller:
//it display all columns
$pack = Author::with(['Article' => function ($query) {
$query->select('id', 'title');
}])
->where('id', $id)
->get();
//also it display all columns
$pack = Author::query()
->with(['Article' => function ($query) {
$query->select('id', 'title');
}])
->where('id', $id)
->get()
//also it display all columns
$pack = Author::with(['Article:id,title'])
->where('id', $id)
->get();
Model:
class Author extends Model
{
protected $guarded = [];
public function Article()
{
return $this->hasMany('App\Models\Article')
//->select('id', 'title'); //If uncommented, then specifying columns works
;
}
}
Why doesn't specifying columns in the controller work for me?

Use whereHas and query pivot table

I have 3 table: tasks, users, group, and a pivot table group_user.
A task has user_id, group_id
A group_user pivot table has user_id, group_id
I want to query the tasks if the task belongs to a group of the user.
I don't want tasks from groups that the user doesn't belong to.
What I have so far:
public function index()
{
$userId = Auth::user()->id;
return TaskResource::collection(
Task::
latest()
->whereHas('group', function($query) use($userId) {
$query->where('group_id', '=', $userId); // = is wrong
})
->get()
);
}
This gives me empty results, I tried to think about it but my head hurts
You want to get the tasks that are related to the group (or groups) of a user.
I supposed the relation name between the user table and the group table is 'users', in the Group model.
$userId = auth()->id();
$tasks = Task::latest()
->whereHas('group', function($query) use ($userId) {
$query->whereHas('users', function($query) use ($userId) {
$query->where('user_id', $userId);
});
})
->get();
Ive not tested this but i imagine you could nest the where has and use something like this in your whereHas block:
$query->whereHas('user', function($q2) use ($userId) {
$q2->where('user_id', $userId);
});

Laravel 5.5 - How to fetch related products in many to many relation

I am developing a e-commerce website.
I have 3 tables
1.prodcts table
2.conditions table
3.condition_product table
A product can have many conditions.
particular condition belongs to many products.
Now I have a product which belongs to 3 conditions.
I want to get all related products which belongs to these 3 conditions.
How can i achieve this?
product.php
public function conditions(){
return $this->belongsToMany(Condition::class)->withTimestamps();
}
condition.php
public function products(){
return $this->belongsToMany(Product::class)->withTimestamps();
}
This is my code:
public function productDetails(Product $product)
{
$a = $product->id;
$relatedProducts = Product::whereHas('conditions.products', function($q) use($a) {
$q->where('id', $a);
})
->get();
return view('pages/product', compact('product','relatedProducts'));
}
But I am getting error.
Use the whereHas() method with nested relationships:
Product::whereHas('conditions.products', function($q) use($productId) {
$q->where('id', $productId);
})
->get();
This will give you products that have conditions of a specified product.
Alternatively, you could do this:
$conditionIds = Product::find($productId)->conditions->pluck('id');
$products = Product::whereHas('conditions', function($q) use($conditionIds) {
$q->whereIn('id', $conditionIds);
})
->get();
A good practice is to always specify witch table the 'id' field is referencing too, because if not, it could both reference the 'conditions' table or the 'products' table 'id' field :
$relatedProducts = Product::whereHas('conditions.products', function($q) use($a) {
$q->where('products.id', $a);
})
->get();
You could see this answer about SQL ambiguous column name.

laravel search many to many Relashionship

I am testing eloquent for the first time and I want to see if it suit my application.
I have Product table:
id, name
and model:
class Produit extends Eloquent {
public function eavs()
{
return $this->belongsToMany('Eav')
->withPivot('value_int', 'value_varchar', 'value_date');
}
}
and eav table:
id, name, code, field_type
and pivot table:
product_id, eav_id, value_int, value_varchar, value_date
class Eav extends Eloquent {
public function produitTypes()
{
return $this->belongsToMany(
'ProduitType'
->withPivot('cs_attributs_produits_types_required');
}
All this is working.
But I want to search in that relashionship:
e.g: all product that have eav_id=3 and value_int=3
I have tested this:
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->get();
But I get all the product, and eav data only for these who have id=3 and value_int=3.
I want to get only the product that match this search...
Thank you
I know the question is very old. But added the answer that works in the latest versions of Laravel.
In Laravel 6.x+ versions you can use whereHas method.
So your query will look like this:
Produit::whereHas('eavs', function (Builder $query) {
// Query the pivot table
$query->where('eav_id', 3);
})->get()
My suggestion and something I like to follow is to start with what you know. In this case, we know the eav_id, so let's go from there.
$produits = Eav::find(3)->produits()->where('value_int', '3')->get();
Eager loading in this case isn't going to save you any performance because we are cutting down the 1+n query problem as described in the documentation because we are starting off with using find(). It's also going to be a lot easier to read and understand.
Using query builder for checking multiple eavs
$produits = DB::table('produits')
->join('eav_produit', 'eav_produit.produit_id', '=', 'produits.id')
->join('eavs', 'eavs.id', '=', 'eav_produit.eav_id')
->where(function($query)
{
$query->where('eav_produit.value_int','=','3');
$query->where('eavs.id', '=', '3');
})
->orWhere(function($query)
{
$query->where('eav_produit.value_int','=','1');
$query->where('eavs.id', '=', '1');
})
->select('produits.*')
->get();
Making it work with what you already have...
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
$query->orWhere('id', '1')->where('value_int', '1');
}))->get();
foreach($produits as $produit)
{
if(!produit->eavs)
continue;
// Do stuff
}
From http://four.laravel.com/docs/eloquent:
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, you wish to pull all blog posts that have at least one comment. To do so, you may use the has method
$posts = Post::has('comments')->get();
Using the "has()" method should give you an array with only products that have EAV that match your criteria.
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->has('eavs')->get();

How to order by another table join by eager loading in Laravel

I got product and stocks table;
products
id int
name varchar
created_at timestamp
stocks
id int
name varchar
product_id varchar
created_at timestamp
Product Model
public function validStock() {
return $this->hasMany('Stock')->where('quantity', '>', 10);
}
If both have created_at, how to order by stocks's created_at, I've tried two methods and it's not work
Product::with('validStock')->orderBy('validStock.created_at', 'DESC');
Product::with(array('validStock' => function($q) {
$q->orderBy('created_at', 'DESC');
}));
Instead of sorting after retrieving all data (which is impossible to do in an efficient way when paginating results) you can use a join.
(This answer is based on this one.)
Product::with('validStock')
->join('stocks', 'stocks.product_id', '=', 'products.id')
->select('products.*') // Avoid selecting everything from the stocks table
->orderBy('stocks.created_at', 'DESC')
->get();
The only thing I don't like about this is that it takes away some of the database abstraction, in that you have to write your table names here.
Note that I haven't tried this with a hasMany relationship in this direction, as you have it in your example (selecting products, and each product has many stocks). I've tried only with the hasMany in the other direction (eg selecting stocks, each of which has exactly one product).
You can not apply an order while querying an eagerly loaded relationship in Laravel. You can order the Collections after the query has been performed.
$products = Product::with(array('validStock'))
->get()
->each(function ($product)
{
$product->validStock = $product->validStock
->sortBy(function ($validStock)
{
return $validStock->created_at;
})
->reverse();
});
You should return $q in the closure:
Product::with(array('validStock' => function($q) {
return $q->orderBy('created_at', 'DESC');
}));

Resources