Mysql
 sql >> база данни >  >> RDS >> Mysql

Печат на йерахични данни в родителски дъщерен формуляр неподреден списък php?

Добре работи от бекенда към предния край...

Можете да извикате една нерекурсивна съхранена процедура (sproc) от вашия php скрипт, който генерира йерархията на съобщенията за вас. Предимството на този подход е, че трябва само да направите ЕДИНИЧНА извикване от php към вашата база данни, докато ако използвате вграден SQL, тогава ще извършвате толкова обаждания, колкото има нива (най-малко). Друго предимство е, че тъй като е нерекурсивен sproc, той е изключително ефективен и също така поддържа вашия php код добър и чист. И накрая, и трябва да кажа това за протокола, че извикването на съхранени процедури е по-сигурно и по-ефективно от всеки друг метод, защото трябва само да ПРЕДОСТАВЯТЕ разрешения за изпълнение на потребителя на вашето приложение и съхранените процедури изискват по-малко двупосочни пътувания до базата данни от всички други методи, включително параметризирани заявки, които изискват поне 2 извиквания за една заявка (1 за настройка на шаблона на заявката в db, другият за попълване на параметрите)

Ето как ще извикате съхранената процедура от командния ред на MySQL.

call message_hier(1);

и ето набора от резултати, който създава.

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

Добре, така че сега имаме възможността да извлечем пълно или частично дърво на съобщенията, като просто извикаме нашия sproc с всеки начален възел, който ни е необходим, но какво ще правим с набора от резултати??

Е, в този пример реших, че ще генерираме XML DOM с него, след което всичко, което трябва да направя, е да трансформирам (XSLT) XML и ще имаме вложена уеб страница за съобщения.

PHP скрипт

PHP скриптът е доста прост, той просто се свързва с базата данни, извиква sproc и зацикля набора от резултати, за да изгради XML DOM. Не забравяйте, че се обаждаме в db само веднъж.

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

XML изход

Това е XML, който php скриптът генерира. Ако запазите този XML във файл и го отворите в браузъра си, ще можете да разгънете и свиете нивата.

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

Сега можете да се откажете от изграждането на XML DOM и да използвате XSL за изобразяване на уеб страница, ако желаете, и може би просто завъртете набора от резултати и изобразете директно съобщенията. Просто избрах този метод, за да направя моя пример възможно най-изчерпателен и информативен.

MySQL скрипт

Това е пълен скрипт, включващ таблици, процесори и тестови данни.

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

Пълният източник за този отговор може да се намери тук:http://pastie.org/1336407 . Както вече сте отбелязали, пропуснах XSLT, но вероятно няма да преминете по XML маршрута и ако го направите, в мрежата има купища примери.

Надявам се да намерите това за полезно :)

РЕДАКТИРАНЕ:

Добавени са малко повече данни, така че да имате повече от едно основно съобщение (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

Сега, ако искате просто да получите съобщенията, които са специфични за главен възел (начално съобщение), можете да извикате оригиналната съхранена процедура, преминаваща в началния msg_id на основния, който ви е необходим. Използвайки новите данни по-горе, това ще бъде 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

можете да подадете всеки msg_id, който искате, така че ако искам всички съобщения под msg 1-2-2-1, тогава ще предадете msg_id =6:

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

Въпреки това, ако искате всички съобщения за всички roots, тогава можете да извикате този нов sproc, който създадох, както следва:

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

Основният проблем с това е, че с нарастването на таблицата ви със съобщения тя ще връща много данни, поради което се фокусирах върху по-специфичен sproc, който извлича съобщения само за даден главен възел или стартиращ msg_id.

Няма да публикувам новия sproc код, тъй като е почти същият като оригиналния, но можете да намерите всички изменения тук:http://pastie.org/1339618

Последната промяна, която ще трябва да направите, е в php скрипта, който сега ще извика новия sproc, както следва:

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

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

Надявам се това да помогне :)

call message_hier_all();


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Най-важните грешки, които трябва да избягвате при репликацията на MySQL

  2. Как да инсталирате phpMyAdmin на управлявани хостинг акаунти

  3. С VBA намерете версията на MySQL ODBC драйвера, инсталиран в Windows

  4. Как да настроите кодирането за char колоните на таблиците в django?

  5. Django модели:стойност по подразбиране за колона