Lithium's Model finders is a great way to avoid repeating the same code blocks over and over. If you google `Lithium finders` you will find many tutorials on this subject but most of the examples don't make much sense (at least to me) since they can be replaced by static methods inside a model class. I have set a simple rule on this matter: methods that don't apply to more than one model class, shouldn't become finders.

To take advantage of finders you must create your own Base model that other models will extend. Below you can see what i use with my two most used finders that i would actually love to see them become official...

class Model extends \lithium\data\Model {
	public static function __init() {
		static::finder('columns', function($self, $params, $chain) {
			$options = &$params['options'];
			$options['return'] = 'resource';
			if ($options['fields'] === null || empty($options['fields'])) {
				$options['fields'] = array($self::meta('key'));
			} elseif (count($options['fields']) > 1) {
				$options['fields'] = array_slice($options['fields'], 0, 1);
			}

			$result = array();
			foreach ($chain->next($self, $params, $chain) as $data) {
				$result[] = $data[0];
			}
			return $result;
		});

		static::finder('pairs', function($self, $params, $chain) {
			$options = &$params['options'];
			$options['return'] = 'resource';
			if ($options['fields'] === null || count($options['fields']) < 2) {
				$key = $self::meta('key');
				$title = $self::meta('title');
				$options['fields'] = array($key, $title);
			} elseif (count($options['fields']) > 2) {
				$options['fields'] = array_slice($options['fields'], 0, 2);
			}

			$result = array();
			foreach ($chain->next($self, $params, $chain) as $data) {
				$result[$data[0]] = $data[1];
			}
			return $result;
		});
	}
}

Finder: `columns`

If you don't provide any fields, it will retrieve the model key and if the fields count is more than one it will be trimmed to include only the first one. It returns a simple array of just values without the field name as key.

// Result: array('13872', '8977', '7251', '5194');
$result = Pages::find('columns', array(
    'fields' => 'views',
    'order' => array('views' => 'desc'),
    'limit' => 4
));

Finder: `pairs`

If you don't provide any fields the result will be exactly the same as the default list finder, but with this method you can include any two fields you want and not only the `key` and `title` fields. As a rule the first field in the list will be the key and the second will be the value. If your list of fields has more than two items, only the first two will make it to the query.

// Result: array(47 => '13872', 65 => '8977', 73 => '7251', 70 => '5194')
$result = Pages::find('pairs', array(
	'fields' => array('id', 'views'),
	'order' => array('views' => 'desc'),
	'limit' => 4
));

If you take a good look the finders force the return option to be the query resource. This is done to improve performance as much as possible.  I hope you enjoyed this tutorial!


Comments

  1. Chris #1   Chris | Jun 19, 2013 at 22:35

    I am using the above snippet in this site, with mysql data source, can someone please confirm it works with mongoDB?

Leave a Comment
It will not be published.