sql >> Base de Datos >  >> RDS >> Mysql

¿Imprimir datos jerárquicos en una lista desordenada de formulario padre-hijo php?

OK, trabajando desde el backend hacia el front-end...

Puede llamar a un solo procedimiento almacenado no recursivo (sproc) desde su secuencia de comandos php que genera la jerarquía de mensajes para usted. La ventaja de este enfoque es que solo necesita hacer una UNA llame desde php a su base de datos, mientras que si usa SQL en línea, realizará tantas llamadas como niveles (como mínimo). Otra ventaja es que, como es un sproc no recursivo, es extremadamente eficaz y también mantiene su código php agradable y limpio. Finalmente, y debo decir esto para que conste, que llamar a los procedimientos almacenados es más seguro y más eficiente que cualquier otro método porque solo necesita OTORGAR permisos de ejecución al usuario de su aplicación y los procedimientos almacenados requieren menos viajes de ida y vuelta a la base de datos que cualquier otro método. otros métodos, incluidas consultas parametrizadas que requieren al menos 2 llamadas para una sola consulta (1 para configurar la plantilla de consulta en la base de datos, la otra para completar los parámetros)

Así es como llamaría al procedimiento almacenado desde la línea de comandos de MySQL.

call message_hier(1);

y aquí está el conjunto de resultados que 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

Bien, ahora tenemos la capacidad de obtener un árbol de mensajes completo o parcial simplemente llamando a nuestro sproc con cualquier nodo de inicio que necesitemos, pero ¿qué vamos a hacer con el conjunto de resultados?

Bueno, en este ejemplo he decidido que vamos a generar un XML DOM con él, entonces todo lo que necesito hacer es transformar (XSLT) el XML y tendremos una página web de mensajes anidados.

Script PHP

El script php es bastante simple, simplemente se conecta a la base de datos, llama al sproc y repite el conjunto de resultados para construir el XML DOM. Recuerde que solo llamamos a la base de datos una vez.

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

Salida XML

Este es el XML que genera el script php. Si guarda este XML en un archivo y lo abre en su navegador, podrá expandir y contraer los niveles.

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

Ahora puede renunciar a construir el DOM XML y usar XSL para representar una página web si lo desea y tal vez simplemente repita el conjunto de resultados y represente los mensajes directamente. Simplemente he elegido este método para que mi ejemplo sea lo más completo e informativo posible.

Secuencia de comandos MySQL

Este es un script completo que incluye tablas, procesos y datos de prueba.

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 fuente completa de esta respuesta se puede encontrar aquí:http://pastie.org/1336407 . Como ya habrás notado, he omitido el XSLT, pero probablemente no vayas por la ruta XML y, si lo haces, hay montones de ejemplos en la web.

Espero que encuentres esto útil :)

EDITAR:

Se agregaron un poco más de datos para que tenga más de un mensaje raíz (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

Ahora, si solo desea obtener los mensajes que son específicos de un nodo raíz (mensaje inicial), puede llamar al procedimiento almacenado original pasando el msg_id inicial de la raíz que necesita. Usando los nuevos datos anteriores, sería 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

puede pasar cualquier msg_id que desee, así que si quiero todos los mensajes debajo de msg 1-2-2-1, entonces debería pasar msg_id =6:

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

Sin embargo, si desea todos los mensajes para todas las raíces, puede llamar a este nuevo sproc que he creado de la siguiente manera:

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

El principal problema con esto es que, a medida que crece la tabla de mensajes, devolverá muchos datos, por lo que me estaba centrando en un proceso más específico que solo recuperaba mensajes para un nodo raíz determinado o para iniciar msg_id.

No publicaré el nuevo código sproc ya que es prácticamente el mismo que el original, pero puedes encontrar todas las enmiendas aquí:http://pastie.org/1339618

El cambio final que deberá realizar está en el script php que ahora llamará al nuevo sproc de la siguiente manera:

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

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

Espero que esto ayude :)

call message_hier_all();