How to Effectively use the Repository and Query Object of Extbase?

Extbase[1] supports very well the idea of Domain-Driven Design[2]. It supplies a Repository class to comfortably fetch your objects from. Some handy methods are already implemented. Let’s have a look at some example code.

$blog = new Tx_BlogExample_Domain_Model_Blog('TYPO3 Development');
$blog->setDescription('A blog for professional TYPO3 developers.');

$administrator = new Tx_BlogExample_Domain_Model_Administrator();
$administrator->setName('Jochen Rau');
$administrator->setEmail('jochen.rau@example.com');
$blog->setAdministrator($administrator);

$post = new Tx_BlogExample_Domain_Model_Post('Effectively use the Query Object of Extbase');
$post->setContent('Extbase is a framework to develop ... hey, we\'re getting recursive now!');

$comment = new Tx_BlogExample_Domain_Model_Comment;
$comment->setDate(new DateTime);
$comment->setAuthor('Peter Pan');
$comment->setEmail('peter.pan@example.com');
$comment->setContent('Do you need some pills?.');
$post->addComment($comment);

$blog->addPost($post);

$blogRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_BlogRepository')
$blogRepository->add($blog);

In a first step, we create a new blog with an administrator, and a post with a comment. We instanciate the BlogRepository in line 21 and add the blog to it. That’s all we have to do. The blog is now persisted and can be fetched later from the repository again. We don’t have to say save($blog) at any time. The repository “takes care” of the blog.
Keep in mind that you have to instanciate the repository with t3lib_div::makeInstance() as it is a Singleton[3]. If you instanciate it with new the Repository will be always an empty one if Extbase searches for objects to be persisted.

The BlogRepository class is as simple as this:

class Tx_BlogExample_Domain_Repository_BlogRepository 
    extends Tx_Extbase_Persistence_Repository {
}

Most of the methods needed are already implemented in the Tx_Extbase_Persistence_Repository. These are:

$repository->add($object)
$repository->remove($object)
$repository->replace($existingObject, $newObject)
$repository->update($modifiedObject)
$repository->findAll()
$repository->countAll() // Since Extbase 1.1
$repository->removeAll()
$repository->createQuery()
$repository->countByProperty($value) // Since Extbase 1.1
$repository->findByProperty($value)
$repository->findOneByProperty($value)

The methods add() and remove() are self-explanatory. replace() takes an untracked object and puts it at the “place” of an existing one where update() just takes an existing object and updates it with the property values of the modified object.

The findByProperty() and findOneByProperty() methods are magic methods where you can replace “Property” with the name of a property of the Blog object. For example we can write $blogRepository->findByTitle('My Blog') to fetch all Blogs with the given title. This also works for objects like in $blogRepository->findByAdministrator($administrator) but not yet for properties holding aggregates. Thus, $blogRepository->findByPost($post) won’t work by now. findOneByProperty() is pretty similar to findByProperty() but returns the only first Object found.

The countAll() and countByProperty() methods are similar to the according find methods. But the don’t build objects. They only count resulting objects.

All these methods keep the underlying storage solution (mostly a relational database) transparent to the user[4]. But what if we have special constraints that are not covered with the methods above? Let’s say we want to find the five most recent posts of a given blog. Then we can use a Query object. Let’s ask the PostRepository for those posts:

class Tx_BlogExample_Domain_Repository_PostRepository
    extends Tx_Extbase_Persistence_Repository {

    public function findRecentByBlog(Tx_BlogExample_Domain_Model_Blog $blog, $limit = 5) {
        $query = $this->createQuery();
        $query->matching($query->equals('blog', $blog));
        $query->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING));
        $query->setLimit((integer)$limit);
        $posts = $query->execute();
        return $posts;
    }
}

The method createQuery() returns an appropriate Query object suitable for Post objects. In Line 6 we define the constraint the posts should match: $query->matching([constraint]). The constraint is returned by $query->equals('blog', $blog). There are some more methods returning a constraint:

We can define the orderings with $query->setOrderings([orderings]) where [orderings] is expected to be an array of property-order pairs. There are two constants defining the order:

  • Order ascending: Tx_Extbase_Persistence_QueryInterface::ORDER_ASCENDING
  • Order descending: Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING

We limit the result set to a maximum of five posts with $query->setLimit((integer)$limit). And finally invoke $query->execute() which returns us the already build posts with all their comments inside (if we are not “lazy” 😉 ). There is also a method $query->count() which does not fetch the resulting posts but counts them. Note that the Query object enables chaining. Thus, the following code does the same as the given above:

public function findRecentByBlog(Tx_BlogExample_Domain_Model_Blog $blog, $limit = 5) {
    $query = $this->createQuery();
    return $query->matching($query->equals('blog', $blog))
        ->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING))
        ->setLimit((integer)$limit)
        ->execute();
}

That’s it for today. I hope you got a deeper insight and I am looking forward to your comments. Maybe you want to join the discussion on the related mailing list. I have taken most of the code from the extension BlogExample which is available in the Extension Repository (TER).

Footnotes

  1. Extbase is a framework to develop Extensions for TYPO3.
  2. For a brief introduction in DDD visit www.typo3-media.com
  3. A Singleton is a design pattern to ensure the uniqueness of an object throughout a given scope.
  4. “User” means in our case the Controller object in which we fetch the blogs and hand them to the View. More on the Model-View-Controller Pattern in a later post.

Info: This post was originally posted on http://blog.typoplanet.de (2010/01/27) by Jochen Rau but the blog disapeared from the web