Fork me on GitHub

Repository

Repository provides interface for entities retrieving, persisting and removing.

In Orm, we use coding standard which assumes, that

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

Retrieving

Repository provides findAll() method, which returns Nextras\Orm\Collection\ICollection instance with all entities in storage. You can add filtering conditions, sort and fetch entities from the collection. Read more about Collection in its chapter.

Repository has to define static method getEntityClassNames() that returns array of entity names that the repsitory produce. Repository itself can contain user defined methods:

final class BooksRepository extends Repository
{
    static function getEntityClassNames()
    {
        return [Book::class];
    }

    /**
     * @return ICollection|Book[]
     */
    public function findLatest()
    {
        return $this->findAll()->orderBy('id', ICollection::DESC)->limitBy(3);
    }

    /**
     * @return ICollection|Book[]
     */
    public function findByTags($name)
    {
        return $this->findBy(['this->tags->name' => $name]);
    }
}

Sometimes, it is needed to write pure SQL query. SQL queries can be written only in mapper layer. You can easily tell repository to proxy these methods by writing php doc @method annotation:

/**
 * @method ICollection|Book[] findBooksWithEvenId()
 */
final class BooksRepository extends Repository
{
    // ...
}


final class BooksMapper extends Mapper
{
    public function findBooksWithEvenId()
    {
        return $this->builder()->where('id % 2 = 0');
    }
}

In the example above you can see that the mapper layer returns Nextras\Dbal\QueryBuilder\QueryBuilder object, but annotation says that repository will return ICollection object. If mapper does not return ICollection, Entity or null value, repository automatically calls IMapper::toCollection() method. You can return only things which your mapper can automatically convert to allowed types.


Identity map

Repository uses Identity Map pattern. Therefore only one instance of Entity can exist in your runtime. Selecting the same entity by another query will still return the same entity, even when entity changes were not persisted.

// in this example title property is unique

$book1 = $orm->books->getById(1);
$book2 = $orm->books->findBy(['title' => $book1->title])->fetch();

$book1 === $book2; // true

Persisting

Each entity can be persisted. It does not matter if you are creating or updating entity, for each case you use IRepository::persist() method. By default, repository will persist all other connected entities. (And also will take care of needed persistence order.) But you can persist only specific entity by providing false as the second argument of persist method.

Persistence is run in transaction. Calling persist method automatically starts a transaction (if not started earlier). The transaction commit is done by IRepository::flush() method. You can persist and flush changes at once by using IRepository::persistAndFlush() method. Persisting automatically attaches entity to repository, if it has not been attached earlier.

$author = new Author();
$author->name = 'Jon Snow';
$author->born = 'yesterday';
$author->mail = 'snow@wall.st';

// let Orm know about entity
// nothing has been inserted into database yet
$orm->authors->attach($author);


$book = new Book();
$book->title = 'My Life on The Wall';
$book->author = $author;
$book->publisher = 1; // id of stored publisher in db


// stores new book and author entity into database
// queries are run in transaction and commited
$orm->books->persistAndFlush($book);

Removing

Use IRepository::remove() method to delete entities from database.

If entity has property with OneHasMany relationship type and the property on other side is not nullable, removing this entity would cause throwing an error, because the property on the other side cannot be nulled. E.g.: you cannot remove author with books, because book has compulsory author property. The solution is:

  • to set new author for the books,
  • remove books first,
  • enable recursive removal.

Recursive removal is not enabled by default; you can enable it by passing optional second boolean parameter.

$author = $orm->authors->getById(1);
// $author->books collection is not empty

// recursively removes entities
// which cannot stay without $author entity
$orm->authors->remove($author, true);

Removing entities is run in transaction as well as persisting. At the end you have to call IRepository::flush() method or use IRepository::removeAndFlush() method.