Querying

Reading data from your database (or any datasource available) is a common
tasks that you will perform. All query-related operations may be done through
the static find() method, along with some additional utility methods provided
for convenience.

  1. // Read all posts.
  2. $posts = Posts::find('all');
  3. // Read the first post.
  4. $post = Posts::find('first');
  5. // Read all posts with the newest ones first.
  6. $posts = Posts::find('all', [
  7. 'order' => ['created' => 'DESC']
  8. ]);
  9. // Read only the title of the newest post.
  10. $post = Posts::find('first', [
  11. 'fields' => ['title'],
  12. 'order' => ['created' => 'DESC']
  13. ]);
  14. // Read only posts where the author name is "michael".
  15. $posts = Posts::find('all', [
  16. 'conditions' => ['author' => 'michael']
  17. ]);

The framework protects against injection attacks by quoting
condition values. Other options i.e. ‘fields’ are
however not automatically quoted. Read more about the topic
and potential countermeasures in the Security chapter.

Built-in Finders

find() utilizes finders to do most of its work. The first parameter to find()
selects the type of finder you want to use. The second parameter provides options to the
finder.

There are four built-in finders. The most commonly used ones are 'all' and 'first'.


You can also create your own custom finders via finder().
  • 'all': Returns a collection of records matching the conditions.
  • 'first': Returns the first record matching the conditions.
  • 'count': Returns an integer count of all records matching the conditions.
  • 'list': Returns a one dimensional array, where the key is the primary
    key and the value the title of the record (a 'title' field is required
    for this). A result may look like: array(1 => 'Foo', 2 => 'Bar').

All bultin-in finders take the same set of options to control which and
how many records they return.

Using Conditions

The 'conditions' option allows you to provide conditions for the query
i.e. 'array('is_published' => true). The following example finds all
posts author name is michael.

  1. $posts = Posts::find('all', [
  2. 'conditions' => ['author' => 'michael']
  3. ]);

By default the conditions are AND’ed together so the following
example would require each post to be published and authored
by michael.

  1. $posts = Posts::find('all', [
  2. 'conditions' => [
  3. 'author' => 'michael',
  4. 'is_published' => true
  5. ]
  6. ]);

To OR conditions together use the following syntax.

  1. $posts = Posts::find('all', [
  2. 'conditions' => [
  3. 'or' => [
  4. 'author' => 'michael',
  5. 'is_published' => true
  6. ]
  7. ]
  8. ]);

To find all records from a set of authors use the array
syntax.

  1. $posts = Posts::find('all', [
  2. 'conditions' => [
  3. 'author' => ['michael', 'nate']
  4. ]
  5. ]);

Ordering

