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
|
class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer
|
||||||
public: false
|
public: false
|
||||||
tags:
|
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\Api\V1\DTO\ListPage;
|
||||||
use App\Magnetico\Entity\Torrent;
|
use App\Magnetico\Entity\Torrent;
|
||||||
use App\Magnetico\Repository\TorrentRepository;
|
use App\Search\TorrentSearcher;
|
||||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||||
use Pagerfanta\Pagerfanta;
|
use Pagerfanta\Pagerfanta;
|
||||||
use Symfony\Component\HttpFoundation\{JsonResponse, Request};
|
use Symfony\Component\HttpFoundation\{JsonResponse, Request};
|
||||||
|
@ -13,12 +13,14 @@ class TorrentController extends AbstractApiController
|
||||||
{
|
{
|
||||||
private const PER_PAGE = 20;
|
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', '');
|
$query = $request->query->get('query', '');
|
||||||
$page = (int) $request->query->get('page', '1');
|
$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 = new Pagerfanta($pagerAdapter);
|
||||||
$pager
|
$pager
|
||||||
->setCurrentPage($page)
|
->setCurrentPage($page)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use App\Magnetico\Entity\Torrent;
|
use App\Magnetico\Entity\Torrent;
|
||||||
use App\Magnetico\Repository\TorrentRepository;
|
use App\Search\TorrentSearcher;
|
||||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||||
use Pagerfanta\Pagerfanta;
|
use Pagerfanta\Pagerfanta;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
|
@ -13,12 +13,14 @@ class TorrentController extends Controller
|
||||||
{
|
{
|
||||||
private const PER_PAGE = 20;
|
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', '');
|
$query = $request->query->get('query', '');
|
||||||
$page = (int) $request->query->get('page', '1');
|
$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 = new Pagerfanta($pagerAdapter);
|
||||||
$pager
|
$pager
|
||||||
->setCurrentPage($page)
|
->setCurrentPage($page)
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
namespace App\Magnetico\Repository;
|
namespace App\Magnetico\Repository;
|
||||||
|
|
||||||
|
use App\Magnetico\Entity\Torrent;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
|
||||||
use Symfony\Bridge\Doctrine\RegistryInterface;
|
use Symfony\Bridge\Doctrine\RegistryInterface;
|
||||||
|
|
||||||
class TorrentRepository extends ServiceEntityRepository
|
class TorrentRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
public function __construct(RegistryInterface $registry)
|
public function __construct(RegistryInterface $registry)
|
||||||
{
|
{
|
||||||
parent::__construct($registry, \App\Magnetico\Entity\Torrent::class);
|
parent::__construct($registry, Torrent::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTorrentsTotalCount(): int
|
public function getTorrentsTotalCount(): int
|
||||||
|
@ -25,25 +25,4 @@ class TorrentRepository extends ServiceEntityRepository
|
||||||
return 0;
|
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