Bonjour à tous,

Je planche depuis un moment sur un gros forum pour l'un de mes projets. N'ayant rien trouvé dans les librairies gratuites et voulant un contrôle total sur le code et sur les vues, j'ai choisi de le coder moi-même.

C'est donc développé sous Cake PHP, et tout fonctionne bien. Cependant j'en arrive aux finitions : la partie post / edit / quote, qui est une question de temps, et la partie read / unread.

Cette dernière me poste quelques soucis : compte tenu de la structure complexe du forum et du nombre d'utilisateurs qu'il va recevoir (la base d'utilisateur potentielle du site étant de 5M d'utilisateurs), j'essaie de trouver un algo qui soit optimisée au mieux :

  • le moins de vérifications possibles
  • le moins de requête possibles
  • le moins de données stockées possibles

A noter que je souhaite stocker ces liaisons en BDD pour plusieurs raisons :

  • l'appli sera multi-device : site web, app mobile, app tablette, etc
  • en terme UX, je ne supporte pas d'arriver sur un forum où toutes les catégories et tous les topics sont marquées comme non-lus quand je reviens, alors qu'une bonne partie n'a pas évolué depuis ma dernière visite, donc pas de session
  • compter sur le client (cookie, local storage, etc) devient vite capricieux si un utilisateur possède plusieurs devices ou si plusieurs utilisateurs utilisent le device

La structure du forum est la suivante :

  • Forum hasMany ForumsCategories
  • ForumsCategories belongsTo Forum & hasMany Topics & hasMany Replies
  • Topics belongsTo ForumsCateogy & belongsTo User & hasMany Replies
  • Replies belongsTo ForumsCategory & belongsTo Topic & belongsTo User

La première idée qui me vient, c'est de faire des tables de liaisons :

  • ForumsCategories_readBy_Users
  • Topics_readBy_Users
  • Replies_readBy_Users

Dans les faits je me dis tout d'abord que les Replies, ça va surcharger complètement le système. Donc ça, c'est à éviter.

Pour les Topics, ça devient un peu plus clair : l'utilisateur lit le topic, on passe la liaison à read.
Cependant, il y a plusieurs interrogations quant au passage à read :

  • dois-je le faire uniquement sur la dernière page de réponses (on exécute un appel à setRead() uniquement si on est sur la dernière page de réponses du topic), voire sur la dernière réponse (en se basant sur l'ID de la réponse, qui est présent dans mon DOM HTML, et en faisant un appel en Ajax quand on attend l'ID en question)
  • ou dois-je le faire sur chaque page du topic, ce qui simplifie l'exécution du code, mais peut nuire à l'expérience utilisateur (il n'a pas lu la fin du topic, mais celui-ci est considéré comme lu)
  • et plus profond, dans le cas où des réponses sont postées s'il est sur le topic ? J'ai pensé à un long polling ou à une requête cyclique en ajax qui recharge les réponses et la pagination si de nouvelles réponses sont détectées, et à ce moment là on repousse le moment où le topic est marqué comme read. Mais ça fait beaucoup plus de requêtes

Pour les ForumsCategories, c'est un peu plus facile : si tous les topics sont read, alors la ForumsCategory est read également.
Mais les questions se posent alors sur la façon de faire :

  • soit on inclus à chaque post de réponse un appel pour marquer la ForumsCategory en question comme unread ce qui peut faire beaucoup d'UPDATE dans la base
  • soit on récupère à chaque affichage du listing ForumsCategories la table de liaison Topics_readBy_Users en tapant sur les topics appartenant à la ForumsCategory en question et le UserId de l'utilisateur connecté

L'une comme l'autre des solutions génèrent forcément beaucoup de requêtes. J'ai tendance à penser qu'en général, un traitement beforeSave est préférable à un traitement beforeFilter, les SELECT étant souvent plus nombreux et nécessitant moins de ressources que les INSERT ou UPDATE, mais en l’occurrence, je risque d'avoir énormément de réponses postées, alors que je pourrais ne générer de requête qu'à l'affichage de la vue par l'utilisateur.

N'ayant que peu de retour d'expérience là dessus (j'ai un peu fouillé PunBB et PHPBB il y a quelques années de cela, mais rien de plus), j'ai tendance à pensé qu'il n'existe pas vraiment de bonnes pratiques en la matière, c'est pourquoi je me tourne vers vous avant de me lancer dans une architecture complexe qui pourrait s'avérer bien trop gourmande.

Merci d'avance pour vos réactions et commentaires constructifs.

2 réponses


Oui non, repliesReadByUsers est clairement inutile.

Pour ta question, tu sais où se situent les dernières réponses dans un topic, puisque tu as le nombre de réponses totales qu'il y a, tu as une pagination, donc tu as tout ce qu'il faut.

Si l'utilisateur n'est pas sur la dernière page > le topic reste unread quoi qu'il arrive.
Si l'utilisateur est sur la dernière page > le topic devient read.

Ensuite, si quelqu'un ré-écrit derrière, alors le topic devient unread pour TOUS.

Et cette dernière fonction va overwrite toutes les autres, puisqu'elle se fera forcément en dernier.

Donc si l'utilisateur rafraichit sa page mais qu'il n'est plus sur la dernière page, le topic est unread, s'il est toujours sur la dernière page, le topic est read, s'il retourne à l'index des topics, il sera unread.

Pour l'UX, c'est comme pour tout, il faut un équilibre.

Tu ne peux pas avoir une UX absolument parfaite sur un forum sans avoir un serveur qui en prend plein la poire. C'est pour ça que tu vois ta catégories de topic qui est inscrite comme non lue, et non pas une icône avec un nombre de topic non lu intra catégorie. La différence de requête est monstrueuse à grande échelle. Donc faut faire des choix de ce côté là.

Pakito
Auteur

Merci pour ta réponse.

Je suis aussi tombé par hasard dans la rubrique Cakephp sur un lien vers la structure des tables de PHPBB : http://stackoverflow.com/questions/3823110/what-is-the-most-efficient-way-to-create-a-forum-lightbulb-unread-system/9053650#9053650

Ce principe a l'air pas trop mal. Mais effectivement dans mon cas vont se poser les soucis de notification (pour le forum et d'autres points) en plus de la simple gestion du lu / non lu.