From d6d7351259e104ab10b5dda3483c281830ce4ff3 Mon Sep 17 00:00:00 2001 From: polo Date: Fri, 3 Oct 2025 02:28:22 +0200 Subject: pagination partie 1, ordre des articles avec ORDER BY, optimisations dans ViewController --- src/controller/Director.php | 79 +++++++++++++++++++++++++++++++++++---- src/controller/ViewController.php | 31 ++++++++------- src/model/entities/Node.php | 6 +-- src/model/entities/NodeData.php | 8 ++++ 4 files changed, 98 insertions(+), 26 deletions(-) diff --git a/src/controller/Director.php b/src/controller/Director.php index 8be9b59..4b9293c 100644 --- a/src/controller/Director.php +++ b/src/controller/Director.php @@ -6,6 +6,8 @@ declare(strict_types=1); use Doctrine\ORM\EntityManager; use App\Entity\Page; use App\Entity\Node; +//use Doctrine\ORM\QueryBuilder; +use Symfony\Component\HttpFoundation\Request; class Director { @@ -37,30 +39,91 @@ class Director } // affichage d'une page ordinaire - public function makeRootNode(string $id = ''): void + public function getWholePageData(Request $request): void { - // on récupère toutes les entrées - $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page OR n.page IS null'; - if($id == '') + $id = CURRENT_PAGE === 'article' ? htmlspecialchars($request->query->get('id')) : ''; + + if($id === '') // page "normale" { + // tous les noeuds sauf les articles, tri par page + $dql = "SELECT n FROM App\Entity\Node n WHERE n.name_node != 'new' AND n.name_node != 'post' AND (n.page = :page OR n.page IS null)"; $bulk_data = $this->entityManager ->createQuery($dql) ->setParameter('page', $this->page) ->getResult(); + + // groupes d'articles triés par bloc, permet de paginer par bloc + foreach($bulk_data as $parent_block){ + if(Blocks::hasPresentation($parent_block->getName())){ // = post_block ou news_block + $qb = $this->entityManager->createQueryBuilder(); + $qb->select('n') + ->from('App\Entity\Node', 'n') + ->where('n.parent = :parent') + ->setParameter('parent', $parent_block); + + if($parent_block->getName() === 'post_block'){ + $qb->orderBy('n.position'); + } + elseif($parent_block->getName() === 'news_block'){ + $qb->join('n.article', 'a'); + if($parent_block->getNodeData()->getChronoOrder() ?? false){ // ordre antichrono par défaut + $qb->orderBy('a.date_time', 'ASC'); + } + else{ + $qb->orderBy('a.date_time', 'DESC'); + } + } + + // pagination + $limit = $parent_block->getNodeData()->getPaginationLimit() ?? 0; // 0 par défaut = pas de pagination, sinon 12 rend bien avec des grilles de 2, 3 ou 4 colonnes + if($limit > 0){ + //$this->paginateWithCursor($qb, $request->query->get('last_position') ?? 0, $limit); + $qb->andWhere('n.position > :last_position') + ->setParameter('last_position', $request->query->get('last_position') ?? 0) + ->setMaxResults($limit); + + $nb_pages = $this->getNumberOfPages($parent_block, $limit); // nombres de "pages" d'articles + if($nb_pages > 1){ + //$parent_block->setNumberOfPages($nb_pages); // => navigation en HTML + } + } + + $bulk_data = array_merge($bulk_data, $qb->getQuery()->getResult()); + } + } } - else // avec $_GET['id'] dans l'URL + else // page "article" { - $dql .= ' OR n.id_node = :id'; + $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page OR n.page IS null OR n.id_node = :id'; $bulk_data = $this->entityManager ->createQuery($dql) ->setParameter('page', $this->page) ->setParameter('id', $id) ->getResult(); } - $this->feedRootNodeObjects($bulk_data); + $this->makeNodeTree($bulk_data); + } + + /*private function paginateWithCursor(QueryBuilder $qb, int $last_position = 0, int $limit = 0): void + { + $qb->andWhere('n.position > :last_position') + ->setParameter('last_position', $last_position) + ->setMaxResults($limit); + }*/ + + // requête à part n'alimentant pas $bulk_data + // fonctionnalité offerte par le Paginator de doctrine si on décidait de s'en servir + private function getNumberOfPages(Node $parent_block, int $limit): int + { + $dql = 'SELECT COUNT(n.id_node) FROM App\Entity\Node n WHERE n.parent = :parent'; + $nb_articles = $this->entityManager + ->createQuery($dql) + ->setParameter('parent', $parent_block) + ->getSingleScalarResult(); + return (int)ceil($nb_articles / $limit); // que PHP fasse une division non euclidienne (pas comme en C) nous arrange ici } - private function feedRootNodeObjects(array $bulk_data): void // $bulk_data = tableau de Node + private function makeNodeTree(array $bulk_data): void // $bulk_data = tableau de Node { // puis on les range // (attention, risque de disfonctionnement si les noeuds de 1er niveau ne sont pas récupérés en 1er dans la BDD) diff --git a/src/controller/ViewController.php b/src/controller/ViewController.php index 9117f0f..9818c6e 100644 --- a/src/controller/ViewController.php +++ b/src/controller/ViewController.php @@ -18,22 +18,15 @@ class ViewController extends AbstractBuilder // ViewController est aussi le prem public function buildView(EntityManager $entityManager, Request $request): Response { - /* 1/ accès au modèle */ - $director = new Director($entityManager, true); - $director->makeRootNode(htmlspecialchars($request->query->get('id') ?? '')); - self::$root_node = $director->getNode(); - - - /* 2/ traitement de quelques paramètres */ + /* 1/ 1er contrôle des paramètres */ - // mode modification d'une page activé + // mode modification d'une page if($_SESSION['admin'] && $request->query->has('mode') && $request->query->get('mode') === 'page_modif' && !in_array(CURRENT_PAGE, ['article', 'nouvelle_page', 'menu_chemins', 'user_edit', 'connection'])) { MainBuilder::$modif_mode = true; } - // page article: mode création et erreurs d'id if(CURRENT_PAGE === 'article'){ if($_SESSION['admin']){ @@ -46,18 +39,30 @@ class ViewController extends AbstractBuilder // ViewController est aussi le prem if($request->query->get('id')[0] === 'n' && $request->query->has('from') && !empty($request->query->get('from'))){ NewBuilder::$new_article_mode = true; } - elseif(self::$root_node->getNodeByName('main')->getAdoptedChild() === null){ // id inconnu - return new Response($this->html, 302); - } } } elseif($request->query->get('id')[0] === 'n'){ // accès page nouvelle article interdit sans être admin return new Response($this->html, 302); } } + //else // l'id dans l'adresse n'a pas d'effet sur la suite + + + /* 2/ accès au modèle */ + $director = new Director($entityManager, true); + $director->getWholePageData($request); + self::$root_node = $director->getNode(); + + + /* 3/ 2ème contrôle utilisant les données récupérées */ + + // article non trouvé en BDD + if(CURRENT_PAGE === 'article' && !$_SESSION['admin'] && self::$root_node->getNodeByName('main')->getAdoptedChild() === null){ + return new Response($this->html, 302); + } - /* 3/ construction de la page avec builders et vues */ + /* 4/ construction de la page avec builders et vues */ $this->useChildrenBuilder(self::$root_node); return new Response($this->html, 200); diff --git a/src/model/entities/Node.php b/src/model/entities/Node.php index f7af6de..ec5f081 100644 --- a/src/model/entities/Node.php +++ b/src/model/entities/Node.php @@ -172,11 +172,7 @@ class Node { $this->children[] = $child; - // cas particulier des news: utilise les dates au lieu des positions (les positions existent mais sont ignorées) - if($this->getName() === 'news_block'){ - $this->sortNews($this->getNodeData()->getChronoOrder() ?? false); // faux = ordre chronologique - } - else{ + if(!\Blocks::hasPresentation($this->getName())){ // post_block et news_block ont leurs enfants ordonnés avec ORDER BY $this->sortChildren(false); } } diff --git a/src/model/entities/NodeData.php b/src/model/entities/NodeData.php index 0d42f3a..1d7db4c 100644 --- a/src/model/entities/NodeData.php +++ b/src/model/entities/NodeData.php @@ -36,6 +36,9 @@ class NodeData #[ORM\Column(type: "integer", nullable: true)] private ?int $grid_cols_min_width = null; // pour le mode grille + #[ORM\Column(type: "integer", nullable: true)] + private ?int $pagination_limit = null; // pour les post_block et news_block + // liaison avec table intermédiaire #[ORM\ManyToMany(targetEntity: Image::class, inversedBy: "node_data")] #[ORM\JoinTable( @@ -105,6 +108,11 @@ class NodeData { $this->chrono_order = $reverse_order; } + + public function getPaginationLimit(): ?int + { + return $this->pagination_limit ?? null; + } /*public function setNode(Node $node): void { -- cgit v1.2.3