Bonjour,

J'aimerai coder un module de commentaires pour un site, et le plus gros du boulot est fait, mais je bute sur plusieurs points :

  • La structure "parents/enfants" n'est pas classique, car on peut commenter sur des tables différentes (les articles, le forum, les commentaires eux mêmes... etc) donc je me retrouve avec un champ parentId et parentType pour pouvoir faire les liaisons, et même si ça fonctionne plutôt bien, le commentsTable est assez horrible au niveau du code (pour lier les tables, pour le counterCache que j'ai du recoder manuellement entre autre)... je suis preneur si vous avez déjà eu ce type de jointure et les difficultés que ça implique.

  • Le plus gros problème, c'est sur ma volonté de faire des commentaires en cascade, comme sur DISQUS. Les users doivent pouvoir répondre à un commentaire, et répondre à la réponse, et répondre à la réponse de la réponse...etc. Je n'ai pas l'impression que le framework soit pensé pour faire ça, non seulement pour récupérer les données dans le controller, mais aussi pour gérer la suppression en cascade (si jamais un comm est supprimé par son auteur, il faut supprimer les réponses et réponses de réponses)

En théorie, je pourrais faire un $this->comments->find()->contain(['Comments.Comments.Comments'])->all();
Quelque chose comme ça, mais je limite la récupération des données à 3 (dans mon exemple), alors que j'aimerai trouver un moyen de créer une requête réellement récursive.

Une idée ?

15 réponses


Bonsoir.

Je n'ai pas l'impression que le framework soit pensé pour faire ça, non seulement pour récupérer les données dans le controller, mais aussi pour gérer la suppression en cascade (si jamais un comm est supprimé par son auteur, il faut supprimer les réponses et réponses de réponses)

Bien sûr que si qu'il est tout à fait capable de faire ceci et même très facilement.Est-ce que tu utilises le Behavior Tree ?

Pour preuve :

Il est courant de vouloir stocker des données hiérarchisées dans une table de base de données. Des exemples de ce type de données pourrait être des catégories sans limite de sous-catégories, les données liées à un système de menu multi-niveau ou une représentation littérale de la hiérarchie

Source : TreeBehavior

Flitflit
Auteur

woaw, merci, et pourtant j'ai parcouru la doc en long en large et en travers...

Si jamais, j'ai créé un plugin qui pourrait te convenir :
https://github.com/Kareylo/CakePHP-Comments

Flitflit
Auteur

ça a l'air parfait mais j'ai ce message d'erreur dans ma console:
Could not find package Kareylo/CakePHP-Comments at any version matching your PHP version 5.5.12.0

pourtant ta doc indique du 5.4 minimum

suis-je bête ?

Je dois penser à changer le README. Les dernières versions de cake demandent la 5.6 minimum. Au pire, inspire toi du code pour faire ton propre module.

Flitflit
Auteur

Oui merci, c'est ce que je suis en train de faire. J'ai épluché ton code c'est tellement bien fait que j'ai foutu en l'air tout ce que j'avais fait pour repartir de zero avec le tiens.
Du coup j'ai une question : le findComments du behavior fonctionne niquel mais il retourne tous les résultats, as tu une idée de comment on peut greffer paginator dessus ? soit pour faire un systeme de chargement en ajax "afficher plus de comm" soit pour l'utiliser sur une page de forum par exemple, avec une limite de 20 réponses principales par page.
Je pense trouver tout seul, mais vu comment tu as l'air de gérer le framework, j'aimerai bien voir ta solution pour ça, ne serait-ce que pour améliorer ton propre plugin.

Je pense que j'utiliserais le finder personnalisé dans les options de paginate.

public $paginate = ['finder' => 'comments' ];

Après, je ne connais pas plus que ça le framework, le plugin vient d'une envie d'apprendre le framework via la création de plugins, ce qui permet de faire profiter la communauté.

Edit : je te redirige vers la doc de pagination : https://book.cakephp.org/3.0/fr/controllers/components/pagination.html

