ugrás a tartalomhoz

DAO – adatbázisműveletek objektumokkal

janoszen · 2009. Jún. 3. (Sze), 14.53
Talán egy középhaladó (PHP) programozó egyik legnagyobb problémája a saját fejlesztésnél az adatbázis-műveletek kezelése. Sokszor sokféle lekérdezést írunk vagy sokféleképpen hivatkozunk egy bizonyos adathalmazra, így bonyolulttá válik bármiféle gyorstárazás vagy egységes elérés implementálása. Itt siet segítségünkre a DAO, avagy az objektumok adatbázisba mentésének módszere.

A dolog voltaképpen egyszerű, de ellentmondásos. Nézzük a következő vázat:

<?php

class MyDataObject implements DAO
{
    protected $vars = array(
        "id"    => "int",
        "myvar" => "string"
    );

    protected $data = array();

    function load($id)
    {
        $data = query_my_database("SELECT * FROM mytable WHERE id = ...");

        if (isset($data[0])) {
            $this->data = $data;
        }
    }

    function save($new = false)
    {
        if ($new || !$this->data['id']) {
            query_my_database("INSERT INTO mytable ...");
        } else {
            query_my_database("UPDATE mytable ...");
        }
    }

    function __get($var)
    {
        if (isset($this->vars[$var]) {
            return $this->data[$var];
        } else {
            return null;
        }
    }

    function __set($var, $value)
    {
        if (isset($this->vars[$var]) {
            $this->data[$var] = $value;
        } else {
            throw new Exception();
        }
    }

}
Mint látható, az adatok betöltése és mentése egységesen történik. Ezzel megspóroltunk magunknak egy rakás munkát, hiszen amint kiolvastuk az adatbázisból az adatokat, nem tömböket kell pakolgatnunk az alkalmazásunkban, hanem egyetlen objektumot, annak minden segédfüggvényével tologathatunk, amerre jól esik. Ezen felül, ha úgy tetszik, a __set függvénybe beépíthetjük a mindenféle ellenőrzést, aminek köszönhetően az egységbe zárás elvét valósítjuk meg.

Ellentmondásosnak tűnhet teljesítmény szempontjából ez a módszer, hiszen a keresések során ki kell válogatnunk a kívánt objektum azonosítóit, majd végig kell példányosítanunk az összeset. Gondoljunk azonban arra, hogy a save illetve load függvény kiváló pontot nyújt a gyorstárazáshoz, hiszen az egész objektumot egy az egyben be tudjuk tenni memóriába, kiolvasni onnan illetve változáskor kiütni.

Még egy utolsó megjegyzés a témához, aztán hagyom a kedves olvasó kreativitását szárnyalni. Ugyan már nem szigorúan a DAO témakör, de tovább fokozhatjuk a fejlesztési hatékonyságot, ha a tipikus keresésekre absztrakt osztályt gyártunk (pl. az egy táblás keresésekhez DAOSimpleSearch), aminek elég belöknünk egy paraméterhalmazt, kimenetként megkapjuk a lepéldányosított osztályok tömbjét/iterátorát.

Kellemes fejlesztést.
 
1

...

carstepPCE · 2009. Jún. 3. (Sze), 15.42
szia, feltetelezem SPL extension, azert szukseges a peldadhoz. Tenyleg objektumokat mentunk az adatbazisba? Nekem ez ujdonsag es lehet, hogy bovebben ki kene fejetened?

-cs-
Sanyi
2

Miért kellene?

janoszen · 2009. Jún. 3. (Sze), 17.19
Miért kellene spl extension hozzá? Egész egyszerűen az adatmezőidet teszed be a táblákba. Nyilván, minden objektum típushoz meg kell írnod az insertet, updatet, selectet, de egész pontosan csak egyszer. Másképp megfogalmazva ha 30 féle képpen jelenítesz meg egy adatot, akkor nem írsz 30 monstre joint, hanem kikeresed a Neked szükséges objektum ID-ket, majd mindegyikre végigpéldányosítod a DAO objektumot, majd annak a paramétereit használod.

A cikket nem komplett, kész megoldásnak használtam, sokkal inkább gondolat indítónak, látókör tágításnak. Hogy mit csinálsz belőle, az teljesen a Te kezedben van.
3

hat szeretnem a DAO ertelmet latni, de nehezen megy

carstepPCE · 2009. Jún. 3. (Sze), 18.16
Igen az SPL tenyleg nem jon ide sehogy, de mostanban ezComponents-sel dolgoztam es o kezelte nagyon hasonloan a dolgot SPL-n keresztul es egybol ez ugrott be.

bocs, de ha protectednek allitom a $vars-t, akkor azt kivulrol ugyan sose fogom tudni allitani, kiveve persze ha __get() fuggvennyel megkerulom ezt vagy irok egy getter-settert hozza.

Tudom, hogy gondolatinditonak szantad, de en valahogy semerre nem tudtam elindulni a menten, hogy mi tortenik az osszetett lekerdezesek eseten. Ezek tobszoros joinokat, beagyazott selecteket is tartalmaznak. Az insert, update, delete operaciok sajnos nagyon atomiak.

Lehet mar eleget programoztam mara es elfaradtam, holnap reggel futok vele meg egy kort :-)

-cs-
Sanyi
4

Jó az

janoszen · 2009. Jún. 3. (Sze), 18.36
Jó a gondolat. Nyilván, az absztrakciók a keresésre csak nagyon egyszerű esetekben működnek. Láttam már próbálkozást bonyolultabb keresésekre is, de azokat inkább nehézkes volt használni. Azonban csinálhatsz a DAO objektumodnak egy "haver" osztályt, ahova összegyűjtöd az összes ilyen függvényt, pl getCommentsByTopicID(), stb. A lényeg ugyanaz marad: a __get()-en keresztül kapsz egy egyszerűen kezelhető felületet az osztályhoz, a __set()-hez tudsz validációkat tenni, a módosító műveletek pedig két jól meghatározható függvénybe vannak téve. Szerintem, ha van 1-2 órád, akkor próbálj meg valami egyszerű példát összedobni magadnak (pl egy blogot access control és belépés nélkül).
5

gondolatindító kifogások

tamicskod · 2009. Jún. 3. (Sze), 20.37
Legtöbbször nekem az okozza a nehézséget ezekkel a megoldásokkal, hogy egyrészt nincs ezen a téren tapasztalatom, másrészt az esetek többségében a téma felvezetése a tiédhez hasonló szinten meg is áll.

Az alapelveket érdekesnek, esetenként kimondottan hasznosnak látom. Olyan példákra gondolok, amikor az objektumokon egyszerű műveleteket végzek. Előveszem, módosítom, törlöm.

A valós életben azonban egyre reálisabb az igény ennél lényegesen összetettebb összefüggések kezelésére. Többféle 'objektum' összefüggő adataiban keresés, szűrés megvalósítását még nem látom, hogy a fenti megoldás milyen kiegészítésekkel képes megvalósítani. Csak egy pár évvel ezelőtti problémakör egy részét kiemelve:

Egy verseny lebonyolító rendszerben vannak versenyzők, vannak különböző kategóriákban (kor/nem/súly) kiírt verseny számok amikre a versenyző leadhatja nevezését. A nevezés lezárása után sorsolás következik, aminek következtében kieséses alapon a versenyzők mérkőzéseket játszanak egymással. A végén minden versenyszámban 1. 2. 3. helyezettet hirdetnek.

A problémakörből tényleg csak pár feladatot kiemelve:

Egy adott versenyszámban induló versenyzők listázása
Egy adott versenyző mérkőzéseinek listázása
Egy adott versenyszám aktuális, lejátszható mérkőzései (több ágon indított párhuzamos futtatás mellett)

Nem részletezem tovább. A feladat leírásban kiemeltem azokat az entitásokat amiket érdemesnek tartok a fenti példa szerint objektumként kezelni. Hogyan támogatja hatékonyan az általad vázolt modell az itt leírt problémák megoldását?

Őszintén örülnék neki, ha ezt egy beszélgetésindító hozzászólásként értékelnéd :) várom a válaszodat.
8

