Codeigniter Group By Returning Only First Row - php

I have a reservation module in codeigniter wherein I limit users to reserve the clubhouse for only 2 hours a day. I am currently creating a validation in codeigniter to limit their reservation. What I did was to select all the reservations, and group them by the rows having the same date in order to properly limit them. The problem is that the model I created is only returning 1 row, and not all the results. This means that my counter is just being changed by only one row, which I expect that all rows should affect the counter.
Below is my database table:
Basically, the second row isn't supposed to be inserted in the database because user '20130123' already has used up his maximum reservation for the day which is two hours. I provided the validation check below on checking whether the user has used up two hours of reservation, and my logic here is that I just subtract the reservation end with the reservation start. Using the table above just as an example and for my explanation, my problem is that in my model, the counter's value becomes only "2" because it only reads the first row (8 - 6 = 2), instead of "3" (result of the first row which is 2, then add the result of second row which is 1 : [(8-6) + (9-8)])
To sum it up, my problem lies on the counter's value, because it is only being added by the first row the query reads.
Here's my code.
Model:
function check_twohours_clubhouse()
{
$query = $this->db->select('*')->from('clubhouse_reservation')->where('username', $this->session->userdata('username'))->group_by('reservation_date')->having('count(reservation_date) > 1')->get();
$result = $query->result();
if($query->num_rows() > 0)
{
$ctr = '0';
foreach($result as $row)
{
$totalhour = $row->reservation_end - $row->reservation_start; // subtract reservation start to reservation end to get the number of hours
$ctr = $ctr + $totalhour;
}
$ctr = $ctr + ($this->input->post('reserveend') - $this->input->post('reservestart')); // add the selected "add reservation" hours to the counter
if($ctr > 2) // counter > 2 hours limit
{
return FALSE;
}
else
{
return TRUE;
}
}
else
{
return TRUE;
}
}
Controller:
function create_reservation_clubhouse()
{
$this->form_validation->set_error_delimiters('<div class="error">','</div>');
$this->form_validation->set_rules('datepick', 'Date', 'required|no_olddate');
$this->form_validation->set_rules('reservestart', 'Reservation Start', 'required|hourselection|unique_reserve_clubhouse|max_twohours');
if ($this->form_validation->run() == FALSE)
{
$this->template->load('user_template', 'view_userreservation_addclubhouse');
}
else
{
if($this->model_reservation_user->check_twohours_courtone())
{
if($query = $this->model_reservation_user->create_reservation_clubhouse())
{
$this->session->set_flashdata('reservefeedback', 'You have successfully reserved a date for the Clubhouse. Please wait for the administrators to accept your reservation.');
redirect('user_reservation/clubhouse');
}
}
else
{
$this->session->set_flashdata('reservefail', 'You cannot reserve more than two hours of Clubhouse per day.');
redirect('user_reservation/clubhouse');
}
}
}

