48 nélkülözhetetlen Drupal fejlesztői tipp a Lullabot-tól

zserno képe

Az alábbi tippeket Justin Emond gyűjtötte egy négynapos Lullabot tréningen, melynek fő témái a sminkelés, form API, menu API, modulfejlesztés és jQuery voltak.

  1. Mindig nyomtassuk ki a $body_classes változót a body elem class attribútumaként a sminkünkben. Ezzel számos hasznos osztály válik elérhetővé CSS-ből, pl. "front", "not-front", "logged-in", stb.
  2. Az /admin/build/block oldal az egyetlen olyan admin oldal, ami nem használja az adminisztrációs sminket.
  3. A két leggyakrabban kifelejtett sminkváltozó a page.tpl.php-ben a $closure és a $tabs.
  4. Alapvető sminkelési módszer, hogy a módosítani kívánt sablon file-t előbb átmásoljuk a sminkünkbe az őt definiáló modul könvytárából, majd a másolatot szerkesztjük, pl. node.tpl.php.
  5. A fordítható szövegekben használjunk paramétereket (ún. placeholder tokeneket), mert a fordításban lehet, hogy más lesz a szórend. Pl. 


     $variables['submitted'] = t('On @date', array('@date'=>format_date($variables['created'],'custom','F jS')));
  6. Főverziók közötti frissítéskor legjobb, ha újraírjuk az összes smink függvényt, amiket korábban felülírtunk, így biztosan nem hagyunk ki fontos kódváltoztatást.
  7. Ha nem szeretnénk használni a teljes $content változót a node.tpl.php file-okban, akkor nyugodtan hagyjuk ki. Helyette nyomtassuk ki a szükséges mezőket egyenként.
  8. Teljesítmény: nézeteknél használjuk a "Mezők" sor stílust a "Tartalom" helyett, mivel utóbbi egy-egy node_load() hívást végez a nézet minden egyes sorára, ami egyenként több mint 50 lekérdezés is lehet. A "Mezők" stílus ezzel szemben csak a valóban szükséges adatokat gyűjti össze.
  9. A dpm() függvény a – CakePHP rendkívül hasznos pr() függvényéhez hasonlóan – komplex adatok rendezett megjelenítésére való, mely főként a hibakeresésben nyújt hasznos segítséget.
  10. Saját moduljainkat tegyük egy külön csomagba, így a modul listában jól elkülönülnek majd, továbbá ezzel a későbbi fejlesztők munkáját is megkönnyítjük.
  11. Használjuk a coder modul-t a Drupal 6 és 7 közötti API változások kiderítéséhez.
  12. Hasznos szokás, ha a "$user" változónevet az aktuálisan (tehát a kód futtatásának pillanatában) az oldalra bejelentkezett felhasználó jelölésére használjuk. Az egyéb okból betöltött felhasználót tartalmazó objektum jelölésére használjuk az "$account" változónevet.
  13. Teljesítmény: A teljes variable tábla előre betöltésre kerül minden oldallekéréskor, ezért ügyeljünk rá, hogy mekkora adatot tárolunk benne.
  14. Teljesítmény: A variable_get() hívása nem kerül semmibe (nem nyúl az adatbázishoz), hiszen minden Drupal változó már a memóriában van.
  15. Ne használjuk a t() függvényt a menük "title" és "description" elemein, mert azokat a Drupal cache-ből olvassa be, így a gyorstár felépítésekori nyelv lesz úgyis a mérvadó.
  16. Használjuk a MENU_LOCAL_TASK konstanst a hook_menu()-ben az ún. fülek (tabok)
    létrehozásához, ahogy az a /node vagy /user oldalon is látható.
  17. Teljesítmény: a terjedelmes menü callback függvényeinket tegyük külön .inc file-okba a menü tömb "file" kulcsának használatával. Így hatékonyabb lesz a memóriakezelés, mivel a Drupal a .module file-okat minden oldalletöltéskor memóriába tölti, viszont a .inc file-okat csak amikor szükséges.
  18. A hook_menu()-ben létrehozott útvonalakban a %user és %node jelölők (ún. magic handler-ek) használata esetén a Drupal automatikusan meghívja a node_load() ill. user_load() eljárásait a jelölők helyén álló aktuális ID-vel. Pl. $items['node/%node/edit'] menü útvonal esetén nem kell külön betölteni a $node objektumot a callback függvényünkben.
  19. Az előzőhöz hasonlóan lehetőség van saját magic handlerek használatára is. Pl. %yourown jelölő esetén a Drupal automatikusan meghívja a yourown_load() eljárásunkat mielőtt a hozzá tartozó menü callback-et meghívná. Fontos, hogy ezek a függvények a .module file-ban legyenek.
  20. Saját modulunkban bármikor használhatjuk a 

     $GLOBALS['conf']['cache'] = FALSE;
    utasítást, hogy kikapcsoljuk a Drupal gyorsítótárát az adott oldalon. (Megjegyzés: ha az oldal már a gyorstárban van, akkor ürítsük azt, különben nem fogjuk látni az utasítás hatását.)
  21. Könnyen kideríthetjük egy webhelyről, hogy Drupal alapú-e. Nézzük meg a page expire date-et a szerver által visszaküldött HTTP header-ben. Ha ez 1978. november 19. (a Drupal készítőjének, Dries Buytaert-nak a születésnapja), akkor nagy valószínűséggel az.
  22. Route-olás Drupalban: custom_url_rewrite_inbound() és custom_url_rewrite_outbound().
  23. A megfelelő dátum mezőtípus kiválasztása CCK-ban: 

    - Date (iso date): történelmi és nem teljes dátumok tárolására alkalmas (pl. csak év és hónap).

    - Datestamp (Unix epoch): a Drupal alaprendszer által használt formátummal megegyező dátum formátum.

    - Datetime (ajánlott): natív formában tárolja a dátumot az adatabázisban, tehát így lehetőség van az adatbázis szintjén dolgozni a dátumokkal, ami gyors.
  24. Osszuk fel a sites/all/modules könvytárunkat contrib és custom alkönyvtárakra. A contrib-ba tegyük a drupal.org-ról letöltött kiegészítő modulokat, míg a custom-ba a sajátjainkat.
  25. Ha egy kiegészítő modul kódján változtatnunk kell, akkor ezt mentsük ki egy patch file-ba, így lehetőség lesz a kódváltozások verziókövetésére, valamint a javítás beküldésére. Ezeket a patch-eket egy külön könyvtárba gyűjtsük. Később, ha frissíteni kell a módosított modulokat, ellenőrizzük, hogy továbbra is szükség van-e a módosításainkra. Amennyiben egy patch bekerült a modul hivatalos kiadásába, törölhetjük. Különben egyszerűen alkalmazzuk a patch-et.
  26. Saját moduljainkban a hook_menu() legyen mindig az első függvény, mert hasznos útmutatóként szolgál arról, hogy az adott modul mit csinál és hol érhető el ez.
  27. A form tömbök kulcsai azért kezdődnek kettőskereszttel (#), hogy lehessen akár újabb formokat is definiálni a tömbben.
  28. A Drupal minden form state-hez hozzáad egy "clicked_button" attribútumot, hogy lehetővé tegye a kép típusú űrlap beküldő gombok használatát Internet Explorer-ben is, ugyanis az nem használja a beküldő gomb name attribútumát a beküldő gombokon, ahogy azt a többi böngésző teszi.
  29. Űrlap beküldésekor beágyazott mezőkön a következő módon lehet hibát jelezni: "parent][child". Példa: "home][street", ahol street a mező, home pedig az őt tartalmazó űrlap.
  30. Az előzővel megegyező feladatot lát el a form_error() függvény, azonban tisztább és érthetőbb módon működik, mint a form_set_error(). 


     form_set_error('home][street','You must enter the street address.');
    form_error($form['home']['city'], 'You must enter the street address.');
  31. Ha a $form_state['storage'] tömbelembe bármit is teszünk, akkor a Drupal figyelmen kívül hagy minden korábban definiált átirányítást (ld. $form_state['redirect']), és újraépíti az űrlapot beküldés után. Hogy ezt elkerüljük, töröljük a $form_state['storage'] elemét (ld. php unset() függvény).
  32. Bármilyen HTML-t használhatunk modulunk smink függvényeiben, mert a Drupal sminkek képesek ezt később felülírni.
  33. Űrlap építéskor a Drupal automatikusan lerendereli a $form tömb maradék elemeit, amiket mi nem rendereltünk le korábban. Tehát elegendő csak azokkal foglalkozni, amiket mi magunk szeretnék lekezelni (ld. drupal_render() eljárás).
  34. Ha menet közben külső adatbázisra szeretnénk váltani, használjuk a db_set_active() függvényt a váltáshoz. Ekkor a settings.php-ban megadott kapcsolatok közül választhatunk.
  35. A Table Wizard modul segítségével bármilyen adatbázis táblából készíthetünk saját nézetet, sőt több táblánál megadhatjuk a kulcsokat is, amik alapján össze is kapcsolhatjuk őket (ld. még Migrate modul).
  36. Ha egy űrlapelemnek beállítunk egy értéket (a "#value" kulccsal), akkor a beküldés után mindig az lesz az értéke, független attól amit a felhasználó esetleg beállított az űrlapon.
  37. A "value" típusú űrlap elemek ("#type => "value") nem jutnak el a felhasználóig. Ezek értékét az űrlap adatok között tárolja a Drupal, ahol modulunk függvényéiből érhetjük el őket.
  38. Az inline módon beszúrt JavaScript betöltéséig a böngészők nem töltenek semmi mást, tehát blokkoljuk az oldalletöltést arra az időre. Vigyázzunk ezzel.
  39. A VisualjQuery.com oldalon egy hasznos, vizuális jQuery API-t találunk.
  40. Firebug tipp: A konzol alján található ">>>" szimbólum után saját JavaScript kódot futtathatunk bármely betöltött weboldalon. (Megjegyzés: Drupal oldalakon akár jQuery kódot is, hiszen a jQuery a Drupal része.)
  41. HTML tipp: néhány böngésző kidobja a href attribútum nélküli A tag-eket.
  42. jQuery tipp: class keresése sokkal gyorsabb, ha megadjuk a HTML elem típusát is, mert ekkor a jQuery a böngészők beépített getElementysByTagName() eljárását használja. Pl. 
 

    - Gyors: $('.content');
    - Gyorsabb: $('div.content');
    (Megjegyzés: hasonlóan, ID alapú kiválasztás esetén ne használjunk tag-et, mert akkor nem használja a böngészők beépített getElementNameById() eljárását, helyette végig járja az összes lehetséges tag-et, a megadott ID után kutatva.) 

  43. jQuery tipp: A szelektor függvényekben használt $(this) gyorsabb, mintha újra meghívnánk az aktuális szelektort.
  44. Nézeteink kezelésére egy hasznos módszer, ha kiexportáljuk őket egy saját modulba. Az így kapott php kód verziókezelhető, és védett az oldal felhasználóitól, ugyanis így akár ki is kapcsolhatjuk a Views UI modult. További előnye, hogy bármikor visszatérhetünk a nézetünk egy korábbi verziójára, ha szükséges.
  45. Nagy patch-ek kezelése: készítsünk egy modult, amiben a hook_update() hurkot használva végezzük el a szükséges beállításokat. Ezután frissítsuk a patch-elni kívánt modul kódját, futtassuk az update.php-t, végül alkalmazzuk a patch-et.
  46. Mikor figyeljünk a felhasználótól származó inputtal: általánosságban kijelenthető, hogy a sablon rétegben levő adatok biztonságosak, minden e fölötti szinten már nem. Használjuk a check_plain() eljárást, ha nincs HTML az adatban, és check_markup()-ot, ha pedig van (utóbbi a webhely alapértelmezett beviteli szűrőjét használja).
  47. A Drush make modullal lehetőségünk van modulok, sminkek, telepítési profilok és külső függvény könyvtárak egy listáját előre rögzíteni egy ún. drush make file-ba, majd ezt a file-t egy drush parancsként lefuttatva egy üres Drupal odalt kapunk pillanatok alatt, a megadott kiegészítőkkel. Előnye, hogy nem kell újra és újra ismételgetni az unalmas feladatokat minden egyes új projekt kezdetén.
  48. Használjuk a cache_get() és cache_set() eljárásokat ahol csak lehet, hiszen a gyorstárból jövő adatok jóval kevesebb adatbázis terhelést okoznak.


Jelen írás egy fordított kivonat, melyet zserno készített. Az eredeti itt található:
http://www.missingfeatures.com/2010/02/16/48-essential-drupal-developmen...

Köszönet drifternek, hogy megmutatta az eredeti cikket twitteren: http://twitter.com/drifter/status/13919347596!

Ha bármi hasznos tipped van amit nem találsz a fenti listában, kérlek írd meg hozzászólásban.

Hozzászólások

eMeLA képe

Nézeteink kezelésére egy hasznos módszer, ha kiexportáljuk őket egy saját modulba. Az így kapott php kód verziókezelhető, és védett az oldal felhasználóitól, ugyanis így akár ki is kapcsolhatjuk a Views UI modult. További előnye, hogy bármikor visszatérhetünk a nézetünk egy korábbi verziójára, ha szükséges.

A 44-es pontot valaki bővebben ki tudná fejteni ? Esetleg valami linket tud valaki ahol van valami bővebb információ erről ?

Hogy lesz az exportált views-ból modul ?

...mit tudok: http://web.termuves.hu

nevergone képe

Kell írni egy mini-modult, ami megvalósítja a hook_views_default_views()-ot.

Pl. a Calendarba less bele, ezt használja.

zserno képe

Geppuskakezu Nevergone :)

zserno képe

Igy: http://drupalcontrib.org/api/function/hook_views_default_views/6
vigyazat, kell majd hozza a hook_views_api is.

szantog képe

"ugyanis így akár ki is kapcsolhatjuk a Views UI"

Tudtommal a views_ui-t bármikor kikapcsolhatjuk, ha már nem kell többet szerkeszteni a nézetet.

----
Rájöttem, miért kérdezek olyan ritkán a drupal.hu-n. Amíg szedem össze az infokat a kérdéshez, mindig rájövök a megoldásra.

Boobaa képe

Nem csak %user és %node esetén fut le a user_load() meg a node_load(), hanem %whatever esetén is lesz whatever_load(), ha implementálva van - továbbá ha az FALSE-t ad vissza, akkor kapsz egy jó kis 403-at (vagy 404-et, nem emlékszem fejből pontosan), valamint utána az így betöltött objektumot megkapja az access és page callback is - mindenesetre lásd a PDD2 77. oldalán a Wildcards and Parameter Replacement fejezetet.

zserno képe

Igen, barmilyen jelolot hasznalhatunk, Lasd 19.
A betoltott objektumot/objektumokat viszont csak akkor kapjak meg a callback fuggvenyek, ha megadjuk azt/azokat a 'page arguments', 'title arguments' ill. 'access arguments' kulcsokkal (igeny szerint). Pl.

 
$items['node/%node'] = array(
  'title callback' => 'node_page_title',
  'title arguments' => array(1),
  'page callback' => 'node_page_view',
  'page arguments' => array(1),
  'access callback' => 'node_access',
  'access arguments' => array('view', 1),
  'type' => MENU_CALLBACK
);
chx képe

Ezt irtam az eredetihez:

Nate called these magic handlers. <= He might but these are called just load functions in menu.inc. There is nothing magical about them, same as _submit or _validate for forms.
In your module, you can use $GLOBALS['conf']['cache'] = false to turn off caching for a page. <= If you want this for development reasons, then in settings.php add $conf['cache_inc'] = './includes/cache-install.inc'; this nulls every cache operation and so breaks multistep forms but otherwise it's great.
To avoid this you must unset $form_storage. <= $form_state['storage']
Use db_set_active() to switch between the database connections you have specified in your settings file on-the-fly in a routine to easily go outside of the core Drupal database for external content or data. <= and switch back ASAP as watchdog and session writes and so on will not be happy when the table they want to write is not there.
A form type ("#type") of "value" is never sent to the user but kept in the form data so you can access the data in other functions in your module. <= this is largely deprecated in Drupal 6, just use $form['#foo']. Earlier $form was not passed around so you needed the values in $form_values (which is now $form_state['values']).
Managing major patches: Create an empty module and use hook_update() to push major site configuration changes (settings, etc). <= nothing to do with patches. Good tip nonetheless.
The reason the keys in form arrays start with a pound sign is to allow the nesting of forms in the array. <= NO! It allows nesting of form _elements_. Nesting forms is a very complicated matter.

zserno képe

Hol irtad ezeket az eredetihez?

chx képe

Es talan meg nem jelentek meg.

zserno képe

Mar kint is van. Koszonom szepen az eszreveteleket. :)

