Vývoj modulů pro CMS systém Drupal 6.x (3. díl)
V třetím pokračování seriálu o vývoji modulů se nejprve seznámíme s databází, která se vytvoří během instalace Drupalu a následně začneme s vývojem vlastního modulu: hlasování pro článek.
Letmý náhled do databáze
Drupal ke svým účelům pracuje s několika databázovými tabulkami. Tabulky, které existují v každé instalaci Drupalu se vztahují k core-required modulům. Další tabulky se vytvoří aktivací dalších modulů, a to jak základních, které jsou součástí instalace Drupalu (ale jsou po nainstalování neaktivní), tak modulů rozšiřujících (třeba jednoho z našich).
Tip: Referenci k tabulkám core modulů Drupalu naleznete na adrese http://drupal.org/node/22754. Při studiu je třeba opět dávat pozor, zda se díváme do reference ke správné verzi Drupalu.
Mezi nejzajímavější tabulky patří:
node
– základní informace o každém nodu v systémunode_revision
– informace o revizích každého nodunode_counter
– statistiky k jednotlivým nodůmcomments
– komentáře k jednotlivým nodůmfiles
– informace k uploadovaným souborůmuser_roles
– vazba mezi uživatelem a typem uživatelského účtuuser
– informace o uživatelíchrole
– informace o rolích (typech uživatelského účtu)cache
– některé stránky jsou cachovány pro zvýšení výkonu sítěsessions
– sessionsblocks
– nastavení a obsah bloků
S těmito (a dalšími neuvedenými) tabulkami mohou pracovat vaše moduly: mohou z nich získávat data, editovat obsah a měnit jejich strukturu (například přidáním nového sloupce), což však nebývá obvyklé.
Váš modul může přirozeně vytvářet tabulky nové. Ty jsou Drupalem automaticky vytvořeny během instalace (první aktivace) příslušného modulu administrátorem. Popis tabulek se musí nacházet v hooku hook_schema
, která musí být definována v souboru nazevmodulu.install
a zapisuje se pomocí speciálního API. Reference k tomuto API se nachází zde a my se s ní seznámíme v souvislosti s našim prvním modulem.
Mohlo by být zajímavé nahlédnout, jak jsou vytvořeny tabulky core modulů během instalace Drupalu. Kód, který hledáme, se nachází v souboru system.install
ve funkci system_install
:
// Create tables. $modules = array('system', 'filter', 'block', 'user', 'node', 'comment', 'taxonomy'); foreach ($modules as $module) { drupal_install_schema($module); }
Funkce drupal_install_schema
, zajišťuje instalaci každého z modulů a nachází se v souboru includes/common.inc
.
Náš první modul – hlasování pro článek
Na místo pokračování v popisu vnitřností Drupalu se pustíme rovnou do psaní jednoduchého modulu, přičemž další nezbytné znalosti o systému získáme „zapochodu“. Modul, který hodláme vytvořit, patří v programování pro CMS mezi kategorii „hello world“ programů. I když (narozdíl od „hello world“ programu) je – dokonce bez dalšího upravování – skutečně použitelný. Co tedy naprogramujeme? Naprogramujeme modul pro hodnocení článků čtenáři. Od ostatních podobných jej trochu modifikujeme v tom smyslu, že návštěvník může udělit/neudělit danému článku pouze jeden hlas (bod).
Modul nazveme „Hlasování“ a jako jednoslovní název, který bude prefixem názvu všech funkcí, volíme „hlasovani“. Náš modul se bude skládat z následujících souborů:
hlasovani.info
– soubor bude obsahovat základní informace, které se zobrazují v administrativní části systému a další informacehlasovani.install
– soubor je aktivní během instalace a odinstalace moduluhlasovani.modul
– samotný PHP kód moduluhlasovani.css
– definice stylů
Jako první krok vytvoříme ve složce sites/all/modules
nový adresář hlasovani
, ve které se budou nacházet všechny (výše zmíněné) soubory našeho modulu. Dále vytvoříme všechny čtyři soubory a necháme je prozatím prázdné. Postupně je budeme vyplňovat kódem.
hlasovani.info – informace o modulu
Soubory .info
jsou formátu .ini
, což je jednoduchý standard pro PHP konfigurační soubory. Více informací o .ini
souborech naleznete například v referenci k PHP.
V našem případě vyplníme ze všech možných položek o nichž hovoří reference Drupalu pouze několik. Každá položka má název (identifikátor) a hodnotu a píše se na samostatný řádek. Název položky a její hodnota jsou odděleny symbolem „=“. Záleží na dobré vůli programátora, které a kolik položek vyplní (a zda vůbec nějaké). My vyplníme položky name
, description
, package
a code
.
Položka name
nese informaci o názvu modulu:
name = Hlasování
Do položky description
, v níž je vyplněn popis modulu, napíšeme:
description = Modul umožňuje čtenářům hlasovat váhou jednoho hlasu pro publikované nody.
V další položce vyplníme informaci o tom, pro jakou verzi Drupalu je náš modul určen:
core = 6.x
V poslední položce vyplníme informaci o tom, do jakého balíčku (položka package) náš modul náleží. Můžeme si vymyslet vlastní název balíčku (například „Pokusné moduly“). Moduly se stejným názvem balíčku jsou v administrační části od sebe graficky odděleny:
package = Pokusné moduly
Na začátek souboru ještě přidáme řádek s prázdným CVS identifikačním tagem (ten budeme přidávat na začátek všech souborů):
; $Id$
Jsme hotovi. Celý soubor hlasovani.info
by měl vypadat takto:
; $Id$ name = Hlasování description = Modul umožňuje čtenářům hlasovat váhou jednoho hlasu pro publikované nody. core = 6.x package = Pokusné moduly
Pokud se nyní podíváte do administrátorské sekce Drupalu, do části, kde se aktivují moduly, měli byste vidět váš modul v samostatné záložce „Pokusné moduly“ s popisem, který odpovídá tomu, co jsme vyplnili v souboru hlasovani.info
. V administraci se zdá, že by bylo možné modul aktivovat. Můžete to zkusit. Drupal sice vypíše hlášku „The configuration options have been saved.“, ale ve skutečnosti se nic nestane. Přirozeně, zatím jsem žádný modul nenapsali.
Návrh databáze a soubor hlasovani.install
Než začneme psát kód, měl bych se zmínit o tom, že existují tzv. Coding standards, které jsou součástí oficiální reference k Drupalu. Budeme se snažit těchto doporučení držet, neboť jsou komunitou Drupalu v podstatě vyžadovány (je považováno za neslušné se od nich příliš odchylovat).
Komunita Drupalu je poměrně háklivá na styl zápisu komentářů. Krátce se tedy o tom zmíníme:
Krátké komentáře k funkcím píšeme:
/** * Funkce konverguje pole na objekt. */ function array2object($array) {
Hook dokumentujeme jednoduše jako:
/** * Implementace hook_help(). */ function blog_help($section) { // ... }
Drupal podporuje styl dokumentace Doxygenu, více informací o tom naleznete opět v referenci. U složitějších funkcí tedy můžeme použít tagy Doxygenu:
/** * Verifikuje syntax emailove adresy. * * Prazdna e-mailova adresa je povolena, viz RFC 2822. * * @param $mail * e-mailova adresa - string * @return * TRUE, pokud je adresa ve spravnem formatu */ function valid_email_address($mail) {
Přistupíme k implemetaci souboru hlasovani.install
. Obecně je soubor .install aktivní během instalace a odinstalace modulu a je v něm definováno schéma databáze. Reference Drupalu říká, že soubor .install
má implementovat tři hooky:
hook_schema
– zde je definováno databázové schémahook_install
– hook instaluje aktuální databázové schéma voláním funkcedrupal_install_schema
, může vykonat i další operace, které souvisejí s instalací modulu.hook_uninstall
– hook je volán při odinstalaci (nikoli deaktivaci) modulu. Může například odstranit databázové tabulky voláním funkcedrupal_unistall_schema
(je otázkou, zda je rozumné při odinstalaci modulu vždy po sobě uklidit tímto způsobem), smazat nepotřebné soubory, odstranit globální proměnné apod.
Je nutné striktně rozlišovat mezi instalací/aktivací a deaktivací/odinstalací. Instalace je první aktivace modulu (po tom co buď vůbec nebyl ještě aktivován, nebo byl předtím odinstalován). Aktivace je opětovné přivedení modulu k životu (po tom, co byl deaktivován). Během instalace je volána hook hook_install
, jehož prací je většinou modifikace databáze. Hook není volán během aktivace modulu (a nedochází tak k pokusu o vytvoření již existujících tabulek, přepisování souborů apod.).
Pokud je v administrativní části modul deaktivován, není ještě odinstalován (i když je deaktivace nutnou podmínkou k odinstalaci, viz administrace). Teprve při odinstalaci je volán hook hook_uninstall
. Ten není volán při deaktivaci modulu a nemůže tak dojít pouhou deaktivací modulu k vymazání obsahu databáze.
Do souboru hlasovani.install
napíšeme:
function hlasovani_schema() { $schema['hlasovani'] = array( // specifikace databázové tabulky ); return $schema; } function hlasovani_install() { // edituj databázi drupal_install_schema('hlasovani'); } function hlasovani_uninstall() { // ukliď po sobě v databázi drupal_uninstall_schema('hlasovani'); }
Funkce hlasovani_install
a hlasovani_uninstall
jsou již hotové. K jejich implementaci nám stačil vždy jeden příklad. Nevyplněna zůstala pouze funkce hlasovani_schema
, kde budeme definovat novou tabulku hlasovani
, která se během instalace vytvoří a která bude uchovávat informaci o hlasování jednotlivých uživatelů.
Tabulku bude obsahovat vazbu na tabulku node
a to sloupcem nid
(nid jednoznačně identifikují nod, viz tabulka node
ve vaší instalaci Drupalu) a dále vazbu na uživatele sloupcem uid (viz tabulka
user
ve vaší instalaci Drupalu). Skutečnost, že uživatel hlasoval pro jistý článek do databáze, zaneseme přidáním nového řádku s příslušnými uid a nid.
Vše bude jasnější z tohoto ER diagramu:
Vytvoření tabulky by odpovídal SQL příkaz:
CREATE TABLE hlasovani ( uid int NOT NULL default '0', nid int NOT NULL default '0', PRIMARY KEY (uid, nid));
Stačí nám tato tabulka? Určitě ano. Každý řádek bude uchovávat informaci o tom, že uživatel s jistým id (uid) hlasoval pro jistý node (s nid). Počet hlasů pro jistý node zjistíme jednoduše:
SELECT COUNT(*) FROM hlasovani WHERE nid = xx;
Primární klíč se skládá z kombinace uid
a nid
. To je vpořádku, protože daný uživatel smí pro daný článek hlasovat maximálně jednou. Databázi bychom mohli navrhnout i jinak, například tak, abychom vždy při generování stránky s nodem nemuseli přepočítávat počet hlasů, ale zde pro jednoduchost ponecháme návrh takto.
Od Drupalu verze 6 se při vytváření tabulek nepoužívá přímo SQL kód, ale vytváří se pole (PHP datový typ array) podle tzv. Schema API. Výhodou tohoto přístupu je, že již není třeba psát SQL příkaz zvlášť pro každou databázi a o vytvoření správného příkazu se postará sám Drupal na základě databáze, kterou právě používá. Drupal například podporuje databáze (mimo obligátních MySQL a PostreSQL) SQLite, Microsoft SQL, Oracle apod.
Samotné Schema API je poměrně jednoduché. Vytváří se pole polí. Na první úrovni jsou jednotlivé tabulky, jako klíč je uveden název tabulky (v našem případě "hlasovani") a obsahem je pole, které již specifikuje danou tabulku. To může mít několik položek (v následujícím výčtu jsou uvedeny všechny, o nichž se zmiňuje oficiální reference):
description
- popis tabulkyfields
- jednotlivé sloupce tabulky, každý sloupec je opět definován jako nové poleprimary key
- pole primárních klíčůunique key
- pole unikátních klíčůindexes
- pole indexů
Pole fields
, definují sloupce. Každý sloupec je opět definován jako vnořené pole, s položkami, které definují daný sloupec tabulky. Klíčem je název sloupce. V následujícím výčtu uvádíme všechny položky o nichž se zmiňuje oficiálná reference Drupalu 6.x):
description
- popis sloupcetype
- určuje datový typ, přehled podporovaných datových typů naleznete zdesize
- určuje velikost datového typu, může nabývat hodnota: "tiny", "small", "medium", "normal", "big"not null
- určuje, zda může být sloupec nevyplněndefault
- jaká má být defaultní hodnota sloupcelength
- maximální velikost typů "varchar", "text" a "int", pro ostatní typy je ignorovánounsigned
- boolean hodnota určuje, zda má být datový typ "int", "float" a "numerics" se znaménkem nebo bezprecision
- pro datový typ "numerics" určuje maximální počet číslicserialize
- boolean hodnota nastavující, zda bude datový typ uložen serializován jako string
Spíše než ze slovního popisu a výčtů, bude vše jasné z následujícího kódu. V něm jsme v Scheme API zapsali tabulku hlasovani
(viz. předchozí SQL kód):
$schema['hlasovani'] = array( 'description' => 'The base table for nodes.', 'fields' => array( 'nid' => array( 'description' => 'The primary identifier for a node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), 'uid' => array( 'description' => 'aentifier for a node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE ) ), 'primary key' => array('nid', 'uid') );
Soubor hlasovani.install
by měl po této úpravě (a dopsaní komentářů) obsahovat kód:
<?php /** * Implementace hook_schema(). * @return array se tabulkou "hlasovani" definovanou pomoci Scheme API */ function hlasovani_schema() { $schema['hlasovani'] = array( 'description' => 'The base table for nodes.', 'fields' => array( 'nid' => array( 'description' => 'The primary identifier for a node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), 'uid' => array( 'description' => 'aentifier for a node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE ) ), 'primary key' => array('nid', 'uid') ); return $schema; } /** * Implementace hook_install(). */ function hlasovani_install() { // edituj databázi drupal_install_schema('hlasovani'); } /** * Implementace hook_uninstall(). */ function hlasovani_uninstall() { // ukliď po sobě v databázi drupal_uninstall_schema('hlasovani'); } ?>
Tím jsme svou práci na souboru hlasovani.install
dokončili. Pokud nyní aktivujete modul, měla by se vytvořit prázdná tabulka podle výše uvedeného schématu.
Zde bych si dovolil velice důležitou poznámku, byť jsme se o tom již zmiňovali. Hook hook_install
je volán pouze při první aktivaci modulu! Není znovu volán, pokud je modul opětovně aktivován po tom, co byl předtím deaktivován. Je však znovu volán, pokud byl předtím modul odinstalován a nyní je znovu instalován (tj. prvně aktivován).
Deaktivace a odinstalace modulu jsou dvě odlišné věci. Teprve po tom, co je modul deaktivován, může být odinstalován a to ve speciální části (uninstall) administrace modulů. Hook hook_uninstall
je volán nikoli při deaktivaci modulu, ale až při odinstalaci modulu.
Databázová abstraktní vrstva Drupalu
Před implementací souboru, ve kterém bude kód našeho modulu, se zmíníme o databázové abstraktní vrstvě Drupalu. Smyslem této vrstvy je (pokud možno) abstrahovat konkrétní SQL server a SQL dotazy použité ve vašem modulu. Referenci naleznete zde.
Nás budou zajímat především následující funkce:
Funkce db_query
slouží k vykonání SQL příkazu (SELECT
, INSERT
, UPDATE
a samozřejmě dalších). Vrací buď hodnotu FALSE
nebo výsledek SQL dotazu. Funkce db_result
, db_fetch_object
, db_fetch_array
použijeme k přečtení dat, které vrací jako návratovou hodnotu příkaz db_query
. Příkaz db_result
použijeme, pokud očekáváme jednu konkrétní hodnotu (například počet článků v databázi). Příkaz db_fetc_object
přečte při každém volání jeden řádek navrácené tabulky jako objekt. Příkaz db_fetc_object
přečte při každém volání jeden řádek navrácené tabulky jako pole (array).
K psaní SQL dotazů je třeba se ještě zmínit o dvou věcech. Název tabulky je z bezpečnostních důvodů (tzv. SQL injection attacks) zabalen mezi symboly {}
. A při zápisu hodnot do SQL dotazu je možné (a doporučené) použit syntaxi, která je známa u příkazu print
v Cčku (viz reference k funkci db_query
):
db_query('INSERT INTO {hlasovani} (nid, uid) VALUES (%d, %d)', $nid, $uid);
Uvedeme si několik příkladů. Vyčtení jedné hodnoty z databáze:
return (int) db_result(db_query('SELECT COUNT(*) FROM {hlasovani} WHERE nid = %d', $nid));
Ilustrační příklad na vyčítání více řádků z tabulky:
$sql = "SELECT nid FROM {node} ORDER BY nid"; $query = db_query($sql); while($data = db_fetch_object($query)) { $output .= ' '.strval($data->nid); }
Tyto znalosti nám při implementaci modulu budou stačit.
Závěr
Nemalou část modulu jsme již implementovali. Ze čtyř souborů: hlasovani.info
, hlasovani.install
, hlasovani.modul
, hlasovani.css
jsme první dva již implementovali. V posledním díle seriálu implementujeme zbývající dva. Se souborem hlasovani.css
nás žádné problémy nečekají, ale v souboru hlasovani.modul
bude obsažena prakticky veškerá aplikační logika.
Mohlo by vás také zajímat
-
Zabezpečení e-mailů: Jak můžete chránit vaši firemní komunikaci
13. prosince 2023 -
Jak se chránit před podvody na internetu – část 2
14. října 2024 -
Aukce CZ domén: Jak vydražit expirovanou CZ doménu?
12. června 2024
Nejnovější
-
Jaký monitor je nejlepší k novému Macu Mini?
25. listopadu 2024 -
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024
frankye
Lis 27, 2009 v 16:36Hmm, tak sme sa dočkali …super článok:-))
NanaNa
Lis 28, 2009 v 10:26Pěknej tut, jen škoda že hlavní část na kterou se všichni těšili pořád neni :(
Jan Sova
Lis 28, 2009 v 10:40tu NanaNA:
v pristim dile cely modul dokoncime do plne funkcni podoby. Tim skonci prvni (4 dilny) miniserial.
V nasledujicich miniserialech (zase po 3-4 dilech) budeme vylepsovat stavajici modul… a pak treba napiseme zase dalsi :-).
Honza
Ivan Jaroš
Pro 3, 2009 v 16:07hlasovani.module, nie hlasovani.modul !!!
Jakub Suchy
Pro 3, 2009 v 16:18Dobry den,
k clanku bych mel dve vyhrady:
1) Meli bychom lidi ucit Drupal coding standards. Neindentace kodu v prikladech je alarmujici.
2) Myslim, ze zasadou v Drupalu by melo byt psani kodu v anglictine. I v CR. Komentovat priklady je mozne v cestine, ale kod by mel byt anglicky. Opet – coding standards.
miroslav.kucera
Pro 3, 2009 v 23:15Dobry den, tim „neindentace“ myslite chybejici odsazeni? To je spise moje chyba, jakozto vkladatele clanku do redakcniho systemu, nez autora. Vsechny zdrojaky tohoto serialu budou nalezite odsazeny :-)
Jan Sova
Pro 3, 2009 v 23:481) Jde o „elemenet“ Id, ktery by se mel vyskytovat v kazdem souboru. Priznam se, ze jsem to vynechal.
2) psani kodu v cestine podle meho nazoru proti coding standards neni
David Monoszon
Pro 7, 2009 v 23:03Přijďte 16. prosince na Drupal MeetUp v Praze, kde vyslechnete mnoho zajímavých přednášek a potkáte ty nejlepší Drupal lidi v čechách http://www.drupal.cz/clanky/drupal/drupal-setkani-praha-16-prosinec-2009
Miroslav Kucera
Pro 8, 2009 v 9:10David Monoszon – a proc nenapisete do redakce? Mohlo to vyjit jako zpravicka/bleskovka…
Jožka
Pro 15, 2009 v 17:25Zdravím, kdy se chystá poslední díl seriálu? :) Velmi akutně řeším výrobu jednoho modulku a nemůžu se dočkat posledního dílu seriálu, abych se doučil zbylou práci :)
Díky moc!
Miroslav Kucera
Pro 15, 2009 v 22:03Dobry den, vyjde ve ctvrtek :-)
frankye
Lis 8, 2010 v 10:24Myslim, ze v casti „Databázová abstraktní vrstva Drupalu“ a druhom odstavci mate chybu v poslednej vete „Příkaz db_fetc_object přečte při každém volání jeden řádek navrácené tabulky jako pole (array).“, by malo namiesto „db_fetc_object“ byť „db_fetch_array“.