You can calculate all this in sql using time_to_sec() and timediff() functions:
select reservation_date, sum(time_to_sec(timediff(reserveend, reservestart)) / 3600 as reserved_hours
from clubhouse_reservation
where username =...
group by reservation_date
having count(*)>1 --although it is not clear to me why you have this restriction
The above query would sum up for a given user per reservation date, the hours for which the clubhouse is reserved (3600 = number of secs in an hour).
In your php code you only need to check if the reserved_hours in the resultset is >2 or not.
I'm not really an expert with CI, therefore I can't really tell how to convert the above sql to CI. But I'm afraid that you have to use raw sql because of the way it sums the time.

Related

Limit number of searches per user per week in php

I have developed a search page, which is only accessible to logged in users. But I want to limit search for each user to only 20 times in one week. One user will not be able to search for more than 20 times. Please help me how can I achieve this.
Implement two colums in the users table:
column 1 : search_counts -default 0
coumn 2 : search_timestamp -default 0
when a user initiate a search:
inspect the timmstap, if it is older than one week set it to current time (now) and set the counter to one and allow the search , if not, check the counter if its equal maximum allowed searches then abort it otherwise increment the counter and allow the search.
Here is an untested function to do this assuming you pass the couner and timstamp values from the user table to it:
function search_check($counter,$ts,$max=20,$period =7) {
$p = $period * 24 * 60 * 60;
$now = time();
if(($now - $ts) >= $p ){
//set the time stamp to now in DB
//set the counter to 1 in DB
return true;
}else {
if($counter < $max){ //
//increment the counter by 1 in DB
return true;
}else{
return false;
}
}
}
usage:
//retrieve counter and timestamp values from the DB users table
$can_search = search_check($counter,$ts);
if($can_search){
//search and return search result
}else{
echo "Sorry, maximum weekly searches reached"
}

Need to iterate through DateTime appointments with multiple bookings

I have a MySql Database with peoples' names as varchars and appointment times as DateTime objects. I need to make a function that returns the next available time slot (DateTime format) found in the database, I'll use it to schedule people for that time slot.
Appointments are only on Tuesdays and Wednesdays. Whether it's Tuesday or Wednesday, 4 people can be seen at 9:00am, and 2 people can be seen at 10:30am.
This function returns the number of people that are booked for a given time slot (DateTime).
function getTimeSlotCount($dateTime)
{
// finds out how many people are scheduled for the given datetime
global $db;
$query = "SELECT * FROM appointments
WHERE AppointmentTime = :dateTime
ORDER BY AppointmentTime";
$statement = $db->prepare($query);
$statement->bindValue(':dateTime', $dateTime);
$statement->execute();
$count = $statement->rowCount();
$statement->closeCursor();
#var_dump($count);
return $count;
}
I plan to use a do while loop to iterate through timeslots. But I don't know how to iterate through appointment times that I mentioned using DateTime. Also how to compare the time portion of a datetime to a specific time of day?
function getNextAvailableTimeSlot()
{
$maxFor9am = 4;
$maxFor1030am = 2;
$isAvailableSlotFound = false;
do {
// How Do I: Assign next Timeslot to $AppointmentTime;
$numBooked = getTimeSlotCount($AppointmentTime)
if ($numBooked >= $maxFor9am && $HowdoIGetTimeFromAppointmentTime == 9am)
continue;
else if ($numBooked >= 2 && $HowdoIGetTimeFromAppointmentTime == 1030am)
continue;
else
$isAvailableSlotFound == true;
} while ($isAvailableSlotFound == false);
return $AppointmentTime;
}
EDIT: When an appointment is added to the database table. The person's name and a DateTime is entered for the record of the appointment.

Improve performance when copying records from table to another one

Hi buddies :) I was required to create a php code to handle some workers' data stored in DB. I got the desired result but it takes seconds and seconds (seconds and seconds! >.<) to finish, so I'm afraid I'm not doing something in a right way :(
The workers' data is stored in a mysql table (table1), something like this:
I'm given a pair of dates: initial_date (a) and final_date (b), so my goal is to copy the given workers' data in a new table (table2), day by day from a to b. The expected table should be as shown below (this table will be used later as a basis for further operations, which is not part of the question)
It's a requirement to overwrite any existing data between a and b dates, and 'jump' weekends and holidays.
To get my goal, I'm coding this (let's assume that the connection and all that is done and the checkworkingday function is given):
$initialdate = '2016-10-10';
$finaldate = '2016-10-12';
$x = $initialdate;
do {
if (checkworkingday($x) == true) {
$query = mysqli_query($connection,"SELECT name,task FROM table1");
while($row = mysqli_fetch_array($query)) {
$task = $row['task'];
$worker = $row['name'];
$query2 = mysqli_query($connection,"SELECT task FROM table2 WHERE name = '$worker' AND date = '$x'");
$row2 = mysqli_fetch_array($query2);
$existingtask = $row2['task'];
if (!isset($existingtask)) {
mysqli_query($connection,"INSERT INTO table2 (date,name,task) VALUES('".$x."','".$worker."','".$task."')");
} else {
mysqli_query($connection,"UPDATE table2 SET task = '".$task."' WHERE date = '".$x."' AND worker = '".$name."'");
}
}
}
$x = date('Y-m-d', strtotime($x . "+1 day"));
} while ($x <= $finaldate);
Just for 3 days as shown in the example, it takes a long to end; and for several weeks or months it takes very, very long (even max execution time is exceeded depending on dates range!).
I'm a newbie and I know the code is quite 'rustic', but I've revised and checked the code and info out there without getting a better performance. What am I doing wrong? Thanks :)
Instead of looping through the enitre data, try INSERT.. SELECT :
INSERT INTO table2 (date,name,task)
SELECT date,name,task
FROM Table1
WHERE < >;

