Laravel group by date and count - laravel

I'm trying to add extra information to a Laravel eloquent collection based on the counting number of start(date) field. so the scenario is I have an "appointments" table, I want to add extra attributes to the object in the collection that's returned by eloquent if any days have more than 10 appointments. I have tried many ways like group by and count but didn't work. anyway I have the code below that's I think I'm so close to it but I have been working on it but couldn't find how to make it achieve what I want. I think I'm stuck and I can't think any more. I have commented most of the lines.
$allAppointments = Appointment::get($columns); //returning all the appointments
$days = Appointment::get()->groupBy(function ($val) {
return Carbon::parse($val->start)->format('d');
}); //here I'm returning appointments by group without the count
$counter = [];
foreach ($days as $day => $appointments) {
foreach ($appointments as $appointment) {
if (Carbon::parse($appointment->start)->format('d') == $day) {
$counter = [Carbon::parse($appointment->start)->format('d') => $day]; //here I'm trying to make an array and count the days but not working
}
dd($counter);
foreach ($allAppointments as $allAppointment) {
if (Carbon::parse($allAppointment->start)->format('d') == $day && count($counter) == 10) //here I want to compare the dates and check if that day have more than 10 appointments
$allAppointment->setAttribute('backgroundColor', 'red'); //here I want to add the extra information
}
}
}

Try the following:
Appointment::select(DB::raw('if(count(*) > 10, true, false) as more_than_ten_appointment'))->groupBy(function ($val) {
return Carbon::parse($val->start)->format('d');
})->get();
if DB class isn't exist add after namespace:
use DB;

It's strange after working on it more than almost 10 hours, just 20 minutes after posting it here I could make it work. the code is below:
public function all()
{
$columns = [
'id',
'title',
'start',
'end',
'note',
'allDay',
'editable',
];
$allAppointments = Appointment::get($columns);
$days = Appointment::get()
->groupBy(function ($val) {
return Carbon::parse($val->start)->format('d');
});
foreach ($days as $day => $appointments) {
foreach ($appointments as $appointment) {
foreach ($allAppointments as $key => $allAppointment) {
if (Carbon::parse($allAppointment->start)->format('d') == $day && $appointments->count() >= 10){
$allAppointment->setAttribute('backgroundColor', 'red');
}
}
}
}
return $allAppointments;
}

Do this
$allAppointments = Appointment::get($columns); //returning all the appointments
$days = Appointment::get()->groupBy(function ($val) {
return Carbon::parse($val->start)->format('d');
}); //returning appointments by group without the count
foreach ($days as $day => $appointments) {
foreach ($appointments as $appointment) {
$counter = 0;
if (Carbon::parse($appointment->start)->format('d') == $day) {
$counter = count($appointments); //appointments counter
}
foreach ($allAppointments as $allAppointment) {
if (Carbon::parse($allAppointment->start)->format('d') == $day && $counter >= 10) //compare the date and check if that day have more than 10 appointments
$allAppointment->setAttribute('backgroundColor', 'red'); //add the extra information here
}
}
}

Related

Laravel how to filter out and get available doctors based on leave

