(Symfony 3.1) PHP Doctrine Group entities by datetime - php

I am stack trying to group my entities by datetime. every entity has the following columns: id, device, start, stop, ipaddress.
I want to be able to get entities within a range of date and group them by date(day) following with how many hours each days has.
This is what i have so far
public function findAllGroupedByDate($from = null, $to = null)
{
$from = isset($from) && !is_null($from) ? $from : date('Y-m-d', time());
$to = isset($to) && !is_null($to) ? $to : date('Y-m-d', time());
$from = new \DateTime("-3 days");
$to = new \DateTime("1 days");
$from = date("Y-m-d", $from->getTimestamp());
$to = date("Y-m-d", $to->getTimestamp());
$q = $this->getEntityManager()->createQueryBuilder()
->select("timelog")
->from("AppBundle:Timelog", "timelog")
->where("timelog.start BETWEEN :from AND :to")
->setParameter("from", $from)
->setParameter("to", $to)
->orderBy("timelog.stop")
->getQuery();
return $q->getResult();
}
public function findFromToday()
{
$q = $this->createQueryBuilder('t')
->select('t.id', 't.start', 't.stop', 't.stop')
->where("t.start >= :today")
->setParameter("today", date('Y-m-d', time()))
->groupBy("t.id", "t.stop")
->orderBy("t.start", "asc")
->getQuery();
return $q->getResult();
}
This is the code for my repository class.
and the code for from my controller looks like this:
$timelogsRepo = $this->getDoctrine()->getRepository('AppBundle:Timelog');
// Grab logs from today
$timelogsCollection = $timelogsRepo->findFromToday();
$tmpLogs = $timelogsRepo->findAllGroupedByDate();
// THIS SECTION IS FOR CALCULATING HOURS & MINUTES
$minutes = 0;
$hours = 0;
foreach($timelogsCollection as $log)
{
$time1 = $log['start'];
$time2 = $log['stop'];
$interval = date_diff($time1, $time2);
$hours += $interval->h;
$minutes += $interval->i;
}
$minutes = $minutes >59 ? $minutes/60 : $minutes;
return $this->render(':Timelog:index.html.twig', [
'timelogs' => $logsOut,
'hours' => $hours,
'minutes' => $minutes
]);
So far i was able to calculate total spent time for a given day(only one day). Now i would like to get alle entities, group them by same date(day) and return data with interval.
Example DB table looks like this[id, device_id, start, stop, ipaddress]
1 1 2016-08-09 09:00:06 2016-08-09 12:00:06 127.0.0.1
2 1 2016-08-08 07:00:00 2016-08-08 13:00:00 127.0.0.1
3 1 2016-08-08 13:10:00 2016-08-08 15:05:00 127.0.0.1
So in this case my output would be something like:
[
0 => ["date" => "2016-08-09", "hours" => 9.00, "ipaddress" => "127.0.0.1"],
1 => ["date" => "2016-08-09", "hours" => 1.45, "ipaddress" => "127.0.0.1"]
]
the interval depends on start and stop both are type of DateTime
I have tried using doctrine-extensions: oro/doctrine-extensions but now i am getting exception error:
[2/2] QueryException: [Syntax Error] line 0, col 50: Error: Expected Unit is not valid for TIMESTAMPDIFF function. Supported units are: "MICROSECOND, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR", got 'stop'
My repository method looks like this:
public function findByToday()
{
$fields = array(
'DATE(stop) as stop',
'TIME(SUM(TIMESTAMPDIFF(stop, start))) AS tdiff',
'device_id',
'ipaddress'
);
$q = $this->createQueryBuilder('t')
->select($fields)
->where('DATE(stop) = :today')
->setParameter('today', date('Y-m-d', time()))
->groupBy('device_id')
->getQuery();
return $q->getResult();
}
my DB table:
id device_id start stop ipaddress
5 1 2016-08-09 09:00:06 2016-08-09 12:00:06 127.0.0.1
6 1 2016-08-08 07:00:00 2016-08-08 13:00:00 127.0.0.1
7 1 2016-08-08 13:10:00 2016-08-08 15:05:00 127.0.0.1
BTW i am using Symfony 3, could that be the problem?