PHP while loop of mysql queries sometimes returns an empty set randomly (they're not empty, but the result is empty)

I am writing a report on data based on new customers for each given month, starting from the earliest month an order was present all the way to the last month that any orders are present (I realize the current code will stop on any month that doesn't have new customers, and will fix later...)
Some background-- I'm using the Flourish Framework (www.flourishlib.com).. The first month/year is set correctly because I have error log'd it out. The first month with orders is 4/2013.
The problem is that for some reason, MySQL randomly returns an empty result at some point that is completely random. I have run the query for that month/year that it returns an empty result for in a MySQL client and it is not an empty result. The script itself proves this to be the case as where it returns the empty result is random and it will go further than it did before sometimes showing the correct information.
I have tried sleeping in between queries, as I originally thought maybe it was throttling or something, no go. Still the same exact behavior. I have tried using retries (when it encounters a count of 0 it will retry up to X times) and EVERY TIME it's empty, which means it cannot be one of those "sometimes it craps out, try again" type of scenarios.
Here is the code as it is now:
function newClients($month, $year) {
$db = fORMDatabase::retrieve();
$noobs = $db->query("
SELECT
id,
email,
(
SELECT completed
FROM orders
WHERE client_id = clients.id
ORDER BY completed ASC
LIMIT 1
) as first_order
FROM clients
HAVING first_order IS NOT NULL
AND MONTH(first_order) = '$month'
AND YEAR(first_order) = '$year'
AND email NOT LIKE '*#********.com'
AND email NOT LIKE '%#********.com'
AND email NOT LIKE '%#********.com'
AND email NOT LIKE '%#********.com'
AND email NOT LIKE '%#********.com'
AND email NOT LIKE '%#********.org'
AND email != '********#gmail.com'
AND email != '********#********.net'
")->fetchAllRows();
return $noobs;
}
$currentMonth = $theFirst['month'];
$currentYear = $theFirst['year'];
$retries = 0;
$noobs = newClients($currentMonth, $currentYear);
while (count($noobs) > 0 || $retries < 3) {
if (count($noobs) == 0) {
error_log('retry #' . ($retries + 1) . '...');
$retries++;
$noobs = newClients($currentMonth, $currentYear);
error_log('count: ' . count($noobs));
sleep(5);
continue;
}
error_log("loop $currentMonth / $currentYear: " . count($noobs));
if ($currentMonth >= 12) {
$currentYear++;
$currentMonth = 1;
} else {
$currentMonth++;
}
sleep(1);
$noobs = newClients($currentMonth, $currentYear);
error_log('count: ' . count($noobs));
}
Couple additional things.. I censored the email addresses for obvious reasons, and I did look at the actual returned data in the MySQL client, it is correct, and I also did vardump the actual array returned and it is indeed empty. (in case you're wondering that maybe count is counting incorrectly or who knows.. I thought maybe it was a countable object/non-array issue or quirk or something)
There may be some confusion as to the retries etc. as that has nothing to do with my desired outcome and were only attempts to solve the issue, here is the original code:
$noobs = newClients($currentMonth, $currentYear);
while (count($noobs) > 0) {
error_log("loop $currentMonth / $currentYear: " . count($noobs));
if ($currentMonth >= 12) {
$currentYear++;
$currentMonth = 1;
} else {
$currentMonth++;
}
$noobs = newClients($currentMonth, $currentYear);
error_log('count: ' . count($noobs));
}
sorry this is probably not really an answer but too big for comments.
i'm not confident this error is in PHP or flakiness with the db connection
can you modify your retry to print out the query and run it in your mysql client by hand ?
maybe you have weird inputs ?
try adding a null check to this
SELECT completed
FROM orders
WHERE client_id = clients.id
AND completed IS NOT NULL
ORDER BY completed ASC
LIMIT 1
i suspect you have your db set to NULLs first and they float to the top of your ordering

mysql next and previous

I currently am using mysql and php to display 9 random results from my table of about 1100 records.
Would it be possible to have a next and previous button even though it's random? I looked at a couple of the examples already posted here but they seem to be application/project specific. Here is what my code looks like..
function executeQuery($searchKey)
{
if ($searchKey == null)
{
$query = "SELECT DISTINCT flightNumber, flightCity FROM allFlights LIMIT 0,9";
//DEBUG -echo "<p>$searchKey</p>";echo "<p>$query</p>";
}
else
{
//DEBUG -echo "<p>$searchKey</p>";echo "<p>var not null</p>";
$query = "Select distinct * from allFlights where flightCity LIKE '%$searchKey%' LIMIT 0,9";
//DEBUG -echo "<p>$searchKey</p>";echo "<p>$query</p>";
}
$result=mysql_query($query);
$numrow=mysql_numrows($result);
if ($numrow === 0)
{
$query = "Select distinct * from allFlights where flightNumber LIKE '%$searchKey%' LIMIT 0,9";
$result=mysql_query($query);
$numrow=mysql_numrows($result);
}
return $result;
}
function populate ()
{
$searchKey = mysql_real_escape_string($_POST["search"]); //assigns user input to searchKey variable
//DEBUG -echo "<p>$searchKey</p>";
$result=executeQuery($searchKey);
$numrow=mysql_numrows($result);
if ($numrow == 0)
{
echo "<center><p><b> No results found, please try another keyword.</p></center></b>";
}
else
{ display results. -- this part i have working.
}
I prefer upon loading the page this happens:
-The current position with respect to the # of flights available are listed. (Now showing 9 of 1100 flights)
-9 random flights are displayed.
-Next button that will show the next 9 flights (random would be nice.)
-Previous button that will show the previous(original) 9 flights (random would be nice.)
When all is said and done I would like to be able to load the page identify the 9 random flights, press next identify the new 9 random flights, then previous and identify the original 9 random flights.
Any help is much appreciated.
You can use ORDER BY RAND(seed) to give a pseudorandom order that is repeatable:
SELECT *
FROM ....
ORDER BY RAND(9325)
LIMIT 99, 9
Adjust the offset to move back and forwards through the results. The seed can be any integer, and you can change it if you want to re-randomize the order, but you must use the same seed when pressing back or forward.

Categories