I have a list of database records that represent some events. Each has a start_time column that contains dates in this format: 2017-10-28 22:00:00. I would like to be able to get all the records whose column start_time is a Friday using Laravel. Something like this:
$fridayEvents = $q->whereDate('start_time', '=', isFriday());
But I am having a hard time creating isFriday() with Carbon.
MySQL (and other SQLs) implement the WEEKDAY() function that extracts the weekday from a date:
WEEKDAY(date)
Returns the weekday index for date (0 = Monday, 1 = Tuesday, … 6 =
Sunday).
mysql> SELECT WEEKDAY('2008-02-03 22:23:00');
-> 6
mysql> SELECT WEEKDAY('2007-11-06');
-> 1
So you can do a query like this:
$q->whereRaw('WEEKDAY(your_table_name.start_date) = 4')
This way is more efficient then filtering results directly on PHP using Carbon:
You will process data using native database functions that are faster then Carbon over PHP.
Only the relevant data will travel from Database to PHP, reducing query time and memory usage
To get the top performance, you'll need to create a column to store the weekday, so your database will be able to use indexes to avoid full-table scan, giving you the best performance.
whereDate() will only check the dates, so you can't check day with whereDate()
For achieving your goal, you need to perform couple of operations, I am using psuedocode as I don't know how you are querying.
$records = Event::get();
$filteredArray = array();
foreach($records as $record){
if(Carbon::parse($record->start_time)->dayOfWeek == Carbon::FRIDAY || Carbon::parse($record->start_time)->dayOfWeek == Carbon::SATURDAY){
$fillteredArray[]= $record;
}
}
I hope it helps :)
Assuming the model's $casts property is properly casting the column to a date, one can easily apply the filter method using Carbon's dayOfWeek property.
/** #var \Illuminate\Support\Collection $events */
$fridays = $events->filter(
fn ($v, $k) => $v->start_time->dayOfWeek === Carbon::FRIDAY
);
Now $fridays will only contain those events that started on a Friday. Note the database method detailed in the other answer remains more efficient in cases where you have the option to use it, but this may be helpful when extracting a subset from an existing collection.
use Carbon\Carbon;
// this will return an array of events
$fridayEvents = $q->start_time;
// assign empty array variable
$dates = [];
//loop on the events
foreach($fridayEvents as $event){
// Use carbon to parse make sure it is a date not a string type then reformat it by using format() and the small 'l' inside the format returns the exact name for the day of that date
// so if it is friday go and push into the array the entire event
if(Carbon::parse($event)->format('l') == 'Friday'){
array_push($dates, $event);
}
}
// die and dump the data after pushing into the $dates the events
dd($dates);
Related
so I'm trying to get data for my chart from the database and I want it to display a number of orders per day scenario well I tried this code and it gives me full unformatted dates from the database. Any ideas on how to change it to date/month??
$data = DB::table('analytics')->get();
$attrs = array();
foreach ($data as $key => $value) {
// -> as it return std object
$attrs[$value->created_at][] = $value->order;
}
// dd($attrs);
return view('analytic::dashboard.test', compact('attrs'));
$value->created_at is a full date/time string. Since you want to organize it by dates, then you'll need to format it first. Thankfully, Laravel's timestamps are automatically converted to Carbon, so it's easy to format
$attrs[$value->created_at->toDateString()][] = $value->order;
If you want just the year/month, then use format() instead
$attrs[$value->created_at->format('Y-m')][] = $value->order;
Edit I see now you're using query builder (DB), not Eloquent, so you'll need to actually parse the timestamp separately.
$date = Carbon::create($value->created_at);
$attrs[$date->toDateString()][] = $value->order; // 2021-07-14
$attrs[$date->format('Y-m')][] = $value->order; // 2021-07
If you are using collection then
$user=DB::table('analytics')->get()
->mapToDictionary(function ($analytics){
$date=Carbon::parse($analytics->created_at)->format('d-m-Y');
$data[$date]=$analytics->order;
return $data;
})->toArray();
My recommendation would be to use Eloquent instead of the DB query builder. If you have an Analytic model, then this is quite simple. You could just add created_at to your $dates array and it will automatically be cast as a Carbon instance. For instance, in your model:
protected $dates = ['created_at'];
I am trying to get user login activity per month and then to show it in statistic in a bar chat. But the problem i am facing is that its not giving me any data. I will share my code first it will easier to explain my problem
My LoginActivity Model
class LogActivity extends Model
{
protected $table = 'laravel_logger_activity';
public function logActivity(){
$videoPerMonth = array();
for ($i=1; $i<=12; $i++){
$age= 12 - $i;
$userPerMonth[$i] = count(LogActivity::whereNotIn('userId', [1])->whereMonth('created_at', '=', date('n') -$age)->get());
}
}
}
In this i do get user's activity per month but this is not accurate because being the first month if u subtract 12 value goes to negative. And i dont get actual reults.
So, after reading few articles i changed my code to this
$userPerMonth =count( LogActivity::whereNotIn('userId', [1])->whereMonth('created_at', '>=', Carbon::now()->subMonth(12))->get());
return json_encode($userPerMonth);
But this returns empty.What shall i do ?
I want to get data by month vs activity
For example nov 2017 : 300 , dec 2017:800,jan 01 2018:100
Something like that so i can put in bar chat
Can anyone please help me with this
Thanks
I would be tempted to tackle this in a different way, instead of trying to get the user's login activity per month in a loop. I would get the user's login activity for a date range such as a year. This would result in one SQL query being run rather than 12 per user. Once you have the data you can loop through the results and sort them into an array or collection.
Or you could do it as the equivalent of this SQL statement.
SELECT COUNT(*) as login_num, DATE_FORMAT(created, '%m') as login_month, user_id
FROM login_tokens
WHERE created_at >= '2017-01-01 00:00:01' AND created_at <= '2017-12-31 23:59:59'
GROUP BY user_id, login_month
I think to do this in eloquent you would need to do the following;
$logActivity = LogActivity::where(‘created_at’, ‘>=’, ‘2017-01-01 00:00:01’)
->where(‘created’, ‘<=’, ’23:59:59’)
->select(\DB:raw(‘COUNT(*) as login_num, DATE_FORMAT(created, '%m') as login_month, user_id’))
->groupBy(‘user_id’)
->groupBy(‘login_month’)
->get();
Downside to this approach is you're having to put in knowledge of the SQL language, which could differ from MySql, to SQLite, MSSQL etc.
A useful tip if you remove the get() and replace it with toSql() you can echo out the query.
echo LogActivity::where(‘created_at’, ‘>=’, ‘2017-01-01 00:00:01’)
->where(‘created’, ‘<=’, ’23:59:59’)
->select(\DB:raw(‘COUNT(*) as login_num, DATE_FORMAT(created, '%m') as login_month, user_id’))
->groupBy(‘user_id’)
->groupBy(‘login_month’)
->toSql();
dd();
Hope that helps a bit.
I've knocked together a simple class that is more or less what I think you're after. Please note that the use statements are probably not quite right and I've not run this code, so use it as an example. Basically what I've done here is get the current date and work out the previous year from now.
That is then used to select the data from the database within getActivity method. An empty array is created $dateRange for storing the results in with the keys being the year and the month i.e. 2018-02, 2018-01, 2017-12, 2017-11 and so on.
Next I'm doing a simple check to see if we actually have any results from the database because if there are no results then the logins would be 0 for each month.
Then get a date range, okay I've swapped back to standard PHP date interval here as I don't know the Carbon syntax of the top of my head, but as Carbon extends PHP's DateTime it probably should work. However, it may need some tweaking here as well. So get the date range between the two dates by a monthly interval.
Then loop through this date range, use the $hasLoginActivity variable we defined earlier and if that's false add the date range formatted to year-month as the key and the value to zero.
If we have results then add the date range with the same formatting and get the data from the results. As we have returned a collection from Laravel's ORM we should be able to where on it again, see Collections docs. Store this to a variable and check to see if we have results before trying to access the property, else set to zero. You might be able to skip this and access it like $loginActivity->where('login_year_month', $date->format('Y-m'))->login_num; but I can't remember of the top of my head if this causes an error trying to access property on a null value.
I hope this helps.
<?php
use Carbon;
use DateInterval;
use DatePeriod;
use LogActivity as LogActivityModel;
class LogActivity
{
public function annualActivity(): array
{
// Get the current date/time and get a year from now.
$now = new Carbon();
$aYearAgo = $now->clone()->subYears(1);
// Get any login activity from the last year to now.
$loginActivity = $this->getActivity($aYearAgo, $now);
$dateRange = [];
$hasLoginActvity = $loginActivity->count();
// Get a date range from a year ago to now and loop through them.
foreach ($this->getDateRange($aYearAgo, $now) as $date) {
// If we there were no results, then just create the array with a result set of zero.
if (! $hasLoginActvity) {
$dateRange[$date->format('Y-m')] = 0;
continue;
}
$monthActivity = $loginActivity->where('login_year_month', $date->format('Y-m'));
$loginCount = $monthActivity ? $monthActivity->login-num : 0;
// Add to the array the date YYYY-MM as the key i.e. 2018-02 and search the collection for the same date.
$dateRange[$date->format('Y-m')] = $loginCount;
}
// Return the array date with the year and month as the key and a integer as the value.
return $dateRange;
}
private function getActivity(Carbon $aYearAgo, Carbon $now)
{
return LogActivityModel::where(‘created_at’, ‘>=’, $aYearAgo->format('Y-m-d H:i:s'))
->where(‘created_at’, ‘<=’, $now->format('Y-m-d H:i:s'))
->select(\DB:raw(‘COUNT(*) as login_num, DATE_FORMAT(created, '%Y-%m') as login_year_month, user_id’))
->groupBy(‘user_id’)
->groupBy(‘login_month’)
->all();
}
private function getDateRange($from, $to)
{
// Get a the date range between the two dates with an interval of a month.
return new DatePeriod($from, new DateInterval('P1M') ,$to);
}
}
the entries in my database have a custom timestamp field (e.g. 1488446089).
How do I use whereDate() or whereDay() with unix timstamps?
This of course just works for Date Fields.
$posts = Post::whereDay('uploaded_time', '=', '2017-05-10')->get();
To run it correctly use whereDate instead of whereDay
$posts = Post::whereDate('uploaded_time', '2017-05-10')->get();
To get all specific day means (date of month like 1-30)
$posts = Post::whereDay('uploaded_time', '10')->get();//provide date of month
get more detail : https://laravel.com/docs/master/queries#where-clauses
One way to do this is fetching all Posts, and then filtering the results in PHP:
$myDate = '2017-05-10';
$posts = Post::all()->filter(function (Post $post) use ($myDate) {
$uploadedAt = Carbon::createFromTimestamp($post->uploaded_time)->format('Y-m-d');
return $uploadedAt === $myDate;
});
Why? Because if I'm right you are trying to fetch all posts that were uploaded on a certain date. This means that there is a range of timestamps valid. (60 * 60 * 24). With Carbon (comes with Laravel) you can easily convert your timestamps to Y-m-d formatted strings. Those you then compare to the date string of your choice.
Edit
I have no way to test this at the moment, but it should be possible to do this with a query builder after all:
$myDate = Carbon::createFromFormat('Y-m-d', '2017-05-10');
$startOfDayTimestamp = $myDate->startOfDay()->timestamp;
$endOfDayTimestamp = $myDate->endOfDay()->timestamp;
$posts = Post::whereBetween('uploaded_time', [
$startOfDayTimestamp,
$endOfDayTimestamp
])->get();
Here we create 2 timestamps, one for the very start of the date you wish to filter by, and one for the very end. After that, we can use the builder's whereBetween() method and pass the 2 timestamps.
I found this solution here, and simplified a little bit by replacing the setTime() calls with startOfDay and endOfDay methods.
I am new to Laravel.
I am building app that counts your daily steps.
And I am building API, so I need to return 0 steps when there are no results in DB for selected days in array.
But if there is data for some of the days to return actual steps and to return 0 for other days.
I have tried this:
$steps = Step::whereIn('date', $dates)
->where('user_id', Auth::id())
->get();
But it returns only matches. As I wrote I want to return data for all days, not only for days in DB. Please, help :)
This will never be possible with a simple Query as the DB can not give you anything, it does not know about.
You can solve this either progrmmatically bei iterating over the result and adding all missing dates with 0-value or by adding a table to your DB which contains all dates and then left join your steps table with it in the query.
Actually you must have a table for all dates that you list. Because you can only get dates if the records exists in the table. Another option is making a loop in PHP for all dates but this creates lots of query that is not optimal solution.
If you have a dates table, you can join it with your steps table and may use COALESCE command. I recommend you to write the query as pure SQL and then convert it to Eloquent query.
If You don't want to add empty record to database for missing dates
it would be headache like this:
$dates = [
'2016-12-01',
'2016-12-02',
...
'2016-12-31'
];
$records = Step::whereIn('date', $dates)
->where('user_id', Auth::id())
->get();
$steps = [];
$dates = array_flip($dates); // flipping array like: ['2016-12-01' => 0, '2016-12-02' => 1, ...]
foreach($records AS $record) { // iterating records
$steps[] = $record; // collecting records
unset($dates[$record->date]); // removing date that has record for that day
}
$dates = array_flip($dates); // flipping back: [0 => '2016-12-01', ...]
foreach($dates as $date) { // iterating dates that do not have data in db and creating model objects from them
// creating missing record "on the fly"
$Step = new Step([
'date' => $date,
'user_id' => Auth::id(),
'steps' => 0
]);
$Step->save();
$steps[] = $Step; // adding model instance to resulting array
}
usort($steps, function($a, $b) { // sorting array of steps by date
if(strtotime($a->date) > strtotime($b->date)) return 1;
if(strtotime($a->date) < strtotime($b->date)) return -1;
return 0;
});
so my recommendation is to have some console command (/app/Console/Commands/) that will do processing every night and make sure that all records are consistent.
I mean to create some background batch process that will "close the day" and create some records in database if user has no record for that date.
this recommendation simplifies everything, so Your controller will just get data as usual without any additional calculations, iterations and etc.
I am working on an application which uses Laravel and Eloquent that determines whether something (a job) has not been completed on time. This is stored as a DATE within the table. E.g. 2016-07-18
I am trying to get all the records where todays date is greater than the "completed_date" stored inside the table.
Here is what I have tried:
$id = \Auth::user()->id;
$now = \Carbon\Carbon::today();
$jobs = Job::whereHas('manager', function ($user) use ($id) {
$user->where('users.id', '=', $id);
})->where('complete_date', '>', $now)->get();
The issue is that it's still retrieving records that are in the future. For example, the record it returns, I checked the table and the "complete_date" is 2016-08-31
Any ideas please?
Your $now variable is a Carbon object, not a date.
You have to convert the object to a date:
$now = \Carbon\Carbon::now()->format('Y-m-d');
Then you have to use this variable in your model query.