zen of coding

Dealing with dates… reporting, etc.

Once in a while I run into a situation, where some sort of reporting is required for an application…
(It happens.)

An example?.. We need to get a list of all active users in the DB.

Obviously CakePHP makes it overly easy for us.
However, it also happens, that once in a while we need to filter the list by using a “from” and “to” date range.

I really wanted to keep things simple and re-usable (yeah, lazy) and one solution came to mind…

It fits nicely within an MVC concept, and since I’ve got nothing else to write about, perhaps it might be worth sharing.

To get things started, we’ll consider a view:

<?php
 echo $form->create('User', array('action' => 'report_to_me'));

  echo $form->input('active_from');
  echo $form->input('active_to');

  echo $form->end('Set date');
?>

<?php if(!empty($allActiveUsers)) : ?>

  <?php foreach($allActiveUsers as $user) : ?>

    <div>
      <?php echo $user['User']['name']; ?>
    </div>

  <?php endforeach; ?>

<?php else : ?>

  <div>
    Ain't got no active users.
  </div>

<?php endif; ?>

Pretty straight-forward… We display a user’s name for all active users from the DB.
Also, there is a way for someone to input a date range and send it off to the controller.

Let’s take a look at the action in the controller:

public function report_to_me() {
      if(!empty($this->data)) {
        $this->User->fromDate = $this->data['User']['active_from'];
        $this->User->toDate = $this->data['User']['active_to'];
      }

      $this->set('allActiveUsers', $this->User->getActive());
    }

If no date is supplied (i.e. $this->data is empty) we show all active users from the DB.
Yet, if the date is supplied we set a few properties in our User model…
You might notice that I like fat models (eh?), therefore the goal is, once again, to avoid any logic in the controller.

With that being said, we’ll take a look at the User model:

<?php
   class User extends AppModel {

     public $fromDate = NULL;
     public $toDate = NULL;

     public function getActive() {

       return $this->find('all', array('conditions' => array('User.is_active' => 1,
                                                              $this->getDateConditions()),
                                 'recursive' => -1));
      }

      private function getDateConditions() {
        if(!empty($this->fromDate) && !empty($this->toDate)) {
         return array($this->alias . '.' . 'created BETWEEN ? AND ?' =>
                array(date('Y-m-d', strtotime($this->fromDate)),
                      date('Y-m-d', strtotime($this->toDate))));
        }

        return FALSE;
      }
    }
?>

The only interesting piece of code here is the getDateConditions() method.
Once we set the $fromDate and $toDate properties, the method gives us a nicely formatted condition for the date range. Otherwise, as promised, we get a list of all active users (no date condition is applied).

  • Pingback: Twitter Trackbacks for Dealing with dates… reporting, etc. « nuts and bolts of cakephp [teknoid.wordpress.com] on Topsy.com()

  • I wonder why you set your condition values to a new model property instead of using a different signature for getDateConditions – like
    private function getDateConditions( $fromDate = null, $toDate = null ) {}

    Or, if you want to keep things more reusable, why not something like
    private function getDateConditions( $conditions = array() ) {}
    where $conditions could have ‘fromDate’ and ‘toDate’ keys.

    There might be a reason I didn’t understand (that you hopefully can point out), or just another way of doing it. I’m just wondering.

  • @jmjjg

    The dates will have to come from the view, so setting the Model properties in the controller seems appropriate. Once the properties are set, all business logic is handled by the Model.

  • @teknoid

    I found it weird that you were setting “strange” model properties, but I didn’t notice that it was the model itself that called getDateConditions().

    I currently manage that kind of requests with the model preparing find options ( http://bin.cakephp.org/view/996003450 ), like “custom find types” ( http://cricava.com/blogs/index.php?blog=6&title=pagination-with-custom-find-types-in-cak&more=1&c=1&tb=1&pb=1 ).

    I think the advantage of this code is that you can use the same code for find and prepare, and that you can add conditions to the request.
    The disadvantage is clearly length :-)

    What do you think ?

  • @jmjjg

    I think it works just as well… There are a few solutions to this problem.
    IMO, as long as the MVC is respected in the way that you keep the logic as much as possible out of the controller, it is already a well-done approach.

  • luke aka boobyWomack

    hi teknoid, nice example – I did a mirror image of this but in the controller (urgh…)

    I was interested to see the way you return FALSE in the privat efunction and it is handled by the cake conditions handler.

  • @luke

    Cool. Well, yeah, that’s really a PHP thing you return ‘false’ and it’s being treated as null, empty, etc. One of the things people hate/love about an unstructured language like this vs. … ooooh let’s say… java. As long as you know the results of your intent, it makes life so much easier.

  • Technoid,

    Advanced searches are something that CakePHP doesn’t really address… at all. I’ve used a search/SQL `conditions` class with a lot of my old applications (non-CakePHP) and rewritten it to be used as a component. It can help create `conditions` with comparisons across multiple fields and types (such as numeric, string or date). Check it out and let me know your comments.

    http://www.jjwdesign.com/blog/2009/09/29/sql-conditions-a-cakephp-component-class/

    Jeff

  • @Jeff Walters

    Looks interesting, although I didn’t have time to study in detail.
    One question, however, is why did you go the component route and not a behavior ;)?

%d bloggers like this: