How to improve multiple eloquent conditions? - laravel

Here is a default eloquent condition:
if($request->has("parent")) => {
Specializatio::where('id', $request->id)
->when($request->parentid, function($query) use ($request){
return $query->where('parent_id', $request->parent_id);
})
}
It grows up so fast into long list of conditions .when based request object. Is there any flexibale mechanism to manage it, maybe devide in another class, helper?

Maybe query scopes and short hand functions will make your code a bit cleaner.
class Specialization extends Model
{
public function scopeParent($query, $parentId)
{
return $query->where('parent_id', $parentId);
}
}
Specialization::whereKey($request->id)
->when($request->filled('parent_id'), fn ($query) => $query->parent($request->parent_id));

Related

Laravel prioritize or operator in relation query (hasMany). How to add parenthesis?

The events method in my model returns all related events from the database. The code below is working fine, the only problem is dat it don't prioritze the or. (See orWhereHas)
public function events()
{
return $this->hasMany(Event::class)
->orWhereHas('organisations', function(Builder $query){
$query->where('organisation_id', $this->id);
});
}
When I extend the query somewhere else in the code, it goes wrong:
$model->events()->whereNull('some_field')
Because it should prioritize the OR operator. But I don't know how to do that in this case because I am imitating the query from a model relation.
So the question is: how to add parenthesis in the query to prioritize the or operator?
You could move the logic outside the relationship method and use a where/orWhere Closure.
public function events()
{
return $this->hasMany(Event::class)
}
$model->events()
->where(function ($sub) {
$sub->orWhereHas('organisations', function(Builder $query){
$query->where('organisation_id', $this->id);
})
->orWhereNull('some_field');
})

Eloquent `with()` with filtering based on relation

I have this tables.
And this model relations, this relations works fine.
class Item extends Model
{
public function translations()
{
return $this->hasMany(ItemTranslations::class);
}
}
class ItemTranslation extends Model
{
public function language()
{
return $this->belongsTo(Language::class);
}
}
I need to return a list of items with the translations, but only the translations related to a specific language.
I can't have this query working, im getting all translations of each item, not only the one filtered with this query. The language related to the translation is not needed on the result.
$query = Item::query();
$query->with('translations')->when('language',function($query) use ($ISOlanguage) {
return $query->where('languages.ISO_code', '=', $ISOlanguage);
});
return $query->paginate();
Any idea who i can have this working? Thanks!
So what you want to do is constraining eager loading
Item::with(["translations" => function ($query) use ($ISOlanguage) {
$query->where('language.ISO_code', $ISOlanguage);
}])->get();
https://laravel.com/docs/5.8/eloquent-relationships#constraining-eager-loads
I finally have it working
Item::with(['translations' => function($query) use ($ISOlanguage) {
$query->whereHas('language', function($query) use ($ISOlanguage) {
$query->where('ISO_code', '=', $ISOlanguage);
});
}])->get();
Thanks #julian-s for your help!

Replace Laravel query callback with function

I have the following base query I use for retrieving collections with their relations, scopes, sorting, filtering,... This base query is used on almost all my models by extending the class this base query is in.
return $this->runSortQuery(
$query->when($request->has('filter'),
function($query) use ($request) {
return $query->search($request->filter);
}
)->when($request->has('with'),
function($query) use ($request) {
return $query->with(
explode(',', $request->with)
);
}
)->when($request->has('scopes'),
function($query) use ($request) {
return $query->scopes(
json_decode($request->scopes, true)
);
}
) /* and so on... */, $request
)->paginate((isset($request->paginate)) ? $request->paginate : 15);
Is it possible to replace the callback in every when with a custom function call? The reason I want this is because this base function is getting really long and I would like the method in the callback in it's own function, for readability and to keep this maintainable.
I tried this, but this obviously does not work.
$query->when($request->has('filter'), $this->filter($query, $request->filter))
->when($request/* and so on... */);
Can this be done in another way or what would be a good pattern or method to do this?
Yes, you can use the callables like so:
$query->when($request->has('filter'), [$this, 'someMethodNameInClass'])
->...
Check out this SO thread to learn more

Query a relationship that is NOT part of a scope in Laravel

For a model I have a complicated scope condition like so:
class Foo {
public function scopeActive(Builder $query) {
$dateNow = $now->format('Y-m-d');
$timeNow = $now->second(0)->format('H:i:s');
$query->whereNull('start_date')
->orWhere('start_date', '<', $dateNow)
->orWhere(function (Builder $query) use ($dateNow, $timeNow) {
$query->where('start_date', '=', $dateNow)
->where('start_time', '>=', $timeNow);
});
}
}
This complicated condition will select all the records in Foo that are considered active (the real scope is even more complicated than that).
I have another class like so:
class Bar {
public function foos() {
return $this->hasMany(Foo::class);
}
}
Which means the Bar model has many Foo models.
Now if I wanted to get all the Bar models as well as all the active Foo models that belong to it, I can do the following:
Bar::with(['foo', function (HasMany $query) {
$query->active();
})->get();
However, how can I write a query that gives me all the Bar records that are NOT active.
Ideally I would want something like this:
Bar::with(['foo', function (HasMany $query) {
$query->whereNot(function (Builder $query) {
$query->active();
});
})->get();
If you want to have a scope that does the opposite, just create another scope method like so, but with inverted query logic (you didn't include your 'Some complicated query', so this is just a guess):
public function scopeInactive($query) {
return $query->where('is_active', false);
}
Laravel: local scopes
There does not seem to be an efficient way to easily invert SQL using Laravel for complicated queries.
You just gotta have to write the inverse.

Eloquent conditional where filter

If I have a variable that is optional, and I only want to have it search on it if it is defined, I know that I can do this, but is there a way to define the function elsewhere, so I don't need to rewrite the same anonymous function over and over again? Or are the any other good alternatives to going about solving this problem?
.......->where(function($query) use ($gender){
if ($gender) {
$query->where('gender', '=', $gender);
}
})->get();
Since Laravel 5.2.27, you can use conditional clauses
For example:
->when($gender, function($query) use ($gender) {
return $query->where('gender', '=', $gender);
})
->get();
You could use a dynamic scope
class User extends Eloquent {
public function scopeGender($query, $gender)
{
if ($gender) {
return $query->whereGender($gender);
}
return $query;
}
}
Then throughout your application
...->gender($gender)->get();

Resources