Extrendre els repositoris. Paginació
Taula de continguts
Introducció
Com ja has vist, l’objecte Repository
permet executar consultes senzilles sense pràcticament cap treball:
// from inside a controller
$repository = $this->getDoctrine()->getRepository(Link::class);
$links = $repository->findAll();
Els mètodes diponibles són els següents:
/**
* @method Link|null find($id, $lockMode = null, $lockVersion = null)
* @method Link|null findOneBy(array $criteria, array $orderBy = null)
* @method Link[] findAll()
* @method Link[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
Però, i si necessitem una consulta més complexa? Quan generem una entitat amb make:entity
, el comandament també genera una classe anomenada ProductRepository.
// src/Repository/LinkRepository.php
namespace App\Repository;
use App\Entity\Link;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
class LinkRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Product::class);
}
}
Quan obtens el repositori (p.e. ->getRepository(Link::class)
), estàs obtenint realment una instància d’aquest objecte!
Anotacions
Cal assegurar-se que l’entitat té correctament vinculat el repositori mitjançant una anotació. En el nostre exemple l’entitat Link haurà d’incloure:
@ORM\Entity(repositoryClass="App\Repository\LinkRepository")
QueryBuilder
Suposem que vols fer una consulta que obtinga tots els objectes Link
amb data posterior a certa data. Hauries d’afegir un nou mètode dins del repositori:
// src/Repository/LinkRepository.php
// ...
class LinkRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Product::class);
}
/**
* @return Link[]
*/
public function findAllAfterDate(DateTime $date): array{
$query = $this->createQueryBuilder('l')
->andwhere("l.createdAt >= :date")
->orderBy('l.createdAt', 'DESC')
->setParameter('date', $date)
->getQuery();
return $query->getResult();
}
Query Builder, és una forma orientada a objectes d’escriure consultes. Es recomana utilitzar-la quan les consultes es creen dinàmicament.
Ara, pots cridar el mètode del repositori:
// from inside a controller
$date = new DateTime("2020-01-01");
$products = $this->getDoctrine()
->getRepository(Link::class)
->findAllAfterDate($date);
// ...
Query
També es possible utilitzar l’objecte Query, així com executar consultes directament amb PDO.
/**
* @return Link[]
*/
public function findAllAfterDate(DateTime $date): array{
$query = $this->createQuery('
SELECT l
FROM App\Entity\Link l
WHERE l.createdAt >= :date
ORDER BY l.createdAt DESC')
->setParameter('date', $date);
return $query->getResult();
}
Consultes SQL
// src/Repository/LinkRepository.php
// ...
public function findAllAfterDate(DateTime $date): array
{
$conn = $this->getEntityManager()->getConnection();
$sql = '
SELECT * FROM link l
WHERE l.created_at > :date
ORDER BY l.created_at DESC
';
$stmt = $conn->prepare($sql);
$stmt->execute(['date' => $date]);
// returns an array of arrays (i.e. a raw data set)
return $stmt->fetchAll();
}
Aquestes consultes SQL tornen arrays, no objectes (a no ser que uses la funcionalitat NativeQuery).
Més informació en Querying for objects the repository
Exemple
En el següent exemple pots observar un ús del nou mètode del repositori. Hem modificat la ruta homepage
perquè comprove si ha rebut pel querystring el paràmetre start-date
si és així executarà el mètode findAllAfterDate()
si no cridarà findAll()
.
/**
* @Route("/", name="homepage")
*/
public function index(Request $request)
{
$repo = $this->getDoctrine()
->getRepository(Link::class);
if ($request->query->has("start-date")) {
$date = new \DateTime($request->query->get("start-date"));
$links = $repo->findAllAfterDate($date);
// TODO: check if the paràmeter start-date is a valid date
}
else {
$links = $repo->findAll();
}
// now pass the array of default object to the view
return $this->render('default/index.html.twig', [
'links' => $links,
]);
}
Implementa el mètode findAllAfterDate
Implementa el mètode findAllAfterDate()
del repositori i modifica la ruta homepage perquè en cas que reba el paràmetre start-date
pel querystring mostre sols aquells enllaços amb data d’actualització posterior a la data indicada.
Paginació
Symfony no inclou un paginador de forma nativa però al incloure Doctrine permet implementar-ho fàcilment sense necessitat d’instal·lar nous components. A continuació presentarem tres possibles opcions d’implementació.
La classe Paginator de Doctrine
En aquest cas crearíem nous mètodes en el repositori. findAllPaginated
crea la consulta i la passa al mètode paginate
que serà el que farà la paginació.
El mètode paginate
ens tornarà un objecte Paginator que conté en la propietat Paginator::results els resultats.
Ens faltaria obtenir el total de registres la qual cosa és ben senzilla ja que la classe Paginator
implementa la interfície Countable
i simplement usant el mètode count()
obtindrem el total de registres.
use Doctrine\ORM\Tools\Pagination\Paginator;
...
public function findAllPaginated($currentPage = 1):?Paginator
{
$query = $this->createQueryBuilder('l')
->orderBy('l.createdAt', 'DESC')
->getQuery();
// No need to manually get get the result ($query->getResult())
$paginator = $this->paginate($query, $currentPage);
return $paginator;
}
// Paginate results.
public function paginate($dql, $page = 1, $limit = 5):?Paginator
{
$paginator = new Paginator($dql);
$paginator->getQuery()
->setFirstResult($limit * ($page - 1)) // Offset
->setMaxResults($limit); // Limit
return $paginator;
}
Més informació en Pagination i Symfony and Doctrine pagination with Twig
Classe Paginator de symfony/demo
Un altra opció és fer ús de la classe Paginator
que s’utilitza en el projecte d’exemple de Symfony. Empra la classe Paginator
de Doctrine i ens proporciona tots els mètodes necessaris per a fer la paginació.
- Classe Paginator.
- Repositori PostRepositori:findLatest.
- Mètode BlogController::index().
- Plantilla de Twig blog/index.html.twig.
knplabs/knp-paginator-bundle
Paginator per a Symfony automatitza la paginació i simplifica l’ordenació i altres característiques.
En la descripció del paquet knplabs/knp-paginator-bundle obtindreu més informació.
Implementa el paginador
Implementa la paginació de links en la pàgina principal de forma que mostre 10 enllaços ordenats des del més actual al més antic. Pots emprar qualsevol dels mètodes que hem demostrat.