Mysql
 sql >> Database >  >> RDS >> Mysql

Stampa i dati gerarchici in un modulo genitore figlio elenco non ordinato php?

OK lavorare dal back-end verso il front-end...

Puoi chiamare una singola procedura memorizzata non ricorsiva (sproc) dal tuo script php che genera la gerarchia dei messaggi per te. Il vantaggio di questo approccio è che devi solo crearne un SINGOLO chiama da php al tuo database mentre se usi SQL inline, farai tante chiamate quanti sono i livelli (almeno). Un altro vantaggio è che poiché è uno sproc non ricorsivo, è estremamente performante e mantiene anche il tuo codice php bello e pulito. Infine, e devo dire questo per la cronaca, che chiamare le stored procedure è più sicuro ed efficiente di qualsiasi altro metodo perché devi solo CONCEDERE le autorizzazioni di esecuzione all'utente dell'app e le stored procedure richiedono meno round trip al database di qualsiasi altri metodi comprese le query parametrizzate che richiedono almeno 2 chiamate per una singola query (1 per impostare il modello di query nel db, l'altro per popolare i parametri)

Quindi ecco come chiamereste la stored procedure dalla riga di comando di MySQL.

call message_hier(1);

ed ecco il set di risultati che crea.

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, quindi ora abbiamo la possibilità di recuperare un albero dei messaggi completo o parziale semplicemente chiamando il nostro sproc con qualsiasi nodo iniziale di cui abbiamo bisogno, ma cosa faremo con il set di risultati ??

Bene, in questo esempio ho deciso di generare un DOM XML con esso, quindi tutto ciò che devo fare è trasformare (XSLT) l'XML e avremo una pagina Web di messaggi nidificata.

Script PHP

Lo script php è abbastanza semplice, si collega semplicemente al database, chiama sproc ed esegue il loop del set di risultati per creare il DOM XML. Ricorda che chiamiamo nel db solo una volta.

<?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();
?>

Uscita XML

Questo è l'XML generato dallo script php. Se salvi questo XML in un file e lo apri nel tuo browser sarai in grado di espandere e comprimere i livelli.

<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>

Ora puoi rinunciare a costruire il DOM XML e utilizzare XSL per eseguire il rendering di una pagina Web se lo desideri e forse semplicemente eseguire il loop del set di risultati e visualizzare direttamente i messaggi. Ho semplicemente scelto questo metodo per rendere il mio esempio il più completo e informativo possibile.

Script MySQL

Questo è uno script completo che include tabelle, sprocs e dati di 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 fonte completa di questa risposta può essere trovata qui:http://pastie.org/1336407 . Come avrai già notato, ho omesso l'XSLT ma probabilmente non seguirai il percorso XML e se lo fai ci sono un sacco di esempi sul web.

Spero che lo trovi utile :)

MODIFICA:

Aggiunti un po' più di dati in modo da avere più di un messaggio di root (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

Ora, se vuoi ottenere solo i messaggi che sono specifici per un nodo radice (messaggio iniziale) puoi chiamare la procedura memorizzata originale passando il msg_id iniziale della radice richiesta. Usando i nuovi dati sopra che sarebbero 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

puoi passare qualsiasi msg_id che ti piace, quindi se voglio tutti i messaggi sotto msg 1-2-2-1, dovresti passare msg_id =6:

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

Tuttavia, se vuoi tutti i messaggi per tutte le radici, puoi chiamare questo nuovo sproc che ho creato come segue:

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

Il problema principale con questo è che man mano che la tabella dei messaggi cresce, restituirà molti dati, motivo per cui mi stavo concentrando su uno sproc più specifico che recuperava solo i messaggi per un determinato nodo radice o avviava msg_id.

Non posterò il nuovo codice sproc poiché è praticamente lo stesso dell'originale, ma puoi trovare tutti gli emendamenti qui:http://pastie.org/1339618

L'ultima modifica che dovrai apportare è nello script php che ora chiamerà il nuovo sproc come segue:

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

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

Spero che questo aiuti :)

call message_hier_all();