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');

$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->setContent('Do you need some pills?.');


$blogRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_BlogRepository')

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->replace($existingObject, $newObject)
$repository->countAll() // Since Extbase 1.1
$repository->countByProperty($value) // Since Extbase 1.1

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));
        $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:

$query->logicalAnd($constraint1, $constraint2);
$query->logicalOr($constraint1, $constraint2);
$query->equals($propertyName, $operand, $caseSensitive = TRUE);
$query->in($propertyName, $operand); // Since Extbase 1.1
$query->contains($propertyName, $operand); // Since Extbase 1.1
$query->like($propertyName, $operand);
$query->lessThan($propertyName, $operand);
$query->lessThanOrEqual($propertyName, $operand);
$query->greaterThan($propertyName, $operand);
$query->greaterThanOrEqual($propertyName, $operand);

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))

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).


  1. Extbase is a framework to develop Extensions for TYPO3
  2. Fo a brief introduction in DDD visit 
  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. 
  1. Thanks for this great post, Jochen.
    One question: Is it planned to execute the “findByProperty” work with aggregates ?

    PS: I like the idea to execute the query object as fluent interface :)

  2. Hey Jochen, thanks a lot for the post. I had to figure these things out myself – I’m now writing my first big extbase application.
    I’m definitly waiting for more extbase related posts. Keep it up!


  3. Thanks a lot for this article. Its short and good. Perhaps it can find the Way in the Forge Wiki.


  4. Hi Jochen,

    thanks for this article, it helps a lot to have the ideas together on a single post.

    Just a small hint: There’s a little typo in the already implemented methods of the repository. I think it should be $repository->remove($object) and not $repository->removes($object)



    • Jochen Rau says:

      Hi Michael.

      Thanks for the hint. I have corrected the typo.


  5. Axel Brand says:


    thanks for the helpfull article.
    Can you tell me how connect to external data-storage (e.g. per soap or external database) and map the results to the model?!
    Tx_Extbase_Persistence_Mapper_DataMapper->map(modelClass, $results) does not work if $results is an simple array cause Tx_Extbase_Persistence_RowIteratorInterface is expected.

    Greetings, Axel

  6. Oliver says:

    Hey Jochen,

    thanks a lot for the post. I had to figure these things out myself.
    I’m definitly waiting for more extbase related posts.

  7. Henjo says:

    Hi Jochen,

    nice! This article doesn’t only help in the sense of providing information about this particular part, but helps to make to read other parts of the source as well.

    Looking forward to more articles! ;-)


  8. Kim says:

    I am looking for a reverse lookup in the repository.

    I often need to get the used categories in a selectbox. so i have to get the categories that was linked in the “Aggregate Root”.

    in SQL
    SELECT FROM caterories AS c
    LEFT JOIN jobs AS j
    ON j.category = c.uid
    GROUP BY c.uid

    I have absolutely no clue how to do this without custom SQL

  9. Daniel P says:

    Hey Jochen – thx for the informative article.

    Realy cool is the automagic behind MM Table relations:

    if (count($tags)>0) {
    $constrains[] = $query->in(‘txPageresourceTags.uid’,$tags);


  10. Patrick F. says:

    Issn’t makeInstance() obsolete?


  11. Still, PageRank is vital for those of us with new locations because the update should show the advancement of our weblog.

  12. Katrin H. says:

    Thank you for this great information.
    I just have tried this, but I have met a problem.
    How can I add data to an 1:m relation?

    gallery {name, image}
    image {img, title}
    One gallery contains several images.
    How would I add a new image to a gallery?

    • Jochen Rau says:

      It should be as easy as $gallery->addImage($image). Have a look at the BlogExample. The relation Blog->Post is equivalent to your problem.

  13. Felix Nagel says:

    “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.”

    Am I wrong or is this explanation wrong? Afaics the update function just gets the uid and uses replace, which just deletes and adds the new object. So its possible (if the modified object does not contain all properties) to lose properties when using update.

    Update is no real update but a shortcut for replace.

    • Jochen Rau says:

      Yes and no. The update function uses in fact the replace function as you described it. But you don’t “loose” the properties (better: their values), it just persists the state of the object you provided. I should better write: “where update() just persists the current state of an existing object.”

      • Felix Nagel says:

        Thanks for the fast response.

        Perhaps I should try to make this more clear:
        We have object1 with attr1, attr2, attr3 in our DB and another object2 with attr1 and attr2. When using replace(object1, object2) the attr3 value will be lost. In my definition thats not updating its replacing.

        • Jochen Rau says:

          You are right, because you replace object1 with object2. That’s the intended behavior. Following your example, object1 and object2 are different objects. But I assume you meant something like update(object). In this case, your distinction between object1 and object2 means something different. Namely, two different states of the same object. Only then, update() makes sense (and Extbase checks this by comparing the uids). So, if you reconstitute an object (with attr1, attr2, attr3), then set attr3 to NULL and invoke update(object), attr3 will be NULL the next time ist is reconstituted again. This is imo the intended behavior. You cannot update an object1 with the attribute values of an object2.

          • Felix Nagel says:

            So I will need to set each poperty manually in order to have a real update :-/
            Thanks for the clarification.

          • Jochen Rau says:

            No need to :-/, Felix. :-)! If you need to use an object as an “interface” to a SQL UPDATE, why don’t you just use $repository->findByUid($uid) to fetch the object, manipulate it and let Extbase take care of the persistence?

          • Felix Nagel says:

            Thats what I meant. Just get the old object and use setXyz for each changed property.

  14. acti says:

    Where is the >effective< way to use the repository and query objects? It's just a way you can use them. The topic suggests an effective way that is not explained… What is effective in this context? :(

  15. Hey Jochen,

    how to get the pid in Controller View?

    Best Regards

  16. Ganybhat says:

    Hi Jochen,
    How to write the UPDATE query ?
    I wanted to update the some other table immediately after create query is executed.

    • Jochen Rau says:

      Adding an object to a repository doesn’t persist it to the database immediately. You need to call persistAll() on the PersistenceManager.

  17. Tom says:

    Extbase halte ich für ein möglicherweise durchdachtes Konzept, aber wie alles in TYPO3 ist das Ganze dermassen schlecht dokumentiert, dass man Stunden für einfachste Dinge benötigt. Versuch mal SQL-Abfragen “zusammenzubauen”, die etwas schwieriger sind, wie z.B. FIND_IN_SET, BETWEEN oder auch Kombinationen von OR- und AND-Befehlen. Da bekommst Du echt Tobsuchtsanfälle.

  18. Even the Article is already older now, I’ve still have an issue where I never found any information:

    Using your technic works fine but not all queries are as easy as findAll().
    As I just want to restrict the access to one user (the current fe_user) I think there must be a smarter way than overwriting the whole repository.
    In general I just want add
    $query->matching($query->equals(‘user’, $fe_user));
    to all queries and it’s a bit difficult and needs some time to rewrite all the querying functions.

    • Jochen Rau says:

      To define a subclass of the Repository class in order to add custom functionality is the way to go in the OOP world. Matching against the fe_user is only one way to authorize the access to a resource. I would rather keep the Repository slim and allow developers adding custom solutions on top of it, than covering all the possible cases in one class.

  19. Jennifer Koenig says:

    This techniques in this article work for me (we are using Typo3 4.7 and Extbase), but for one problem: in the table where a record for the object is inserted, all the fields except uid and pid are empty. I’ve checked the data being set and it is there. Is there some kind of mapping from object properties to fields that one has to do – in the TCA maybe? Or the Configuration/setup.txt ?

  20. This shit is Porn? Don’t Hump

  21. Fantastic beat ! I wish to apprentice while you amend your site, how can i subscribe for a blog
    site? The account helped me a acceptable deal.
    I were a little bit acquainted of this your broadcast offered vibrant clear idea

  22. Shantell says:

    I read a lot of interesting articles here. Probably you spend a lot of time writing, i know how to save you a lot of time, there is an online tool that creates high quality, google friendly posts in minutes, just search in google – laranitas free content

  23. soccer news says:

    You’ve got the most effective webpages.

  24. I love looking at your website. Thanks!

  25. Thank you a lot for sharing this with all of us you really understand what you are talking about!
    Bookmarked. Kindly also seek advice from my
    site =). We could have a hyperlink trade contract
    among us

  26. soccer news says:

    Basically desired to tell you Now i’m thankful that i stumbled on the site!

  27. Hi colleagues, its fantastic post about tutoringand completely explained, keep it up all the time.

  28. shelve says:

    Also, they cost more as they are designed to work in smaller spaces
    with less airflow. To give your new born baby, a warm and pleasing ambiance just keep
    in mind below mentioned points. With the snow melting, temperatures rising, and the heavy spring rains coming down, it.

  29. Hello to every one, the contents existing at this site are truly remarkable
    for people knowledge, well, keep up the nice work fellows.

  30. shelve says:

    Also, they cost more as they are designed to work in smaller
    spaces with less airflow. The idea of changing or replacing the console housing can be a
    lot of fun. Today you can print them from your computer within a few
    seconds of paying your first installment, which is also done at super

  31. shelve says:

    Also, they cost more as they are designed to work in smaller spaces with less airflow.

    He learned his early education of music from his father by practicing.
    The main attraction of this product is simply the price.

  32. shelve says:

    It is inside the first room on first floor, near the dead body.
    On almost all the major business platforms like e – Bay
    and Craigslist they are available. I could have easily ordered a super-sized #5 with extra cheesy sauce and
    the 5,000 empty calories that came with it during my fast-food drive-thru experience or I
    could get creative.

  33. shelve says:

    How about growing your own,not that difficult,get an old rotting log which may well have spores on it already,and
    keep it in the garden and see what happens,should that not work,
    try some mushroom compost,kept warm and damp you will be suprised what will grow.
    The shelving would ideally be adjustable so books
    can be stored upright and central to the shelve, again to promote air circulation. The overall intent is that you want
    to start seeking support that will nourish you and make you feel good.

  34. shelve says:

    Don’t cut price – without an equivalent exchange in value or a reduction in your
    costs. To give your new born baby, a warm and pleasing ambiance just keep in mind below mentioned points.
    Today you can print them from your computer within a few seconds of paying your first installment, which is also done at
    super speeds.

  35. Erica says:

    What’s up, I wish for to subscribe for this webpage to obtain latest updates, therefore where can i do it please help out.

  36. Robin says:

    I’d like to thank you for the efforts you’ve put in writing this website.
    I really hope to check out the same high-grade content
    from you in the future as well. In truth, your creative
    writing abilities has inspired me to get my very
    own site now ;)

  37. to says:

    This is a very good tip particularly to those new to the
    blogosphere. Simple but very precise information… Many thanks for
    sharing this one. A must read article!

  38. Really no matter if someone doesn’t be aware of after that its up to other viewers that
    they will assist, so here it takes place.

  1. [...] TYPO3Development » Blog Archive » How to Effectively use the Repository and Query Object of Extbas… Extbase[] supports very well the idea of Domain-Driven Design[]. [...]