0

I am trying to organize an output of DATA in the following way: Today, Yesterday and everything before Yesterday is present with a respective full DateTime minus the clock time of course. For a better understanding, have a look at the screenshot below:

enter image description here

I have written this code:

try{
    $db = new PDO("mysql:host=" .$hostname. ";dbname=" .$database. "", "" .$user. "", "" .$pass. "");
  $db->setAttribute(PDO::ATTR_PERSISTENT, true);
  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  $notifications = $db->prepare("SELECT * FROM reports
    ORDER BY timestamp DESC");
  $notifications->execute();

  $result = (object)$notifications->fetchAll(PDO::FETCH_OBJ);

  echo "<pre>";

  print_r($result);

    echo "</pre>";

}
catch(PDOException $e)
{
    echo $e->getMessage();
}

I am trying to figure out a way to split things up, for instance, "Today", "Yesterday", "Day before Yesterday" and so forth for as long as considered normal e.g. an entire month maybe.

*How would I structure this up correctly with a PDO prepared statement? *

   [Today] => stdClass Object
        (
            [id] => 1
            [timestamp] => 2015-04-09 13:20:05
            [seen] => 0
        )
    [Yesterday] => stdClass Object
        (
            [id] => 2
            [timestamp] => 2015-04-08 15:30:50
            [seen] => 0
        )

Clearly: I want to print everything with timestamp of TODAY. Next, everything with YESTERDAYS timestamp. And so forth.

SQL:

// Today
AND DATE(from_unixtime(comment_date)) = CURRENT_DATE

// Yesterday
AND DATE(from_unixtime(comment_date)) =  DATE_SUB(CURRENT_DATE,INTERVAL 1 DAY)

// This week
AND YEARWEEK(from_unixtime(comment_date), 1) =  YEARWEEK(CURRENT_DATE, 1)

// This month
AND YEAR(from_unixtime(comment_date)) = YEAR(CURRENT_DATE)
AND MONTH(from_unixtime(comment_date)) = MONTH(CURRENT_DATE)
John Smith
  • 465
  • 4
  • 15
  • 38
  • 2
    And you need all of that in one query? If yes why do you complicate things? – pregmatch May 27 '15 at 17:04
  • @pregmatch I am trying not to complicate anything, I am trying to figure out how to organize thet SELECT query and once I get the data to output it like on the printscreen I posted here. Thanks for your input. – John Smith May 27 '15 at 17:06
  • Way too complicated. Just fetch all results until X and aggregate during the PHP iteration. It's just a DateTime..... – Daniel W. Jun 02 '15 at 10:38

5 Answers5

3

Here's an example of how I would handle this. I wouldn't change your query - it's fine as is. Assuming you want to show everything in the database sorted from latest to earliest post. Let PHP handle the heavy lifting. I've purposely broken some things in to multiple lines instead of nesting the functions to make it easier to read. Condense as you see fit.

I'm not claiming this is the BEST way to do it. Only what I use.

//MOCK UP SOME DISPLAY DATA - YOU WOULD USE YOUR QUERY RESULT INSTEAD
$rows = array();
$rows[] = date('Y-m-d H:i:s');
$rows[] = date('Y-m-d H:i:s');
$rows[] = date('Y-m-d H:i:s');
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, date('n'), date('j') - 1, date('Y')));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 12, 24, 2014));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 12, 25, 2014));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 12, 26, 2014));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 3, 2, 2001));

//CREATE AN ARRAY OF THE REPLACEMENTS YOU WANT
$aArray = array();
$aArray[date('Y-m-d')] = 'Today';
$aArray[date('Y-m-d', mktime(0, 0, 0, date('n'), date('j') - 1, date('Y')))] = 'Yesterday';
$aArray[date('Y-m-d', mktime(0, 0, 0, date('n'), date('j') - 2, date('Y')))] = 'Day before Yesterday';
$aArray['2014-12-25'] = 'Christmas 2014';

//INITIALIZE SOME VARIABLES
$cLastHeader = '';
$cCurrHeader = '';

//THIS WOULD BE YOUR QUERY RESULTS LOOP
foreach ($rows AS $nNull => $cDate) {
    $cLookup = substr($cDate, 0, 10);  //TRIM OUT THE TIME FROM CURRENT RECORD

    //IS DATE IN ARRAY? IF NOT, FORMAT
    if (isset($aArray[$cLookup])) {
        $cCurrHeader = $aArray[$cLookup];
    } else {
        $cCurrHeader = $cLookup; //WOULD SHOW 'YYYY-MM-DD'
        $cCurrHeader = date('F Y', strtotime($cLookup)); //WOULD SHOW 'MONTH YYYY'
    }

    //HAS HEADER CHANGED? IF SO PRINT
    if ($cCurrHeader != $cLastHeader) {
        $cLastHeader = $cCurrHeader;
        print($cCurrHeader . "\n");
    }

    //PRINT RECORD
    print("\t" . $cDate . "\n");
}

