Mysql
 sql >> Base de données >  >> RDS >> Mysql

Imprimer des données hiérarchiques dans un formulaire parent enfant liste non ordonnée php?

OK en travaillant du backend vers le frontend...

Vous pouvez appeler une seule procédure stockée non récursive (sproc) à partir de votre script php qui génère la hiérarchie des messages pour vous. L'avantage de cette approche est que vous n'avez qu'à créer un UNIQUE appelez de php à votre base de données alors que si vous utilisez SQL en ligne, vous ferez autant d'appels qu'il y a de niveaux (au minimum). Un autre avantage est que, comme il s'agit d'un sproc non récursif, il est extrêmement performant et il garde également votre code php agréable et propre. Enfin, et je dois le dire pour mémoire, l'appel de procédures stockées est plus sûr et plus efficace que toute autre méthode, car vous n'avez qu'à accorder des autorisations d'exécution à l'utilisateur de votre application et les procédures stockées nécessitent moins d'allers-retours vers la base de données que n'importe quelle autre méthode. d'autres méthodes, y compris les requêtes paramétrées qui nécessitent au moins 2 appels pour une seule requête (1 pour configurer le modèle de requête dans la base de données, l'autre pour remplir les paramètres)

Voici donc comment appeler la procédure stockée à partir de la ligne de commande MySQL.

call message_hier(1);

et voici le jeu de résultats qu'il crée.

msg_id  emp_msg    parent_msg_id    parent_msg   depth
======  =======    =============    ==========   =====
1        msg 1            NULL          NULL          0
2        msg 1-1             1          msg 1         1
3        msg 1-2             1          msg 1         1
4        msg 1-2-1           3          msg 1-2       2
5        msg 1-2-2           3          msg 1-2       2
6        msg 1-2-2-1         5          msg 1-2-2     3
7        msg 1-2-2-1-1       6          msg 1-2-2-1   4
8        msg 1-2-2-1-2       6          msg 1-2-2-1   4

Ok, nous avons maintenant la possibilité de récupérer un arbre de messages complet ou partiel en appelant simplement notre sproc avec le nœud de départ dont nous avons besoin, mais qu'allons-nous faire avec le jeu de résultats ??

Eh bien, dans cet exemple, j'ai décidé que nous allons générer un DOM XML avec, puis tout ce que j'ai à faire est de transformer (XSLT) le XML et nous aurons une page Web de messages imbriqués.

Script PHP

Le script php est assez simple, il se connecte simplement à la base de données, appelle le sproc et boucle le jeu de résultats pour construire le DOM XML. N'oubliez pas que nous n'appelons la base de données qu'une seule fois.

<?php

// i am using the resultset to build an XML DOM but you can do whatever you like with it !

header("Content-type: text/xml");

$conn = new mysqli("localhost", "foo_dbo", "pass", "foo_db", 3306);

// one non-recursive db call to get the message tree !

$result = $conn->query(sprintf("call message_hier(%d)", 1));

$xml = new DomDocument;
$xpath = new DOMXpath($xml);

$msgs = $xml->createElement("messages");
$xml->appendChild($msgs);

// loop and build the DOM

while($row = $result->fetch_assoc()){

    $msg = $xml->createElement("message");
    foreach($row as $col => $val) $msg->setAttribute($col, $val); 

    if(is_null($row["parent_msg_id"])){
        $msgs->appendChild($msg);
    }
    else{
        $qry = sprintf("//*[@msg_id = '%d']", $row["parent_msg_id"]);
        $parent = $xpath->query($qry)->item(0);
        if(!is_null($parent)) $parent->appendChild($msg);
    }
}
$result->close();
$conn->close();

echo $xml->saveXML();
?>

Sortie XML

C'est le XML que le script php génère. Si vous enregistrez ce XML dans un fichier et que vous l'ouvrez dans votre navigateur, vous pourrez développer et réduire les niveaux.

<messages>
    <message msg_id="1" emp_msg="msg 1" parent_msg_id="" parent_msg="" depth="0">
        <message msg_id="2" emp_msg="msg 1-1" parent_msg_id="1" parent_msg="msg 1" depth="1"/>
        <message msg_id="3" emp_msg="msg 1-2" parent_msg_id="1" parent_msg="msg 1" depth="1">
            <message msg_id="4" emp_msg="msg 1-2-1" parent_msg_id="3" parent_msg="msg 1-2" depth="2"/>
            <message msg_id="5" emp_msg="msg 1-2-2" parent_msg_id="3" parent_msg="msg 1-2" depth="2">
                <message msg_id="6" emp_msg="msg 1-2-2-1" parent_msg_id="5" parent_msg="msg 1-2-2" depth="3">
                    <message msg_id="7" emp_msg="msg 1-2-2-1-1" parent_msg_id="6" parent_msg="msg 1-2-2-1" depth="4"/>
                    <message msg_id="8" emp_msg="msg 1-2-2-1-2" parent_msg_id="6" parent_msg="msg 1-2-2-1" depth="4"/>
                </message>
            </message>
        </message>
    </message>
</messages>