+1

inf · 2009. Jún. 3. (Sze), 22.11
Így magában én sem látom sok értelmét ennek a szerkezetnek, nekem az a (kevés) tapasztalatom, hogy MVCben, egy adott funkcióhoz kapcsolódó adatkezelést érdemes DAOval(vagy Modellel) megoldani, de szimplán egy tábla lekezelésére nem túl hasznos, mert legtöbbször legalább 2-3 tábla kapcsolatával dolgozik az ember. Szóval ez az 1 táblás megoldás így önmagában elég értelmetlennek tűnik.

Én annyit kérdeznék, hogy az elején a változótípusok megadásának mi a célja?
10

Nyilván

janoszen · 2009. Jún. 3. (Sze), 22.33
Sziasztok,

nyilván, így önmagában meglehetősen kevés haszna van. Két érvet tudnék csatasorba állítani a dolog mellett. Az egyik a szervezési. Ha az ember olyan adatszerkezettel dolgozik, amiben jól definiált objektumok vannak (tehát mint egy blogbejegyzés) akkor könnyebb fejleszteni. Gondolj bele, ha egy bejegyzés hozzászólásait akarom kilistázni, akkor megírom a queryt, ha szerkeszteni akarom, megírom a queryt, ha ki akarom listázni a legújabb hozzászólásokat, megírom a queryt, aztán ha változik az adatszerkezet, akkor írom át az összeset. Ez akkor tud nagyon hatékony lenni, ha az ember a kereséseket is összefogja, mondjuk például egy kereső osztályba, aminek meg lehet mondani, hogy ennek a mezőnek legyen ennyi az értéke, az legyen nagyob ennyinél, stb. majd egy függvénnyel összeállítja a queryt és végigpéldányosított osztályokat kapunk.

