Apply conditions using closure on Laravel Model collection - laravel

I am using Laravel Model collection and want to filter the records
in the first statement, I am checking that I only check the ends when it's has some date otherwise doesn't compare the value.
How to check the null value in this collection?
2nd if is working properly. I also used the filter method but it's not working for me. Can anyone suggest to me how to do this in a proper laravel way? I know I can do this using for each loop but I want to make the query faster. Please suggest your answers. Thank you
$subscriptionsCount = Subscription::where( function($query) {
if($query->where('ends_at', '!=' , null)){
$query->where('ends_at', '>=', now());
}
else if($query->where('subscription_ends_at', '>=', now())){
$query->where('subscription_ends_at', '>=', now());
}
})->where('name', $betTypeName)->count();

Can you try this code as I understood from your question you need whereNotNull
$subscriptionsCount = Subscription::where(function ($query) {
$query->where(function ($query) {
$query->whereNotNull('ends_at')->where('ends_at', '>=', now());
})->orWhere(function ($query) {
$query->whereNotNull('subscription_ends_at')
->where('subscription_ends_at', '>=', now());
});
})->where('name', $betTypeName)->count();

You can try with below whereRaw condition :
$subscriptionsCount = Subscription::where( function($query) {
$query->whereRaw("(CASE WHEN ends_at is not null THEN ends_at = now() WHEN subscription_ends_at >= now() THEN subscription_ends_at >= now() END)" );
})->where('name', $betTypeName)->count();
Hope Above code will be help you. Code is not tested but logic will help you. :)

Related

how to use whereHas in laravel

