JeuWeb - Crée ton jeu par navigateur
Problème tout bête - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Problème tout bête (/showthread.php?tid=1467)

Pages : 1 2


Problème tout bête - Maegia - 13-07-2007

Bonjour !

J'ai une table MySQL pour la messagerie à l'intérieur de mon jeu. J'ai donc, bien entendu, un champ utilisé pour indiqué qui doit avoir accès aux messages envoyés.

Ex :

Code PHP :
<?php 
id
(Int) | titre (Varchar 50) | text (Text) | cibles (Text) | date (Int)
2 | Ceci est un test | Bonjour les amis ! | 3,456,19,34 | 19382832

À l'intérieur de mon champ "cibles", j'enregistre, séparé par des virgules, l'id des joueurs ayant la possibilité de lire ledit message. Pour ce message-ci, en bref, les joueurs possédant les id 3, 456, 19 et 34 ont la possibilité de lire le message.

J'aimerais simplement savoir comment vous vous y prendriez pour créé la boîte de réception d'un joueur X... Comment iriez-vous chercher les messages auxquels le joueur a accès ?

Au début, je pensais que cette simple query aurait fonctionner, mais non...

Code PHP :
<?php 
mysql_query
('SELECT titre, text FROM messagerie WHERE '.$id_joueur.' IN (cibles) ORDER BY date DESC')

Évidemment, j'ai pu contourner le problème, mais je ne suis pas satisfait du résultat final... Comment vous vous y prendriez, bref ?

P.S. C'est probablement facile, c'est moi qui doit ne simplement pas voir la solution... Y'a des jours comme ça...


RE: Problème tout bête - manip - 14-07-2007

Bah avec l'id nan ?
Code PHP :
<?php 
mysql_query
('SELECT titre, text FROM messagerie WHERE cibles='.$id_joueur.' ORDER BY date DESC')


ensuite mysql_fetch_assoc, puis j'affiche comme ça :
Titre (date)
Message
Auteur
[Répondre]


RE: Problème tout bête - Maegia - 14-07-2007

Hum... C'est qu'il y a plusieurs ID dans le même champ, puisqu'on peut envoyer le message à de multiples personnes. Dans mon exemple, je n'utilisais qu'une seule ligne, mais quatre personnes avaient la possibilité de lire le message. Désolé, je n'étais pas assez clair.


RE: Problème tout bête - elazard - 14-07-2007

Bah je ne vois pas quoi faire de mieux dans ces conditions: un rang (genre dans le champ du mets modo admin etc ou mieux un enum qui regroupe tes différents rangs) à la place de l'id aurait été plus simple mais dans cette configuration je ne vois pas, évidement surement qu'un érudit comme Naholyr aura une réponse plus poussée^^ à voir


RE: Problème tout bête - Maegia - 14-07-2007

J'attenderai donc avec impatience la réponse de cet érudit ! Ou peut-être existe-t'il une meilleur moyen pour n'enregistrer qu'en une seule ligne un message destiné à plusieurs joueurs ?


RE: Problème tout bête - naholyr - 14-07-2007

Merci pour l'érudit :lol: :hahaha:

Je suis justement en train de refléchir à un système de messagerie privée avec multiples destinataires sur mon site, les principaux problèmes auxquels j'ai pensé sont :
  • Si un destinataire supprime le message de sa boite, il ne faut surtout pas que ça supprime le message de toutes les boites de tout le monde :lol: cela n'impose pas grand-chose comme contrainte en fait, si ce n'est que si on ne veut pas avoir un système de suppression lourdingue, il vaut mieux stocker message d'un côté et destinataires de l'autre avec une clé étrangère.
  • Si un destinataire supprime le message de sa boite, il ne faudrait pas qu'il disparaisse de la liste des destinataires pour les autres utilisateur qui ont accès au message. cela implique qu'il faut stocker quelque part la liste initiale des destinataires qui elle ne sera jamais modifiée.
  • Si un destinataire disparait (désinscription) il faudrait quand-même que son login reste dans la liste des destinataires d'un message. Cela implique que la liste initiale des destinataires doit contenir l'id ET le login du destinataire.

Ces contraintes posées, le modèle s'impose de lui-même :
message ( id, id_expediteur, login_expediteur, sujet, corps )
message_destinataire ( id_message, id_destinataire, login_destinataire )
boite ( id_personnage, id_message, etat, dossier )
etat est un ENUM('read', 'unread')
dossier est une chaine, ou si on ne veut pas gérer la création de dossier un ENUM('inbox', 'sent', 'trash', 'archives')