I have a method to let users to choose a date from a selected month where it will exclude out any dates found in my holiday tables.
I want to further filter it out by checking my leaves table for the selected day to find out which doctors are available for booking selection for that selected date (e.g. 28 May). E.g. Doctor A will be on leave from 27 May - 28 May, Doctor B does not have a leave data table entry. My current codes below is able to get for this scenario. But for the case of where e.g. Both Doctor A and B are on leave on the 28 May, I will get an error
Invalid parameter number: parameter was not defined
Below are the codes I have tried out:
private function checkLeave($selecteddate)
{
$leave = Leave::where('clinic_id', '=', $_SESSION['clinic_ID'])
->where('start_date', '<=', $selecteddate)
->where('end_date', '>=' , $selecteddate)->pluck('doctor_id');
$leavesList = DB::table('leaves')
->join('accounts', 'accounts.accountID', '=', 'leaves.doctor_id')
->where('leaves.clinic_id', '=', $_SESSION['clinic_ID'])
->where('leaves.doctor_id', '!=' ,$leave)->pluck('name', 'doctor_id');
return $leavesList;
}
Below are my current methods on filtering out the available dates for appointment:
private function getSelectedMonth($select_month)
{
$holidays = $this->getHolidays();
$results = [];
$mthselected = Carbon::parse($select_month);
$workinghr = WorkingHour::where('clinic_id', $_SESSION['clinic_ID'])
->where('off_day', false)
->pluck('day')->toArray();
$firstDayOfMth = $mthselected->startOfMonth()->format('Y-m-d');
$lastDayOfMth = $mthselected->endOfMonth()->format('Y-m-d');
$today = Carbon::now('Asia/Singapore');
$todayMonth = $today->format('M Y');
$mthOfSelected = $mthselected->format('M Y');
if($mthOfSelected == $todayMonth) {
$todayf = $today->format('Y-m-d');
$dateRange = CarbonPeriod::create($todayf, $lastDayOfMth);
foreach ($dateRange as $date) {
$name = $date->format('l');
if (in_array($name, $workinghr)
&& !in_array($date->format('Y-m-d'), $holidays)) {
$results[] = ' '. $date->format('d M Y');
}
}
} else {
$dateRange = CarbonPeriod::create($firstDayOfMth, $lastDayOfMth);
foreach ($dateRange as $date) {
$name = $date->format('l');
if (in_array($name, $workinghr)
&& !in_array($date->format('Y-m-d'), $holidays)) {
$results[] = ' '. $date->format('d M Y');
}
}
}
return $results;
}
Is there a way where I can better filter out and check whether the selected date have available doctors?

Loop through array, and change item details with adding counter

I am new in Laraver, I am using Eloqunet model... deleteItem function, delete item with offer_in and nbr...When delete item I want to add a new nbr from each item with equal offer_id, starting from 1. I need to define counter = 1, and increase by 1 for every item.. I don't know how to write a for loop, or foreach loop, which pass through selected item, and change 'nbr' with the corresponding value of the counter?
my code is:
public function deleteItem(Request $request, $offer_id, $nbr) {
$item = OfferItem::select('id', 'offer_id', 'nbr', 'product', 'quantity', 'item_price', 'item_tax', 'item_total')
->where('offer_id', $offer_id)
->where('nbr', $nbr)
->first();
$item->delete();
//select new items for offer, after deleting item
$items = OfferItem::select('id', 'offer_id', 'nbr', 'product', 'quantity', 'item_price', 'item_tax', 'item_total')
->where('offer_id', $offer_id)
->get();
//todo
return response()->json($item);
}
Using the Laravel collections, you can just loop the collection and set new count to nbr. Using $count as a reference for the closure, to make the logic work.
$int = 1;
$items->each(function (OfferItem $item) use (&$count) {
$item->nbr = $count;
$item->save();
$count++;
});
You can achieve this purely in SQL like in these examples.
You can achieve what you want using Laravel by:
public function deleteItem(Request $request, $offer_id, $nbr) {
OfferItem::query()
->where('offer_id', $offer_id)
->where('nbr', $nbr)
->delete();
// Select new items for offer, after deleting item
$items = OfferItem::where('offer_id', $offer_id)
->where('nbr', '>', $nbr)
->get()
->sortBy('nbr')
->map(function ($item) use ($nbr) {
$update = [
'id' => $item->id,
'nbr' => $nbr,
];
$nbr++;
return $update;
});
OfferItem::query()->update($items);
return response()->json($item); // It is not clear from your question what you
// are trying to return here. Why would you
// return a deleted item?
}

I want to calculate the total work_time of DB