The 'order' option allows to control the order in which the data will be returned, i.e.
'created ASC' sorts by created date in ascending order, 'created DESC' sorts
the same field in descending order. To sort by multiple fields use the array
syntax array('title' => 'ASC', 'id' => 'ASC).

The following are equal groups:

  1. Posts::find('all', ['order' => 'title']);
  2. Posts::find('all', ['order' => 'title ASC']);
  3. Posts::find('all', ['order' => ['title']]);
  4. Posts::find('all', ['order' => ['title' => 'ASC']]);
  5. Posts::find('all', ['order' => ['title', 'id']]);
  6. Posts::find('all', ['order' => ['title' => 'ASC', 'id' => 'ASC']]);

Restricting Returned Fields

The 'fields' option allows to specify the fields that should be retrieved. By default
all fields are retrieved. To optimize query performance, limit to just the ones actually
needed.

  1. Posts::find('all', [
  2. 'fields' => ['title', 'author']
  3. ]);

Limiting Number of Records

The 'limit' option allows to limit the set of returned records to a maximum number.

  1. Posts::find('all', [
  2. 'limit' => 10
  3. ]);

Pagination

The 'page' option allows to paginate data sets. It specifies the page of the set
together with the limit option specifying the number of records per page. The first
page starts at 1.

  1. $page1 = Posts::find('all', [
  2. 'page' => 1,
  3. 'limit' => 10
  4. ]);
  5. $page2 = Posts::find('all', [
  6. 'page' => 2,
  7. 'limit' => 10
  8. ]);

Shorthands

li3 also provides some additional basic methods around the find() method which
make your code less verbose and easier to read:

  1. // Shorthand for find first by primary key.
  2. Posts::find(23);
  3. Posts::all();

Note list is a keyword in PHP and so cannot be called magically like other finders.

The example below shows two different approaches to finding all the posts related to the
username “Michael”. The first bare approach shows how to use find() directly. The second
example uses camelCase convention to tell li3 to filter by a specific field name and
value.

  1. $posts = Posts::findAllByUsername('michael');
  2. // ... is functionally equal to ...
  3. $posts = Posts::find('all', [
  4. 'conditions' => ['username' => 'michael']
  5. ]);

Custom Finders

The basic finders are nice, but the framework also provides you with a set of highly
dynamic methods that match against your dataset. It allows you to build
custom finders to extend functionality.

As you use your models, you might start to wish for a shortcut. For example,
instead of having to do this repeatedly:

  1. $recentComments = Comments::find('all', [
  2. 'conditions' => [
  3. 'created' => [
  4. '>=' => date('Y-m-d H:i:s', time() - (86400 * 3))
  5. ]
  6. ]
  7. ]);

You could create a custom finder method that packages the specified conditions into a one-liner:

  1. $recentComments = Comments::find('recent');
  2. // or, as a "magic" method:
  3. $recentComments = Comments::recent();

At a basic level, this is done by utilizing the finder() method of the model.
You call finder() and supply the name of the finder, along with a definition
so li3 knows how to form the query. The definition in this simple case looks
just like the query array we supplied to find() earlier:

  1. Comments::finder('recent', [
  2. 'conditions' => [
  3. 'created' => [
  4. '>=' => date('Y-m-d H:i:s', time() - (86400 * 3))
  5. ]
  6. ]
  7. ]);

Some finder implementations might require a little processing in addition to a default set
of conditions. In that case, you can define a finder using a closure that will be called
as part of find chaining. In this use case, you supply the name of the finder along with a
closure that looks much like a filter definition:


The syntax for filter and thus for finders has changed in 1.1.
Below you find the new syntax. The old one receives 3 parameters
($self, $params, $chain).
  1. Comments::finder('recentCategories', function($params, $next){
  2. // Set up default conditions
  3. $defaults = [
  4. 'created' => [
  5. '>=' => date('Y-m-d H:i:s', time() - (86400 * 3))
  6. ]
  7. ];
  8. // Merge with supplied params
  9. $params['options']['conditions'] = $defaults + (array) $params['options']['conditions'];
  10. // Do a bit of reformatting
  11. $results = [];
  12. foreach ($next($params) as $entity) {
  13. $results[] = $entity->categoryName;
  14. }
  15. // Returns an array of recent categories given the supplied query params.
  16. return $results;
  17. });

Rich Returned Values

The response from a model method is not just a plain array but actually an
entity object (or a collection of them). This means that you can perform a
variety of actions on them if you need to. Here are a few examples:

  1. // Find all Posts
  2. $posts = Posts::all();
  3. // Get the first and last post of the collection
  4. $first = $posts->first();
  5. $last = $posts->last();
  6. // Iterate over all posts and print out the title
  7. foreach ($posts as $post) {
  8. echo $post->title;
  9. }

Entities can be easily converted into other formats.

  1. Posts::find('all')->data();
  2. Posts::find('all')->to('array');

Default Query Options

In cases where you always want finders results constrained to i.e. certain conditions,
default query options can be used. Default options may be defined by using the query()
method or alternatively by defining the $_query property on the model class.

Specific query options overwrite default ones. As both are merged by simply using the +
operator for arrays. Note that this can also be a common pitfall.

  1. Posts::query([
  2. 'conditions' => ['is_published' => true],
  3. 'limit' => 4
  4. ]);
  5. // Will retrieve maximum of 4 results which are published.
  6. Posts::find('all');
  7. // Potential pitfall: will retrieve results published or not
  8. // for author michael. Limited to 4 results maximum.
  9. Posts::find('all', [
  10. 'conditions' => ['author' => 'michael']
  11. ]);
  12. // Will retrieve only published results for author michael.
  13. // Limited to 4 results.
  14. Posts::find('all', [
  15. 'conditions' => ['author' => 'michael', 'is_published' => true]
  16. ]);