Does "where" works with mutattors in eloquent? - laravel

I have a mutattor in an eloquent model that generates a "status" atribute.
public function getStatusAttribute(){
if(){
return "enabled";
}
else
{
return "disabled";
}
}
Can I use?
$query = Anuncio::query();
$query->where('status', "enabled" );
return $query->get();
I seems that I cannot. I getting "status" column not defined. How can I get around this problem?

No doesn't works it works on model level
you can use after query from database in collection result

No, when you are doing a query you are asking the database, therefor there needs to be a column status.
There is a way, retrieve x elements from the database and use the Laravel Collection method where(). This is not optimal if you have many elements in teh database, it will retrieve all of them.
Anuncio::all()->where('status', 'enabled')->all();

Related

Query Builder filter for multi level deep relationship in Laravel

I have a selection of plots which each belong to a development by a hasManyThrough relationship through housetypes. I want to filter these by development on their overview page. Plots has a housetype_id column and housetypes has a development_id column.
public function plots()
{
return $this->hasManyThrough(Plot::class, Housetype::class);
}
When I use my filter it returns the developments ID number as $development, I then need this to only show plots which are linked to that development.
I have looked into using whereHas or Join methods but have been unable to figure this out. Current filter scope is below. Thanks
public function scopeFilterDevelopment($query)
{
$development = request()->input('filter_development');
if ($development == "") {
return;
}
if(!empty($development)){
$query->where('development_id', $development);
}
}
If I can understand it right you wish to assert a condition on other Model, HasMany will load all the objects to the related model once the query is completed. Eloquent then binds the related model objects to each.
Try joins from Laravel instead. I feel this is what you exactly want: https://laravel.com/docs/5.8/queries#joins
I would use whereHas to filter the relationship:
YourModel::whereHas('plots', function($query) {
$query->filterDevelopment();
})->get();
I would also edit the query scope not to rely on the request global function, but instead pass the development of value as a parameter.
you have make a leftjon and then use when, you dont have to use
if(!empty($development)){
$query->where('development_id', $development);
}
this any more, you can use
->when($development=="" ? false : true, function($query) use ($development){
return $query->where('development_id', $development);
})
this is a full example
$queryBuilder = DB::table('facturas')->
leftJoin('clientes','clientes.id','=','facturas.clientes_id')->
select('facturas.estados_id as estado','facturas.numero as
numero',DB::raw('concat(clientes.nombre," ",clientes.apellido) as cliente'))->
when($estados===null ? false: true,function($query) use ($estados){
return $query->whereIn('facturas.estados_id', $estados);
})
It was a whereHas that solved this in the end! (another developer at work walked me through this)
Relationship -
public function housetype()
{
return $this->belongsTo(Housetype::class);
}
Function -
public function scopeFilterDevelopment($query)
{
if (request()->input('filter_development') == "") {
return;
}else{
$query->whereHas('housetype', function($housetype){
$housetype->where('development_id', request()->input('filter_development'));
});
}
}
This then returns any plot where its housetype has a matching development_id for the filter_development from the request.
Thanks for everyone's input

Laravel eloquent query with sum of related table

I have a table users and posts with columns user_id and post_views.
In post_views I keep information how many times post was display.
And now, in query I would like to get user with sum of post_views all his posts.
I tried do something like this:
User::where(['id'=>$id])->with('posts')->get();
And in model I defined:
public function posts()
{
return $this->hasMany('App\Models\Post')->sum('post_views','AS','totalViews');
}
But without success.
How to do it?
Thank you
You can use a modified withCount():
public function posts()
{
return $this->hasMany('App\Models\Post');
}
$user = User::withCount(['posts as post_views' => function($query) {
$query->select(DB::raw('sum(post_views)'));
}])->find($id);
// $user->post_views
You can use
User::withCount('posts')->find($id)
to get the user with the id $id and a posts_count attribute in the response
I'm not fully sure what the intention of ->sum('game_plays','AS','totalVies'); is - you would need to add more context if you want this
Just something to add with regards to your shown code: No need to query by id using where + the get() at the end will make you query for a collection. If you want to get a single result use find when searching by id
As always laravel has a method for that : withSum (Since Laravel v8)
Note : I know that at the time of the message was posted, the method did not exist, but since I came across this page when I was looking for the same result, I though it might be interesting to share.
https://laravel.com/docs/9.x/eloquent-relationships#other-aggregate-functions
In your case it should be :
$user = User::withSum('posts as total_views', 'post_views')->find($id);
Then you can access to the result :
$user->total_views

Laravel Query Relationship on One Model Instance

I am aware that I can use count() to query for Eloquent relationships in Laravel, like so:
if(count($question->answers()))
Where answers() is a hasMany relationship:
public function answers()
{
return $this->hasMany('App\Models\Answer', 'question_id');
}
My question is, how do I do this when $question is not an entire collection but one Model instance?
$question = Question::where('id',$key)->first();
How do I query the above question, and only that question, for a potential relationship using count()?
I always am getting a count() of greater than zero, even when the selected question has no associated answers, which means my if block always runs and returns unwarranted null values:
if(count($question->answers()))
{
//returns nulls
}
Since calling $question->answers() is returning a QueryBuilder instance, calling count() on that will most likely always return 1. If you access $question->answers (as a property and not a method), or use the full logic $question->answers()->get(); it should properly return a Collection, which count() will function correctly on:
$question = Question::where('id',$key)->first();
if(count($question->answers) > 0){
// Do something
}
// OR
if(count($question->answers()->get()) > 0){
...
}
As suggested by #maraboc, you could also eager load your $question with answers using a ->with() clause:
$question = Question::with(["answers"])->where('id',$key)->first();
But even in this case, $question->answers() would still be returning a QueryBuilder instance, so access it as a property for count() to function correctly.
As already pointed count($question->answers()) has no meaning because $question->answers() is a Relation instance, you can call dynamic query method on that but if you want to count elements you need a collection, i.e $question->answers.
So you have two choice:
count the collection: count($question->answers)
ask the database to do the count: $question->answers()->count()
Parentheses matters

How to use custom query scopes in an Eloquent subselect query?

This works by manually getting the data as an array then repassing it:
public function scopeWhereWhitelisted($query, $value=true, Tenant $tenant)
{
return $query->where(function($query)use($value,$tenant)
{
$user_id_list = $tenant->getWhiteListedUsersGroup()
->users()
->select('users.id')
->lists('id')
->all()
;
$query->{ $value ? 'whereIn' : 'whereNotIn' }('users.id',$user_id_list);
});
}
But I want this to work (comment // indicates the only difference):
public function scopeWhereWhitelisted($query, $value=true, Tenant $tenant)
{
return $query->where(function($query)use($value,$tenant)
{
$user_id_list = $tenant->getWhiteListedUsersGroup()
->users()
->select('users.id')
//->lists('id')
//->all()
;
$user_id_list = $tenant->getWhiteListedUsersGroup()->users()->select('users.id');//->lists('id')->all();
$query->{ $value ? 'whereIn' : 'whereNotIn' }('users.id',$user_id_list);
});
}
I want to be able to create a "real" subselect without having to have duplicate copies of custom query scopes and relationship queries just for each scope. $tenant->getWhiteListedUsersGroup()->users() is a many-to-many relationship
Here is an example of what has to be done to get a real subselect:
public function scopeWhereWhitelisted($query, $value=true, Tenant $tenant)
{
return $query->where(function($query)use($value,$tenant)
{
$query->{ $value ? 'whereIn' : 'whereNotIn' }('users.id',function($query)
{
$query->from('groups_memberships')
// recreating an existing relationship function
->join('groups','groups.id','group_memberships.group_id')
->select('users.id')
// recreating an already existing query scope
->whereNull('deleted_at')
;
});
});
}
This question will most likely apply to both Laravel 4.0+ and 5.0+
This question is NOT answered by How to do this in Laravel, subquery where in
Restructing code so that the query starts from intended sub query will not work as soon as I need a second non-trivial subselect.
The inclusion/exclusion of ->getQuery() has not made a difference.
I have to choose between a fake subselect or non-DRY custom query scopes.
It seems that the main issue is that the subselect engine is forcing me to use a pre-existing $query object that can't be initialized from an existing relationship.
The recreation of soft deletes (whereNull('deleted_at')) is a trivial example, but I might have to recreate a queryscope that could be relatively complicated already.
Is this whats your going after?
$value; //true or false
$tenant->whereHas('user', function($query) use ($value){
$query->whereHas('groupMembership', function($query) use ($value){
$query->whereHas('group', function($query) use ($value){
if($value){ $query->onlyTrashed(); )
});
})
})
This assumes the group relation includes a withTrashed() call on the relation

Laravel Has One Relation changing the identifier value

I'm not sure this is a real relation. I will try to explain the best way I can.
So first of all, I have three models :
Appartement,
AppartementPrice
The AppartementPrice depends on :
- appartement_id
I would like the AppartementPrice to be retrieve like that :
If there is a specific price for the appartement, then retrieve it, If not retrieve the price for all appartement which is stored in the database with an appartement_id = 0.
So basically what I would like is to do something like that :
public function price()
{
if(isset($this->hasOne('AppartementPrice')->price) // Check that relation exists
return $this->hasOne('AppartementPrice');
else
return $this->hasOne('AppartementPrice')->where('appartement_id', '0');
}
But this is not working.
It does not retrive me the default price.
I guess anyway this is not a best practice ?
I first tried to get the informations like that :
//Check if appartment has a specific price or retrieve default
if($priceAppartement = AppartementPrice::getPriceByCompanyAppartement($this->id))
return $priceAppartement;
else
return AppartementPrice::getDefaultPrice();
But I had this error :
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
when doing :
echo $app->price->price;
How can I check that a relation exists ? And is there a way to do as I describe ?
Thank you
You can't replace relation like this, as what you intend is not logical - you want to retrieve relation that doesn't exist.
Instead you can do this:
public function getPriceAttribute()
{
return ($this->priceRelation) ?: $this->priceDefault();
}
public function priceDefault()
{
// edit: let's cache this one so you don't call the query everytime
// you want the price
return AppartmentPrice::remember(5)->find(0);
}
public function priceRelation()
{
return $this->hasOne('AppartementPrice');
}
Then you achieve what you wanted:
$app->price; // returns AppartmentPrice object related or default one
HOWEVER mind that you won't be able to work on the relation like normally:
$price = new AppartmentPrice([...]);
$app->price()->save($price); // will not work, instead use:
$app->priceRelation()->save($price);
First of all something really important in Laravel 4.
When you do not use parentheses when querying relationship it means you want to retreive a Collention of your Model.
You have to use parentheses if you want to continue your query.
Ex:
// for getting prices collection (if not hasOne). (look like AppartementPrice)
$appartment->price;
// for getting the query which will ask the DB to get all
//price attached to this appartment, and then you can continue querying
$priceQuery = $appartment->price();
// Or you can chain your query
$appartment->price()->where('price', '>', 0)->get() // or first() or count();
Secondly, your question.
//Appartement Model
// This function is needed to keep querying the DB
public function price()
{
return $this->hasOne('AppartementPrice')
}
// This one is for getting the appartment price, like you want to
public function getAppartmentPrice()
{
$price_object = $this->price;
if (!$price_object) // Appartment does not have any price {
return AppartementPrice->where('appartement_id', '=', 0)->get();
}
return $price_object;
}

Resources