Fork me on GitHub

Collection

Each entity selection is returned as an instance implementing Nextras\Orm\Collection\ICollection interface. ICollection extends \Traversable interface and adds other API to do further operations with the collection.

In Orm, we use coding standard which assumes, that

  • get* methods return an IEntity instance or a null,
  • find* methods return a ICollection instance.

Collection itself is immutable, all methods that modify the collection return a new ICollection instance. Collection provides the following methods:

  • getBy(array $conds): ?IEntity – applies additional filtering and returns the first result's entity or a null,
  • findBy(array $conds): ICollection – applies additional filtering,
  • orderBy($column, $direction): ICollection – applies additional ordering,
  • resetOrderBy(): ICollection – removes all defined ordering,
  • limitBy($limit, $offset): ICollection – limits the collection, optionally sets the starting offset,
  • fetch(): ?IEntity – returns the unfetched result's entity, repeated calls iterate over the whole result set,
  • fetchAll(): IEntity[] – returns the whole result's entities as an array,
  • fetchPairs($key, $value): array – process the whole result and returns it as an array. The first argument accepts a property name that will be used as an key. If a null is provided, the result array will be indexed naturally (from zero). The second argument accepts a property name that will be used as a value. If a null is provided, the whole entity will be used as the value.
// all book entities indexed by their primary key
$orm->books
    ->findAll()
    ->fetchPairs('id', null);

// all books' titles sorted backward and naturally indexed
$orm->books
    ->findAll()
    ->orderBy('title', ICollection::DESC)
    ->fetchPairs(null, 'title');

Filtering#

Each collection can be filtered by an array of conditions. These conditions are passed as a parameter of the findBy() method. The array consists of entity property names and values. Keys can contain an optional operator. The default operator is equality. Let's see the example:

$books = $orm->books->findBy([
    'author' => $author->id,
    'publishedAt<=' => new DateTime(),
]);

Allowed operators are =, !=, <=, <, >= and >.

You can filter the collection using conditions using entity relationshpis. To filter collection by a relationship, use a traversing expression: it consists of the path delimited by -> – an arrow. You have to start the expression by referencing the starting point using this keyword.

// find all books which were authored by Jon Snow
$orm->books->findBy(['this->author->name' => 'Jon Snow']);

// find all books which were not translated by Jon Snow
$orm->books->findBy(['this->translator->name!=' => 'Jon Snow']);

The described syntax may be expanded to support a OR logical conjunction. Unshift the operator ICollection::OR as a first value of the query array:

// finds all books which were authored od translated by one specific person
$books = $orm->books->findBy([
    ICollection::OR,
    'author' => $person->id,
    'translator' => $person->id,
]);

You may nest the query array structure; use the same syntax repeatedly:

// find all man older than 10 years and woman younger than 10 years
$books = $orm->author->findBy([
    ICollection::OR,
    [
        ICollection::AND,
        'age>=' => 10,
        'sex' => 'male',
    ],
    [
        ICollection::AND,
        'age<=' => 10,
        'sex' => 'female',
    ],
]);

The previous example can be shortened because the AND operator is the default logical conjunction.

// find all man older than 10 years and woman younger than 12 years
$books = $orm->author->findBy([
    ICollection::OR,
    [
        'age>=' => 10,
        'gender' => 'male',
    ],
    [
        'age<=' => 12,
        'gender' => 'female',
    ],
]);

There are few restrictions:

  • Filtering does not support any kind of aggregation. If you need to write more complex queries, proxy your methods to a mapper layer. Learn more in Repository chapter.
  • Relationship filtering is currently supported only over the persisted (non-virtual) properties. Support for virtual properties has not been implemented yet.

Sorting#

You can easily sort the collection by orderBy() method, which accepts the property name and the sorting direction. By default, columns are sorted in an ascending order. To change the order, use ICollection::ASC or ICollection::DESC constants.

$orm->books->findAll()->orderBy('title', ICollection::DESC);

You can add more ordering rules that will be used if the previously defined ordering properties will be evaluated as the same. The sorting may be reset by resetOrderBy() method.


Limiting#

To limit the data collection, just use limitBy() method.

// get the last 10 published books
$orm->books->findAll()->orderBy('publishedAt', ICollection::DESC)->limitBy(10);

// get the 10 penultimate published books
$orm->books->findAll()->orderBy('publishedAt', ICollection::DESC)->limitBy(10, 10);

Counting#

It is easy to count entities returned in a collection. There are two methods:

  • count() fetches the entities from the storage and counts them in PHP,
  • countStored() asks the storage for the collection count; the implementation depends on the mapper layer, basically, the countStored() method runs an SQL query.

The count() method is quite useful if you know that you will need the fetched entities in the collection. The countStored() is needed if you do a pagination, etc.

public function renderArticles($categoryId)
{
    $articles = $this->orm->articles->findBy(['category' => $categoryId]);

    $limit = 10;
    $offset = $this->page * 10;

    $this->paginator->totalCount = $articles->countStored();
    $this->template->articles = $articles->limitBy($limit, $offset);
}
{if $articles->count()}
    {foreach $articles} ... {/foreach}
{else}
    You have no articles.
{/if}