Les actions principales se traduisent comme ça :

naholyr(#1) envoie un message à elazard(#2) et Maegia(#3)
Code :
INSERT INTO message (id_expediteur, login_expediteur, sujet, corps) VALUES (1, 'naholyr', 'coucou', 'mon message');
INSERT INTO message_destinataire (id_message, id_destinataire, login_destinataire) VALUES ($id_message, 2, 'elazard'), ($id_message, 3, 'Maegia');
INSERT INTO boite (id_personnage, id_message, etat, dossier) VALUES (1, $id_message, 'unread', 'sent'), (2, $id_message, 'unread', 'inbox'), (3, $id_message, 'unread', 'inbox');

Maegia(#3) lit le message
Code :
UPDATE boite SET etat='read' WHERE id_personnage=3 AND id_message=$id_message;

Maegia(#3) supprime le message (sans passer par la corbeille)
Code :
DELETE FROM boite WHERE id_personnage=3 AND id_message=$id_message
On est tranquille ça n'a rien changé au contenu de la table `message_destinataire`, donc il apparait toujours comme faisant partie des destinataires Smile

elazard(#2) se désinscrit du site
Code :
DELETE FROM boite WHERE id_personnage=2
Idem, ça n'a rien changé Wink

Pour afficher un message, on peut faire une jointure entre `message` et `message_destinataire` :
Code :
SELECT id_expediteur, login_expediteur, sujet, corps, id_destinataire, login_destinataire FROM message, message_destinataire WHERE id_message=$id_message
Ça ne fait qu'une requête, par contre chaque ligne répète le sujet et le message, dans notre exemple ça donnerait ce résultat :
Code :
+---------------+------------------+--------+-------------+-----------------+--------------------+
| id_expediteur | login_expediteur | sujet  | corps       | id_destinataire | login_destinataire |
+---------------+------------------+--------+-------------+-----------------+--------------------+
| 1             | naholyr          | coucou | mon message | 2               | elazard            |
| 1             | naholyr          | coucou | mon message | 3               | Maegia             |
+---------------+------------------+--------+-------------+-----------------+--------------------+
Donc quand on va parcourir les résultats de notre requête on va simplement définir sujet et corps au premier passage, et les destinataires dans les passages suivants.
Code PHP :
<?php 
$result
= $db->query("SELECT id_expediteur, login_expediteur, sujet, corps, " .
"id_destinataire, login_destinataire FROM message, message_destinataire " .
"WHERE id_message=?", $id_message);
if (!
$result) {
// Erreur dans la requête, message d'erreur
$erreur = "Erreur SQL";
} else {
$first_loop = true;
// On parcourt les résultat
while ($row = $db->fetch($result)) {
if (
$first_loop) { // Premier résultat rencontré : on stocke les infos communes
$message = array(
'sujet' => $row['sujet'],
'corps' => $row['corps'],
'expediteur' => array( $row['id_expediteur'] , $row['login_expediteur'] ),
'destinataires' => array()
);
$first_loop = false;
}
// Ajout d'un destinataire
$message['destinataires'][] = array( $row['id_destinataire'] , $row['login_destinataire'] );
}
if (
$first_loop) { // Si $first_loop est toujours vrai, cela signifie qu'il n'y avait aucun résultat
// Message d'erreur
$erreur = "Aucun message correspondant à l'id $id_message";
} else {
// On peut afficher le message
// Le message a ses infos directement dans le tableau
// L'expéditeur et chaque destinataire est un couple, exemple : list($id, $login) = $message['expediteur']
}
}

Si on veut afficher différemment les personnages qui n'existent et les personnages qui existent, je pense qu'on peut difficilement se passer d'une deuxième requête (mais je ne suis pas un chef en SQL donc c'est peut-être possible). Quelque chose comme SELECT id FROM personnages WHERE id IN (1,2,3) (pour notre exemple). Et on récupère la liste des id des personnages qui existent, ainsi on peut faire que les logins des persos qui n'existent plus ne soient pas cliquables (là on est dans le deuxième «else» du code précédent) :
Code PHP :
<?php 
$ids
= array($message['expediteur'][0]);
foreach (
$message['destinataires'] as $destinataire) {
$ids[] = $destinataire[0];
}
$result = $db->query("SELECT id FROM personnage WHERE id IN (" . implode(',',$ids) . ")");
if (!
$result) {
// Erreur dans la requête, message d'erreur
$erreur = "Erreur SQL";
} else {
// On crée un tableau qui dit si l'utilisateur existe ou non
$perso_existe = array();
while (
$row = $db->fetch($result)) {
$perso_existe[$row['id']] = true;
}
// On poursuit…
}

On a maintenant tout ce qu'il faut Smile Si on veut par exemple que les logins des persos qui existent encore pointent sur leur profil, on peut simplement avoir un code de ce genre
Code PHP :
<?php 
$destinataires_html
= array();
foreach (
$message['destinataires'] as $destinataire) {
list(
$id, $login) = $destinataire;
if (isset(
$perso_existe[$id]) && true === $perso_existe[$id]) {
$destinataires_html[] = '<a href="profil.php?id=' . $id . '">' . $login . '</a>';
} else {
$destinataires_html[] = '<em>' . $login . '</em>';
}
}
$destinataires_html = implode(', ', $destinataires_html);

Ce qui donnera, toujours pour notre exemple :
Citation :À : elazard, Maegia
Et voilà le travail Tongue

Les inconvénients de ce système c'est qu'on stocke beaucoup d'informations dans les tables, en particulier les logins qui peuvent alourdir de beaucoup, et qu'il est parfaitement inutile de supprimer dans 99% des cas (cas où l'utilisateur existe encore dans la base). Une idée pour y pallier serait de laisser login_destinataire et login_expediteur à NULL, de faire une jointure avec `personnage` pour récupérer ces infos si besoin, et lorsqu'un personnage se désinscrit là on stocke son login avec un ou deux UPDATE. Mais bon, c'est comme toujours avec ce type de problème : soit on a plus d'infos dans les tables, soit on fait plus de requête, soit on perd en fonctionnalités, il faut choisir le compromis le plus adapté à sa situation.

Personnellement je pense faire un choix assez drastique : on ne peut pas supprimer un personnage. Ça évitera les résurrections farfelues, et ça me permet de m'exempter de stocker le login dans les infos du messages. J'ai simplement un champ "deleted" dans la table des personnages. S'il est à 1, alors le personnage n'existe plus (le principe d'affichage reste le même).

Dans tous les cas il faut prévoir un nettoyage régulier, donc il faut ajouter un horodatage (date/heure) au message, et tous les soirs faire une purge de tous les messages (et faire le ménage dans les tables associées) expirés. Sinon c'est le genre de tables qui grossit trop vite.


RE: Problème tout bête - elazard - 14-07-2007

ouais bah voilà ce que je disais quoi mdr^^ évidement c'est limite parfait et parfaitement optimisé


RE: Problème tout bête - Mysterarts - 14-07-2007

Il est fort, ce Naholyr ^^
Mais moi, y'a encore une question qui me tourmente...
Comment tu fais les tableaux comme ça :
Code :
+---------------+------------------+--------+-------------+-----------------+--------------------+
| id_expediteur | login_expediteur | sujet  | corps       | id_destinataire | login_destinataire |
+---------------+------------------+--------+-------------+-----------------+--------------------+
| 1             | naholyr          | coucou | mon message | 2               | elazard            |
| 1             | naholyr          | coucou | mon message | 3               | Maegia             |
+---------------+------------------+--------+-------------+-----------------+--------------------+
Parce que moi, quand j'essais, c'est tout de travers ^^ :respect:

Mysterarts


RE: Problème tout bête - naholyr - 14-07-2007

Mysterarts a écrit :Mais moi, y'a encore une question qui me tourmente...
Comment tu fais les tableaux comme ça :
Je les fais dans mon éditeur de code (police à pas fixe), et je copie-colle dans la balise code Tongue


RE: Problème tout bête - Maegia - 14-07-2007

C'est en effet ce que j'appelle une réponse d'érudit ! Merci beaucoup naholyr, réponse très complète et bien expliquée. Par contre, quelque chose me chicotte à mon tour... Si je reprends ma première query du début, il est donc toujours impossible, si je comprends bien, de faire un truc du genre :

Code PHP :
<?php 
mysql_query
('SELECT titre, text FROM messagerie WHERE '.$id_joueur.' IN (cibles) ORDER BY date DESC')

Pourtant, cette requête fonctionne à merveille pour le premier id de la liste. Dans mon premier exemple, le joueur possédant l'ID 3 aurait eu le message dans sa boîte, mais les autres ne le voyait pas... Ça me pertube... Qu'est-ce qui cloche dans cette simple et petite requête ? Qu'est-ce qui permet au premier joueur d'être détecté, mais pas aux autres ?