Creating the Repositories

We have already introduced the Repositories in Chapter 3. They serve with capabilities to save and reaccess our objects. We set up such a Repository object for every Aggregate-Root object which are, then again, used for accessing all the Aggregate-Root’s corresponding objects. In our concrete example \MyVendor\SjrOffers\Domain\Model\Organization is such an Aggregate-Root object. The Repository’s class name is derived from the class name of the Aggregate-Root object concatenated with the suffix Repository. The Repository needs to extend the class \TYPO3\CMS\Extbase\Persistence\Repository. The class file \MyVendor\ SjrOffers\Domain\Repository\OrganizationRepository will be saved in the directory EXT:sjr_ offers/Classes/Domain/Repository/. Thus the directory Repository is on the same hierarchy-level as the directory Model. In our case, the class body remains empty because all the important functionalities are already generically implemented in the super-class \TYPO3\CMS\Extbase\Persistence\Repository.

<?php

namespace MyVendor\SjrOffers\Domain\Repository;

use TYPO3\CMS\Extbase\Persistence\Repository;

class OrganizationRepository extends Repository
{

}

We create a \MyVendor\SjrOffers\Domain\Repository\OfferRepository exactly the same way but we will later extend it with own methods for accessing offers. It’s very likely that we have to access the other objects for categories, regions and update data of contact information of certain persons independent of the offers or their organizations. Thus we define some additional Repositories for those objects for easier access from the Frontend.

Note

You have to resist the urge to define Repositories for each object and limit yourself to a minimal number of Repositories. Instead, you should define the access methods within the Aggregate-Root objects as find methods.

\TYPO3\CMS\Extbase\Persistence\Repository serves with the following methods which are of course accessible and overridable in the extending child derivations:

add($object)

Adds an object to the Repository which is then persistent in the sense of Domain-Driven Design. But be careful, it will not written (and enhanced with an UID) to the database before finishing the loop through the Extension, to be precise after the call of the method persistAll() of the PersistenceManager.

remove($object) and removeAll()

The opponent of add(). An object will be removed from the Repository and is gonna be deleted from the database after finishing the Extension’s loop. The method removeAll() empties the whole Repository.

update($modifiedObject)

An existing object in the Repository will be updated with the properties of the given object. Extbase finds the to-be-updated object by the uid of the given object and throws an exception if it does not exist.

findAll() and countAll()

Returns all the Repository’s objects that are currently persisted in the database. However, this slightly confusing behaviour is intended. Whereas findAll() returns an Array of objects the method countAll() only counts the currently persisted objects (if the database backend is of type SQL it just executes the query SELECT COUNT) and returns an Integer number.

findByProperty($value), findOneByProperty($value) and countByProperty($value)

Those three methods help by finding one or several objects and by counting all the objects that correspond to the given value. The substring Property must be replaced by the uppercase-written property name of the class that is managed by the Repository. The methods then only return the objects as well count the objects whose properties Property correspond to the given value. Whereas the method findByProperty() returns an Array of all the matching objects, the method findOneByProperty() only returns the first object that was found. That is, assuming that no certain sorting order was given, the order in which the objects were created in the Backend. Last but not least, the method countByProperty() returns the count of the objects that would be returned if findByProperty() was given the same value and is, of course, an Integer number.

createQuery()

In opposite to the methods above, this function does not manage objects in the Repository. Instead, it returns a Query object which can be helpful to assemble own queries to the Storage-Backend. The details for this procedure will be given in the following chapter.

Before accessing the defined objects on the Repository you need to tell Extbase on which pages on TYPO3’s page tree (see below for TYPO3’s concept of the page tree) it should seek and file the objects. Without any further definitions Extbase will use the page tree’s root (the globe).

