Something on Everything

The “new” way to define find methods!

August 28, 2008 · 14 Comments


Thanks to Nate, as of changeset [7640], this post is not “Awful Crap” anymore, and has it’s implementation totally supported, as Model::$_findMethods is now protected, and can be “design safe overrided”.

I know you’ve already read on phparch, cakebaker and debuggable about
how to define diferent find types, but *happily* they are all “deprecated” now, since
nate’s revision – see changelog [6481] – made 6 months ago,
customizing find methods is now much more fun, clean and flexible.

First you need to define wich find methods your model will be able to use.
Lets say we want use find(’conflicteds’) in our Event model,
so we need to include the method name in Event::$_findMethods var:


class Event extends AppModel {
  var $_findMethods = array (
    'conflicteds' => true,
    'all' => true,
    'first' => true,
    'list' => true,
    'count' => true,
    'neighbors' => true,
    'threaded' => true);
  );
}

As you can see we need to redefine all built-in methods again, or
our model wont be able to use them.

Now we can implement the method, it should be named _find{$MethodName} – uppercase on first letter – and should
take three parameters: $state, $query, $results, and the last shall be optional:


  function _findConflicteds($state, $query, $results = array()) {
    if($state === 'before') {
      // parse $query and return it
      return $bendedQuery;
    }
    else {
      // parse $results and return it
      return $parsedResults;
    }
  }

The method will be called twice during the find process: before and after db query. You’ll know
the stage you are dealing with by the value of $state wich will be one or another – _doh_.
In the first stage you should return the query, in the second you should return the results.


The paragraphs above are not valid anymore as it was fixed in [7549]

Notice that your customized find method will be called right before beforeFind() callbacks,
if they were enabled by $query['callbacks'], but they won’t use afterFind() callbacks by default,
to make them do so you should check if callbacks are enabled and use $this->_filterResults():


[...]
  if($state === 'after') {
    if($query['callbacks'] === true || $query['callbacks'] === 'after') {
      $results = $this->_filterResults($results);
    }
    // parse $results and return it
    return $parsedResults;
  }
[...]

As you can see you should use callbacks before you play around with the result set, so
if want to crack the default data structure you won’t mess with yours and your behaviors’ afterFind().

I think afterFind callbacks, if enableds, should be called automatically from Model::find() method, and
if a find method don’t want to be catched by them, they could just set $query['callbacks'] to false, or
to use only ‘after’ or ‘before’. What do you think? – see the ticket : #5344. – fixed in [7549]

Last post is being updated with nate’s tips. Be sure to come back and see how to it the right way… and not with a cheap hack figured in the middle of a busy work journey…

Categories: CakePhp
Tagged: , ,