Namost, ez teljesen nyilvánvalóan nem minden esetben hatékony. Ha olyan adatszerkezeted van, ami nem definiálható jól objektumokként vagy egész egyszerűen nincs szükséged ilyen jellegű absztrakcióra, akkor tök fölösleges erölködni vele. Erről minig ez a cikk jut eszembe.

A másik érvem a cachelés. Sokkal könnyebb az objektumokat cacheélni, ha nem kell 60 helyen átírni őket. Ennél tovább megyek, ha az ember savekor kiüti őket, nincs is meg az probléma, hogy egyes elemek esetleg még régiként vannak a cacheben.

Ami a változó típusokat illeti, az alapján alapvető formai validációkat lehet beépíteni a __set metódusba. Ezt itt nem valósítottam meg, de minimális erölködéssel beletehető.
12

Fordítsunk

janoszen · 2009. Jún. 3. (Sze), 22.45
Fordítsuk le queryk nyelvére:

Egy adott versenyszámban induló versenyzők listázása


Gyakorlatilag ez egy egyszerű select:
SELECT versenyzo_id FROM nevezesek WHERE versenyszam_id=1;
Ebből megkaptad az ID-k listáját, ami alapján betöltheted a versenyzők objektumait. Mint látod, nem kell hozzá egyetlen join sem, egy szimpla keresés megoldotta a problémát. Ha ez gyakori művelet, érdemes egy függvénybe burkolni.

Egy adott versenyző mérkőzéseinek listázása
SELECT merkozes_id FROM merkozesek WHERE versenyzo_id=1
Megint ugyanez.

Egy adott versenyszám aktuális, lejátszható mérkőzései (több ágon indított párhuzamos futtatás mellett)


Ezt nem biztos, hogy értem. Le tudod fordítani a fentiek mintájára queryre?
16

Lényeg

inf · 2009. Jún. 4. (Cs), 00.24
Végülis eljutottunk a lényeghez, hogy a query-ket függvényekkel burkoljuk le, és osztályba rendezzük őket. Szerintem ez az, ami igazán számít a dologban, hogy egyrészt átlátható, hogy milyen kérés mit csinál, másrészt meg a query kódját szabadon módosíthatod, ha éppen úgy tartja kedved. Mondjuk ha kitaláltál egy gyorsabb megoldást a témára, vagy áttérsz másik adatbázisra, stb... Szóval a függvényekbe,osztályokba szortírozás az adatkezelés fölé emel egy burkolófelületet, ami alatt szabadon változtathatsz azon, hogy mi történik.
6

a varazsszó:

mrbond · 2009. Jún. 3. (Sze), 20.52
az ORM.
vannak jo orm megoldasok php-hez. lattam mar belso fejlesztestut is es vannak opensource fejlesztesek is.
ilyenek pl a propel illetve a doctrine.
elegge kiforrottak, tamogatjak a relaciokat (1-1, 1-n, n-m), a beagyazott objektumokat, entitasokat, stb...

ami a closedben (belso fejlesztes) tetszett az a __get() es __set() otletes hasznalata:
phpdoc doksival es reflection apival karoltve tipusvizsgalat volt belove...
tetszett, bar ez megint jo kis bottleneck lehet. Ha jol emlekszem, akkor az ezComponents is hasznal hasonlo megoldast.

egyebkent szvsz erdemes lehet a fenti megoldasba belekeverni egy kis SPL-t mert az viszont teljesitmeny javulast hozhat, pl az objektumok iteralasa soran

egyebkent: igazaból ha nem natív - pl. pdo queryket (az is kepes visszaadni kozvetlen sdtClass tipusu objektumot) - hasznalsz akkor a teljesitmeny az mindenfelekeppen roszabb lesz. de nem is arrol szol a dolog. boven karpotolnak az egyeb feature-ok
9

ezComponents

carstepPCE · 2009. Jún. 3. (Sze), 22.15
igen, az ezComponents persistentObjectet hasznal az adattablak elerese, bar en nem melyedtem bele, mert a standard db orm-je nagyon jol ki van talalva. Bar egy-ket hianyossagat mar eszrevettem, de nem rossz. Nekem nagyon kezre allt.

A doktrint is hasznaltam egy peldaprojekt erejeig, de a dokumentacio nagyon rossz. Abszolut nem az aktualis verziohoz keszult, anno es nehezen boldogultam vele az elejen, ezert hagytam is a hasznalatat. A yaml az jo volt, de szinten doksi az szinte hasznalhatatlan volt hozza.

A propel, ha jol emlekszem mar abbahagytak.

En a DAO-t inkabb egy Factory patternnel egybekotve illetve sokeroforrasos (xml,faltfile,db) kezeles eseten nagyon is jol el tudom kepzelni. Persze a bonyolultabb lekerdezeseket azert mar kezzel kene megirni, vagy egy orm szeru osztalycsomagot letrehozni neki.