The output from this is :

Today
    2015-05-28 18:40:35
    2015-05-28 18:40:35
    2015-05-28 18:40:35
Yesterday
    2015-05-27 00:00:00
December 2014
    2014-12-24 00:00:00
Christmas 2014
    2014-12-25 00:00:00
December 2014
    2014-12-26 00:00:00
March 2001
    2001-03-02 00:00:00
DragonYen
  • 958
  • 9
  • 22
  • This is interesting, I will try it out. Wait. Thanks a lot dude! – John Smith May 28 '15 at 18:20
  • So I guess, I should have listened, my instinct some days back told me that PHP alone would have to handle this, yet I refused to listen. – John Smith May 28 '15 at 18:27
  • Hmm, I am wondering if this solution will work with DATA that goes beyond "Day before Yesterday"? – John Smith May 28 '15 at 18:33
  • 1
    Happy to help. If you are running a LARGE site. You could probably optimize this by using some multidimensional arrays and grouping things by their post date first. As to your new question -- yes, the last bucket here is March 2001. Unless you mean could you make "day before the day before the day before yesterday". In which case the answer is yes. You can add as many dates as you want. For example $aArray[date('Y') . '-12-25'] = 'Christmas'; Which would end up splitting December 2015, Christmas, December 15. – DragonYen May 28 '15 at 18:35
  • 1
    I've added an example of the "Christmas" idea. You could also put in two dates (with the same text - as long as the two days are consecutive) and it will group them under one header. – DragonYen May 28 '15 at 18:46
  • Thanks a lot. I will try this out, and see how it works with the dynamic array of data from the mysql query. And if it works as expected, I will report back and mark your post as Answer and give you the bounty points. I will optimize this accordingly. – John Smith May 28 '15 at 18:53
  • So I guess this didn't work out? Did you hit an issue I could possibly help with? – DragonYen Jun 02 '15 at 21:27
  • thanks for your reply! I am not done with the code, I know, a few days have iterated however, I need to find some free time so that I can re-run everything and try things out. I am very sorry for now. Once again thanks a lot. – John Smith Jun 03 '15 at 10:16
3
SELECT IF( DATEDIFF( NOW( ) , `timestamp` ) =0, "Today", if( DATEDIFF( NOW( ) , `timestamp` ) =1, "Yesterday", DATE_FORMAT( `timestamp`, '%M %d' ) ) ) AS day,
id,timestamp,seen
FROM reports
ORDER BY timestamp DESC

Trying to solve your issue through query,check if it is helpful for you.

As per given screenshot, after today and yesterday this query will give you month and date.

Sanal K
  • 723
  • 4
  • 14
  • 1
    I am not sure I can do it through query for various of other reasons, including that my system is translatable, meaning, it gets translated into other human readable languages. As a result, the output from your query means that, "Today", "Yesterday" cannot be translated into a different language, mainly the local settings of the user. – John Smith Jun 02 '15 at 12:11
  • @JohnSmith i guess this is the best you can have through query :) anyways all the best for your result. – Sanal K Jun 02 '15 at 12:31
  • 2
    @JohnSmith, To run any multilingual website, You don't need to worry about what query is giving you. You can take 'Today', 'Yesterday' as unique word. Pass this to your language function which gives you Translated word. – Sanjay Mohnani Jun 03 '15 at 07:47
  • 1
    @JohnSmith as Sanjay Mohnani explained, you can do the translation.I have edited my query as per your attached image.Also if data is big i think,the load should be given to mysql rather than using php conditions and functions(with proper indexing).Its my thought only please correct me if iam wrong. – Sanal K Jun 03 '15 at 09:49
1

What about get all the rows and checking the date in PHP then group the results which exists at the same time-frame in separate array like the following :

$notifications = $db->prepare("SELECT * FROM reports
ORDER BY timestamp DESC");
$notifications->execute();
$rows = $notifications->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row ){
 if ( date('Ymd') == date('Ymd', strtotime($row['timestamp'])) ){
     $today_rows[] = $row;
    }
else if (date('Ymd', strtotime('yesterday')) == date('Ymd', strtotime($row['timestamp'])) ){
 $yesterday_rows[] = $row;
 }
else if () ... etc 
}

Now you can use $today_row , $yesterday_rows ... etc in your view and display them as you want.

