Skip to content
dr. Hannibal Lecter edited this page Apr 12, 2021 · 2 revisions

NOTE: The content of this article was migrated from lecterror.com, with some minor changes.

Intro

Although CakePHP takes good care of the basic CRUD functionality, I often need to do one more thing with my data. And that's filtering or "searching", if you like it more that way. What I hate about it is the fact that it's not there by default. I was working on an analytical app at work and our client demanded a search feature. We're talking about loads of medical data, and more often then not, you don't want to see everything. So, instead of writing a search feature for all our models, I decided to create a plugin which would do the dirty work for me, so I could reuse it in the future.

One thing that was bothering me was pagination, sorting and search combined, which obviously has to work. Although it's not pretty, I managed to get that working too. Not only that, I've also added "persistence", the ability of this plugin to remember your search when you leave the page and return later (in the same session of course, but I'm sure you can easily serialize plugin data to work as a user preference).

The idea here is to get things working fast, but reserving some flexibility for the future, if the need arises for a more complex filtering options (more about that later). Basically, if you just want to slam a "search form" on top of your data, you can do that in a few minutes. If you need something more complex, it's not there by default, but it can be easily implemented.

Please note that this plugin was written hastily, so the code is not something I'm proud of. But, it works for our app perfectly, so I'm not changing it - for now. If you have suggestions or bugfixes, let's hear them!

Basic usage

To use this plugin, you need to include the plugin's Filter component just like any other. For the sake of this example, let's say you have a Document model, and it belongs to a User model. It also has a text description, a year (integer) and a user id. To enable filtering for your index action, this is what you do:

class DocumentsController extends AppController
{
    var $components = array('Filter.Filter');

    var $filters = array
        (
            'index' => array
            (
                'Document' => array
                (
                    'Document.description',
                    'Document.year' => array('condition' => '='),
                    'Document.user_id' => array
                    (
                        'type' => 'select',
                        'label' => 'Document owner',
                        'selector' => 'getOwnerList'
                    )
                )
            )
        );
}

First some details. Document description is a text field, and the filter will take all the defaults for this field, it will output a text box, and the search operator is LIKE %your_text%.

The second input, year, will also be a text input, but we want the user to be specific here, search operator is "=".

The third field has type set to 'select', which obviously means it will be a select box (dropdown list, or whatever you call it..). Since we don't want our users to see a label "User Id", we tell the plugin that the label text should be "Document owner".

Additionally, since some users can't be document owners, we'll give the plugin a custom function which will return a list of potential owners. That's the 'selector' parameter, in this case a method of the Document model, getOwnerList(). Of course, you can just let the plugin deal with the default, and it will acquire the Document model and call Document::find('list', ...).

That's all you need to do in your controller. Now, since you said plugin needs to use Document::getOwnerList(), you better have one ready. What the function does is irrelevant. What matter is that it needs to return data for Cake's FormHelper to use (for $form->select()). So once you sort that out, you're ready to show your search form to the world: open /views/documents/index.ctp and add this where you want the search form to be:

// for cake 1.3.x
echo $filter->filterForm('Document', array('legend' => 'Search'));
// for cake 2.x
echo $this->Filter->filterForm('Document', array('legend' => 'Search'));

What it does is simply outputs everything we said above, for the Document model, with all the fields and the submit button.

You may have noticed that we didn't add the Filtered behaviour to our model, or add the FilterHelper anywhere. That's because the component takes care of that.

Now, since the Filtered behaviour is added to the model, it will filter your data on any query you do in this action. Since that may not be what you always want, there is an option to switch it off for the current query, just add the 'nofilter' param:

$this->Document->find('all', array('nofilter' => true));

OK, that's our simple usage example. With this code you can search, paginate, go to other pages in your app to destroy data at random, return to this page and your search will be remembered. Of course, sometimes you don't want that. You can switch it off as well:

var $components = array
    (
        'Filter.Filter' => array
        (
            'nopersist' => array('index')
        )
    );

There are of course some other options for the plugin:

var $filters = array
    (
        'index' => array
        (
            'YerModel' => array
            (
                'YerModel.some_bool_value' => array
                (
                    'type' => 'checkbox',
                    'default' => true
                ),
                'RelatedModel.some_field' => array
                (
                    'type' => 'select',
                    'selectOptions' => array
                    (
                        'order' => 'RelatedModel.arse ASC'
                        /* other options too.. */
                    ),
                    'required' => false,
                    'label' => 'Feck, Drink?',
                    'filterField' => 'YerModel.related_model_id'
                ),
                'YerModel.craggy_island' => array
                (
                    'condition' => 'like'
                    /*
                    can also be: = < > like => > < =<
                    */
                )
            )
        )
    );

Advanced usage

Advanced usage includes using Model callbacks (beforeDataFilter() and afterDataFilter()), creating custom fields in the search form, handling their input etc.. If there's interest in that, let me know and I'll do my best to write some examples in another article.

Update 21/04/2011: About that, here it is: CakePHP Filter Plugin advanced usage

Code

The code is available at GitHub:

Again, please note that this was a hastily written plugin and the code is not nice or optimal. I'd like to hear your ideas and suggestions for some ways to improve the plugin and make it more generic and more extendable.

Happy baking!

Clone this wiki locally