-cs-
Sanyi
19

ORM-ek

virág · 2009. Jún. 4. (Cs), 09.32
A Doctrine dokumentációja eléggé siralmas, de nem szeretek Open Source dolgokat ilyen téren kritizálni, ugyanis az Open Source egyik lényege az lenne, hogy ha valakinek valami hiányzik akkor továbbfejleszti és megosztja. Doksit is lehet írni :) A Propel doksija ha lehet akkor még rosszabb :) Viszont egy csomó helyen találni leírásokat erről a két ORM-ről és mindenképpen érdemes őket használni, mert a felmerülő problémák nagy részére megoldást nyújtanak és mióta használják a PDO-t azóta a teljesítményre sem lehet panasz. Természetesen vannak olyan szituációk ahol a burkolók használata nem kívánatos - ilyenek például a nagyon nagy méretű adatbázisokon végrehajtott kötegelt műveletek, ilyen esetekben még véletlenül sem szabad ORM-et használni, kivéve ha valakinek az idő != pénzzel. :-)
A bonyolult lekérdezéseket pedig általában tényleg SQL-ben kell megírni, de ez egyáltalán nem baj - sőt... :) A legtöbbször mégis célszerű ORM-et használni, legalábbis én még a legkisebb projekthez is használok - nem beszélve arról, hogy a fejlett ORM-ek mindegyike támogatja a közvetlen SQL parancsok futtatását is (a Doctrine szintaxisa pedig eleve hasnlít az SQl-hez - a neve DQL).
A házilag barkácsolt ORM-ben nem tudok hinni :) sok szörnyűséget láttam már ezen a téren... :-)
13

Használni, módjával

janoszen · 2009. Jún. 3. (Sze), 22.52
Jó dolgok ezek, ha az ORM jól meg van valósítva, nagyon sok terhet levesz az ember válláról. Nyilván, az alkalmazás függvényében el kell gondolkozni rajta, hogy megéri-e az esetleges overheadet (amit nyilván illik kimérni a natív megoldáshoz képest).

Mindig lesznek esetek, amikor egyedileg kell lefejleszteni, mert az emberek 80-90%-ának gyártott megoldás nem jó és ezért szeretem, ha valaki saját kútfőből is tud alkotni, de ha kell, tud kész szoftvert is használni. (Pl. LDAPhoz nem láttam még annyira ORM megoldást hirdetve. Persze, biztos van.)
7

Hmm

inf · 2009. Jún. 3. (Sze), 21.53
load és save helyett nem lehetne __construct és __destroy?
11

Nem

janoszen · 2009. Jún. 3. (Sze), 22.36
Egyrészt nem biztos, hogy minden __destroy-kor (?) menteni akarsz. (Ez nem __destruct akart lenni?) Ha nem változott az adat, minek menteni? Kíméljük az adatbázist, ha lehet.
15

Destruct

inf · 2009. Jún. 4. (Cs), 00.16
Jah destruct, nem destroy :D

Ezt a __set-be illesztett kóddal, ami menti egy tömbbe, hogy milyen paraméterek módosultak elég egyszerűen meg lehet oldani.
18

Próbáld ki

janoszen · 2009. Jún. 4. (Cs), 07.39
És hogy kapod vissza a hibákat? Ugyanis a destruktornak nincs explicit hívófüggvénye, nem tudsz exceptiont dobni, stb. a kimenet ugyanis már elment a böngészőnek, debuggolni meg nehézkes.
22

Igaz

inf · 2009. Jún. 4. (Cs), 18.38
Jah, ez végülis igaz.
25

Set-eléskor?

Adam · 2009. Jún. 4. (Cs), 22.52
És ha már set-eléskor ellenőrzi az adatokat az objektumod? Akkor még tudsz exception-t dobni. És azt le is tudod kezelni a programban.
26

Valóban

janoszen · 2009. Jún. 5. (P), 01.22
Valóban, de ha savekor kapsz egy unique key exceptiont, akkor azzal mit kezdesz? Vagy akármi. A felhasználónak hiba esetén is értelmes hibaüzenetet kell tudni adni. A destruktor takarításra való by design, ilyesmire nem is tudok nyelvet, amely föl lenne készítve.
14

Nincs semmi problémám az

Protezis · 2009. Jún. 3. (Sze), 23.39
Nincs semmi problémám az írásoddal, félre ne érts, de szerintem annyi megoldás van már erre, hogy nem szabad ilyen fejlesztésre időt pazarolni (hacsak nem fontos a fejlesztés során szerzett tapasztalat, az tényleg nem elhanyagolható). Lehet valaki nagyon jó programozó, értelmes időn belül nem fog összerakni egy komoly ORM-et. A másik dolog az, hogy tök jó objektumokkal dolgozni, de amikor egy tízezres rekordszámú táblából kell .csv-t generálni, vagy hasonló méretű táblák között szinkronizálni, akkor ez a megközelítés nem játszik. Doctrine-nal egy ilyen művelet (nem HYDRATE_ARRAY-jel) le se fut 2-3 perc alatt (több száz MB memória elzabálása mellett), míg natív sql-lel 5-6 mp. (Ez egy konkrét példa). Az esetek többségében tényleg jó dolog, de ésszel kell használni.
17

