How return all rows for groupBy - laravel

I use the query to get the models that the user has, but only 1 model is returned for each user. How to get all? If I set $count=3 I should receive 3 or more models in group, but only first row is returned
$items->where(/*.....*/)
->groupBy('user_id')
->havingRaw("COUNT(*) >= {$count}")->get()
UPDATE
I solved it. I created a separate function for preparing the query and used it 2 times.
I think this may be an incorrect solution, but it works
$items = Items::query();
$this->prepareQuery($request, $items)
$items->whereHas('user', function ($q) use ($count, $request){
$q->whereHas('items', function ($query) use ($request){
$this->prepareQuery($request, $query);
}, '>=', $count);
})
->paginate(4);

This may work.Sometimes take can help.
$items->where(/*.....*/)
->groupBy('user_id')
->take({{$count}});

If you have relationships set up you can quite simply call:
$model->models()
$model being the model you're wanting the list for.
models() being the name of the relationship between the two items.
For example, a post may have many comments. You can call $post->comments() to get a list of the posts comments.
You may also query this from the database directly with some eloquent magic.
Post::with('comments')->where(/*....*/)->get();
EDIT:
You can check to see if a model has X number of related models like so.
Post::has('comments', '>=', 3)->where(/*...*/)->get();

Related

Laravel Eloquent query model which has exactly a number of association in a n-to-m relationship

I have a project where I have the following Models :
Products
FeatureTypes
Features
Variants
And the tables and relationships are the following :
I have a request where I want to retrieve the Variants from a Product with some specific Features.
A Variant can have 1 or many Features but I want to have all Variants if I give one Feature, and have a more specific search on the Variants if I gave more Features, until I have only one Variant which can have only one set of specific Feature.
Here's the code I have for now :
$variants = $product
->variants()
->whereHas('features', function($query) {
$query->whereIn('id', json_decode(request('features')));
})
->with('features', 'features.featureType')
->get();
The request('features') contains a stringified array with ids of Features.
I used Eloquent's whereIn method thinking that it would give me only the variants that have exactly all the Features given in the request.
But while checking the documentation again, I see that it will return any Variants that has at least one Feature given in the request which is not what I need here.
How can I make that Eloquent's query to return me only the Variants that are associated with all the Features given by the request ?
Thanks in advance for you help ! ;)
whereHas() accepts a fourth argument with the number of rows:
$variants = $product
->variants()
->whereHas('features', function($query) use ($features) {
$query->whereIn('id', $features);
}, '=', count($features))
->with('features', 'features.featureType')
->get();

Accessing parent properties inside relationship query

An example:
$passing_students = App\Exam::whereHas('students', function ($query) {
$query->where('mark', '>=', $exam->pass_mark);
})->get();
I am interested in fetching all exams with students who passed however in the relationship query function I'm not sure how to access the parent model's properties such that I can complete the comparison. What should be in place of $exam->pass_mark?
Note that I'm looking for a solution that is done within the single query builder as I'm aware that this can be easily done in a separate foreach loop.
Probably instead of:
$query->where('mark', '>=', $exam->pass_mark);
you should use here:
$query->whereColumn('mark', '>=', 'exams.pass_mark');
Above exams.pass_mark is name of Exam model table (I assumed you use exams and column name from this table.

Laravel Eloquent - How Do You Add a Where Condition to a Distantly Related Model Using the ORM?

I currently have the following:
Cars::with('cases')->with(['parts', 'parts.engines', 'parts.engines.metals'])
->orderBy('car_name', 'DESC')->orderBy('id', 'DESC');
The above will list all rows in my cars table along with the metal associated with the engine in each of those cars. The metals table is related to the cars table through the parts and then the engines tables.
I've tried using:
Cars::with('cases')->whereHas(['parts', 'parts.engines', 'parts.engines.metals'], function($query){
$query->where('weight', '=', 45)
})->orderBy('car_name', 'DESC')->orderBy('id', 'DESC');
But this errors out since whereHas() does not accept an array for its first parameter and I don't see a way to link to distant relationships with it.
How do I apply a WHERE conditional on a column in the metals table using the built-in ORM?
whereHas() only needs the name of the relationship for which you'd like to add the conditions. So, if you're trying to add the condition to the metals, you just need to restrict the parts.engines.metals relationship.
On a side note, when you eager load nested relationships, you don't need to also specify to load the intermediate relationship. That is, when you eager load parts.engines, you don't need to also eager load parts.
So, your query would look something like:
Cars::with(['cases', 'parts.engines.metals'])
->whereHas('parts.engines.metals', function($query) {
$query->where('weight', '=', 45)
})
->orderBy('car_name', 'DESC')
->orderBy('id', 'DESC');
This query will only retrieve cars that have a related metal with a weight of 45. Additionally, for those cars that are retrieved, it will also eager load all of the cases, parts, engines, and metals related to those cars.
I think you mean this:
Cars::with(['cases', 'parts', 'parts.engines', 'parts.engines.metals' => function($query){
$query->where('weight', '=', 45);
}])->orderBy('car_name', 'DESC')->orderBy('id', 'DESC');

How to detach only the last record in the pivot table using detach() in Laravel 4?

If I do this:
return $this->roles()->detach($role);
all roles are removed.
How to limit that to only the last one?
You can do it without timestamps:
$lastRole = $user->roles()
->orderBy( $user->roles()->getTable() .'id', 'desc')
->first();
$user->roles()->detach($lastRole);
or with timestamps:
$lastRole = $user->roles()->latest()->first();
$user->roles()->detach($lastRole);
you may try this, I did not test it:
return $this->roles()->orderBy('id', 'desc')->first()->detach($role);
You can also order by timestamps, if no primary id is present, like this:
return $this->roles()->orderBy('created_at', 'desc')->first()->detach($role);
for this to work, you also have to edit your model, from the docs:
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('Role')->withTimestamps();
Another thing would be not to use model at all, because looking at this issue #3585 it is not that easy. Taylor closed it without a comment, so I assume it not get implemented. The solution should be (assuming you have timestamps columns migrated in the table). Tested code:
$last = DB::table($user->roles()->getTable())
->orderBy('created_at', 'desc')
->first();
DB::table($user->roles()->getTable())
->where('created_at', '=', $last->created_at)
->delete();

Eloquent ORM, how to avoid redundant queries?

Suppose I have the field status in all my database tables. When I want to delete a record, instead of purging it I set the value of field status to 0. This means of course all my queries will always use a where clause such as:
WHERE status = 1
However, this means I have to write and append where('status', '=', 1) to all methods of my eloquent model. It will always be like:
Post::find(1).where('status', '=', 1)
Post::where('status', '=', 1)->get()
Post::find(1).where('status', '=', 1).comments().where('status', '=', 1)->get()
Is there a way to define something as a default scope to have where status = 1 always present in all methods of my model and all the time?
I appreciate any help!
This should do the trick, at least regarding soft delete:
http://four.laravel.com/docs/eloquent#soft-deleting
You could use the scope methods.
public function scopeActive($query)
{
$query->where('status', '=', 1);
}
Then, you use like this:
Post::active()->get();

Resources