remind/extbase-searchfilter

3.0.0 2024-10-02 11:14 UTC

This package is auto-updated.

Last update: 2024-11-07 12:00:44 UTC


README

An effort was made to provide a generalized approach to providing search filters.

travis-img codecov-img styleci-img php-v-img github-issues-img contrib-welcome-img license-img

How to use

Start with a n-dimensional array filter representation.

<?php
/* filter.php */

use Remind\SearchImpl\SearchFilter\MyFilterSearchFilterStrategy;

/*
 * The filter is a simple n-dimensional array.
 * The first dimension contains the filter group names.
 * The second dimension contains the field names with their respective values.
 */
$filter = [
    /* The filter group name that is mapped to the strategy class name */
    'myFilter' => [
        /* A record type with a given value */
        'someRecord' => 123
    ],
    'someOtherFilter' => [
        'anotherThing' => [ 'foo', 'bar', 'baz' ]
    ]
];

$strategy = new MyFilterSearchFilterStrategy();
$strategy->setObjectManager($objectManager);
$strategy->setFilter($filter);
$strategy->setQuery();

/*
 * This will return a TYPO3\CMS\Extbase\Persistence\QueryInterface instance
 * that can be used like any other TYPO3 query object or NULL when an error
 * occured.
 */
$query = $strategy->parse();

$result = $query === null ? null : $query->execute();

Create implementing class.

<?php
/* MyFilterSearchFilterStrategy.php */

declare(strict_types=1);

namespace Remind\SearchImpl\SearchFilter;

use Remind\RmndSearchFilter\AbstractSearchFilterStrategy;
use Remind\SearchImpl\Domain\Repository\SomeRepository;

/**
 * Implementation of a search filter strategy for contact searches.
 */
class MyFilterSearchFilterStrategy extends AbstractSearchFilterStrategy
{
    /**
     * Namespace used for building the full field strategy class namespaces.
     * @var string
     */
    public const STRATEGY_NS = 'Remind\SearchImpl\SearchFilter\FieldStrategy\\';

    /**
     *
     */
    public function __construct()
    {
        parent::__construct();

        /* Use this namespace to look for field strategy classes */
        $this->searchFieldNamespace = self::STRATEGY_NS;
    }

    /**
     * This methods implementation should apply group based processing,
     * or if no special group handling is necessary, it should call
     * processFieldGroup() and return the result.
     *
     * @return void
     */
    protected function applyGroupRules(): void
    {
        $constraint = $this->processFieldGroup();

        if ($constraint !== null) {
            $this->constraints[] = $constraint;
        }
    }

    /**
     * Implement this method and create a query instance that will be used
     * by this instance.
     *
     * #TODO The methods visibility is likely an artifact from an earlier
     * implementation and is bound to change in a later version.
     *
     * @return void
     */
    public function setQuery(): void
    {
        /* @var $repository SomeRepository */
        $repository = $this->objectManager->get(SomeRepository::class);

        $this->query = $repository->createQuery();
    }
}

Create class to process filter values.

<?php
/* SomeRecordFieldStrategy.php */

declare(strict_types=1);

namespace Remind\SearchImpl\SearchFilter\FieldStrategy\MyFilter;

use Remind\RmndSearchFilter\FieldStrategy\AbstractFieldStrategy;
use Remind\SearchImpl\Domain\Model\SomeOtherModel;
use Remind\SearchImpl\Domain\Repository\SomeOtherRepository;
use TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;

/**
 * This class will handle the 'someRecord' filter part.
 *
 * $filter = [
 *     'myFilter' => [
 *         'someRecord' => 123
 *      ]
 * ]
 *
 * It will be used to get the records for the SomeRepository, from which the
 * query originates, by using the uid of another Record from SomeOtherRepository
 * that has some relation to the records of SomeRepository.
 *
 */
class SomeRecordFieldStrategy extends AbstractFieldStrategy
{
    /**
     * The $this->filterValue should be set to the value from the filter
     * object, which is '123' in this case.
     *
     * @return ConstraintInterface|null
     */
    public function process(): ?ConstraintInterface
    {
        /* If no numeric value was given */
        if (!is_numeric($this->filterValue)) {
            return null; // do not process any further
        }

        /* @var $repository SomeOtherRepository */
        $repository = $this->objectManager->get(SomeOtherRepository::class);

        /* Get all records matching the '123' filter value in whatever field */
        $records = $repository->findBySomeField($this->filterValue);

        /* Abort processing */
        if ($records->count() === 0) {
            return null;
        }

        $constraints = [];

        foreach ($records as $record) {
            /* @var $record SomeOtherModel /*

            /* @var $objects ObjectStorage */
            $objects = $record->getRelatedObjects();

            /* Abort processing */
            if ($objects->count() === 0) {
                return null;
            }

            $uids = [];

            /* Get uid values from objects */
            foreach ($objects as $object) {
                $uids[] = $object->getUid();
            }

            /*
             * Match query uid from SomeRepository with values returned
             * from SomeOtherRepository
             */
            return $this->query->in('uid', $uids);
        }

        /* Default return */
        return null;
    }
}