Flitflit
Auteur

ok, j'en profite pour te signaler une erreur (je crois) dans le commentableBehavior, tu as écris une option "dependant" mais c'est "dependent" avec un e.
j'ai aussi remarqué que le countercache fonctionne bien sur l'ajout d'une donnée, mais pas sur sa supression (après j'ai pas copier/coller tout ton code donc ça peut venir de moi)

Hésite pas à mettre une issue sur le github, je pourrai voir ça en rentrant du boulot.
Mais pour le dependent, oui, possible que j'ai fait une petite erreur de frappe.

Flitflit
Auteur

j'essaye de créer d'autres finder dans le behavior, par exemple, récupérer seulement les 5 premiers commentaires, et tous les enfants imbriqués, j'ai une piste qui donne ça :

    public function findFiveComments(Query $query, $options = [])
    {
        $data = $query->contain([
            'Comments' => function (Query $q) use ($options) { 
                return $q->contain(
                    ['Children'=> function (Query $q) use ($options) {
                        return $q->find('threaded')->contain(['Users'=>['fields'=>['id','username']]]);
                    },
                    'Users'=>['fields'=>['id','username']]])
                ->where(['parent_id IS NULL'])->order(['Comments.id'=>'asc'])->limit(5);
            }
        ]);
        return $data;
    }

Tout fonctionne sauf... le threaded dans les enfants, il récupère le premier niveau d'enfants, mais pas les enfants d'enfants....
( j'ai renommé le hasmany(ChildComments) en (Children) par soucis de simplicité, car le finder threaded renomme en children dans les résultats)
Une idée du problème ? J'imagine même pas pour faire un paginate...

Tu cherches un poil loin :

public function findFiveComments(Query $query, $options = [])
    {
       return $query->contain([
            'Comments' => function (Query $q) use ($options) { 
                return $q->find('threaded')->contain(['Users'=>['fields'=>['id','username']]]);
            }
        ])->limit(5);
    }
Flitflit
Auteur

haha merci de ta réponse, mais c'est le premier truc que j'ai essayé. Il ignore totalement le limit il n'apparait même pas dans le sqllog. Je crois que c'est impossible vu le fonctionnement de threaded, il a besoin de tout charger pour ensuite dispatcher les enfants par id...

J'ai essayé et ça fonctionne...
Bon. Par contre, pour faire de la pagination comme ça, bonne chance...

J'ai ça comme code :

    public function findComments(Query $query, $options = [])
    {
        return $query->contain(['Comments' => function (Query $q) use ($options) {
                return $q->find('threaded')->contain('Users')->limit(2);
        }]);
    }

Et ça comme requête SQL :

SELECT 
  Comments.id AS `Comments__id`, 
  Comments.content AS `Comments__content`, 
  Comments.ref AS `Comments__ref`, 
  Comments.ref_id AS `Comments__ref_id`, 
  Comments.ip AS `Comments__ip`, 
  Comments.parent_id AS `Comments__parent_id`, 
  Comments.user_id AS `Comments__user_id`, 
  Comments.created AS `Comments__created`, 
  Comments.modified AS `Comments__modified`, 
  Users.id AS `Users__id`, 
  Users.username AS `Users__username`, 
  Users.password AS `Users__password`, 
  Users.email AS `Users__email`, 
  Users.role AS `Users__role`, 
  Users.posts_count AS `Users__posts_count`, 
  Users.created AS `Users__created`, 
  Users.modified AS `Users__modified` 
FROM 
  comments Comments 
  LEFT JOIN users Users ON Users.id = (Comments.user_id) 
WHERE 
  (
    Comments.ref = 'Posts' 
    AND Comments.ref_id in (1)
  ) 
LIMIT 
  2

Je suis pas certain que c'est le résultat que tu veux, par contre

Flitflit
Auteur

bizarre... j'essaye un milliard de combinaison j'ai jamais quelque chose de convenable... je trouverai ;)

@Kareylo le plugin est bien fait mais il manque la suppression