Repository

A basic repository can be quite a short class. The shortest possible repository is an empty class inheriting from TYPO3\CMS\Extbase\Persistence\Repository:

EXT:tea/Classes/Domain/Repository/Product/TeaRepository.php
<?php

declare(strict_types=1);

namespace TTN\Tea\Domain\Repository\Product;

use TYPO3\CMS\Extbase\Persistence\Repository;

class TeaRepository extends Repository
{
}
Copied!

The model the repository should deliver is derived from the namespace and name of the repository. A repository with the fully qualified name TTN\Tea\Domain\Repository\Product\TeaRepository therefore delivers models with the fully qualified name TTN\Tea\Domain\Model\Product\Tea without further configuration.

In the EXT:tea extension some additional settings are required. These can be done directly in the Repository or in a trait. It is important to know, that a trait overrides parameters and method from the parent class but can be overridden from the current class.

The TeaRepository configures the $defaultOrderings directly in the repository class and imports additional settings from the trait.

EXT:tea/Classes/Domain/Repository/Product/TeaRepository.php
<?php

declare(strict_types=1);

namespace TTN\Tea\Domain\Repository\Product;

use TTN\Tea\Domain\Model\Product\Tea;
use TTN\Tea\Domain\Repository\Traits\StoragePageAgnosticTrait;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;

/**
 * @extends Repository<Tea>
 */
class TeaRepository extends Repository
{
    use StoragePageAgnosticTrait;

    protected $defaultOrderings = ['title' => QueryInterface::ORDER_ASCENDING];

    /**
     * @param positive-int $ownerUid
     */
    public function findByOwnerUid(int $ownerUid): QueryResultInterface
    {
        $query = $this->createQuery();
        $query->matching($query->equals('ownerUid', $ownerUid));

        return $query->execute();
    }
}
Copied!

We override the protected parameter $defaultOrderings here. This parameter is also defined in the parent class TYPO3\CMS\Extbase\Persistence\Repository and used here when querying the database.

The trait itself is also defined in the extension:

EXT:tea/Classes/Domain/Repository/Traits/StoragePageAgnosticTrait.php
<?php

declare(strict_types=1);

namespace TTN\Tea\Domain\Repository\Traits;

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;

/**
 * This trait for repositories makes the repository ignore the storage page setting when fetching models.
 */
trait StoragePageAgnosticTrait
{
    public function initializeObject(): void
    {
        $querySettings = GeneralUtility::makeInstance(Typo3QuerySettings::class);
        $querySettings->setRespectStoragePage(false);
        $this->setDefaultQuerySettings($querySettings);
    }
}
Copied!

Here we inject the $querySettings and allow to fetch tea objects from all pages. We then set these as default query settings.

The advantage of using a trait here instead of defining the parameters and methods directly in the repository is, that the code can be reused without requiring inheritance. Repositories of non-related models should not inherit from each other.

Using the repository

The \TTN\Tea\Domain\Repository\Product\TeaRepository can now be used in a controller or another class.

Require it via Dependency Injection:

Class TTN\Tea\Controller\TeaController
use TTN\Tea\Domain\Repository\Product\TeaRepository;

class TeaController extends ActionController
{
    private TeaRepository $teaRepository;

    public function __construct(TeaRepository $teaRepository)
    {
        $this->teaRepository = $teaRepository;
    }
}
Copied!

Then it can be used:

Class TTN\Tea\Controller\TeaController
use Psr\Http\Message\ResponseInterface;
use TTN\Tea\Domain\Repository\Product\TeaRepository;

class TeaController extends ActionController
{
    private TeaRepository $teaRepository;

    public function indexAction(): ResponseInterface
    {
        $this->view->assign('teas', $this->teaRepository->findAll());
        return $this->htmlResponse();
    }
}
Copied!

The method $this->teaRepository->findAll() that is called here is defined in the parent class Repository.

You can also add additional methods here to query the database. See chapter "Repository" in the Extbase reference. As this example is very basic we do not need custom find methods.