Maintenant, vous pouvez renoncer à créer le DOM XML et à utiliser XSL pour afficher une page Web si vous le souhaitez et peut-être simplement boucler le jeu de résultats et afficher les messages directement. J'ai simplement choisi cette méthode pour rendre mon exemple aussi complet et informatif que possible.

Script MySQL

Il s'agit d'un script complet comprenant des tables, des sprocs et des données de test.

drop table if exists messages;
create table messages
(
msg_id smallint unsigned not null auto_increment primary key,
msg varchar(255) not null,
parent_msg_id smallint unsigned null,
key (parent_msg_id)
)
engine = innodb;

insert into messages (msg, parent_msg_id) values
('msg 1',null), 
  ('msg 1-1',1), 
  ('msg 1-2',1), 
      ('msg 1-2-1',3), 
      ('msg 1-2-2',3), 
         ('msg 1-2-2-1',5), 
            ('msg 1-2-2-1-1',6), 
            ('msg 1-2-2-1-2',6);


drop procedure if exists message_hier;

delimiter #

create procedure message_hier
(
in p_msg_id smallint unsigned
)
begin

declare v_done tinyint unsigned default(0);
declare v_dpth smallint unsigned default(0);

create temporary table hier(
 parent_msg_id smallint unsigned, 
 msg_id smallint unsigned, 
 depth smallint unsigned
)engine = memory;

insert into hier select parent_msg_id, msg_id, v_dpth from messages where msg_id = p_msg_id;

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */

create temporary table tmp engine=memory select * from hier;

while not v_done do

    if exists( select 1 from messages e inner join hier on e.parent_msg_id = hier.msg_id and hier.depth = v_dpth) then

        insert into hier select e.parent_msg_id, e.msg_id, v_dpth + 1 
            from messages e inner join tmp on e.parent_msg_id = tmp.msg_id and tmp.depth = v_dpth;

        set v_dpth = v_dpth + 1;            

        truncate table tmp;
        insert into tmp select * from hier where depth = v_dpth;

    else
        set v_done = 1;
    end if;

end while;

select 
 m.msg_id,
 m.msg as emp_msg,
 p.msg_id as parent_msg_id,
 p.msg as parent_msg,
 hier.depth
from 
 hier
inner join messages m on hier.msg_id = m.msg_id
left outer join messages p on hier.parent_msg_id = p.msg_id;

drop temporary table if exists hier;
drop temporary table if exists tmp;

end #

delimiter ;

-- call this sproc from your php

call message_hier(1);

La source complète de cette réponse peut être trouvée ici :http://pastie.org/1336407 . Comme vous l'aurez déjà noté, j'ai omis le XSLT mais vous n'emprunterez probablement pas la voie XML et si vous le faites, il y a des tas d'exemples sur le Web.

J'espère que cela vous sera utile :)

MODIF :

Ajout d'un peu plus de données pour que vous ayez plus d'un message racine (msg_ids 1,9,14).

truncate table messages;

insert into messages (msg, parent_msg_id) values
('msg 1',null), -- msg_id = 1
  ('msg 1-1',1), 
  ('msg 1-2',1), 
      ('msg 1-2-1',3), 
      ('msg 1-2-2',3), 
         ('msg 1-2-2-1',5), 
            ('msg 1-2-2-1-1',6), 
            ('msg 1-2-2-1-2',6),
('msg 2',null), -- msg_id = 9
    ('msg 2-1',9), 
    ('msg 2-2',9), 
    ('msg 2-3',9), 
        ('msg 2-3-1',12),
('msg 3',null); -- msg_id = 14

Désormais, si vous souhaitez uniquement obtenir les messages spécifiques à un nœud racine (message de démarrage), vous pouvez appeler la procédure stockée d'origine en transmettant le msg_id de démarrage de la racine dont vous avez besoin. En utilisant les nouvelles données ci-dessus, ce serait msg_ids 1,9,14.

call message_hier(1); -- returns all messages belonging to msg_id = 1

call message_hier(9); -- returns all messages belonging to msg_id = 9

call message_hier(14); -- returns all messages belonging to msg_id = 14

vous pouvez passer n'importe quel msg_id que vous aimez donc si je veux tous les messages ci-dessous msg 1-2-2-1 alors vous passerez msg_id =6 :

call message_hier(6); -- returns all messages belonging to msg_id = 6

Cependant, si vous voulez tous les messages pour toutes les racines, vous pouvez appeler ce nouveau sproc que j'ai créé comme suit :

call message_hier_all(); -- returns all messages for all roots.

Le principal problème avec cela est qu'à mesure que votre table de messages grandit, elle renverra beaucoup de données, c'est pourquoi je me concentrais sur un sproc plus spécifique qui récupérait uniquement les messages pour un nœud racine donné ou commençant par msg_id.

Je ne publierai pas le nouveau code sproc car il est pratiquement le même que l'original mais vous pouvez trouver tous les amendements ici :http ://pastie.org/1339618

Le dernier changement que vous devrez apporter est dans le script php qui appellera maintenant le nouveau sproc comme suit :

//$result = $conn->query(sprintf("call message_hier(%d)", 1)); // recommended call

$result = $conn->query("call message_hier_all()"); // new sproc call

J'espère que cela vous aidera :)

call message_hier_all();