I want the total working time when completion is selected.
workController
public function store(CreateWorkRequest $request, Project $project)
{
$work = new Work;
$work->fill($request->all())->save();
$project -> fill(['status' => $request->status])->update();
if($request->status === 'completed')
{
$alltime = $item->select('work_time')->get();
$project -> fill(['total_work_time' => $alltime])->save();
}
return redirect()->route('work.index', ['project' => $project->id]);
}
But none of that works.
How do I find the sum of a particular column in a selected table?
I tried:
if($request->status === 'completed')
{
$alltime = 0;
$items = Work::where('project_id', $project->id)->get();
foreach($items as $item)
{
$time = $item->select('work_time')->get();
$alltime += $time;
}
$project -> fill(['total_work_time' => $alltime])->save();
}
But it didn't run as expected.
You should be able to get the total working time with the following query:
$totalWorkingTime = Work::query()
->where('project_id', $project->id)
->sum('work_time');

Order items logic with Laravel

For my Laravel-application I've implemented a sort-functionality. In the list of the options I show two buttons (up and down) which trigger the functions up and down in the OptionController (see below).
Question 1
At the moment I am justing a DECIMAL(30,15) field for the sort-column in the database. I choose this 30,15 randomly. Can you give me an advice, which DECIMAL(?,?) is best for this sort field?
Question 2
I want to move the up and down logic to a place, where I can use it in different controllers with generic models (e.g. Sort::up($models, $item). What would be the right place to place such a logic? Service? Helper-function? ...?
Question 3
When I create a new item (e.g. option in my example below) I need to set the sort automatically to the sort of the last item + 1. Of course, I could do this in the controller when storing it, but can I put this logic to the model itself? And: Where can I put this logic to use it in more than one model without repeating the code?
namespace App\Http\Controllers;
use App\Models\Option;
use App\Models\Attribute;
class OptionController extends Controller
{
public function up($id, $attributeId) {
$options = Attribute::findOrFail($attributeId)->options;
$option = Option::findOrFail($id);
foreach ($options as $index => $o) {
// Search for the current position of the
// option we have to move.
if( $option->id == $o->id ) {
// Will be first element?
if( $index == 1) {
// Set the sort to current first element sort - 1
$option->sort = $options[0]->sort-1;
} else if( $index > 1) {
// Get the previous and the pre-previous items from the options
$pre = $options[$index-1]->sort;
$prepre = $options[$index-2]->sort;
$diff = ($pre - $prepre) / 2;
$option->sort = $prepre + $diff;
}
break;
}
}
$option->save();
Session::flash('message', __(':option moved up.', [ 'option' => $option->name ]));
Session::flash('message-type', 'success');
return redirect()->back();
}
public function down($id, $attributeId) {
$options = Attribute::findOrFail($attributeId)->options;
$option = Option::findOrFail($id);
foreach ($options as $index => $o) {
// Search for the current position of the
// option we have to move.
if( $option->id == $o->id ) {
// Will be last element?
if( $index == count($options)-2 ) {
// Set the sort to current last element sort + 1
$option->sort = $options[count($options)-1]->sort+1;
} else if( $index < count($options)-2) { // ???
// Get the previous and the pre-previous items from the options
$next = $options[$index+1]->sort;
$nextnext = $options[$index+2]->sort;
$diff = ($nextnext - $next) / 2;
$option->sort = $next + $diff;
}
break;
}
}
$option->save();
Session::flash('message', __(':option moved down.', [ 'option' => $option->name ]));
Session::flash('message-type', 'success');
return redirect()->back();
}
}
You can use a trait for this. See link for more details.

How to insert big data on the laravel?

I am using laravel 5.6
My script to insert big data is like this :
...
$insert_data = [];
foreach ($json['value'] as $value) {
$posting_date = Carbon::parse($value['Posting_Date']);
$posting_date = $posting_date->format('Y-m-d');
$data = [
'item_no' => $value['Item_No'],
'entry_no' => $value['Entry_No'],
'document_no' => $value['Document_No'],
'posting_date' => $posting_date,
....
];
$insert_data[] = $data;
}
\DB::table('items_details')->insert($insert_data);
I have tried to insert 100 record with the script, it works. It successfully insert data
But if I try to insert 50000 record with the script, it becomes very slow. I've waited about 10 minutes and it did not work. There exist error like this :
504 Gateway Time-out
How can I solve this problem?
As it was stated, chunks won't really help you in this case if it is a time execution problem. I think that bulk insert you are trying to use cannot handle that amount of data , so I see 2 options:
1 - Reorganise your code to properly use chunks, this will look something like this:
$insert_data = [];
foreach ($json['value'] as $value) {
$posting_date = Carbon::parse($value['Posting_Date']);
$posting_date = $posting_date->format('Y-m-d');
$data = [
'item_no' => $value['Item_No'],
'entry_no' => $value['Entry_No'],
'document_no' => $value['Document_No'],
'posting_date' => $posting_date,
....
];
$insert_data[] = $data;
}
$insert_data = collect($insert_data); // Make a collection to use the chunk method
// it will chunk the dataset in smaller collections containing 500 values each.
// Play with the value to get best result
$chunks = $insert_data->chunk(500);
foreach ($chunks as $chunk)
{
\DB::table('items_details')->insert($chunk->toArray());
}
This way your bulk insert will contain less data, and be able to process it in a rather quick way.
2 - In case your host supports runtime overloads, you can add a directive right before the code starts to execute :
ini_set('max_execution_time', 120 ) ; // time in seconds
$insert_data = [];
foreach ($json['value'] as $value)
{
...
}
To read more go to the official docs
It makes no sense to use an array and then convert it to a collection.
We can get rid of arrays.
$insert_data = collect();
foreach ($json['value'] as $value) {
$posting_date = Carbon::parse($value['Posting_Date']);
$posting_date = $posting_date->format('Y-m-d');
$insert_data->push([
'item_no' => $value['Item_No'],
'entry_no' => $value['Entry_No'],
'document_no' => $value['Document_No'],
'posting_date' => $posting_date,
....
]);
}
foreach ($insert_data->chunk(500) as $chunk)
{
\DB::table('items_details')->insert($chunk->toArray());
}
Here is very good and very Fast Insert data solution
$no_of_data = 1000000;
$test_data = array();
for ($i = 0; $i < $no_of_data; $i++){
$test_data[$i]['number'] = "1234567890";
$test_data[$i]['message'] = "Test Data";
$test_data[$i]['status'] = "Delivered";
}
$chunk_data = array_chunk($test_data, 1000);
if (isset($chunk_data) && !empty($chunk_data)) {
foreach ($chunk_data as $chunk_data_val) {
DB::table('messages')->insert($chunk_data_val);
}
}
I used the code below to check the update or insert data of 11 thousand rows. I hope it useful for you.
$insert_data = [];
for ($i=0; $i < 11000; $i++) {
$data = [
'id' =>'user_'.$i,
'fullname' => 'Pixs Nguyen',
'username' => 'abc#gmail.com',
'timestamp' => '2020-03-23 08:12:00',
];
$insert_data[] = $data;
}
$insert_data = collect($insert_data); // Make a collection to use the chunk method
// it will chunk the dataset in smaller collections containing 500 values each.
// Play with the value to get best result
$accounts = $insert_data->chunk(500);
// In the case of updating or inserting you will take about 35 seconds to execute the code below
for ($i=0; $i < count($accounts); $i++) {
foreach ($accounts[$i] as $key => $account)
{
DB::table('yourTable')->updateOrInsert(['id'=>$account['id']],$account);
}
}
// In the case of inserting you only take about 0.35 seconds to execute the code below
foreach ($accounts as $key => $account)
{
DB::table('yourTable')->insert($account->toArray());
}

Resources