csg képe

A 42-esnél a megjegyzés nincs rosszul fogalmazva? Nem kéne pont az ID alapú kiválasztásnál is megadni a tag-et, hogy ne nézze végig az összeset?

Vagy ha nem, akkkor nem értem, hogy az miért jó, hogyha a böngésző beépített getElementysByTagName() eljárását használja a kódunk, és az miért nem jó, ha a getElementNameById()-t. Ide jó lenne egy kis pontosítás. Próbáltam az eredetiben megnézni, de ott ez a megjegyzés nem is szerepel.

Amúgy nagyon jó kis lista, köszi a beküldést!

--
Csonka Gergely
http://cheppers.com

nevergone képe

Ott kezdődik a történet, hogy ID-ből csak egy lehet az oldalon, ha az oldal szabályos.
A getElementById() egy elég gyors függvény (bár ha jól tudom, a jQuery már nem is a böngészők által megvalósított kódot használja, hanem egy jóval gyorsabb sajátot). A getElementysByTagName() is elég gyors a lehetőségekhez képest, viszont csak most jelent meg a Javascriptben olyan metódus, amivel egy adott osztály elemei is lekérdezhetőek (Selector API).

Szóval ha csak CSS osztálynevet használsz, akkor végig kell menni a DOM minden elemén egyesével, és megvizsgálni, hogy az adott elem rendelkezik-e a kért osztállyal. -> lassú

Ha használod a tag-et is a keresésnél, akkor a böngésző használhatja először a getElementysByTagName()-t azért, hogy kigyűjtse az adott típusú HTML elemeket, majd ezen az eredménylistán kell már csak végiglépkednie ciklussal, hogy az osztály egyezőségét vizsgálja. -> gyorsabb

Ha csak ID-t adsz meg, akkor nincs mese, használja a getElementNameById()-t az adott ID-jű elem elérésére, és nagyon bízik benne, hogy az oldalon nincs több ilyen ID-vel rendelkező elem. -> leggyorsabb

Ha van Tag és ID is megadva, akkor először lekéri az adott típusú elemeket a getElementysByTagName()-vel, majd abban kutakodik az adott ID-jű elem után. Plusz mivel a getElementysByTagName() eredménye nem DOM-töredék, ezért az eredményére nem használható a getElementNameById(), hanem ciklus kell -> majdnem ugyanott vagyunk, mint a második pontban.

janoka képe

Köszönet a fordításért! Igazán hasznos munka!