Existuje mnoho způsobů. Zde je jeden přístup, který se mi líbí (a používám ho pravidelně).
Databáze
Zvažte následující strukturu databáze:
CREATE TABLE comments (
id int(11) unsigned NOT NULL auto_increment,
parent_id int(11) unsigned default NULL,
parent_path varchar(255) NOT NULL,
comment_text varchar(255) NOT NULL,
date_posted datetime NOT NULL,
PRIMARY KEY (id)
);
vaše data budou vypadat takto:
+-----+-------------------------------------+--------------------------+---------------+
| id | parent_id | parent_path | comment_text | date_posted |
+-----+-------------------------------------+--------------------------+---------------+
| 1 | null | / | I'm first | 1288464193 |
| 2 | 1 | /1/ | 1st Reply to I'm First | 1288464463 |
| 3 | null | / | Well I'm next | 1288464331 |
| 4 | null | / | Oh yeah, well I'm 3rd | 1288464361 |
| 5 | 3 | /3/ | reply to I'm next | 1288464566 |
| 6 | 2 | /1/2/ | this is a 2nd level reply| 1288464193 |
... and so on...
Je poměrně snadné vybrat vše použitelným způsobem:
select id, parent_path, parent_id, comment_text, date_posted
from comments
order by parent_path, date_posted;
řazení podle parent_path, date_posted
obvykle vytvoří výsledky v pořadí, v jakém je budete potřebovat při generování stránky; ale budete si chtít být jisti, že máte v tabulce komentářů index, který to bude správně podporovat – jinak dotaz funguje, ale je opravdu, ale opravdu neefektivní:
create index comments_hier_idx on comments (parent_path, date_posted);
Pro každý daný komentář je snadné získat celý strom podřízených komentářů daného komentáře. Stačí přidat klauzuli where:
select id, parent_path, parent_id, comment_text, date_posted
from comments
where parent_path like '/1/%'
order by parent_path, date_posted;
přidaná klauzule where bude využívat stejný index, který jsme již definovali, takže můžeme začít.
Všimněte si, že jsme nepoužili parent_id
dosud. Ve skutečnosti to není nezbytně nutné. Ale zahrnuji to, protože nám umožňuje definovat tradiční cizí klíč k vynucení referenční integrity a implementovat kaskádové mazání a aktualizace, pokud chceme. Omezení cizího klíče a kaskádová pravidla jsou k dispozici pouze v tabulkách INNODB:
ALTER TABLE comments ENGINE=InnoDB;
ALTER TABLE comments
ADD FOREIGN KEY ( parent_id ) REFERENCES comments
ON DELETE CASCADE
ON UPDATE CASCADE;
Správa hierarchie
Abyste mohli použít tento přístup, musíte se samozřejmě ujistit, že jste nastavili parent_path
správně, když vložíte každý komentář. A pokud komentáře přesouváte (což by jistě byl zvláštní případ použití), budete se muset ujistit, že ručně aktualizujete každou parent_path každého komentáře, který je podřazený přesunutému komentáři. ... ale obojí je docela snadné udržet krok.
Pokud si opravdu chcete udělat fantazii (a pokud to vaše databáze podporuje), můžete napsat spouštěče pro transparentní správu parent_path -- nechám to na cvičení pro čtenáře, ale základní myšlenkou je, že spouštěče vložení a aktualizace by se spustily před potvrzením nové vložky. šli by po stromu (pomocí parent_id
vztah cizího klíče) a znovu sestavit hodnotu parent_path
podle toho.
Je dokonce možné prolomit parent_path
do samostatné tabulky, která je zcela spravována spouštěči v tabulce komentářů, s několika pohledy nebo uloženými procedurami pro implementaci různých dotazů, které potřebujete. Tím zcela izolujete svůj kód střední vrstvy od potřeby znát mechanismus ukládání informací o hierarchii nebo se o něj starat.
Samozřejmě, žádná z ozdobných věcí není v žádném případě vyžadována - obvykle zcela postačí umístit parent_path do tabulky a napsat nějaký kód ve vaší střední vrstvě, abyste zajistili, že bude správně spravován spolu se všemi ostatními poli. už musíte zvládnout.
Uvádění limitů
MySQL (a některé další databáze) vám umožňuje vybrat "stránky" dat pomocí LIMIT
klauzule:
SELECT * FROM mytable LIMIT 25 OFFSET 0;
Bohužel při práci s hierarchickými daty, jako je tato, samotná klauzule LIMIT nepřinese požadované výsledky.
-- the following will NOT work as intended
select id, parent_path, parent_id, comment_text, date_posted
from comments
order by parent_path, date_posted
LIMIT 25 OFFSET 0;
Místo toho musíme samostatně vybrat úroveň, na kterou chceme limit uložit, a pak to spojit zpět s naším dotazem „podstromu“, abychom poskytli konečné požadované výsledky.
Něco jako toto:
select
a.*
from
comments a join
(select id, parent_path
from comments
where parent_id is null
order by parent_path, post_date DESC
limit 25 offset 0) roots
on a.parent_path like concat(roots.parent_path,roots.id,'/%') or a.id=roots.id)
order by a.parent_path , post_date DESC;
Všimněte si příkazu limit 25 offset 0
, pohřbený uprostřed vnitřního selektu. Toto prohlášení načte nejnovějších 25 komentářů na "kořenové úrovni".
[editovat:možná zjistíte, že si musíte s věcmi trochu pohrát, abyste získali možnost objednávat a/nebo omezovat věci přesně tak, jak chcete. to může zahrnovat přidání informací v rámci hierarchie, která je zakódována v parent_path
. například:místo /{id}/{id2}/{id3}/
, můžete se rozhodnout zahrnout post_date jako součást parent_path:/{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/
. To by velmi snadno usnadnilo získání požadovaného pořadí a hierarchie na úkor nutnosti vyplňovat pole předem a spravovat je podle změn dat]
doufám, že to pomůže. hodně štěstí!