Search in last record of relationship - Laravel (Eloquent) - laravel

i need to search in last record of an relationship in Laravel 7. Here is my code, hope u understand my question :)
$Collection = ServiceRequest::whereHas('ServiceRequestSignatureFlows', function ($query) use ($ou_id) {
return $query->latest()->where('user_id',666);
})
->orderBy('name', 'ASC')
->get();
a ServiceRequest has many SignatureFlows, but i need only search on last flow inserted. I think that when i make ->latest(), my search could search 666 only in last rows, but doesnt happens.
Thanks

you can create the new relation HasOne and filter by it.
public function ServiceRequestSignatureLastFlow()
{
return $this->hasOne(ServiceRequestSignatureFlows::class)->orderByDesc('id');
}
and than
$collection = ServiceRequest::whereHas('ServiceRequestSignatureLastFlow', function ($query) use ($ou_id) {
$query->where('user_id', $ou_id);
})->orderBy('name', 'ASC')->get();

Remove the return inside the whereHas

Related

Laravel: How to query a Model Relationship only if it exists

I have a model Survey with a column installer_id which is related to hasOne another model Installation which is related to hasMany another model Assignment.
I want to write a query to fetch all Survey where the installer_id is not null, and where IF an Assignment exists check all the assignments if any status is != 2 ie status = 0 or 1 return the survey.
I tried this query but it does not capture the "if an assignment exists then check the status" part
$surveys = Survey::whereNotNull('installer_id')
->orWhereHas('installation',function ($query) {
return $query->whereHas('assignments',function ($q){
return $q->where('status','!=', 2 );
});
})->get();
I also tried to define a hasManyThrough relationship in the model.
public function assignments()
{
return $this->hasManyThrough(Assignment::class,Installation::class);
}
and then using this query
$schedulables = Survey::whereNotNull('installer_id')
->orWherehas('assignments',function ($query){
return $query->where('assignments.status','!=', 2 );
})->get()
Please any suggestions and help would be appreciated
I think you had the correct approach, however whereNotNull is not necessary if you're already going to check with whereHas, so this way is sufficient I think:
$surveys = Survey::whereHas('installation',function ($query) {
return $query->whereHas('assignments',function ($q){
return $q->where('status','!=', 2 );
});
})->get();
and I think you're missing the ->get(); in your code, thats why you were not getting the results
Original answer
What i needed was the whereDoesntHave method.
$surveys = Survey::whereNotNull('installer_id')
->whereDoesntHave('installation.assignments', fn ($query) => $query->where('status', 2))
->get();

How To Use groupBy in whereHas laravel

i am using Metable in my project for creating meta for orders
but i have one problem
i want group orders that have same email
this is metable table image : image 1 image 2
i need code like this can work :)
Order::whereHas( 'meta', function($query) {
$query->where("key" , "gmail");
})->groupBy('meta.value')->get();
and this is meta relation that called by trait 'use Metable' in Order Model:
public function meta(): MorphMany
{
return $this->morphMany($this->getMetaClassName(), 'metable');
}
Thanks
This query may work, we start querying from the Meta model:
Meta::with('metable')
->whereHasMorph('metable', Order::class)
->where('key', 'gmail')
->get()
->groupBy('value')
->map(function ($metaCollection) {
return $metaCollection->map->metable->unique('id');
});
This is untested, but I would give it a try to retrive the models from the Order class:
Order::whereHas('meta', function($query) {
$query->where('key', 'gmail');
})->with(['meta' => function($query) {
$query->groupBy('value')
}])->get();
Try this solution instead:
Order::whereHas( 'meta', function($query) {
$query->where("key" , "gmail")
$query->groupBy('meta.value');
})->get();

how to add Limit() to pivot table