Valóban

janoszen · 2009. Jún. 4. (Cs), 07.38
Valóban, ha szükség van akkora komplexitásra. De pont a saját példámnál maradva, én elég sokat dolgozom LDAP-pal vagy MySQL-LDAP kombinációval, ahol ráadásul a kettőnek együtt kell módosulnia. Keresgettem (igaz, nem túl intenzíven) de nem nagyon találtam olyan ORM réteget, ami ilyet támogatott volna. Szóval mégsincs mindenre megvalósítás.
20

Sziasztok! Először is mint

CSI · 2009. Jún. 4. (Cs), 12.31
Sziasztok!

Először is mint felhasználó új vagyok, bár már régóta olvasgatom a Weblabort. A cikkért először is gratulálok, ugyanis a kreativitást és a gondolkodásmódot fejleszti a webfejlesztésben.

Hozzászólásokra reagálva:

A hozzászólásokat végig olvastam, és nem értek azokkal egyet, akik azt mondják, hogy kár ilyenre időt fordítani. Az tény, hogy van rájuk szép és open source megoldás, de annak ellenére az elv is érdekelhet és valakit mint például engem érdekelhet.

Miért is jó ez a megoldás amit a cikk írója írt:

  • Adatok egységbe zárja!!!
  • Több helyen tudja használni és nem szemeteli a php kódját SQL utasításokkal.
  • Loadba nem csak id-t hanem where feltételt is adhatsz át (ha bele írod)
  • Loadba nem tőrvényszerű könnyű selekteket tárolni, akkár nagyobb selektekkel is
    dolgozhat és ezt eltakarja a feljesztő elő (view egy szép megoldás).
  • A fenti példát könnyen átalakítható Modellé, ami több rekordot kezel!

Egyéni ötletek

A szakdolgozatomban én is írtam egy DAO, DTO -okat kezelő struktúrát, bár nekem ketté válik a DAO és a DTO objektum. A DAO csak adatbázis műveletek végez és DTO-okat hoz létre míg a DTO a rekordra vonatkozó struktúrát és adatokat tárolja melyeken a fejlesztő dolgozik, amikor végez vele átadja a DAO-nek, hogy mentse le és törölje .... A DAO a mentéskor ellenőrzi, hogy az adatok megfelelőek e, kötelező mező kivan e töltve megfelelő az érték stb... (ezeket az ellenőrzéseket mind adatbázisbó szedi).

Ha valakit érdekel az én megoldásom, igaz csak PostgreSQL-re működik jelenleg, akkor a szakdolgozatom eme részét feltudom tenni ide cikk formájában. Persze akkor csak ha van rá érdeklődő!

A cikk írójának meg gratulálok, csak így tovább és a lelkesedésed ne apadjon le :-)
21

Köszönöm

janoszen · 2009. Jún. 4. (Cs), 16.47
Szia,

először és köszönöm és üdv köreinkben.

Egyetlen megjegyzést tennék: tekintve, hogy a felvázolt DAO megoldás osztályonként egyetlen egy entitást / objektumot kezel, szerintem, a where feltételt nem célszerű belevenni. Sokkal jobbnak / használhatóbbnak gondolol azt a megoldást, ha van egy külön kereső osztály hozzá. Ha ugyanis mindenféle feltételt is megengedsz, a DAO osztályod a maga egyszerűségét hirtelen elveszti.

Egyébként persze, hogy érdekel a megoldásod. Szerintem, ha egy cikk hosszára le tudod rövidíteni, akkor tedd föl, vagy blogmarkként küldd be!
23

A cikket elküldöm a

CSI · 2009. Jún. 4. (Cs), 18.39
A cikket elküldöm a weblabornak államvizsgám után (Kedden(Jún.9)-Szerdán(Jún.10))! Remélem addig megérthető, hogy nem adom ki a kezemből!

A dolgozatomba 3 rész található ami érdekelheti az embereket:

1. Egy Adatbázist kezelő vékony réteg (singleton osztály gyakorlatilag csak az adatok vissza szedésében add keretet PSQL-ben (de MySql-re is átírható 2 perc alatt))

2. DAOBase, DTO struktúra melyről már szóltam

3. Egy egyedi fejlesztésű MVC (Java GUI-ra hasonlít Komponensek, Kontínerek, Vezérlők)

Mind a 3 rész érdekes lehet webfejlesztők számára! Amint végeztem a védéssel elküldöm!
24

Oks

inf · 2009. Jún. 4. (Cs), 18.56
Oks, köszönjük!
27

Bocs, hogy mindig ezzel hozakodok elő