Based on your table above (if I got it right) I would try to get the data directly with MYSQL and then work my way to Doctrine.
If you have the below table:
CREATE TABLE `testdevices` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`device` INT(11) NULL DEFAULT NULL,
`dstart` DATETIME NULL DEFAULT NULL,
`dend` DATETIME NULL DEFAULT NULL,
`ip` TINYTEXT NULL,
PRIMARY KEY (`id`)
);
And populated with below test data (I've created two devices the second one was conveniently active for exact 3 hours on today's date):
mysql> select * from testdevices;
+----+--------+---------------------+---------------------+-----------+
| id | device | dstart | dend | ip |
+----+--------+---------------------+---------------------+-----------+
| 1 | 1 | 2016-08-09 09:00:06 | 2016-08-09 12:00:06 | 127.0.0.1 |
| 2 | 1 | 2016-08-08 07:00:00 | 2016-08-08 13:00:00 | 127.0.0.1 |
| 3 | 1 | 2016-08-08 13:10:00 | 2016-08-11 22:14:46 | 127.0.0.1 |
| 4 | 2 | 2016-08-11 13:00:00 | 2016-08-11 14:00:00 | 127.0.0.1 |
| 5 | 2 | 2016-08-11 15:00:00 | 2016-08-11 16:00:00 | 127.0.0.1 |
| 6 | 2 | 2016-08-11 17:00:00 | 2016-08-11 18:00:00 | 127.0.0.1 |
+----+--------+---------------------+---------------------+-----------+
6 rows in set (0.00 sec)
Following MYSQL query will then I believe output the data you want:
SELECT DATE(dend) as dend, TIME(SUM(TIMEDIFF(dend, dstart))) AS tdiff, device, ip
FROM testdevices WHERE DATE(dend) = '2016-08-11'
GROUP BY device ;
And the result would look like this:
+------------+----------+--------+-----------+
| dend | tdiff | device | ip |
+------------+----------+--------+-----------+
| 2016-08-11 | 81:04:46 | 1 | 127.0.0.1 |
| 2016-08-11 | 03:00:00 | 2 | 127.0.0.1 |
+------------+----------+--------+-----------+
2 rows in set (0.01 sec)
Note that second device time is correct. Now, the remaining question is how to do that in Doctrine. As far as I know the TIMEDIFF function is not a part of Doctrine yet so I would propose 2 different approaches:
Write your own function as in
http://www.doctrine-project.org/2010/03/29/doctrine2-custom-dql-udfs.html#date-diff (There is example of DATEDIFF in this link so it is easily adjustable), or
Use https://packagist.org/packages/oro/doctrine-extensions
Your final repository function in Doctrine/Symfony2 with oro/doctrine-extensions installed would look something like this:
$fields = array(
'DATE(dend) as dend',
'SUM(TIMESTAMPDIFF(SECOND, dend, dstart)) AS tdiff',
'device',
'ip'
);
public function findFromToday()
{
$q = $this->createQueryBuilder('t')
->select($fields)
->where('DATE(dend) = :today')
->setParameter('today', date('Y-m-d', time()))
->groupBy('device')
->getQuery();
return $q->getResult();
I wrote this function without testing it at all and from the top of my head. You will probably find that it fails. This would give the time difference in seconds as the TIMESTAMPDIFF requires units to be specified.
Install oro/doctrine-extensions with
composer require oro/doctrine-extensions

Related

how to check booking period is over from current date

I'm having trouble to determine whether booking period is over or not?
i have 2 unix timestamp like this start =>2017-06-20 12:00:00 and End => 2017-06-23 12:00:00
on each time the query is run i want to check whether time is elapsed or not (i,e booking period is reached or not) from my current date (which i can pass using php)
my pseudo code:
select timestampdiff(DAY, '2017-06-20 12:00:00', '2017-06-23 12:00:00'); returns 3
returnedDate = 3; // returns difference either in date format or no.of days
if((returnedDate - mycurrentDate) == 0){
//your booking period is over
}else{
// no of remaining days
}
Desired Solution: i'm looking for mysql specific solution, good php solution is also welcomed.
Question: how to know the pre-booking date is expired?
please help me to solve this, thanks in advance!!!
Assuming mycurrentDate is NOW(), start and end are the dates in which the event occurs, and the threshold for booking is the end date.
You could use DATEDIFF to determine the number of days remaining until the event ends and check if the end date has already passed for the event to show 0.
EG: How many days until the event(s) ends
Demo: http://sqlfiddle.com/#!9/95b548/2
Assuming NOW() is 2017-06-21 12:00:00 using a past, current, and a future events.
| id | start | end |
|----|---------------------|---------------------|
| 1 | 2017-06-20T12:00:00 | 2017-06-23T12:00:00 | #current
| 2 | 2017-06-01T12:00:00 | 2017-06-03T12:00:00 | #past
| 3 | 2017-07-01T12:00:00 | 2017-07-03T12:00:00 | #future
SELECT
id,
NOW() AS `current_date`,
`tn`.`start` AS `starts_on`,
`tn`.`end` AS `ends_on`,
IF(`tn`.`end` >= NOW(),
DATEDIFF(`tn`.`end`, NOW()), #show days until event ends
0 #the event has already passed
) AS `days_remaining`
FROM `table_name` AS `tn`
Results:
| id | current_date | start | end | days_remaining |
|----|---------------------|------------------------|------------------------|----------------|
| 1 | 2017-06-21T12:00:00 | June, 20 2017 12:00:00 | June, 23 2017 12:00:00 | 2 |
| 2 | 2017-06-21T12:00:00 | June, 01 2017 12:00:00 | June, 03 2017 12:00:00 | 0 |
| 3 | 2017-06-21T12:00:00 | July, 01 2017 12:00:00 | July, 03 2017 12:00:00 | 12 |
If end is stored as a unix timestamp, you can use FROM_UNIXTIME() to convert it to a DATETIME value:
IF(FROM_UNIXTIME(`tn`.`end`) >= NOW(),
DATEDIFF(FROM_UNIXTIME(`tn`.`end`), NOW()),
0
) AS `days_remaining`

How to check given date period is between two dates in MySQL and PHP

I have a travel history of the employee. I want to check, for the particular month, whether he is in outstation (traveled outside) or in the office, to calculate number of hours in travel. Just we are maintaining the travel database, in that we entered employee name with client place traveled with travel date and returned date.
One employee have the following data:
Traveled date: '2015-08-29' (29th Aug 2015)
returned date: '2015-11-06' (6th Nov 2015)
So here, I want to check in the month of October, all employees that are out of the office. Obviously this guy should come in that category, but I could not get him.
I also tried directly in MySQL workbench, but I didn't get the result.
My original PHP code:
// $req['date_start'] = '2015-10-01'
// $req['date_end'] = '2015-10-31'
$employeeTravel = new EmployeeTravelRecord();
$TravelEntryList = $employeeTravel->Find("(travel_date between ? and ? or return_date between ? and ? )",array($req['date_start'], $req['date_end'],$req['date_start'], $req['date_end']));
$startdate = $req['date_start'];
$enddate = $req['date_end'];
foreach($TravelEntryList as $Travelentry){
$key = $Travelentry->employee;
if($startdate >= $Travelentry->travel_date)
{
$firstdate = $startdate;
}
else
$firstdate = $Travelentry->travel_date;
if($enddate <= $Travelentry->return_date )
{
$lastdate = $enddate;
}
else
$lastdate = $Travelentry->return_date;
$holidays = $this->getholidays($firstdate,$lastdate);
$totalhours = $this->getWorkingDays($firstdate,$lastdate,$holidays); //It returns in total time of outstation in hours excluding company holidays
$amount = $totalhours;
if(isset($TravelTimeArray[$key])){
$TravelTimeArray[$key] += $amount;
}else{
$TravelTimeArray[$key] = $amount;
}
}
But my input data doesn't retrieve that particular employee record, because both traveled date and returned date don't fall in my input dates.
MySQL Workbench:
SELECT * FROM employeetravelrecords where travel_date between '2015-10-01' and '2015-10-31' or return_date between '2015-10-01' and '2015-10-31';
I got only the following result:
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
| id | employee | type | project | place | travel date | return date | details |
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
| 13 | 38 | International | PVMTC Training | Hongkong | 11/10/2015 13:33 | 28/11/2015 13:33 | PVMTC Training |
| 14 | 48 | International | PVMT | VIETNAM | 10/10/2015 9:28 | 1/1/2016 9:28 | PETRO |
| 17 | 57 | International | PVMT | Saudi | 10/10/2015 11:39 | 2/1/2016 11:39 | |
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
The following record didn't get retrieved by this query:
+---+----+---------------+------+-----+-----------------+-----------------+-----------------+
| 7 | 22 | International | MOHO | XYZ | 29/8/2015 18:00 | 6/11/2015 18:00 | FOR DDS review |
+---+----+---------------+------+-----+-----------------+-----------------+-----------------+
SELECT * FROM employeetravelrecords
WHERE
return_date >= '2015-10-01' /* start parameter */
and travel_date <= '2015-10-31' /* end parameter */
The logic seems a little mind-bending at first and I've even reordered the comparisons a little from my original comment above. Think of it this way: you need to return after the start of the range and leave before the end of the range in order to have an overlap.
For a longer explanation and discussion you might find this page useful: TestIfDateRangesOverlap
SELECT '2015-08-29' < '2015-10-31' AND '2015-11-06' >= '2015-10-01' on_leave;
+----------+
| on_leave |
+----------+
| 1 |
+----------+
You can use between for get data between two dates. Here is the working example.
Here is my table structure :
Table User
user_id user_name created_date modified_date
1 lalji nakum 2016-01-28 17:07:06 2016-03-31 00:00:00
2 admin 2016-01-28 17:25:38 2016-02-29 00:00:00
And here is my mysql query :
Query
SELECT * FROM `user` WHERE created_date between '2015-12-01' and '2016-12-31' or modified_date between '2015-12-01' and '2016-12-31'
Result of above query :
Result
user_id user_name created_date modified_date
1 lalji nakum 2016-01-28 17:07:06 2016-03-31 00:00:00
2 admin 2016-01-28 17:25:38 2016-02-29 00:00:00
You want to find all those people who are not available for the complete duration say between T1 & T2 (and T2 > T1). The query you used will only give users whose start date and return date both lie between the given interval which is not the required output.
So to get the desired output you need to check for all employees who have started their journey on or before T1 and return date is on or after T2 (thus unavailable for complete interval [T1, T2]). So the query you can use is:
SELECT * FROM employeetravelrecords where travel_date <= '2015-10-01' and return_date >= '2015-10-31';
If you want employees who are even partially not available between the given duration then we need employees who started their travel before T1 and have any return date later than T1 (thus making them atleast unavailable for a part of the given interval):
SELECT * FROM employeetravelrecords where travel_date <= '2015-10-01' and return_date > '2015-10-01';

php Open daily day rage

I building restaurant service and currently i'm stuck figuring out how to display open daily restaurant like google Business.
I have table design
+-----+----------+------+----------+----------+
| ref | id_resto | day | open | close |
+-----+----------+------+----------+----------+
| 2 | 5 | 1 | 13:00:00 | 21:00:00 |
| 5 | 5 | 2 | 13:00:00 | 21:00:00 |
| 7 | 5 | 3 | 13:00:00 | 21:00:00 |
| 9 | 5 | 4 | 13:00:00 | 21:00:00 |
| 10 | 5 | 6 | 13:00:00 | 22:00:00 |
| 11 | 5 | 7 | 14:00:00 | 21:00:00 |
+-----+----------+------+----------+----------+
day 7 = sunday, 1 = monday, 2 = tuesday, 3 = Wednesday and soo on
according to my table it should be display
Monday-Thursday 13:00 - 21:00
Saturday 13:00 - 22:00
Sunday 14:00 - 21:00
i've try some algorithm, but noting happened -_-
any idea?
Thanks
With a small change to your table structure, it should be simple to implement.
Here is my code to create the tables:
CREATE TABLE `daytype` (
`id` int(11) NOT NULL,
`description` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `daytype` VALUES (1,'Monday - Thursday'),(2,'Saturday'),(3,'Sunday');
CREATE TABLE `hours` (
`ref` int(11) NOT NULL,
`id_resto` int(11) DEFAULT NULL,
`id_daytypes` int(11) DEFAULT NULL,
`open` char(5) DEFAULT NULL,
`close` char(5) DEFAULT NULL,
PRIMARY KEY (`ref`)
);
INSERT INTO `hours` VALUES (2,5,1,'13:00','21:00'),(5,5,2,'13:00','21:00'),(7,5,3,'14:00','21:00');
From this you can then easily get the data you want with a simple join:
SELECT d.description, CONCAT(open, ' - ', close) as hours FROM test.hours AS h INNER JOIN daytype AS d ON (d.id = h.id_daytypes);
Output:
This structure has the added benefit of allowing you to set up different breaks, and different displays for each of the restaurants, if needed. For example, if a restaurant had different hours on Monday, you could add an entry to the daytypes table for Monday, and Tuesday-Thursday.
Overall, this should save some space since you don't need to enter duplicate info for each of the days that are the same.
Hopefully this will help you get started.
UPDATE: Per request, here is an example of how to check if something is open today.
You could have a php function that returns a comma delimited list of all valid daytypes for the current day. Something like this:
<?php
/*
1 Monday - Thursday
2 Saturday
3 Sunday
4 Friday
5 Monday
*/
function getDayType()
{
$dayOfWeek = date('l');
switch($dayOfWeek)
{
case "Monday":
return "1, 5"
case "Tuesday":
case "Wednesday":
case "Thursday":
return "1";
case "Friday":
return "4":
case "Saturday":
return "2";
case "Sunday":
return "3"
}
}
Then is is simple to determine the shops that are open today.
For example, like this query which will return all the restaurants that are open.
SELECT
h.id_resto
FROM
test.hours AS h
INNER JOIN
daytype AS d ON (d.id = h.id_daytypes)
WHERE
id_daytypes IN (1 , 5)
AND (
(HOUR(NOW()) > HOUR(open) AND HOUR(NOW()) < HOUR(close))
OR
(HOUR(NOW()) = HOUR(open) AND MINUTE(NOW()) >= MINUTE(open))
OR
(HOUR(NOW()) = HOUR(close) AND MINUTE(NOW()) < MINUTE(close))
);

Convert int 60 to a time value of 60 minutes

This seems it should be trivially simple but I can't find the time format I would need.
A value comes from the database as 240. This means 240 minutes. How can I store this in a php variable so that php knows it's minutes. So that later in the script I can add it to a HH:MM value?
(I have edited the code below to reflect one of the answers)
$startTime = new datetime($row['startTime']); #08:07:00.0000000
$endTime = new datetime($row['endTime']); #12:10:00.0000000
$everyMinutes = new dateInterval('P'.$row['everyMinutes'].'M'); #60?
$updatedTime = $startTime->add($everyMinutes); # this should read 09:07:00.0000000
The date coming into $row comes from sql. startTime and endTime are of time(7) datatype
| taskID | startTime | endTime | freq |
|________|__________________|__________________|______|
| 1 | 08:07:00.0000000 | 12:10:00.0000000 | 60 |
| 2 | 08:10:00.0000000 | 17:40:00.0000000 | 30 |
| 3 | 08:40:00.0000000 | 14:49:00.0000000 | 60 |
| 4 | 08:43:00.0000000 | 14:49:00.0000000 | 60 |
| 5 | 09:05:00.0000000 | 15:05:00.0000000 | 180 |
| 6 | 10:00:00.0000000 | 22:00:00.0000000 | 5 |
With this code I am getting one of two issues. With new datetime() the error thrown is Fatal error: Uncaught exception 'Exception' with message 'DateTime::__construct() expects parameter 1 to be string, object given'
Without new datetime() the error thrown is Notice: Object of class DateTime could not be converted to int
You should read this page.
$date = new DateTime($row['startTime']);
$myInterval = new DateInterval('P'.$row['everyMinutes'].'M');
$date->add($myInterval);
echo $date->format('Y-m-d') . "\n";
Use this function
function convertToHoursMins($time, $format = '%d:%d') {
settype($time, 'integer');
if ($time < 1) {
return;
}
$hours = floor($time/60);
$minutes = $time%60;
return sprintf($format, $hours, $minutes);
}
and pass your time in this like
convertToHoursMins(240);

30 days in Doctrine Symfony

i would like add clause where - max 30 days from now.
in database i have timestamp:
2011-08-30 20:29:35
id | name | date
1 | aaa | 2011-08-30 20:29:35
2 | vvv | 2011-08-10 20:29:35
3 | bbb | 2011-07-10 20:29:35
4 | fff | 2011-08-14 20:29:35
5 | ddd | 2011-06-10 20:29:35
$query = Doctrine_Core::getTable('News')->createQuery('a');
$query->addWhere('date ????????');
How can i get all news recent 30 days?
$query->andWhere('date > ?', date('Y-m-d', time() - 60*60*24*30))
MySQL provides another handy solution :
WHERE date > DATE_SUB(NOW(), INTERVAL 30 DAY)
see : http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-add

Categories