I have two tables products and categories and a pivot table category_product.
I have added relation in Product.php is below.
public function categories()
{
return $this->belongsToMany(Category::class);
}
I have added relation in Category.php is below.
public function products()
{
return $this->belongsToMany(Product::class);
}
Both are working fine.
But what I want is, I want to take all the categories which has products and the products should not return more than 6 records.
Meaning I have a two categories Music, Movies and both have more than 50+ products.
The code should be return only 6 products for both Music, Movies categories.
This is what I tried,
Category::with(['products' => function ($query){
return $query->select()->limit(6);
}, 'products.properties'])
->whereHas('products', function ($query){
return $query->select()->limit(6);
})->when($slug, function($query) use($slug){
$query->whereIn('slug', $slug);
})->get();
and this,
Category::addSelect([
'products' => Product::select('id')->whereColumn('products.id', 'categories.id')->orderBy('id')->limit(6)
])->when($slug, function($query) use($slug){
$query->whereIn('slug', $slug);
})->get()
Second try returns error,
"stripos() expects parameter 1 to be string, object given"
I am using laravel 6.
Thanks in advance, any answers and suggestion welcome.
Added My Answer Below
please use this. Also add all your logics to eloquent, not in controller.
public function categories()
{
return $this->belongsToMany(Category::class)->limit(6);
}
I found a solution for my question. Please have a check and please advice me if any better way to solve it.(mainly I want to reduce the number of queries)
The solution is
public function getProductsBasedOnCategory(array $slug){
$categories = Category::select()->when($slug, function($query) use($slug){
$query->whereIn('slug', $slug);
})->get();
$res = new Collection();
foreach ($categories as $category){
$category->products = $category->products()->limit(6)->get();
$res->push($category);
}
return $res;
}
This function executing queries how many categories I have.
If I have 10 categories, its executing 10 queries. Exclude $categories query
Is this answer is good or you have any better solution. I want to reduce the number of queries execution.
Please suggest.
you can use like below.
$cat = Category::with('products')->when($slug, function($query) use($slug){
$query->whereIn('slug', $slug);
})->get();
foreach ($cat as $key => $value) {
$value->products->first();
}

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

Where NOT in pivot table

In Laravel we can setup relationships like so:
class User {
public function items()
{
return $this->belongsToMany('Item');
}
}
Allowing us to to get all items in a pivot table for a user:
Auth::user()->items();
However what if I want to get the opposite of that. And get all items the user DOES NOT have yet. So NOT in the pivot table.
Is there a simple way to do this?
Looking at the source code of the class Illuminate\Database\Eloquent\Builder, we have two methods in Laravel that does this: whereDoesntHave (opposite of whereHas) and doesntHave (opposite of has)
// SELECT * FROM users WHERE ((SELECT count(*) FROM roles WHERE user.role_id = roles.id AND id = 1) < 1) AND ...
User::whereDoesntHave('Role', function ($query) use($id) {
$query->whereId($id);
})
->get();
this works correctly for me!
For simple "Where not exists relationship", use this:
User::doesntHave('Role')->get();
Sorry, do not understand English. I used the google translator.
For simplicity and symmetry you could create a new method in the User model:
// User model
public function availableItems()
{
$ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
return \Item::whereNotIn('id', $ids)->get();
}
To use call:
Auth::user()->availableItems();
It's not that simple but usually the most efficient way is to use a subquery.
$items = Item::whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
})
->get();
If this was something I did often I would add it as a scope method to the Item model.
class Item extends Eloquent {
public function scopeWhereNotRelatedToUser($query, $user_id)
{
$query->whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
});
}
}
Then use that later like this.
$items = Item::whereNotRelatedToUser($user_id)->get();
How about left join?
Assuming the tables are users, items and item_user find all items not associated with the user 123:
DB::table('items')->leftJoin(
'item_user', function ($join) {
$join->on('items.id', '=', 'item_user.item_id')
->where('item_user.user_id', '=', 123);
})
->whereNull('item_user.item_id')
->get();
this should work for you
$someuser = Auth::user();
$someusers_items = $someuser->related()->lists('item_id');
$all_items = Item::all()->lists('id');
$someuser_doesnt_have_items = array_diff($all_items, $someusers_items);
Ended up writing a scope for this like so:
public function scopeAvail($query)
{
return $query->join('item_user', 'items.id', '<>', 'item_user.item_id')->where('item_user.user_id', Auth::user()->id);
}
And then call:
Items::avail()->get();
Works for now, but a bit messy. Would like to see something with a keyword like not:
Auth::user()->itemsNot();
Basically Eloquent is running the above query anyway, except with a = instead of a <>.
Maybe you can use:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Source: http://laravel.com/docs/4.2/queries#advanced-wheres
This code brings the items that have no relationship with the user.
$items = $this->item->whereDoesntHave('users')->get();

Resources