Im using cyrildewit/eloquent-viewable package to get page views. When I check out the documentation, I don't see a method where I can only get views for a single date. It uses Period and Carbon to get dates in a particular period so the (since and upTo) functions.
Any fix or alternative package to get page views.
I am trying to create a analytics page.
public function pastDateViewsChart(){
$days = [];
$view_count = [];
foreach($this->views as $v){
//get views for the past 28 days
if($v->viewed_at >= Carbon::create("28 days ago")){
if(!in_array(Carbon::parse($v->viewed_at)->format('Y-m-d'),$days,true))
array_push($days, Carbon::parse($v->viewed_at)->format('Y-m-d'));
}
}
//get views for each day
$count = views($this)
->period(Period::upto(Carbon::parse($days[0])))
->count();
array_push($view_count,$count);
return $view_count;
}
You're not limited to since and upto, Period has (as per what I can see there https://github.com/cyrildewit/eloquent-viewable/blob/master/src/Support/Period.php) a create method that can take any date as start and end:
->period(Period::create(
Carbon::parse($days[0])->startOfDay(),
Carbon::parse($days[0])->endOfDay()
))
Related
The background
I am building a Laravel application and I have an upsert method on a Booking Controller for updating/inserting bookings.
On upsert.blade.php I want to display a <select> element with a list of days into which a booking can be moved (or inserted).
There is a 'holidays' table with only one column: 'day' (of type datetime, precision 6). Each entry on this table means the system will be on holidays for that day, so bookings cannot be made or transfered into days that appear on this table.
Now, I want the <option>s in the above mentioned <select> to be disabled when they correspond to a holiday.
What I tried:
The view (upsert.blade.php)
<select>
<option value="" disabled selected>Select</option>
#foreach($days as $day)
<option value="{{ $day['value'] }}" #disabled($day['disabled'])>
{{ $day['display'] }}
</option>
#endforeach
</select>
The controller action:
public function upsert()
{
$now = Carbon::now();
$last = Carbon::now()->addDays(30);
$holidays = DB::table('holidays');
$days = [];
// Populate $days with dates from $now until $last
while($now->lte($last))
{
array_push($days, [
'value' => $now->toDateString(),
'display' => $now->format('l j F Y'),
/*
* Mark day as disabled if holidays matching current
* day is greater than 1
* DOESN'T WORK
*/
'disabled' => $holidays->whereDate('day', $now)->count()
]);
$now->addDay();
}
return view('upsert', [
'days' => $days,
]);
}
The problem
The line labelled 'DOESN'T WORK' doesn't work as expected (I expect the query to return 1 if there is a holiday for the current day in the loop, thus marking the day as disabled). It only matches the first day of the loop if it's a holliday, but it won't match any other days.
Note: I have cast the 'day' property of the Holiday model to 'datetime' so Laravel casts the value to a Carbon object when accessing it.
Attempts to solve it
I tried replacing
$holidays = DB::table('holidays');
with
$holidays = Holiday::all();
but that throws the following exception
Method Illuminate\Database\Eloquent\Collection::whereDate does not exist.
So I tried rewriting the query to (note whereDate was replaced by where):
'disabled' => $holidays->where('day', $now->toDateString().' 00:00:00.000000')->count()
But this would never match
The solution
After around 6 hours of fiddling about with this line, reading Laravel documentation and talking to ChatGPT, I couldn't come up with an answert to why this is happening so I replaced the problematic line with
'disabled' => Holiday::whereDate('day', $now)->count()
Which does the job but I think is terrible for performance due to so many (in my opinion unecessary) round trips to the database.
The question
Could anyone shed some light on this?
Although I've found a solution, I don't think it would scale and I also didn't learn a thing from the experience, I still have no idea why the first query is only matching the first day and no other days. Or why the second one using where() doesn't match any days at all when it is comparing strings and I am using the exact format the strings are stored in on the database.
Or maybe the problem is not on the query, but on the Carbon object?
If you want to reproduce it, follow steps on this gist:
https://gist.github.com/alvarezrrj/50cd3669914f52ce8a6188771fdeafcd
DB::table('holidays') instantiates an Illuminate\Database\Query\Builder object. The where method modifies that object in place.
So if you're looping from January 1st-3rd and are adding a new where condition on each loop, that's going to fail because now you are basically querying this. Obviously the day column cannot match 3 different dates.
SELECT * FROM holidays
WHERE DATE(day) = '2022-01-01'
AND DATE(day) = '2022-01-02'
AND DATE(day) = '2022-01-03'
That's also why it only worked on the first loop for you, because at that point there is only 1 where condition.
You would need to move the instantiation inside the while loop so that it gets reset on each loop. Which is basically what you did in your solution.
Re: performance, what you were trying to do would not have saved you any DB cycles anyway. Each time you call count() you are hitting the database, regardless of whether it's a new $holidays object or not.
If you're concerned about performance, one thing you could do is fetch all of the holidays between the start & end date in a single query.
// May need to call toDateString() on $now and $last
$holidays = Holiday::whereBetween('day', [$now, $last])
->get()
->pluck('id', 'day'); // Assuming day is a DATE column not DATETIME or TIMESTAMP
// This will give you a collection with an underlying array like this:
// ['2022-07-04' => 1, '2022-12-25' => 2]
while($now->lte($last))
{
array_push($days, [
// Now you can instantly look it up in the array by the date
'disabled' => isset($holidays[$now->toDateString()]),
]);
$now->addDay();
}
I'm try to fetch due month which, students not submitted there fee in his profile its show like your fee is due in this month,example- if a student not paid his/her fee in february and january then its his account that you fee is due in february and january please submit fee.I'm able to fetch all students who not submitted fee but how to show month(<=current month) all previous month fee dues
here is my code for profile
public function show($reg_no = null)
{
$s = Student::where('reg_no', $reg_no)->with('courses','states','sections','city','ccity','sstates')->first();
return view('students.student',compact('s'));
}
for students who not submitted fee in current month all students
$dt = Carbon::now();
$query = Student::where('status',1)->whereDoesntHave('subscriptions', function ($queryt) use($dt) {
$queryt->whereMonth('created_at','=',$dt->month);
})->with('courses')->paginate(15);
and here is my subscriptions table
I'll show you an example, but don't just copy paste, because this code is only to help you to understand how to achieve it. You'll need to make some change to fit in your app.
$messages = array();
foreach ($Student->subcriptions as subcription) {
if ($subcription->late_fee != null) {
$month = $subcription->month->month(); // I'm not sure if Carbon will handle this part. If not, you'll need to do an explode of $subcription->month then take the $month[1]...
if ($month == 1) {$month = 'January';}
if ($month == 2) {$month = 'February';}
... //Do it for every month...
$message = 'Your fee are due for '.$month; //Or you can only push the month then into the view then implode the array.
array_push($messages, $message);
}
}
Into your view, if you are using blade, you simply do this where you want to show the messages. Don't forget to pass $messages to the view.
#foreach($messages as $message)
<span>{{$message}}</span>
#endforeach
There is multiple way to do what you want. But this will show every month where the student have late_fee not null. Now adapt it to your code.
Don't forget that if you don't want something like:
Your fee are due for January.
Your fee are due for February.
Your fee are due for Mars.
....
Look for implode of the messages array without adding Your fee are due for into the loop then don't use the for each loop into the view, simply display the new imploded variable.
I have an iOS app where users can reserve a car either daily or hourly and if a user reserves one car other user can't reserve it till the first reservation is over. I'm using Laravel as the API to save data to MySQL database.
The format of time and date are like this 27 Dec 2016 15:21. I'm saving the data
public function requested(Request $request)
{
$time = new Reservation();
$time->from_date = $request->from_date;
$time->to_date = $request->to_date;
$time->from_time = $request->from_time;
$time->to_time = $request->to_time;
}
but this won't prevent time overlapping for sure so I tried this
$carCount = Reservation::where(
function ($query) use ($startTime, $endTime) {
$query->where('from_time', '<', $endTime)
->where('to_time', '>', $startTime);
}
)->count();
if ($carCount > 0) {
return response()->json(['request' => 'no']); // this response just to check
} else {
$carRoom->save();
response()->json(['request' => 'yes']);
}
then I thought this isn't working because of date/time format. But I wanted to make sure what format I should convert the date in laravel?
this is my migration for Reservation:
$table->string('from_date')->nullable();
$table->string('to_date')->nullable();
$table->string('from_time')->nullable();
$table->string('to_time')->nullable();
I made the string because I wasn't sure if Time or anything is the right one
My main question is how can I avoid time and date overlapping in my app's database?
Store the dates in the database as timestamps
then your query should work as intended then convert the incoming dates:
27 Dec 2016 15:21
to timestamps using strtotime() or with datetime objects
both are part of PHP check the PHP documentation to see more information.
I have a array of events with unixtimestamp and i want to show them according to year. Mean section wise.
2015
Event 1
Event 2
Event 3
2014
Event 1
Event 2
Event 3
What i do:
$yearlyEvents=array();
foreach ($events as $event) {
$eventPost = get_post($event->post_id);
$timestamp=$event->start;
$eventYear=gmdate("Y", $timestamp);
if($index=in_array($eventYear, $yearlyEvents, true)){
print_r($index);
}
else{
$tempObj['name']=$eventYear;
$tempObj['events']=$event;
$yearlyEvents[]=$tempObj;
}
}
But not get the desired results.Anybody help?
You need to get events as sub arrays of year.
Add sub arrays, one for each year append events to it.
This is my preferred logic, please feel free to change it in accordance with your project needs.
Corrected Code:
$yearlyEvents=array();
foreach ($events as $event) {
$eventPost = get_post($event->post_id);
$timestamp=$event->start;
$eventYear=gmdate("Y", $timestamp);
if($index=in_array($eventYear, $yearlyEvents, true)){
print_r($index);
}
else{
$tempObj['name']=$eventYear;
$tempObj['events']=$event;
$yearlyEvents[$eventYear][] = $tempObj; // Check here.
}
}
first of all you need to get result from Database in such a format which is easy to manipulate for your output.
Here you are trying to display events years wise in descending order and events in ascending order. So first of all get result from database like
SELECT * FROM events ORDER by events.start DESC,events.post_id ASC
Now it is in an order which is easy to display. loop through and display result until next year found. check if next year come display year too.
I have an app that posts a scheduled post to a page 3 hours after the last post.
the problem is that there doesn't seem to be a quick way (that I've found) that can retrieve all scheduled posts in descending order so the post with the latest date is first in the list
is there a fql query or another way that I can bring back all scheduled posts in reverse order so I can grab the scheduled_publish_time from the first array key easily?
the code I'm using is
$posts = $facebook->api($fanpage.'/promotable_posts?limit=500');
$scheduledposts = array();
if($posts){
// find last published post
$time = time();
foreach($posts['data'] as $post){
if($post['is_published'] == false){
$scheduledposts[] = $post;
if($time < $post['scheduled_publish_time']){
$time = $post['scheduled_publish_time'];
}
}
}
$timetopost = $time + 10800;
}
// sort scheduled posts by order of scheduled_publish_time in reverse
usort($scheduledposts, function($a, $b) {
return $b['scheduled_publish_time'] - $a['scheduled_publish_time'];
});
the order of the $posts array isn't always chronological and what if there are more than 500 posts in the schedule? I want to be able to retrieve just the last 5 scheduled posts that have the scheduled_publish_date furthest away