Gixx · 2009. Jún. 5. (P), 11.45
... de mivel én napi szinten a Zend Frameworkkel dolgozok, teljesen oda- meg vissza vagyok a jól átgondolt megoldásaitól.

Korábban használtam már saját fejlesztésű DAO-t, a CakePHP-ból és a Ruby-ból ihletett átiratokat és ADOdb-t is, de eddig a ZF megoldása tetszik a legjobban.

Érdemes megnézni.

Alább egy kis példa. De ezt tulajdonképpen bármelyik ZF-es tutorial is leírja, de így azért mégiscsak szemléletesebb, mint egy link.

config.xml

<?xml version="1.0" encoding="UTF-8"?>
<configdata>
  <site>
    <database>
      <adapter value="Pdo_Mysql" />
      <params>
        <charset value="UTF8" />
        <host value="localhost" />
        <port value="3306" />
        <dbname value="teszt" />
        <username value="kispista" />
        <password value="hazipalesz" />
      </params>
      <init value="SET NAMES 'utf8';" />
    </database>
  </site>
<configdata>
Bootstrap

$config = new Zend_Config_Xml('config.xml', 'site', true);
$db = Zend_Db::factory($config->database);
$db->setFetchMode(Zend_Db::FETCH_OBJ);
$db->query($config->database->init);
Zend_Db_Table::setDefaultAdapter($db);
Users.php


class Users extends Zend_Db_Table
{
  protected $_name = 'users';
  protected $_dependentTables = array('Members');
  protected $_referenceMap = array();
}
Groups.php

class Groups extends Zend_Db_Table
{
  protected $_name = 'groups';
  protected $_dependentTables = array('Members');
  protected $_referenceMap = array();
}
Members.php

class Members extends Zend_Db_Table
{
  protected $_name = 'members';
  protected $_dependentTables = array();
  protected $_referenceMap = array(
    'Users' => array(
      'columns' => 'user_id',
      'refTableClass' => 'Users',
      'refColumns' => 'user_id'
    ),
    'Groups' => array(
      'columns' => 'group_id',
      'refTableClass' => 'Groups',
      'refColumns' => 'group_id'
    )
  );
}
Es akkor a haszálata pár példában:

$is_active_only = true;
$limit = null;
$table = new Users();
$groupTable = new Groups();
$memberTable = new Members();

$select = $table->_db->select()
->from(array('u'=>'users'),array('u.username','u.full_name'))
->joinLeft(array('m'=>'members'),'u.member_id = m.member_id',array('m.title'))
->joinLeft(array('g'=>'groups'),'m.group_id = g.group_id',array('g.group_name'))
->order('u.user_id');

if($is_active_only)
{
  $select->where('u.is_active = ?', 1);
}

if(isset($limit))
{
  $select->limit($limit, 0);
}

$resultArray = $select->query()->fetchAll();

if($row = $table->fetchRow(
  array(
    'username = ?' => $username,
    'is_active = 1'
  )
))
{
  $row->last_login = date('Y-m-d H:i:s');
  $row->save();
  $data = $row->toArray();

  $memberRow = $row->findDependentRowset('Members');
  $data['group_id'] = $memberRow->group_id;
}

$row = $table->createRow($data);
$row->user_id = null;
$row->is_active = 0;
$row->save();
Stb. stb. Látható, hogy egész jól használható. Szerintem. Meg kell szokni, de utána egyszerű. Nagyon bonyolult lekérdezésekhez még mindig meg van a lehetőség, hogy saját kezűleg írt SQL lekérdezést etessünk meg vele. Szóval abszolút flexibilis.

Btw... valamit csináljatok légyszi a colorer-rel, mert IE8 alatt teljesen elveszti a formázást...
28

Halmozás

krisy · 2009. Jún. 6. (Szo), 18.00
Sziasztok!

Pár hónapja én is Zend-et használok, és nekem is teljesen bejött az adatbázis elérése, használata.
Ami szerintem egy nagyon jó gondolat, az a lekérdezések "összerakása" halmozással.
Konkrétan erre a részletre gondolok:
$select = $table->_db->select()  
 ->from(array('u'=>'users'),array('u.username','u.full_name'))  
 ->joinLeft(array('m'=>'members'),'u.member_id = m.member_id',array('m.title'))  
 ->joinLeft(array('g'=>'groups'),'m.group_id = g.group_id',array('g.group_name'))  
 ->order('u.user_id');  
Először én sem hittem volna, hogy van ennek értelme, de szerintem három dolog miatt nagyon jó:
- sokkal olvashatóbb a kód tőle
- elfedi az adatbázis, amin fut a kód, tehát az adatbázis specifikus szintaktikát is (valamilyen szinten) támogatja
- egyszerű paramétertől függő lekérdezéseket írni; itt például erre a részre gondolok:
if($is_active_only)  
 {  
   $select->where('u.is_active = ?', 1);  
 } 
Régebben mi is használtunk DAO/DTO alapú megoldást (bár nem php-ban); annak a legnagyobb előnye a könnyű tesztelhetőség volt, viszont szerintem elég nagy hátrány, hogy még egy "réteget" kellett szinkronban tartani az adatokkal. Minden egyes alkalommal, amikor változott az adatbázis újra kellett generálni/fordítani a DTO-kat, ami nem volt rövid móka :-(
29

Query-vel mi a baj?

CSI · 2009. Jún. 6. (Szo), 20.49
Hali!

Mondjuk átláthatóság véget szerintem azért ez olvashatóbb:

function user(){
 $select = "SELECT U.*,
                  G.*,
                  M.* 
           FROM USERS U
           LEFT JOIN members M ON U (U.member_id = M.memeber_id)
           LEFT JOIN groups G ON M (M.group_id = G.group_id)";

 $resault = $DB->select($select);
 
 return $resault;
}
Már csak azért is, mert ha baj van a lekérdezéssel akkor csak fogom kimásolom és beillesztem a konzolba és megnézem mit ad vissza! Ez az objektum szerkezetnél nem mondható el!

Férre ne értsd, de szerintem ez megint konvenció kérdése. Nekem mondjuk egyik megoldással sincs bajom, de bevallom a fenti lekérdezést én gyorsabban átlátom!
30

Engem is érdekelne

saxus · 2009. Jún. 6. (Szo), 21.28
Engem is érdekelne, mert szerintem is lényegesen olvashatóbb az SQL query.

Nézegetem én is a frameworkoket, de néha olyan érzésem van, hogy időnként kitalálnak mindent, csakhogy kiváltsák azt, amit egyébként az adott feladatra találtak ki.
31

Refactoring és

CSI · 2009. Jún. 6. (Szo), 21.52
Refactoring és újrafelhasználás véget fontos, hogy a lekérdezések objektum szinten legyenek (Singleton minta teljesen jó).

pl:

class DBTools {
 
  function q1(){
  }
  
  function q2(){
  }

  function q3(){
  }
  
  function q4($re_init = false){
      static $eredmeny = NULL;
      
      if(is_null($eredmeny) || $re_init){
          $query = "....";
          $eredmeny = $DB->select($query);
      }

      return $eredmeny;
  }
}

//haszánlat
$eredmeny = DBTools::q1();
DBTools osztály ilyen használattal statikus osztály. q4()-es fügvényben meg láthattok egy kis cache-lést

Szerintem az így tárolt utasítások is teljesen korrektek, csak itt is konvenciókat kell hozni.

Összetettebb lekérdezésekhez meg Modelt kell használni a fenti DAO leírás egy kis átalakítással könnyen lehet modell.
32

A query-vel semmi baj sincs,

krisy · 2009. Jún. 6. (Szo), 21.54
A query-vel semmi baj sincs, sőt!
Szerintem egyszerűen kényelmesebb a halmozásos megoldás, de persze ízlések és pofonok! :-)

Két dolgot tudnék előnyként felhozni:
- nehezebb elrontani egy-egy lekérdést: jó esetben már az IDE szól, hogyha elgépelsz 1-1 halmozott függvényt, nem pedig egy "SELETC"-et írva rejtve marad a hiba, amíg le nem próbálod futtatni
- könnyebb, hogy nem string-műveletek kell végezned ha mondjuk egy-egy új szűrőfeltételt szeretnél hozzátenni a lekérdezéshez, vagy egy új oszlopot, stb. Például pont mostanában jött elő, hogy egy form-on van rengeteg keresőmező. Ha a keresőmezők üresek, akkor természetesen nem kell az üres string-nek illeszkedni semelyik adatbázisbeli oszlopra. A halmozásos módszerrel egyszerűen hozzá tudsz még venni egy where feltételt a select objektumodhoz, nem kell olyanokkal törődnöd, hogy van-e már benne where feltétel, és akkor AND-et kell bele raknod, vagy esetleg egy default "1=1"-et mindenképpen beleraknod.
Konkrétan erre gondolok:
 if($is_active_only)    
  {    
    $select->where('u.is_active = ?', 1);    
  }
Ez szerintem a halmozás nélkül sokkal macerásabb lenne.

Természetesen ha inkább a kézzel megírt lekéredzéseket részesíti előnyben az ember, a Zend azt is le tudja kezelni! :-)

Query kiírása:
Zend-ben van beépített profiler, amivel ki lehet íratni a lekérdezéseket, szóval nem vesznek el :-)

Nézegetem én is a frameworkoket, de néha olyan érzésem van, hogy időnként kitalálnak mindent, csakhogy kiváltsák azt, amit egyébként az adott feladatra találtak ki.

Igazából annyi előnye van, hogy -szerintem- sokkal gyorsabban lehet benne fejleszteni.
Anno mi framework nélkül kezdtünk el egy nagyobb project-et. Amikor vége lett, valamiért előkerült a Zend, és egyik ámulatból a másikba estem, amikor visszaemlékeztem, hogy amik benne vannak a Zend-ben alalpból, azokat mind kézzel kellett megírni korábban (MVC, form-kezelés, adatbázis elérés, lekérdezésekhez profiler, layout-ok, multipart e-mail küldés, pdf generálás, stb.)
33