I am new to laravel,
I need to create a query for the db,
$query = Deal::query();
I want to use the wherehas operator.
this is my code that is worked.
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus);
// \Log::info('The request precedence: '.$precedenceStatus);
}
I want to add this code also to the query
if($person) {
$query->whereHas('personnel', function ($subQuery) use ($person) {
$subQuery->where('id', '=', $person);
});
}
So I need to change the first code?
how I can convert the first code to wherehas?
the first code is from table called deal, the second section is from realtionship called personnel.
the second section worked in other places in the code, I just need to fix the first section and not understand what to write in the use
I try this and get error on the last }
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus)
-> when ($person, function($query) use($person) {
$query->whereHas('personnel', function ($query) use ($person) {
$query->where('id', '=', $person);
});
})
}
There is a method you can use called when(, so that you can have cleaner code. The first parameter if true will execute your conditional statement.
https://laravel.com/docs/9.x/queries#conditional-clauses
$result = $query
->where('precedence', '=', $precedenceStatus)
->when($person, function ($query) use ($person) {
$query->whereHas('personnel', fn ($q) => $q->where('id', '=', $person));
})
->get();
You should also be able to clean up your precedence code prior to that using when( to make the entire thing a bit cleaner.
Querying to DB is so easy in laravel you just need to what you want what query you want execute after that you just have to replace it with laravel helpers.Or you can write the raw query if you cant understand which function to use.
using,DB::raw('write your sql query').
Now Most of the time whereHad is used to filter the data of the particular model.
Prefer this link,[Laravel official doc for queries][1] like if you have 1 to m relation ship so u can retrive many object from one part or one part from many object.like i want to filter many comments done by a user,then i will right like this.
$comments = Comment::whereHas('user', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
$comments = Here will be the model which you want to retrive::whereHas('relationship name', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
you can also write whereHas inside whereHas.
[1]: https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence

Laravel Eloquent when date condition

I have an eloquent query, and inside it I have 2 condition. The first is when attendance_time == today and second is attendance_time == yesterday. I want to use that condition but I don't know how.
I've tried like code bellow, but still not working.
Attendance::where('employee_id', $request->employee_id)
->where('in_out', 'in')
->when('attendance_time' == Carbon::today(), function ($q) {
return $q->where('attendance_time', Carbon::today());
})->when('attendance_time' == Carbon::yesterday(), function ($q) {
return $q->where('attendance_time', Carbon::yesterday());
})->first();
Format for attendance_time is Y-m-d H:i:s
You can use whereBetween method:
use Carbon/Carbon;
...
Attendance::where('employee_id', $request->employee_id)
->where('in_out', 'in')
->whereBetween('attendance_time', [
Carbon::now()->subDay()->toDateString(),
Carbon::now()->toDateString()
])->first();
You can use whereDate, here is additional info. However, your query needs some improvement. If you use when it is like an if statement. It checks your condition and if it is true then add query inside of its function.
If you create something like that:
Attendance::where('employee_id', $request->employee_id)
->where('in_out', 'in')
->when($request->attendace_time, function ($q) {
return $q->where('attendance_time', Carbon::today());
})
That means if your request has attendace_time then add where query.
Or If you want to just filter todays attandees then you can use:
Attendance::where('employee_id', $request->employee_id)
->where('in_out', 'in')
->whereDate('attendance_time', Carbon::today()->format('Y-m-d'));

Laravel: Use order by in combination with whereHas

I want to retrieve a collection of data, which is ordered by the start_date of the relation
Basically I want to achieve this, with Laravel Models (the code below works perfectly)
$posts = DB::table('posts')
->leftJoin(
'threads',
'posts.id',
'=',
'threads.postable_id'
)
->where('threads.postable_type', '=', 'App\Post')
->orderBy('threads.start_date')
->paginate($request->input('limit', 2));
So in this case, I'm fetching ALL Posts and those are ordered by the start_date of the thread relation.
Those are not my actual tables but this works perfectly!
Because I'm using https://laravel.com/docs/8.x/eloquent-resources this is not the ideal solution to retrieve sorted data.
So instead I want to use the orderBy clause somewhere here
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now());
});
But I just cannot make this work. I've tried this
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
and I also appended this to the actual relation:
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
If you look at your code:
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
the whereHas will only return Post associate with a thread which the function return true.
Try this:
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
This will fetch all Post with Thread only if they have at least one Thread and then orderBy the start_date of the Thread.
You don't have to do the whereHas function because when you call ->with('thread') it'll use you this :
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
whereHas doesnt retrieve the relationship.
If you need even more power, you may use the whereHas and orWhereHas methods to define additional query constraints on your has queries, such as inspecting the content of a comment: Laravel whereHas
Don't do :
$posts = Post::with('thread')->orderBy('thread.start_date');
If there is no thread on some post, post without thread will be fetch with value null for their key thread and you will have an unexpected result when you try to orderBy.
First of all I want to thank Elie Morin for his help but I found out that I definitely need to use joins for that task.
In my example, I wanted to order the main query (posts) by the relation's start_date
Doing what you suggested
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
Would only order the thread by start_date and not the ENTIRE query.
Which is why I came up with something like this:
$posts = Post::has('thread')
->select('posts.id')
->leftJoin(
'thread',
'posts.id',
'=',
'thread.postable_id'
)
->where('thread.postable_type', '=', 'App\Post')
->where('thread.end_date', '>=', Carbon::now())
->orderBy('thread.start_date')
->with('thread');
return PostResource::collection($posts->paginate(2));

Laravel collection with multiple date filter with or operator

I'm trying to filter several items from a Collection in Laravel 5.7. The items have a startdate and an (optional) enddate.
The filter i'm trying to create is the following.
startdate <= now() AND
( enddate >= now() OR enddate = '' OR enddate = NULL )
I've tried the following but it doesn't work:
$this->items->where([
['startdate', '<=', date('Y-m-d'))],
['enddate', '>=', date('Y-m-d')]
])->orWhere([
['startdate', '<=', date('Y-m-d'))],
['enddate', '=', '']
])->orWhere([
['startdate', '<=', date('Y-m-d'))],
['enddate', '=', null]
]);
More cleaner way would be using closure like this:
$this->items->where('startdate', '<=', date('Y-m-d'))
->where(function($q) {
$q->where('enddate', '>=', date('Y-m-d'))
->orWhere('enddate', '')
->orWhereNull('enddate');
});
And probably as suggested in comment you should in fact use items() instead of items because using items you probably get elements from database instead of adding constraints to database query before.
Thanks to Marcin Nabialek i've got the solution.
I've had to use $this->items() instead of $this->items and had to append it with the ->get() function.
The result:
$this->items()->where('startdate', '<=', date('Y-m-d'))
->where(function($q) {
$q->where('enddate', '>=', date('Y-m-d'))
->orWhere('enddate', '')
->orWhereNull('enddate');
})->get();

Making a query to get elements active on a time between 2 specific dates

Cant seem to get the logic right. I have a function that takes 2 dates ($date1 and $date2). I have database records of events that also have 2 dates (starting_at and ending_at).
I want to get all events that completely or partially overlap with time between $date1 and $date2. I have browsed other similar topics and came up with this... but i have a feeling it is not correct.
$events = Event::with(['user']);
$events->where(function($query) use ($date1,$date2) {
$query->where(function($query) use ($date2) {
$query->where('starting_at', '<=', $date2);
});
$query->where(function($query) use ($date1) {
$query->where('ending_at', '>=', $date1);
});
});
If dates are in the same table, use:
$events = Event::with('user')
->where('starting_at', '<=', $date2)
->where('ending_at', '>=', $date1)
->get();
Make sure you've added starting_at and ending_at to the dates array:
protected $dates = ['starting_at', 'ending_at'];
With Laravel 5.3 (maybe 5.2, but 5.3 for sure) you can do:
$events = $events->whereBetween($date1, $date2)->get();
Lower version could be:
$events = $events->where('starting_at', '<=', $date2)
->where('ending_at', '>=', $date1)
->get();
Laravel has a simple way of doing this.
$events = Event::with(['user'])->whereBetween('field_name', array($date1, $date2))->get();
Check the docs here https://laravel.com/docs/5.3/queries#where-clauses
UPDATE
If date1 and date2 are from entirely different columns on the database, then refer #Alexey Mezenin answer which is more appropriate.

Resources