separate array object according to date - php

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.

Related

Matching rows by date on a Laravel 9 application using php-carbon objects

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();
}

add row to the DB:: result in Laravel

I have a table BirthDeath like below:
id birthmonth deathmonth
1 1 3
2 4 4
3 2 5
The query is for getting either birth or death month,
$events = DB::table('BirthDeath')->where('birthmonth',4)->orWhere('deathmonth',4)->paginate(10);
This query results in 1 row, however, I need this row to be duplicated (because the birth and death month are the same) and in the view, I am intending to show them separately. My current solution is to duplicate the results if the birth and death month are the same but it does not work. Here is the code
events2 = $events;
foreach ($events as $event) {
if ($event->birthmonth == $CurrentMonth || $event->deathmonth == $CurrentMonth) {
$events2->appends($event); <-------- this is the cause of the error
}
}
If you want to add items to a Collection you can use the add or push method. add takes a single item and push can take many (variable amount of arguments):
$events->add($event);
// or
$events->push($event);

cyrildewit/eloquent-viewable package views

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()
))

how to retrieve due fee month wise student?

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.

Insert into array based on items insert date

I am trying to make an array with posts in them, now, i need to insert the posts into the array based on their post date, eg if a post was posted on day 4 it will come after the post that was posted on day 5, so the newest posts gets added and displayed first, is there a good way to do this?
The easiest and not right method at all:
$sortedNews = []; //sorted news
foreach($newsArray as $news) {
$sortedNews[strtotime($news['date'])] = $news; //take date and switch to unix timestamp as key of value
}
krsort($sortedNews); //sort by key (reverse) new to old
The right way it's let your database sort it itself ORDER BY `date` DESC

Categories