Mennyit eszik százon?

Adam · 2009. Jún. 7. (V), 16.11
És ez mérhetően mennyivel lassabbá teszi a futási időt? Van esetleg valami benchmark róla? Mert az biztos, hogy lassít, szerintem nem is keveset. Márpedig a vas, az drága mulatság, és ha lehet optimalizálni, és a fejlesztőnek nem nehezíti meg a dolgát, akkor igenis azt az utat kell választani!
34

Nem hiszem, hogy érzékelhető a lassúlás felhasználói szemmel

krisy · 2009. Jún. 7. (V), 21.46
Mennyire lassít? Nem tudom, nem volt lehetőségem még kipróbálni ugyanazokat a lekérdezéseket Zend-del, illetve simán query-vel.

Cáfoljatok meg, de szerintem ez nem lehet érezhető, lényegi lassulás egy oldal betöltésénél (kivéve talán a _nagyon_ extrém eseteket).

Konkrétan arra gondolok, hogy nagyjából az alábbi dolgok befolyásolják egy oldal megjelenését:
- sokáig futó adatbázis lekérdezések
- nem optimális php kód
- lassú hálózat
- az oldal renderelése

Eddig tapasztalataim szerint az esetek 99%-ban nem a php kód lassúsága volt szűk keresztmetszet a sebességgel kapcsolatban. Vagy másképp fogalmazva, hacsak nincsen _nagyon_ elrontva a php kód, akkor nagy valószínűséggel az a leggyorsabb a fentiek közül. A ZF kódját szerintem nem rontották el nagyon :-)

Szóval szerintem az optimalizálást a másik hárommal érdemes kezdeni, és utána esetleg a php kódra is érdemes ránézni. Ez persze nem azt jelenti, hogy a php kód nem fontos, hogy optimális legyen, inkább azt, hogy a másik három nagyságrendekkel jobban lassíthat.

(az persze más kérdés, hogy magának egy framework-nek a használata mennyit lassít egy oldal megjelenésén, illetve nagy terhelés/sok párhuzamos lekérdezésnél mellett biztosan lassít ...)
35

+1

inf · 2009. Jún. 8. (H), 04.01
Szerintem sem lassíthat valami sokat, max néhány msec, az meg kit érdekel.
A vas meg egyébként is elhanyagolható szerintem a sávszélhez képest nagyobb oldalaknál.
37

Az adatbázis cache...

Gixx · 2009. Jún. 8. (H), 11.11
... mellett használható a Zend_Cache is.

$frontendOptions = array(
   'lifetime' => 120,
   'automatic_serialization' => true
);

$backendOptions = array(
	'cache_dir' => '../tmp/cache/',
	'file_name_prefix' => 'zend_cache',
	'cache_file_umask' => 0777,
	'hashed_directory_level' => 2,
	'hashed_directory_umask' => 0777,
);

$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);

if(!$data = $cache->load('myresult_'.$user_id)) 
{
	$userData = $table->fetchRow(...)->toArray();
	$cache->save($userData, 'myresult_'.$user_id);
}

36

Ahogy írták is, ki lehet íratni

Gixx · 2009. Jún. 8. (H), 11.02

echo $select->__toString();
38

Régóta

Szalbint · 2009. Jún. 8. (H), 23.07
Régóta használom őket, de saját megoldást.
Gyártószkriptekkel hozom létre az osztályokat, paramétertömböket felhasználva alapul, még fejlesztésidőben.
Gyorsabban futó, és flexibilisebb megoldás szerintem mint az extends.

Amúgy meg a sebesség miatt nem kell aggódni, mert eltörpül az SQL kapcsolat+lekérdezés sebességéhez viszonyítva.
39

a sajatod?

Sulik Szabolcs · 2009. Jún. 11. (Cs), 09.51
Amúgy meg a sebesség miatt nem kell aggódni, mert eltörpül az SQL kapcsolat+lekérdezés sebességéhez viszonyítva.


A sajatodrol van szo, vagy a mar letezo megoldasokrol.

Anno keszitettem egy kis tesztet. Propel, Doctrine es PDO-n keresztul kerdeztem le ugyanazt. Az eredmeny nalam a PDO javara 10x kevesebb futasi ido, 10x kevesebb memoriahasznalat mellett.
40

...

carstepPCE · 2009. Jún. 11. (Cs), 13.26
a Doctrine eseteben ezt en is meg tudom erositeni, nekem is ez volt a tapasztalatom, azert maradt csak testprojektben a doktrine.

-cs-
Sanyi
41

igen

Szalbint · 2009. Júl. 11. (Szo), 22.20
A saját aktív rekord megoldásomról van szó.

Amúgy nemrég tettem át PDO-ra.