Omar
  • 542
  • 1
  • 6
  • 27
1

There are multiple ways to approach this. First, you can create separate calls and merge the data on php side.

Second, you can use subquery but I don't recommend subqueries as they are kind of tricky and can hog up a lot of resources.

Third, you can use unions. You create 3 separate queries to select based on different scenarios and then use union. This way, you will get all the results in one call.

SELECT *, 'today' as day FROM table WHERE DATE(from_unixtime(comment_date)) = CURRENT_DATE
UNION
SELECT *, 'yesterday' as day FROM table WHERE MONTH(from_unix_MONTH(CURRENT_DATE)) = MONTH(CURRENT_DATE)

So basically, unions are different select statements put together into one query. You can union as many select statements as you like. HOWEVER, note that you want to have all select statements return the SAME COLUMNS or it will error. You also might wanna read up on UNION ALL. There are some minor differences between UNION and UNION ALL but not very.

RisingSun
  • 1,693
  • 27
  • 45
  • @khudem I've read a little bit about UNIONS, and they are quite interesting. Is there a possibility for you to show me how to apply UNION on my current mysql SELECT query? – John Smith May 27 '15 at 17:42
  • @khudem, an SQLfiddle would be appreciated in this context. Thanks a lot. – John Smith May 27 '15 at 17:43
  • @khudem, your Third mentioned option, is it something like this: http://stackoverflow.com/questions/8572821/group-by-with-union-mysql-select-query – John Smith May 27 '15 at 17:48
  • Yes that is exactly it. The answer in that question has a good query for reference. – RisingSun May 27 '15 at 17:50
  • So this line below, would get me: Today + Yesterday with one single query execution. SELECT * FROM table DATE(from_unixtime(comment_date)) = CURRENT_DATE UNION SELECT * FROM table WHERE DATE(from_unixtime(comment_date)) = DATE_SUB(CURRENT_DATE,INTERVAL 1 DAY) – John Smith May 27 '15 at 17:53
  • if you looks at the answer's query, you can see that the select statements above and below the `UNION ALL` are selecting from different tables. Once the union applied, it merges the results from the select statements into one – RisingSun May 27 '15 at 17:53
  • Yes it would as long as you are using the same table on bot selects if you are using the *. I would recommend specifying column names as it could error out if the column names dont match up – RisingSun May 27 '15 at 17:55
  • "nce the union applied, it merges the results from the select statements into one", yes, I do understand that is emerges them into one single output. – John Smith May 27 '15 at 17:55
  • In the next few days, I will try this out, and if it is successful and does the work I want, then I'll mark your answer = TRUE. – John Smith May 27 '15 at 17:57
  • Thanks a lot man! Happy coding to you as well, and have a wonderful time in the meantime. ;-) – John Smith May 27 '15 at 18:01
  • UNION didn't make any difference at all, the output in form of array remains the same. Is there a way to organize it so that I would get it the same way that I posted here? – John Smith May 28 '15 at 07:29
  • Like this, "[Today] => stdClass Object, [Yesterday] => stdClass Object" – John Smith May 28 '15 at 07:33
  • 1
    The union will give the results in one call. However, in order to structure your data like that, you need to make more than one call and assign the results to an object with keys today, yesterday, etc – RisingSun May 28 '15 at 17:14
  • How can I assign those keys in the array? – John Smith May 28 '15 at 17:16
  • $notifications = new StdClass(); $notifications->today = the sql call to get today's results. $notifications->yesterday = the sql call for yesterday. etc – RisingSun May 28 '15 at 17:18
  • other than making separate calls for each day or date, you can't get the result from the db already separated and assigned to some variables – RisingSun May 28 '15 at 17:21
  • I made a change to my query. I added `'today' as day` in the select. This way, there will be a column named `day` in the results indicating which day the result is for. Kind of a workaround for that structure you need. – RisingSun May 28 '15 at 17:27
1

You should alter your MySQL query to get date difference as well.

SELECT *,DATEDIFF(NOW(),`timestamp`) as `dateDifference` FROM `reports` ORDER BY `dateDifference`

an example is provided here http://sqlfiddle.com/#!9/ecad0/6

Then run an if statement or a switch-case on dateDifference to organize your results the way you want. Example of a suitable switch-case statement is given below

switch (true) {
    case ($dateDifference===0):
        echo "Today's reports!";
        break;
    case ($dateDifference===1):
        echo "Yesterday's reports!";
        break;
    case ($dateDifference>1):
        echo "Even older reports!";
        break;
    case ($dateDifference<0):
        echo "An unexpected error occurred. Please contact your system administrator!";
}