Merged in feature_users (pull request #7)
Torrent search results list ordering.
This commit is contained in:
commit
47175f489b
|
@ -38,4 +38,9 @@ services:
|
|||
class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
|
||||
public: false
|
||||
tags:
|
||||
- { name: serializer.normalizer, priority: 1 }
|
||||
- { name: serializer.normalizer, priority: 1 }
|
||||
|
||||
# Torrent searcher
|
||||
App\Search\TorrentSearcher:
|
||||
arguments:
|
||||
$metadataFactory: '@doctrine.orm.magneticod_entity_manager.metadata_factory'
|
|
@ -4,7 +4,7 @@ namespace App\Api\V1\Controller;
|
|||
|
||||
use App\Api\V1\DTO\ListPage;
|
||||
use App\Magnetico\Entity\Torrent;
|
||||
use App\Magnetico\Repository\TorrentRepository;
|
||||
use App\Search\TorrentSearcher;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Symfony\Component\HttpFoundation\{JsonResponse, Request};
|
||||
|
@ -13,12 +13,14 @@ class TorrentController extends AbstractApiController
|
|||
{
|
||||
private const PER_PAGE = 20;
|
||||
|
||||
public function search(Request $request, TorrentRepository $repo): JsonResponse
|
||||
public function search(Request $request, TorrentSearcher $searcher): JsonResponse
|
||||
{
|
||||
$query = $request->query->get('query', '');
|
||||
$page = (int) $request->query->get('page', '1');
|
||||
$orderBy = $request->query->get('order-by');
|
||||
$order = $request->query->get('order', 'asc');
|
||||
|
||||
$pagerAdapter = new DoctrineORMAdapter($repo->createFindLikeQueryBuilder($query));
|
||||
$pagerAdapter = new DoctrineORMAdapter($searcher->createSearchQueryBuilder($query, $orderBy, $order));
|
||||
$pager = new Pagerfanta($pagerAdapter);
|
||||
$pager
|
||||
->setCurrentPage($page)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Controller;
|
||||
|
||||
use App\Magnetico\Entity\Torrent;
|
||||
use App\Magnetico\Repository\TorrentRepository;
|
||||
use App\Search\TorrentSearcher;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
@ -13,12 +13,14 @@ class TorrentController extends Controller
|
|||
{
|
||||
private const PER_PAGE = 20;
|
||||
|
||||
public function searchTorrent(Request $request, TorrentRepository $repo): Response
|
||||
public function searchTorrent(Request $request, TorrentSearcher $searcher): Response
|
||||
{
|
||||
$query = $request->query->get('query', '');
|
||||
$page = (int) $request->query->get('page', '1');
|
||||
$orderBy = $request->query->get('order-by');
|
||||
$order = $request->query->get('order', 'asc');
|
||||
|
||||
$pagerAdapter = new DoctrineORMAdapter($repo->createFindLikeQueryBuilder($query));
|
||||
$pagerAdapter = new DoctrineORMAdapter($searcher->createSearchQueryBuilder($query, $orderBy, $order));
|
||||
$pager = new Pagerfanta($pagerAdapter);
|
||||
$pager
|
||||
->setCurrentPage($page)
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
namespace App\Magnetico\Repository;
|
||||
|
||||
use App\Magnetico\Entity\Torrent;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\RegistryInterface;
|
||||
|
||||
class TorrentRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(RegistryInterface $registry)
|
||||
{
|
||||
parent::__construct($registry, \App\Magnetico\Entity\Torrent::class);
|
||||
parent::__construct($registry, Torrent::class);
|
||||
}
|
||||
|
||||
public function getTorrentsTotalCount(): int
|
||||
|
@ -25,25 +25,4 @@ class TorrentRepository extends ServiceEntityRepository
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function createFindLikeQueryBuilder(string $query): QueryBuilder
|
||||
{
|
||||
$qb = $this->createQueryBuilder('t');
|
||||
|
||||
$where = $qb->expr()->andX();
|
||||
|
||||
$query = trim($query);
|
||||
$query = preg_replace('/\s+/', ' ', $query);
|
||||
|
||||
$parts = explode(' ', $query);
|
||||
|
||||
foreach ($parts as $idx => $part) {
|
||||
$where->add($qb->expr()->like('LOWER(t.name)', ':part_'.$idx));
|
||||
$qb->setParameter('part_'.$idx, '%'.strtolower($part).'%');
|
||||
}
|
||||
|
||||
$qb->where($where);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
80
src/Search/TorrentSearcher.php
Normal file
80
src/Search/TorrentSearcher.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace App\Search;
|
||||
|
||||
use App\Magnetico\Entity\Torrent;
|
||||
use App\Magnetico\Repository\TorrentRepository;
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
class TorrentSearcher
|
||||
{
|
||||
private const ORDER_DISABLED_FIELDS = ['infoHash'];
|
||||
|
||||
/** @var TorrentRepository */
|
||||
private $torrentRepo;
|
||||
|
||||
/** @var ClassMetadataFactory */
|
||||
private $metadataFactory;
|
||||
|
||||
public function __construct(TorrentRepository $torrentRepo, ClassMetadataFactory $metadataFactory)
|
||||
{
|
||||
$this->torrentRepo = $torrentRepo;
|
||||
$this->metadataFactory = $metadataFactory;
|
||||
}
|
||||
|
||||
public function createSearchQueryBuilder(string $query, string $orderBy = null, string $order = 'asc'): QueryBuilder
|
||||
{
|
||||
$qb = $this->createFindLikeSplitPartsQueryBuilder($query);
|
||||
|
||||
if ($orderBy) {
|
||||
$this->addOrder($qb, $orderBy, $order);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
private function createFindLikeSplitPartsQueryBuilder(string $query): QueryBuilder
|
||||
{
|
||||
$qb = $this->torrentRepo->createQueryBuilder('t');
|
||||
|
||||
$where = $qb->expr()->andX();
|
||||
|
||||
foreach ($this->splitQueryToParts($query) as $idx => $part) {
|
||||
$where->add($qb->expr()->like('LOWER(t.name)', ':part_'.$idx));
|
||||
$qb->setParameter('part_'.$idx, '%'.strtolower($part).'%');
|
||||
}
|
||||
|
||||
$qb->where($where);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
private function addOrder(QueryBuilder $qb, string $orderBy, string $order): void
|
||||
{
|
||||
if (!\in_array(strtolower($order), ['asc', 'desc'])) {
|
||||
throw new \InvalidArgumentException('Invalid sort order');
|
||||
}
|
||||
|
||||
if ($this->canOrderBy($orderBy)) {
|
||||
$qb->orderBy('t.'.$orderBy, $order);
|
||||
}
|
||||
}
|
||||
|
||||
private function canOrderBy(string $orderBy): bool
|
||||
{
|
||||
return (
|
||||
!\in_array($orderBy, self::ORDER_DISABLED_FIELDS, true)
|
||||
&& $this->metadataFactory->getMetadataFor(Torrent::class)->hasField($orderBy)
|
||||
);
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function splitQueryToParts(string $query): array
|
||||
{
|
||||
$query = trim($query);
|
||||
$query = preg_replace('/\s+/', ' ', $query);
|
||||
|
||||
return explode(' ', $query);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue