OK werken van de backend naar de front-end...
Je zou vanuit je php-script een enkele niet-recursieve opgeslagen procedure (sproc) kunnen aanroepen die de berichthiërarchie voor je genereert. Het voordeel van deze aanpak is dat u slechts een SINGLE . hoeft te maken oproep van php naar uw database, terwijl als u inline SQL gebruikt, u zoveel aanroepen zult doen als er niveaus zijn (minimaal). Een ander voordeel is dat, aangezien het een niet-recursieve sproc is, het extreem performant is en het houdt ook je php-code mooi en schoon. Tot slot, en ik moet dit voor de goede orde zeggen, dat het aanroepen van opgeslagen procedures veiliger en efficiënter is dan welke andere methode dan ook, omdat u alleen machtigingen hoeft uit te voeren aan uw app-gebruiker en opgeslagen procedures minder retourvluchten naar de database vereisen dan welke andere methode dan ook. andere methoden, waaronder geparameteriseerde zoekopdrachten die ten minste 2 aanroepen nodig hebben voor een enkele zoekopdracht (1 om de querysjabloon in de db in te stellen, de andere om de parameters in te vullen)
Dus hier is hoe je de opgeslagen procedure zou aanroepen vanaf de MySQL-opdrachtregel.
call message_hier(1);
en hier is de resultatenset die het maakt.
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, dus nu hebben we de mogelijkheid om een volledige of gedeeltelijke berichtenboom op te halen door simpelweg onze sproc aan te roepen met het startknooppunt dat we nodig hebben, maar wat gaan we doen met de resultatenset ??
In dit voorbeeld heb ik besloten dat we er een XML DOM mee gaan genereren, dan hoef ik alleen maar de XML te transformeren (XSLT) en we hebben een geneste berichtenwebpagina.
PHP-script
Het php-script is vrij eenvoudig, het maakt gewoon verbinding met de database, roept de sproc aan en laat de resultatenset doorlopen om de XML DOM te bouwen. Onthoud dat we de database maar één keer bellen.
<?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-uitvoer
Dit is de XML die het php-script genereert. Als je deze XML opslaat in een bestand en het in je browser opent, kun je de niveaus uit- en samenvouwen.
<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>
Nu kunt u afzien van het bouwen van de XML DOM en het gebruik van XSL om een webpagina weer te geven als u dat wilt en misschien gewoon de resultatenset herhalen en de berichten direct weergeven. Ik heb gewoon deze methode gekozen om mijn voorbeeld zo uitgebreid en informatief mogelijk te maken.
MySQL-script
Dit is een compleet script inclusief tabellen, sprocs en testdata.
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);
De volledige bron voor dit antwoord is hier te vinden:http://pastie.org/1336407 . Zoals je al hebt opgemerkt, heb ik de XSLT weggelaten, maar je zult waarschijnlijk niet de XML-route volgen en als je dat doet, zijn er enorm veel voorbeelden op internet.
Ik hoop dat je dit nuttig vindt :)
BEWERKEN:
Iets meer gegevens toegevoegd zodat je meer dan één hoofdbericht hebt (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
Als je nu alleen de berichten wilt ontvangen die specifiek zijn voor een root-knooppunt (startbericht), kun je de oorspronkelijke opgeslagen procedure aanroepen door de start-msg_id van de root die je nodig hebt, door te geven. Met de nieuwe gegevens hierboven zou dat msg_ids 1,9,14 zijn.
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
je kunt elke gewenste msg_id doorgeven, dus als ik alle berichten onder msg 1-2-2-1 wil, geef je msg_id =6 door:
call message_hier(6); -- returns all messages belonging to msg_id = 6
Als je echter alle berichten voor alle wortels wilt hebben, kun je deze nieuwe sproc die ik heb gemaakt als volgt noemen:
call message_hier_all(); -- returns all messages for all roots.
Het grootste probleem hiermee is dat als je berichtentabel groeit, het veel gegevens zal retourneren. Daarom concentreerde ik me op een meer specifieke sproc die alleen berichten ophaalde voor een bepaald rootknooppunt of msg_id startte.
Ik zal de nieuwe sproc-code niet posten omdat deze vrijwel hetzelfde is als het origineel, maar je kunt alle wijzigingen hier vinden:http://pastie.org/1339618
De laatste wijziging die u moet aanbrengen, bevindt zich in het php-script dat nu de nieuwe sproc als volgt zal aanroepen:
//$result = $conn->query(sprintf("call message_hier(%d)", 1)); // recommended call
$result = $conn->query("call message_hier_all()"); // new sproc call
Ik hoop dat dit helpt :)
call message_hier_all();