Merged in feature_users (pull request #7)

Torrent search results list ordering.
This commit is contained in:
Alexey Eschenko 2018-06-29 00:30:49 +00:00
commit 47175f489b
5 changed files with 98 additions and 30 deletions

View file

@ -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'

View file

@ -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)

View file

@ -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)

View file

@ -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;
}
} }

View 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);
}
}