OK, vom Backend zum Frontend zu arbeiten...
Sie könnten eine einzelne nicht rekursive gespeicherte Prozedur (sproc) aus Ihrem PHP-Skript aufrufen, die die Nachrichtenhierarchie für Sie generiert. Der Vorteil dieses Ansatzes ist, dass Sie nur ein SINGLE erstellen müssen Aufruf von PHP an Ihre Datenbank, während Sie bei Verwendung von Inline-SQL mindestens so viele Aufrufe wie Ebenen durchführen. Ein weiterer Vorteil ist, dass es, da es sich um einen nicht rekursiven Sproc handelt, extrem leistungsfähig ist und außerdem Ihren PHP-Code schön und sauber hält. Abschließend, und das muss ich fürs Protokoll sagen, ist das Aufrufen gespeicherter Prozeduren sicherer und effizienter als jede andere Methode, da Sie Ihrem App-Benutzer nur Ausführungsberechtigungen GEWÄHREN müssen und gespeicherte Prozeduren weniger Roundtrips zur Datenbank erfordern als alle anderen andere Methoden, einschließlich parametrisierter Abfragen, die mindestens 2 Aufrufe für eine einzelne Abfrage erfordern (1 zum Einrichten der Abfragevorlage in der Datenbank, der andere zum Auffüllen der Parameter)
So würden Sie also die gespeicherte Prozedur von der MySQL-Befehlszeile aus aufrufen.
call message_hier(1);
und hier ist die Ergebnismenge, die es erstellt.
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, jetzt haben wir die Möglichkeit, einen vollständigen oder teilweisen Nachrichtenbaum abzurufen, indem wir einfach unseren Sproc mit dem gewünschten Startknoten aufrufen, aber was machen wir mit der Ergebnismenge ??
Nun, in diesem Beispiel habe ich entschieden, dass wir damit ein XML-DOM generieren werden, dann muss ich nur noch das XML transformieren (XSLT), und wir haben eine verschachtelte Nachrichten-Webseite.
PHP-Skript
Das PHP-Skript ist ziemlich einfach, es stellt nur eine Verbindung zur Datenbank her, ruft das Sproc auf und durchläuft das Resultset, um das XML-DOM zu erstellen. Denken Sie daran, dass wir die DB nur einmal aufrufen.
<?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-Ausgabe
Dies ist das XML, das das PHP-Skript generiert. Wenn Sie dieses XML in einer Datei speichern und in Ihrem Browser öffnen, können Sie die Ebenen erweitern und reduzieren.
<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>
Jetzt könnten Sie auf das Erstellen des XML-DOM und die Verwendung von XSL zum Rendern einer Webseite verzichten, wenn Sie dies wünschen, und vielleicht einfach die Ergebnismenge in einer Schleife ausführen und die Nachrichten direkt rendern. Ich habe diese Methode einfach gewählt, um mein Beispiel so umfassend und informativ wie möglich zu gestalten.
MySQL-Skript
Dies ist ein vollständiges Skript mit Tabellen, Sprocs und Testdaten.
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);
Die vollständige Quelle für diese Antwort finden Sie hier:http://pastie.org/1336407 . Wie Sie bereits bemerkt haben, habe ich XSLT weggelassen, aber Sie werden wahrscheinlich nicht den XML-Weg gehen, und wenn Sie das tun, gibt es haufenweise Beispiele im Web.
Ich hoffe, Sie finden das hilfreich :)
BEARBEITEN:
Etwas mehr Daten hinzugefügt, damit Sie mehr als eine Root-Nachricht haben (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
Wenn Sie jetzt nur die Nachrichten abrufen möchten, die für einen Stammknoten spezifisch sind (Startnachricht), können Sie die ursprüngliche gespeicherte Prozedur aufrufen, indem Sie die Start-msg_id des gewünschten Stamms übergeben. Unter Verwendung der neuen Daten oben wären das 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
Sie können jede beliebige msg_id übergeben, wenn ich also alle Nachrichten unter msg 1-2-2-1 haben möchte, dann würden Sie msg_id =6 übergeben:
call message_hier(6); -- returns all messages belonging to msg_id = 6
Wenn Sie jedoch alle Nachrichten für alle Wurzeln haben möchten, können Sie diesen neuen Sproc, den ich erstellt habe, wie folgt nennen:
call message_hier_all(); -- returns all messages for all roots.
Das Hauptproblem dabei ist, dass Ihre Nachrichtentabelle, wenn sie wächst, viele Daten zurückgeben wird, weshalb ich mich auf einen spezifischeren Sproc konzentriert habe, der nur Nachrichten für einen bestimmten Root-Knoten abgerufen oder msg_id gestartet hat.
Ich werde den neuen Sproc-Code nicht posten, da er praktisch derselbe ist wie das Original, aber Sie können alle Änderungen hier finden:http://pasti.org/1339618
Die letzte Änderung, die Sie vornehmen müssen, befindet sich im PHP-Skript, das nun den neuen Sproc wie folgt aufruft:
//$result = $conn->query(sprintf("call message_hier(%d)", 1)); // recommended call
$result = $conn->query("call message_hier_all()"); // new sproc call
Hoffe das hilft :)
call message_hier_all();