0

I am using TYPO3 8. In my extension I have a database table "company" in which I store for each company the total number of places (number_places) and the number of occupied places (occupied_places). Now I want to limit the search to companies which have available places left. In MySQL it would be like this:

SELECT * FROM company WHERE number_places > occupied_places;

How can I create this query in the extbase repository?

I tried to introduce the virtual property placesLeft in my model but it did not work.

I don't want to use a raw SQL statement as mentioned below, because I already have implemented a filter which uses plenty of different constraints.

Extbase query to compare two fields in same table

Peter Kraume
  • 3,577
  • 2
  • 21
  • 39
Ralf
  • 65
  • 10

3 Answers3

2

You can do it like this in your repository class, please note the comments inside the code:

class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
    public function findWithAvailablePlaces(bool $returnRawQueryResult = false)
    {
        // Create a QueryBuilder instance
        $queryBuilder = $this->objectManager->get(\TYPO3\CMS\Core\Database\ConnectionPool::class)
            ->getConnectionForTable('company')->createQueryBuilder();

        // Create the query
        $queryBuilder
            ->select('*')
            ->from('company')
            ->where(
                // Note: this string concatenation is needed, because TYPO3's
                // QueryBuilder always escapes the value in the ExpressionBuilder's
                // methods (eq(), lt(), gt(), ...) and thus render it impossible to
                // compare against an identifier.
                $queryBuilder->quoteIdentifier('number_places')
                . \TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder::GT
                . $queryBuilder->quoteIdentifier('occupied_places')
            );

        // Execute the query
        $result = $queryBuilder->execute()->fetchAll();

        // Note: this switch is not needed in fact. I just put it here, if you
        // like to get the Company model objects instead of an array.
        if ($returnRawQueryResult) {
            $dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
            return $dataMapper->map($this->objectType, $result);
        }
        return $result;
    }
}

Notes:

  • If you have lots of records to deal with, I would - for performance reasons - not use the data mapping feature and work with arrays.

  • If you want to use the fluid pagination widget, be sure you don't and build your own pagination. Because of the way this works (extbase-internally), you'd get a huge system load overhead when the table grows. Better add the support for limited db queries to the repository method, for example:

class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
    public function findWithAvailablePlaces(
        int $limit = 10,
        int $offset = 0,
        bool $returnRawQueryResult = false
    ) {
        // ...
        $queryBuilder
            ->setMaxResults($limit)
            ->setFirstResult($offset);
        $result = $queryBuilder->execute()->fetchAll();
        // ...
    }
}
1

I think you cant do this using the default Extbase Query methods like equals() and so on. You may use the function $query->statement() for your specific queries like this. You also can use the QueryBuilder since TYPO3 8 which has functions to compare fields to each other: https://docs.typo3.org/typo3cms/CoreApiReference/latest/ApiOverview/Database/QueryBuilder/Index.html#quoteidentifier-and-quoteidentifiers It's fine to use this QueryBuilder inside Extbase repositories. After this you can use the DataMapper to map the query results to Extbase models.

In case of using "statement()" be aware of escaping every value which may cause any kind of SQL injections.

Paul Beck
  • 2,675
  • 1
  • 12
  • 14
0

Based on the current architecture of TYPO3, the data structure is such that comparing of two tables or, mixing results from two tables ought to be done from within the controller, by injecting the two repositories. Optionally, you can construct a Domain Service that can work on the data from the two repositories from within the action itself, in the case of a routine. The service will also have to be injected.

Note: If you have a foreign relation defined in your table configuration, the results of that foreign relation will show in your defined table repository. So, there's that too.

John Miller
  • 527
  • 4
  • 15