Generally there are three cases which need to be distinguished: Persisting a newly created object, reaccessing an existing object and updating the properties of an existing object. When creating a new object Extbase determines the destination pages in the following rule hierarchy:

  • If, as already described in Chapter 4, the option source is checked then the objects will be searched in the corresponding pages
  • If the TypoScript-Setup of the page contains the definition of plugin.tx_*extensionname*.persistence.storagePid with a comma-separated list of PIDs then those pages will be consulted.
  • If the TypoScript-Setup of the page contains the definition of config.tx_extbase.persistence.storagePid with a comma-separated list of PIDs then those pages will be consulted.
  • If none of the cases from above applies, then the root page will be consulted for the objects.

When insertion of new Domain objects happens, then the procedure will be as follows:

  • If there’s a TypoScript setup at plugin.tx_extensionname.persistence.classes.*FullClassName*.newRecordStoragePid with a single page value, then this is gonna be used.
  • If there’s a TypoScript setup at config. tx_extbase.persistence.classes.*FullClassName*.newRecordStoragePid with a single page value, the this is gonna be used.
  • If none of the cases above apply, then the object will be inserted at the first item in the list of search pages. So to say, in the end the root page (the one with the globe) is gonna be used for insertion.

When updating the Domain objects their PID is not changed. However, you can implement the property pid in your domain object with its corresponding set- and get-methods. Then a domain object may be moved from one page to another by setting a new pid.

Note

Most occurring mistake for seemingly empty Repositories is a mis-configured Storage-PID. Thus, you should firstly evaluate the Template Module whether it is set correctly.

Besides of the options for setting the Page UID there exist two other possibilities for configuring the Persistence Layer: enableAutomaticCacheClearing and updateReferenceIndex. The option config.tx_extbase.persistence.enableAutomaticCacheClearing = 1 within the TypoScript setup leads to a deletion of the cache whenever the data is rewritten. This option is normally activated.

Note

Usually, data sets will be saved into Folders in the Page Tree though the pages using those data sets will be somewhere else. If their cache should be cleared as well then you should set up their PIDs in the field TSConfig of the page’s preferences of the directory. For example, out Offers will be shown on the pages with the PIDs 23 and 26 (let’s say for a Single and a List View). Then we will configure the variable TCEMAIN.clearCacheCmd = 23,26 in the page preferences of the SysFolder. Then the cache of these pages will be cleared as well and changes of an offer will show up immediately.

Internally, TYPO3 manages an index of all relationships between two data sets the so-called RefIndex. Due to this index it’s possible to show the number of associated data sets in the list module’s column [Ref.]. By clicking on the number you get further information about the incoming and outgoing references of the dataset. This index is automatically updated when any data sets get edited. The configuration config.tx_extbase.persistence.updateReferenceIndex = 1 effects an update when data sets get edited in the Frontend though it is normally deactivated due to its huge effects on performance.

Before calling a Repository’s methods they need to be instantiated at first with use of the ObjectManager or via dependency injection.

Dependency injection example from chapter 2
 /**
  * @var ProductRepository
  */
 private $productRepository;

 /**
  * Inject the product repository
  *
  * @param \MyVendor\StoreInventory\Domain\Repository\ProductRepository $productRepository
  */
 public function injectProductRepository(ProductRepository $productRepository)
 {
     $this->productRepository = $productRepository;
 }

Warning

Repositories are Singletons therefore there may only exist one instance of each class at one time of script-execution. If a new instance is requested, the system will prove whether an instance of the requested object exists and will instead of creating a new object return the existing one. This is ensured by using the dependency injection. Thus, never ever use the PHP syntax keyword new for creating a repository object because the objects that are placed there will not be automatically persisted.

Now you know all the basic tools for durable persistation and recovering of your objects. Extbase offers a lot more sophisticated functionalities for special needs because it happens quite frequently that the standard methods of saving and seeking data in a repository are not sufficient for the individual case. Thus Extbase let’s you define individual requests without losing the existing abstractions of the existing persistence backend. Additionally, Extbase let’s you use “foreign” data sources which are most often data tables of the same database.