14 responses so far ↓

  • rafaelbandeira3 // August 28, 2008 at 5:00 am | Reply

    Don’t forget to correct my typos!
    http://rafaelbandeira3.wordpress.com/correct-my-typos/

  • Daniel Hofstetter // August 29, 2008 at 8:36 am | Reply

    This approach is a hack, and only works because cake doesn’t make use of the visibility keywords of PHP5 and everything is public. But by Cake convention all properties/methods starting with two underscores are private…

  • rafaelbandeira3 // August 29, 2008 at 11:14 am | Reply

    @Daniel Hofstetter: a hack? What do you mean? It’s the intended approach for customized find calls – so you won’t have to have a 100+ lines overrided find method as shown in Tim’s approach at debuggable.com, and it allows you to have a clean API, and you can still implement them at Behaviors level, what is very very nice…

    by the way: saddly PHP4 still’s out there, and cake intends to support programmers that, by any reason, still use it, and doing such, visibility keyword will never be used as they are not implemented in this old fellow.

  • Daniel Hofstetter // August 29, 2008 at 12:05 pm | Reply

    Well, the __findMethods property of the Model class is private, which means you cannot override it in your models. And you cannot access __filterResults() in your models for the same reason. At least if you follow the Cake conventions ;-)

    Yeah, unfortunately, cake still supports php4. But the support for it will be dropped in Cake 2.0, at least according to the roadmap.

  • rafaelbandeira3 // August 29, 2008 at 12:24 pm | Reply

    @Daniel Hofstetter: Thanks for your reply.
    Oh yeah, saddly that’s true, and I haven’t bothered noticing.
    They should be protected though as they are very handy and offer all the pros I mentioned.

    > Well, a hacky solution to still use it in a PHP5 fashion would be overriding Model::find() with a copy and paste version of itself! LOL…

  • rafaelbandeira3 // August 29, 2008 at 12:35 pm | Reply

    Just noticed an inconsitency on all this structure: Why would filterResults method and findMethods var be private and all find{$method} methods be protected.

    The lack of a real OO approach instead of a convention really messes with visibility of visibility params…

  • Joel Perras // August 29, 2008 at 2:29 pm | Reply

    The correct method of defining your own find types is to override the Model::find() method. There have been many posts on this topic in the Cakesphere, including one by DH:

    http://cakebaker.42dh.com/2008/03/23/defining-custom-find-types/

    The Model::find() method (AFAIK) was in part refactored to make custom find types easier to implement.

  • rafaelbandeira3 // August 29, 2008 at 4:56 pm | Reply

    @Joel Perras: Daniel’s article is already mentioned and linked at the top of the post.

    > The Model::find() method (AFAIK) was in part
    > refactored to make custom find types easier to
    > implement.

    Actually that’s what this post is all about, I do think that the way I described it is much more flexible and easy to mantain, but, as pointed out by Daniel, it would actually be a hack as Model::$__findMethods should be private by following cake’s conventions.

  • Joel Perras // August 29, 2008 at 7:04 pm | Reply

    Didn’t see the links; light-grey links next to white text on a grey background is not the easiest thing to read.

    How is your method more flexible & easier to maintain than using basic OO design to override the method? Nate himself advocates the methods that you linked to in the top of your post, and while I believe you should make your own opinions, taking the advice from the lead developer of Cake on how to do something in Cake is usually a sound idea. I can see about a half-dozen bad ideas and incorrect implementation techniques in the code you supplied above.

    ” but *happily* they are all “deprecated” now, since
    nate’s revision – see changelog [6481]”

    You seem to be confused. If you look at the timestamp of the changelog, you’ll see that those modifications were committed on 02/27/08, whereas the article on phparch was published on 2008-03-10, two weeks later. The refactoring was done specifically to allow this kind of method override, something which was not possible beforehand.

  • rafaelbandeira3 // August 29, 2008 at 7:31 pm | Reply

    @Joel Perras: Thanks for your comment!
    WP theme, I got no power$ to handle it…

    I must admit that I missed a lot of points when I came up with this post, and nate’s post timestamp was just another – and that was nothing compared to what Daniel pointed.

    About code maintainability, in my opinion it still’s a way better, mainly because of the number of lines that your find() method will have.

    I’m adding some tags to the post that indicates how wrong it is.

    Thanks Joel and Daniel.

  • Joel Perras // August 31, 2008 at 2:53 am | Reply

    Good call on the WordPress theme change. Much easier on the eyes :-D . If I might make one more constructive suggestion: increase the default font-size of “pre” HTML blocks.

    I must admit that I’ve gained a significant amount of respect for you; tagging your own post as “awful crap” and admitting to mistakes that you have made is something that many developers aren’t able to cope with. I still have problems doing it, but then again I never make mistakes ;-)

    Cheers!

  • rafaelbandeira3 // September 12, 2008 at 11:47 am | Reply

    @Joel Perras: Much better uh? Well I couldn’t figure out how to change styles on WP – not that I have tried too hard…

    Yes Devs really don’t like to admit thei are wrong… but well, living and learning, it’s not a big deal.

    Cheers!

  • rafaelbandeira3 // September 22, 2008 at 6:25 pm | Reply

    Well.. the solution is back to black then, many people didn’t like it, even I had came out with a somewhat alike solution but in using only the concept of not having a find() method with thousands of lines, but now it’s totally fine to use this approach, see ins on post for more info.

  • An alternative way to define custom find types - cakebaker // September 23, 2008 at 12:22 pm | Reply

    [...] 7640 an alternative way for defining custom find types is possible. Rafael Bandeira already mentioned this approach around one month ago, only, at that time it was a hack because it violated the [...]

Leave a Comment