So in my PHP program I'm creating a calendar feature and one of the classes is "CalendarDay". What I want to do is be able to instantiate a new day for each day count, so for example new CalendarDay (22) means a new 22nd of the month date. There is also a show() function used for displaying each day. The class itself functions normally but when I try instantiating new days using recursion it no longer seems to work as everything related to the instantiated object disappears from the webpage.
class CalendarDay{
private $current_month;
private $current_year;
private $current_date;
public $reminderSet;
public $reminders;
public function __construct($current_day_of_month){
$current_year = date("Y");
$current_month = date("m");
$this->days_in_month = cal_days_in_month(CAL_GREGORIAN, $current_month, $current_year);
$this->current_date = date("d");
$this->current_day_of_month = $current_day_of_month;
}
public function show(){
$output = '<div class = "generator>"';
//$output .= $this->current_date;
//$output .= '<h1>' . $this->current_date . '</h1>';
$output .= '</div>';
$output .= $this->current_day_of_month;
echo $output;
}
}
My failed attempt at recursion:
for ($countTo31 == 0; $countTo31 == 31; $countTo31++){
$holder = $countTo31;
$date = new CalendarDay ($holder);
$date->show();
}
For the reference, this original block of code without the recursion works normally:
$holder = $countTo31;
$date = new CalendarDay ($holder);
$date->show();
I'm very confused with what you're trying to accomplish...
You have a "day" class which takes input to initialise a specific day but instead actually works out the current day based on date("Y-m-d");?.. And then outputs the input day anyway?
Honestly, it looks more like you want a "month" object
Initial problems
You use == to define your starting point
== is not an assignment operator, it's a comparison.
It effectively adds an additional iteration of the loop at the start of the loop
for($i == 1; $i < 5; $i++){
echo $i;
}
// Loop 1:
// --> Notice on $i == 1
// --> Notice on $i < 5
// --> Notice on echo $i
// --> Notice on $i++
// Loop 2:
--> $i = 1 BECAUSE of the previous $i++
so the intended loop starts...
Additionaly the loop, in this case, should start with 1 not 0(!)
You use == to define your condition
for loops like yours work effectively as:
for ( A ; B ; C ){
// Do something...
}
// Loop works as:
// Check A against B
// If TRUE then "Do something..."
// and then do C
// IF FALSE then break
However, even if your assignment (A) was correct (i.e. $countTo31 = 1), it still wouldn't work because 1 !== 31 and therfore the loop breaks from the start
It should be $countTo31 <= 31
Looping over object, rewriting the variable
Your code currently rewrites the variable which holds the date object with every loop
Effectively you create an object to output the day, output that data, and instantly remove the object so that it can't be used for anyting else...
Your HTML output has a " in the wrong place:
$output = '<div class = "generator>"';
//Should be...
$output = '<div class = "generator">';
Some of the variables in your class are not assigned or declared properly
$current_month is declared but never assigned
$current_year is declared but never assigned
$current_day_of_month is assigned but not declared
$days_in_month is assigned but not declared
Interim solution
Without further information on what you are intending to do it isn't possible to give good/accurate guidance, so I will leave a working example which should show you what to do:
$days_in_month = cal_days_in_month(
CAL_GREGORIAN,
date("m"),
date("Y")
);
for ($day = 1; $day <= $days_in_month; $day++){
echo "Day {$day}<br>";
}
Proposed changes
It doesn't look as though you really even want a "day" class for the functions you're trying to implement. So, in my mind, it would be better to first create a "month" object with all of the days of the month and then have that generate a "day" object for each day of the month which then can gather the information for each day e.g. reminders.
Doing it this way you can then update each day as you go with, for example, user input or database data.
class Month
{
private $month;
private $year;
private $days = [];
public function __construct($month, $year)
{
$this->month = $month;
$this->year = $year;
$number_of_days = cal_days_in_month(
CAL_GREGORIAN,
$month,
$year
);
for ($i = 1; $i <= $number_of_days; $i++){
$date = "{$this->year}-{$this->month}-{$i}";
// $days[] = new Day($date);
$this->days[$i] = new Day($date);
}
}
public function getDay($day)
{
return $this->days[$day];
}
public function getNumberOfDays()
{
return count($this->days);
}
}
class Day
{
private $date;
private $reminders = [];
public function __construct($date)
{
$this->date = $date;
// Initialise day...
# Get reminders
# Get meetings
# Get bills to pay
}
public function getReminders()
{
return $this->reminders;
}
public function setReminder($content, $time)
{
// Set reminders
$this->reminders[] = [
"content" => $content,
"time" => $time
];
}
public function show()
{
return date("d / m / Y", strtotime($this->date));
}
}
$month = new Month(12, 2020);
for ($i = 1; $i <= $month->getNumberOfDays(); $i++){
echo $month->getDay($i)->show()."<br>";
}
I have built an Employee Management System using Laravel 5.7. Calculating salary works fine for 100-150 employees (but taking a long time to process the data) & for more than 150 employees it is showing a time out error.
I want to add the feature to calculate the salary of all employees in one go & also reduce the processing time for the same. Would it be appropriate to use chunk() for that? If yes, how do I implement that?
Note:
The web application is for a BPO so there are so many types of logic to apply while calculating a salary. The code is too lengthy to share here, but if anyone wants to see the code, I can share it.
Front-end form screenshot
Page after calculation
<?php
public function calculate_salary(Request $request){
$this->validate($request,[
'employee_id' => 'required',
'attendance_types' => 'required',
'month' => 'required_without:choose_date',
'choose_date' => 'required_without:month'
]);
$download_salary = false;
$submit_salary = false;
if ($request->has('submit_data'))
{
$submit_salary = true;
}
else
{
$submit_salary = false;
}
if ($request->has('download_data'))
{
$download_salary = true;
}
else
{
$download_salary = false;
}
$dept = $request->input('salary_department');
$role = $request->input('salary_role');
$process = $request->input('salary_process');
$salary_dept = Department::where('id', $dept)->pluck('dept_name')->first();
$salary_role = Role::where('id', $role)->pluck('role_name')->first();
$salary_process = Process::where('id', $process)->pluck('process_name')->first();
if ($submit_salary == true || $download_salary == true)
{
$months = $request->input('month');
$month = $months[0];
$employee_ids = $request->input('employee_id');
$attendance_types = $request->input('attendance_types');
$attendance_type_temp_insert = $attendance_types[0];
$attendance_type = explode(',', $attendance_type_temp_insert);
if (!empty($request->input('choose_date')))
{
$dates = $request->input('choose_date');
$date = $dates[0];
}
else
{
$date = "";
}
}
else
{
$month = $request->input('month');
$employee_ids = $request->input('employee_id');
$attendance_type = $request->input('attendance_types');
$attendance_type_temp_insert = implode(',', $request->input('attendance_types'));
$date = $request->input('choose_date');
}
// Get start & end dates
$data = $this->get_salary_dates($month, $date);
$start_date = $data['start_date'];
$end_date = $data['end_date'];
$salary_start_date = $start_date->toDateString();
$salary_end_date = $end_date->toDateString();
$working_days_by_month = $this->get_working_days_of_given_month($start_date, $end_date);
$working_days = $working_days_by_month['working_days'];
$no_days = $working_days_by_month['no_days'];
Schema::create('temp_salary', function (Blueprint $table) {
$table->increments('id');
$table->integer('employee_id');
$table->string('employee_name');
$table->integer('working_days');
$table->float('emp_working_days');
$table->float('basic');
$table->float('hra');
$table->float('conveyance');
$table->float('spcl_inc');
$table->float('gross_salary');
$table->float('pf');
$table->float('esi');
$table->integer('headphone');
$table->integer('id_card');
$table->float('deductions');
$table->integer('pl_balance');
$table->integer('combo_balance');
$table->float('net_payable');
$table->string('month')->nullable();
$table->string('attendance_types');
$table->string('choose_date')->nullable();
$table->string('department')->nullable();
$table->string('role')->nullable();
$table->string('process')->nullable();
$table->timestamps();
$table->temporary();
});
$csv[] = ['Employee ID', 'Name','Department','Role','Process','Bank','Bank Account Number', 'Basic', 'HRA', 'Conveyance', 'Spcl Inc', 'Gross Salary','PF', 'ESI', 'Deductions', 'Total Working Days', 'Net Payable','Salary Month', 'Salary Start Date','Salary End Date'];
$csv1[] = ['Employee ID', 'Name','Department','Role','Process','Bank','Bank Account Number', 'Basic', 'HRA', 'Conveyance', 'Spcl Inc', 'Gross Salary','PF', 'ESI', 'Deductions', 'P','H','A','WO','WOP','WOH','UAL','Paid Leaves','PL','Combo','ual_deduct','Total Working Days', 'Net Payable','Salary Month', 'Salary Start Date','Salary End Date'];
foreach($employee_ids as $id) {
if($submit_salary == true){
DB::table('attendances')->where('employee_id', '=', $id)->whereBetween('attendance_date', [$start_date, $end_date])->update(['salary_status' => 1]);
}
$employee = Employee::find($id);
$name = $employee->name;
$emp_role = $employee->empRole->role_name;
$emp_department = $employee->empDepartment->dept_name;
$emp_process = $employee->empProcess->process_name;
$bank_account_number = $employee->bank_account_number;
$bank_name = $employee->bank_name;
$salary = Salary::where('employee_id', $id)->first();
$pl_balance = $salary->pl_balance;
$combo_balance = $salary->combo_balance;
$attendances = DB::table('attendances')->where('employee_id', '=', $id)->get();
if ($attendances->count() > 0)
{
foreach($attendances as $attendance)
{
$att_id = $attendance->id;
$att_date = Carbon::parse($attendance->attendance_date);
$dialer_in = strtotime($attendance->dialer_in_time);
$dialer_out = strtotime($attendance->dialer_out_time);
$bio_in = strtotime($attendance->biometric_in_time);
$bio_out = strtotime($attendance->biometric_out_time);
$bio_diff = $bio_out - $bio_in;
$crm_in = strtotime($attendance->crm_in_time);
$crm_out = strtotime($attendance->crm_out_time);
$week_off = $attendance->week_off;
$combo = $attendance->combo;
$holiday = $attendance->holiday;
$ual = $attendance->ual;
$dialer_duration = $attendance->dialer_difference;
$bio_duration = $attendance->biometric_difference;
$crm_duration = $attendance->crm_difference;
$chkatt = $this->get_attendance($attendance_type, $attendance->dialer_in_time, $attendance->dialer_out_time,$attendance->crm_in_time,$attendance->crm_out_time, $dialer_duration, $bio_duration, $crm_duration);
$attendance_status = $this->attendance_status($id, $chkatt, $holiday, $week_off, $ual);
DB::table('attendances')->where('id', $att_id)->update(['attendance_status' => $attendance_status]);
//echo $attendance_status;
}
}
$totalWorkingDays = $this->get_work_days($id,$start_date,$end_date,$combo_balance,$pl_balance, $submit_salary);
$totalWorkingDays = number_format((float)$totalWorkingDays, 2, '.', '');
$salary_data = $this->get_calculated_salary_data($no_days, $totalWorkingDays, $id, $submit_salary);
$basic = $salary_data['basic'];
$hra = $salary_data['hra'];
$conveyance = $salary_data['conveyance'];
$spcl_inc = $salary_data['spcl_inc'];
$gross_salary = $salary_data['gross_salary'];
$pf = $salary_data['pf'];
$esi = $salary_data['esi'];
$hp_charges = $salary_data['hp_charges'];
$idcard_charges = $salary_data['idcard_charges'];
$deductions = $salary_data['deductions'];
$net_payable = $salary_data['net_payable'];
DB::table('temp_salary')->insert(['employee_id' => $id, 'employee_name' => $name, 'working_days' => $working_days, 'emp_working_days' => $totalWorkingDays, 'basic' => $basic, 'hra' => $hra, 'conveyance' => $conveyance, 'spcl_inc' => $spcl_inc, 'gross_salary' => $gross_salary, 'pf' => $pf, 'esi' => $esi, 'headphone' => $hp_charges, 'id_card' => $idcard_charges, 'deductions' => $deductions, 'pl_balance' => $pl_balance, 'combo_balance' => $combo_balance, 'net_payable' => $net_payable, 'month' => $month, 'attendance_types' => $attendance_type_temp_insert, 'choose_date' => $date, 'department' => $salary_dept, 'role' => $salary_role, 'process' => $salary_process]);
$csv[] = [$id, $name, $emp_department, $emp_role, $emp_process, $bank_name, $bank_account_number, $basic, $hra, $conveyance, $spcl_inc, $gross_salary, $pf, $esi, $deductions, $totalWorkingDays, $net_payable, $month, $start_date, $end_date];
$get_days_status = $this->get_days_status($id, $start_date, $end_date);
$pCount = $get_days_status['pCount'];
$HalfDaysCount = $get_days_status['hCount'];
$lateLeaveCount = $get_days_status['late_leave'];
$wopCount = $get_days_status['wopCount'];
$leaves_count = $get_days_status['leaves_count'];
$UalCount = $get_days_status['UalCount'];
$woCount = $get_days_status['woCount'];
$wohCount = $get_days_status['wohCount'];
$ual_deduct = ($UalCount * 1.5) - $UalCount;
$get_paid_leaves_row = $get_days_status['pl_leaves'];
$csv1[] = [$id, $name, $emp_department, $emp_role, $emp_process, $bank_name, $bank_account_number, $basic, $hra, $conveyance, $spcl_inc, $gross_salary, $pf, $esi, $deductions, $pCount,$HalfDaysCount,$leaves_count,$woCount,$wopCount,$wohCount,$UalCount,$get_paid_leaves_row,$pl_balance, $combo_balance,$ual_deduct, $totalWorkingDays, $net_payable, $month, $start_date, $end_date];
}
$datas = DB::table('temp_salary')->get();
if($submit_salary == true){
return Excel::create('Employee_salary_report', function($excel) use ($csv) {
$excel->sheet('Employee_salary_report', function($sheet) use ($csv) {
$sheet->fromArray($csv, null, 'A1', false, false)
->getStyle('A1')
->getAlignment()
->setWrapText(true);
});
})->download('csv');
}
if($download_salary == true){
return Excel::create('salary_report', function($excel) use ($csv1) {
$excel->sheet('salary_report', function($sheet) use ($csv1) {
$sheet->fromArray($csv1, null, 'A1', false, false)
->getStyle('A1')
->getAlignment()
->setWrapText(true);
});
})->download('csv');
}
Schema::drop('temp_salary');
$departments = Department::all();
$processes = Process::all();
$roles = Role::all();
return view('sys_mg.salaries.get-salary')->with(['datas'=>$datas,'departments' => $departments, 'processes' => $processes, 'roles' => $roles, 'salary_dept' => $salary_dept, 'salary_role' => $salary_role, 'salary_process'=>$salary_process, 'salary_month'=>$month,'salary_startDate' => $salary_start_date, 'salary_endDate' => $salary_end_date, 'attendance_check_type' => $attendance_type]);
}
This is a huge process, doing many things at once, some of them deeply nested in each other.
The function is very, very long, which means it probably needs to be abstracted into various methods and classes (better OOP).
You may also be running into the N+1 problem with some of your Laravel queries, although it's hard to say at a glance.
I would recommend using Laravel's Queues, and adding each employee's payroll calculation to the queue: https://laravel.com/docs/5.8/queues
You can then use a worker process to perform each one individually.
All the database updates and transactions means PHP is constantly going to-and-fro from your database. Try to perform as many operations in pure code as possible, and then once completed, write to the DB. (Where possible, this is not a blanket rule).
Initially I would say, consider creating a SalaryCalculator class with methods such as getAttendances() and calculateSalaryFromEmployeeAttendances().
Separately, create a CSV exporter class. Process all your payroll calculations first, store the results in the DB, and then convert to CSV on demand later.
You'll refactor this later once you are able to look at all the different parts, someone else may be able to suggest a better way to break it down, but in some ways there are no right answers... just start with some OOP and abstraction of methods, and it will get better.
Use microtime(true) to get and calculate the time differences between when you start and finish operations, to start to track how long each function runs for... and go looking for the big optimisations first. What is the slowest part? Why?
You could probably ask a dozen Stack Overflow questions to optimise each of those methods, and that's Ok!
I am using CodeIgniter. I am working on the small project which is a Batch list. Now If an admin wants to create the batch list then should enter the start date and end date and start time and end time then it will check in the database that batch is running on the same date and time? If yes then it will display the message if not then it will create a new batch list.
If the date is the same the time should be different.
Now My logic is,
I am comparing the first new_start_date with exist_start_date and exist_end_date if date found in between then it will check the time.
It's working till date compare. Even it's checking the time but from there how to exit the process and call the JSON? because from there my JSON not working.
I added "echo "time not match";" from there I am not able to call the JSON I am getting the output on my network tab.
I am getitng the output
enter 1enter 2{"error":true,"msg":"Batch Created"}time not match
Would you help me out in this?
$id = $this->input->post('venue_id');
$venue_id = implode(',',$id);
$activity_list_id = $this->input->post('activity_name');
$new_batch_start_date = date('Y-m-d',strtotime($this->input->post('start_date')));
$new_batch_end_date = date('Y-m-d',strtotime($this->input->post('end_date')));
$new_batch_start_time = $this->input->post('start_time');
$new_batch_end_time = $this->input->post('end_time');
$days = implode(',',$this->input->post('days'));
//print_r($days);
if($new_batch_start_date >= $new_batch_end_date)
{
$response['error'] = false;
$response['msg'] = "End Date Should be Greater than Start Date";
echo json_encode($response);
return false;
}
//convert in Time Format
$new_batch_start_time = strtotime($new_batch_start_time);
$new_batch_end_time = strtotime($new_batch_end_time);
$venue = $this->input->post('name');
$data = array(
'activity_list_id' => $this->input->post('activity_name'),
'batch_venue_id' => $venue_id,
'batch_name' => $this->input->post('batch_name'),
'start_date' => date('Y-m-d',strtotime($this->input->post('start_date'))),
'end_date' => date('Y-m-d',strtotime($this->input->post('end_date'))),
'start_time' => $this->input->post('start_time'),
'end_time' => $this->input->post('end_time'),
'total_capacity' => $this->input->post('total_capecity'),
'batch_status' => 1,
'created_by' => trim($this->session->userdata['login_data']['user_id']),
'created_date' => date('d-m-Y h:i:s A'),
'batch_days' => $days
);
$get_batch_details = $this->Batch_model->fetchBatches();
if(!empty($get_batch_details))
{
foreach ($get_batch_details as $rows)
{
$exist_batch_start_date = $rows->start_date;
$exist_batch_end_date = $rows->end_date;
$batch_time1 = strtotime($rows->start_time);
$batch_time2 = strtotime($rows->end_time);
$batch_venue_id = explode(',',$rows->batch_venue_id);
$common_venue_id = array_intersect($id,$batch_venue_id);
//print_r($common_venue_id);
if($common_venue_id)
{
echo "enter 1";
//if new batch start date between existing batch start date
if($exist_batch_start_date <= $new_batch_start_date && $exist_batch_end_date >= $new_batch_start_date ){
echo "enter 2";
if($batch_time1 <= $new_batch_start_time && $batch_time2 > $new_batch_start_time){
$msg = "Other Batch Alredy Running On from Date $batch_start_date to $exist_batch_end_date on Time : $batch_time1 to $batch_time2.
Please Change Time Slot or Start And End Date";
$response['error'] = false;
$response['msg'] = $msg;
echo json_encode($response);
exit;
}
else{
$result = $this->Batch_model->createBatch($data);
echo "time not match";
print_r($result);
}
break;
}
//if date is different
else
{
$result = $this->Batch_model->createBatch($data);
}
}else
{
$result = $this->Batch_model->createBatch($data);
}
}
}
//first time creating batch
else
{
$result = $this->Batch_model->createBatch($data);
}
Mobel
function createBatch($data){
if($this->db->insert('batch_list',$data))
{
$response['error'] = true;
$response['msg'] = "Batch Created";
echo json_encode($response);
}
else
{
$response['error'] = true;
$response['msg'] = "Failed to Create Batch";
echo json_encode($response);
}
}
function fetchBatches()
{
$result = $this->db->where(['batch_list.batch_status'=>1,'activity_list.act_status'=>1])
->from('batch_list')
->join('activity_list','activity_list.activity_id = batch_list.activity_list_id')
->get()
->result();
return $result;
}
Ajax
success: function(response){
var data = JSON.parse(response);
if (data.error == true){
swal({
title: "Success",
text: data.msg ,
type: "success"
}).then(function(){
location.reload();
}
);
} else {
swal({
title: "Warning",
text: data.msg ,
type: "warning"
});
}
}
Would you help me out in this issue?
your entire approach is a bit messy because you find yourself in a ton of redundant code fragments and nobody is able to understand what exactly you want - i gv you some hints here including an example based on your code
Use Exceptions - it's perfect for your case - if something goes wrong - stop it
Try to filter your need to an extent of one single task und try to solve it - and only after that go to the next task
Always - remember always - think about one term - if you find repeatedly the same code in your application - you know something is wrong - and you should refactor it - don't be ashamed about redundancies - they do always happen - but if you find them, you must refactor those code snippets
Now to your example
What are your tasks here ?
you can try to ask your database if a batch is already running - you dont need to iterate over the entire table entries
Compare both input Dates from Administrator - if start date is in the future of end date, instantely stop the application
your intersection isn't really clear to me what you want to achieve here - but i'm really convinced you can ask the database here too (catchword: find_in_set)
Based on that information we can start to develop things now ;) (if i don't have everything just complete the list above and try to implement your task)
Controller:
try
{
$id = $this->input->post('venue_id');
$venue_id = implode(',',$id);
$activity_list_id = $this->input->post('activity_name');
$new_batch_start_date = date('Y-m-d',strtotime($this->input->post('start_date')));
$new_batch_end_date = date('Y-m-d',strtotime($this->input->post('end_date')));
$new_batch_start_time = $this->input->post('start_time');
$new_batch_end_time = $this->input->post('end_time');
$days = implode(',',$this->input->post('days'));
$objDateStart = DateTime::createFromFormat('Y-m-d h:i a', $new_batch_start_date.' '.$new_batch_start_time);
$objDateEnd = DateTime::createFromFormat('Y-m-d h:i a', $new_batch_end_date.' '.$new_batch_end_time);
if ($objDateEnd < $objDateStart) throw new Exception('End Date Should be Greater than Start Date');
if ($this->Batch_model->hasBatchesBetweenDates($objDateStart, $objDateEnd)) throw new Exception('Other Batch already running On from '.$objDateStart->format('d-m-Y H:i').' to '.$objDateEnd->format('d-m-Y H:i').'. Please Change Time Slot for Start and End Date');
$data = array(
'activity_list_id' => $this->input->post('activity_name'),
'batch_venue_id' => $venue_id,
'batch_name' => $this->input->post('batch_name'),
'start_date' => $objDateStart->format('Y-m-d'),
'end_date' => $objDateEnd->format('Y-m-d'),
'start_time' => $objDateStart->format('H:i'),
'end_time' => $objDateEnd->format('H:i'),
'total_capacity' => $this->input->post('total_capecity'),
'batch_status' => 1,
'created_by' => trim($this->session->userdata['login_data']['user_id']),
'created_date' => date('d-m-Y h:i:s A'),
'batch_days' => $days
);
$this->Batch_model->createBatch($data);
}
catch(Exception $e)
{
$arrError = [
'error' => false,
'msg' => $e->getMessage()
];
echo json_encode($arrError);
}
Model:
public function hasBatchesBetweenDates(DateTime $objDateStart, DateTime $objDateEnd)
{
$query = $this->db
->from('batch_list')
->join('activity_list','activity_list.activity_id = batch_list.activity_list_id')
->where('CONCAT(start_date,\' \',start_time) >=', $objDateStart->format('Y-m-d H:i:s'))
->or_group_start()
->where('CONCAT(end_date, \' \', end_time) <=', $objDateEnd->format('Y-m-d H:i:s'))
->where('CONCAT(end_date, \' \', end_time) >=', $objDateStart->format('Y-m-d H:i:s'))
->group_end()
->get();
return ($query->num_rows() > 0);
}
i hope you understand the concepts here - if you've questions - don't hesitate to ask
How to check if one UNIX timestamp range is overlapping another UNIX timestamp range in PHP?
I am developing an application which takes future reservations. But, only one (1) reservation is allowed per period.
Example:
Mr. X has a reservation for a resource from 10:00 A.M. to 12:00 P.M. (noon). Later, Ms. Y wants to reserve that same resource from 8:00 A.M. to 11:00 P.M.. My application should reject Ms. Y's attempted reservation because it overlaps Mr. X's prior reservation.
I am storing the start and end times of existing reservations in UNIX timestamps (integers), but I could convert them into the following format "yyyy-mm-dd hh:mm:ss" if required, or vice versa.
I do not understand how to solve this problem. If I check the new start time with all the existing reservation start times, and the new end time in a similar fashion, the logic will have many if statements and make the application slow.
Would you please help me to solve this issue in an efficient way without using lots of server resources.
Your help is greatly appreciated.
Thanks
Introduction
In other words, you need to do a comparison of all reservation intervals (UNIX timestamps) for a particular resource to determine if a new reservation is valid (within the domain for new reservations).
Step 1
First, a SQL query similar to this might help. While key words like ANY, ALL, NOT, EXISTS and others may seem tempting, it is up to you to decide how much information you need in the event of a scheduling conflict (based on your UI). This query provides the opportunity to extract the maximum amount of information (in PHP, etc ...) about a potential reservation with look ahead forecasting.
// A query like this might help. It's not perfect, but you get the idea.
// This query looks for ALL potential conflicts, starting and ending.
$sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
FROM `reservations` AS `t1`
INNER JOIN `resources` AS `t2`
ON `t1`.`resourceId` = `t2`.`resourceId`
WHERE `t2`.`resourceId` = :resourceId
AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
ORDER BY `t1`.`startTime` ASC";
Potentially. this will leave you with a multi-dimentional array. The following logic allows you to get a report detailing why the reservation cannot be made. It is up to you to interpret the report in another module.
Step 2
Generalize the solution as a methods of a Reservation class. Depending on your RDBMS, you may be able to do something like this in SQL. Although, it will probably be far less specific and you may want that granularity later. You could send the report in JSON format to a JavaScript front end (just something to think about).
private function inOpenDomain(array $exclusion, $testStart, $testEnd)
{
$result = null;
$code = null;
$start = $exclusion[0];
$end = $exclusion[1];
if (($testStart > $end) || ($testEnd < $start)) {
$result = true;
$code = 0; //Good! No conflict.
} elseif ($testStart === $start) {
$result = false;
$code = 1;
} elseif ($testStart === $end) {
$result = false;
$code = 2;
} elseif ($testEnd === $start) {
$result = false;
$code = 3;
} elseif ($testEnd === $end) {
$result = false;
$code = 4;
} elseif (($testStart > $start) && ($testEnd < $end)) { //Middle
$result = false;
$code = 5;
} elseif (($testStart < $start) && ($testEnd > $start)) { //Left limit
$result = false;
$code = 6;
} elseif (($testStart < $end) && ($testEnd > $end)) { //Right limit
$result = false;
$code = 7;
} elseif (($testStart < $start) && ($testEnd > $end)) { //Both limits
$result = false;
$code = 8;
} else {
$result = false;
$code = 9;
}
return ['start' => $start, 'end' => $end, 'result' => $result => 'code' => $code];
}
Step 3
Make a method that manages the checking of prior reservation times (assuming PDO::FETCH_ASSOC).
private function checkPeriods(array $periods, $newStartTime, $newEndTime)
{
$report = [];
if (!isset($periods[0])) { //If NOT multi-dimensional
$report = inOpenDomain($periods, $newStartTime, $newEndTime)
} else {
for ($i = 0, $length = $count($periods); $i < $length; ++$i) {
$report[$i] = inOpenDomain($periods[$i], $newStartTime, $newEndTime);
}
}
return $report;
}
Step 4
Fashion a method for doing a SELECT on the reservations table using a PDO prepared statement. Generally, ...
private function getReservationTimes($resourceId, $minTime, $maxTime)
{
$sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
FROM `reservations` AS `t1`
INNER JOIN `resources` AS `t2`
ON `t1`.`resourceId` = `t2`.`resourceId`
WHERE `t2`.`resourceId` = :resourceId
AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
ORDER BY `t1`.`startTime` ASC";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(:resourceId , $resourceId);
$stmt->bindParam(:minTime1 , $minTime);
$stmt->bindParam(:maxTime1 , $maxTime);
$stmt->bindParam(:minTime2 , $minTime);
$stmt->bindParam(:maxTime2 , $maxTime);
$stmt->execute();
return $stmt->fetchAll();
}
Step 5
Make a public method (interface) for the entire process.
public function isOpen($minTime, $maxTime)
{
$periods = $this->getReservationTimes($this->resource->getResourceId(), $minTime, $maxTime);
if (empty($periods)) {
return true; //You may reserve the resource during the time period.
}
return $this->checkPeriods($periods, $this->start, $this->end));
}
Step 6
Separate the concerns.
Create a class hierarchy for the actual items being reserved.
abstact class Product
{
}
class Resource extends Product implements Reservable //Or, something ...
{
private $resourceId;
//etc ....
}
Create a class hierarchy for reservations.
abstract class Interval
{
private $start;
private $end;
public function __construct($start, $end)
{
$this->start = $start;
$this->end = $end;
}
}
class Reservation extends Interval
{
private $db;
private $resource;
public function __construct(PDO $pdo, Reservable $resource, $reqStartTime, $reqEndTime)
{
parent::__construct($reqStartTime, $reqEndTime);
$this->db = $pdo;
$this->resource = $resource;
}
}
Step 7
Run within try/catch
When you instantiate the Reservation object, supply at least a Reservable object, the requested start time, and requested end time (as UNIX timestamps, in this case).
try
{
$day = 84600; // Seconds per day.
$future = $day * 90; // Application specific.
//User requested times.
$reqStartTime = 1488394687 + $day; // Tomorrow.
$reqEndTime = 1488394687 + ($day * 2); // Two day duration.
//Search constraints.
$minTime = time(); // Today. Right now.
$maxTime = 1488394687 + $future; // 90 day look ahead.
$reservation = new Reservation($pdo, $resourceObj, $reqStartTime, $reqEndTime);
$availability = $reservation->isOpen($minTime, $maxTime);
if($availability === true){
$reservation->confirm();
} else {
//Have some other object deal with the report
$reporter = new Reporter($availability);
$reporter->analyzeReport();
//Have some other object update the view, etc ...
}
}
catch(Exception $e)
{
//Handle it.
}
I beg to excuse me for my poor english.
So, I have Laravel 5 ORM, and i need to make request that should add date to some rows, like MySQL DATE_ADD. Input is single date interval and array of id's, output is rows of database, that was changed by adding date interval. Ideally, it should be one ORM request. I know that it is possible to use "bad" way and get all rows, update it in a code, and insert to database, but imho it's not good.
I hope answer will be link to some help site or some code if it's does not complicate you. Thanks for your attention!
public function update($id)
{
$user_id = Auth::user()->id;
$rep_task = RepTask::find($id);
$cl_task = \Request::has('json') ? json_decode(\Request::input('json'),true) : \Request::all();
$ids = [];
$task_id = $rep_task->task_id;
$rep_tasks = RepTask::where('task_id', '=', $task_id)
->select('id')
->get();
$new_date = date_create_from_format(self::DATE_FULLCALENDAR_FORMAT, $cl_task['new_date']);
$selected_task_date = date_create_from_format(self::DATE_MYSQL_FORMAT, $rep_task->start_date);
$diff = date_diff($selected_task_date, $new_date);
if(\Request::has('json'))
{
$ids = [1,2,3]; //this for easy understanding
DB::table('rep_task')
->whereIn('id', $ids)
->update(['start_date' => DB::raw('DATE_ADD(start_date, INTERVAL ' . $diff->d . ' DAY)')]);
$out_json = ['updated' => $ids];
return json_encode($out_json, JSON_UNESCAPED_UNICODE);
}
else
{
$start_date = 0;
$end_date = 0;
if (!isset($cl_task['name']) || !isset($cl_task['text']))
return "{'error':'columns are not defined'}";
if (isset($cl_task['start_date']) && isset($cl_task['end_date']))
{
$dt = date_create_from_format(self::DATE_FULLCALENDAR_FORMAT, $cl_task['start_date']);
$start_date = $dt->format(self::DATE_MYSQL_FORMAT);
$dt = date_create_from_format(self::DATE_FULLCALENDAR_FORMAT,$cl_task['end_date']);
$end_date = $dt->format(self::DATE_MYSQL_FORMAT);
}
$rep_task->name = $cl_task['name'];
$rep_task->text = $cl_task['text'];
$rep_task->start_date = $start_date;
$rep_task->end_date = $end_date;
$rep_task->users_id = $user_id;
$rep_task->save();
}
$user_id = Auth::user()->id;
$tasks = Task::getAllTasksByUserFullcalendar($user_id);
return view(
'task.index',
[
'tasks' => $tasks
]
);
}