sql >> Databáze >  >> RDS >> Mysql

Tisknout hierarchická data v nadřazeném podřízeném formuláři neuspořádaný seznam php?

OK, práce od backendu směrem k front-endu...

Můžete zavolat jednu nerekurzivní uloženou proceduru (sproc) z vašeho php skriptu, který za vás vygeneruje hierarchii zpráv. Výhodou tohoto přístupu je, že stačí vytvořit JEDNOTLIVÉ volání z php do vaší databáze, zatímco pokud používáte inline SQL, budete provádět tolik volání, kolik je úrovní (minimálně). Další výhodou je, že jde o nerekurzivní sproc, je extrémně výkonný a také udržuje váš php kód pěkný a čistý. A konečně, a musím to říci pro pořádek, že volání uložených procedur je bezpečnější a efektivnější než jakákoli jiná metoda, protože stačí udělit oprávnění ke spuštění vaší aplikace a uložené procedury vyžadují méně zpátečních cest do databáze než jakákoli jiná metoda. další metody včetně parametrizovaných dotazů, které vyžadují alespoň 2 volání pro jeden dotaz (1 pro nastavení šablony dotazu v db, druhé pro naplnění parametrů)

Zde je návod, jak byste volali uloženou proceduru z příkazového řádku MySQL.

call message_hier(1);

a zde je výsledná sada, kterou vytvoří.

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

Dobře, takže nyní máme možnost načíst úplný nebo částečný strom zpráv pouhým voláním našeho sproc s jakýmkoli počátečním uzlem, který požadujeme, ale co uděláme s výslednou sadou?

V tomto příkladu jsem se rozhodl, že s ním vygenerujeme XML DOM, pak vše, co musím udělat, je transformovat (XSLT) XML a budeme mít vnořenou webovou stránku zpráv.

PHP skript

Skript php je poměrně jednoduchý, jen se připojí k databázi, zavolá sproc a vytvoří smyčku resultset pro vytvoření XML DOM. Pamatujte, že do databáze voláme pouze jednou.

<?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 výstup

Toto je XML, které generuje php skript. Pokud uložíte tento XML do souboru a otevřete jej v prohlížeči, budete moci rozbalit a sbalit úrovně.

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

Nyní byste se mohli vzdát vytváření XML DOM a použití XSL k vykreslení webové stránky, pokud si přejete, a možná jen opakovat sadu výsledků a přímo vykreslit zprávy. Jednoduše jsem zvolil tuto metodu, aby byl můj příklad co nejkomplexnější a nejinformativnější.

Skript MySQL

Toto je kompletní skript včetně tabulek, sprocs a testovacích dat.

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

Úplný zdroj této odpovědi lze nalézt zde:http://pastie.org/1336407 . Jak jste si již všimli, vynechal jsem XSLT, ale pravděpodobně nepůjdete cestou XML, a pokud ano, na webu je spousta příkladů.

Doufám, že vám to pomůže :)

UPRAVIT:

Přidáno trochu více dat, takže máte více než jednu kořenovou zprávu (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

Nyní, pokud chcete pouze získat zprávy, které jsou specifické pro kořenový uzel (spouštěcí zpráva), můžete zavolat původní uloženou proceduru předáním počátečního msg_id požadovaného kořenového adresáře. Při použití výše uvedených nových dat by to byly 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

můžete zadat jakékoli msg_id, které se vám líbí, takže pokud chci všechny zprávy níže msg 1-2-2-1, pošlete mi msg_id =6:

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

Pokud však chcete všechny zprávy pro všechny kořeny, můžete tento nový sproc, který jsem vytvořil, nazvat následovně:

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

Hlavním problémem s tím je, jak se vaše tabulka zpráv rozrůstá, že bude vracet spoustu dat, a proto jsem se zaměřil na konkrétnější proces, který stahoval zprávy pouze pro daný kořenový uzel nebo spouštěl msg_id.

Nebudu zveřejňovat nový kód sproc, protože je prakticky stejný jako původní, ale všechny pozměňovací návrhy najdete zde:http://pastie.org/1339618

Poslední změna, kterou budete muset provést, je ve skriptu php, který nyní bude volat nový sproc následovně:

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

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

Doufám, že to pomůže :)

call message_hier_all();


  1. EF Core 2.0 Identity – Přidání vlastností navigace

  2. Jak vrátit všechna nedůvěryhodná omezení cizích klíčů na SQL Server (příklad T-SQL)

  3. Vyberte odlišné záznamy ve spojení

  4. Zkontrolujte překrývání časových období v MySQL