Doctrine Object Relational Mapper

Modell létrehozása

A Doctrine alacsony szintjén a sémát php osztályok halmaza reprezentálja, amit adatbázistáblákra képezünk le. Ez a fejezet arról szól, hogyan képezzük le az osztályokat adatbázistáblákra.

Oszlopok

Az egyik probléma, hogy az adatbázis kezelők viselkedése különböző, pl. hogy hogyan tér vissza az eredmények halmazával. A MySQL a mezők neveit változatlanul hagyja, pl ha a lekérdezés így kezdődik: "SELECT myField FROM ...", akkor az eredmények közt lesz egy myField nevű mező

Sajnos ez a MySQL és néhány más adatbázis kezelő kivételével nem így történik. Pl a Postgres az összes mező nevét kisbetűvé alakítja, míg az Oracle adatbázis kezelője mindent nagybetűsít. Szerencsére Doctrine használatával nem kell törődni ezekkel a különbségekkel.

A Doctrine elfedi ezeket a problémákat. Ez azt jelenti, hogy ha definiálunk egy származtatott Record osztályt és definiálunk egy myField nevű mezőt mindig el fogjuk érni a $record->myField (vagy $record['myField']) hivatkozáson ketesztül, nem fontos, hogy MySQL, Postgres vagy Oracle adatbázis kezelőt használunk.

Röviden: Úgy nevezed a mezőidet, ahogy kedved tartja, tartalmazhat aláhúzás_jelet, lehet camelCase vagy amit szeretsz. Azt azért figyelembe kell venni, hogy a Doctrine case sensitive.

Oszlopok hossza

Doctrine-ban a mezőknek van egy hossz tulajdonsága. Néha ettől a számtól függ egy mező típusa. Pl. egy 1000 hosszúságú string mysql-re képezve TEXT lesz VARCHAR helyett.

A hossz a következő típusoknál fontos tényező:

Oszlopok álneve

A Doctrine lehetőséget ad álnevet adni az oszlopoknak. Ez akkor hasznos, ha az alkalmazáslogikát és az adatbázis logikát függetleníteni szeretnénk egymástól. Pl. ha meg szeretnéd változtatni az adatbázis mezőinek a nevét, az alkalmazásban csak a definíciókat kell módosítanod.

// models/Book.php class Book extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('bookTitle as title', 'string'); } }

Ugyanez a példa YAML fájl használata esetén:

--- # schema.yml # ... Book: columns: bookTitle: name: bookTitle as title type:

Most az adatbázisban bookTitle a mező neve, míg az objektumban title néven érjük el ezt a tulajdonságot.

// test.php // ... $book = new Book(); $book->title = 'Some book'; $book->save();

Alapértelmezett értékek

Doctrine-ban minden adattípusnak van saját alapértelmezett értéke. Ha alapértelmezett értéket adunk egy mezőnek, az két dolgot jelent: Ez lesz az alapértelmezett értéke minden újonnan létrehozott Record-nak, és amikor a Doctrine legenerálja az adatbázistáblát, ez lesz az alapértelmezett érték a tábla definícióban is.

// models/generated/BaseUser.php class User extends BaseUser { public function setTableDefinition() { $this->hasColumn('username', 'string', 255, array('default' => 'default username')); // ... } // ... }

Az előző példa YAML fájl használata esetén:

--- # schema.yml # ... User: # ... columns: username: type: string(255) default: default username # ...

Ha most létrehozunk egy új User példányt, az alapértelmezett username kerül kiírásra:

// test.php // ... $user = new User(); echo $user->username; // default username

Adattípusok

Bevezetés

Az adatbázis kezelők több lehetőséget nyújtanak az adatok tárolására, amit a táblákban tárolhatunk. Az adattípusok lehetőséget adnak áttérni más adatbázis kezelőre.

Hogy egyszerűbb legyen az együttműködés adatbázis kezelőkkel, adattípusok egy halmaza előre van definiálva. Az alkalmazások gyártófüggetlenül érik el ezeket.

A Doctrine ügyel az adattípusokra amikor az objektumokat adattáblákra map-peli. Adatok írása és olvasása közben az adatbázis kezelő drivere konvertál.

A következő adattípus példák a Doctrine createTable() metódusával használhatók. Az adattípusok fejezet végén levő példatömb a createTable() metódussal használandó, arra, hogy a válaszott adatbázis kezelőn egy hordozható táblát készítsünk( A Doctrine dokumentációban szerepel, hogy mely adatbázis kezelők támogatottak ). Megjegyzendő, hogy a következő példák nem fedik le az indexek készítését és a karbantartását, ez a fejezet csak az adattípusokkal és azok helyes felhasználásával foglalkozik.
Megjegyzendő, hogy az oszlopok hossza befolyással van az adatbázis szint típusára, csakúgy, mint az elfogadási szint hosszára(az a hossz, ami a Doctrine validátorokkal van validálva).

1. Példa: A 'content' nevű oszlop 'string' típussal és 3000 hosszal az adatbázisban 'TEXT' típust eredményeznek, aminek az adatbázisban 4000 a hossza. Ennek ellenére az alkalmazásban a 'content'-oszlop hossza maximum 3000 lehet.

2. Példa: Egy 1 hosszú 'integer' típusú oszlop a legtöbb adatbázis kezelőben 'TINYINT' mezőt eredményez.

A Doctrine általában elég okos ahhoz, hogy kitalálja, hogy melyik integer/string típus szükséges a hossztól függően.

Módosítók

A Doctrine API-ban szerepel néhány opcionálisan használható módosító:

Megállapíthatjuk, hogy a módosítók megváltoztatják a mező definíciót, létrehozva még több speciális mezőtípust speciális esetekre. A notnull módosítót arra fogjuk használni, hogy beállítsuk a NOT NULL Flag-et az adatbázisban true vagy false értékekre. Ez attól függ, hogy az adatbázis kezelő alapértelmezetten hogyan működik: PostgreSQL-ban a "NOT NULL" alapesetben be van kapcsolva, míg pl. MySQL-ben a "NULL" alapértelmezetten "NO"-ra van állítva.. Hogy bekapcsoljuk a "NOT NULL" módosítót, nincs más dolgunk, mint hozzáadni egy új paramétert a deffiníciós tömbhöz:
'sometime' = array( 'type' => 'time', 'default' => '12:34:05', 'notnull' => true, ),
A következő példában egy 12 hosszúságú mezőt hozunk létre az adatbázisban. Ha a hossz nincs definiálva, a Doctrine a típus alapértelmezett értékét fogja használni, ami esetleg problémákat okozhat. Az a legjobb, ha minél több/az összes mezőhöz megadjuk a maximális értéket.
'sometext' = array( 'type' => 'string', 'length' => 12, ),

Kapcsolatok

Bevezetés

Doctrine-ban a táblák összekapcsolását a Doctrine_Record::hasMany, Doctrine_Record::hasOne műveletekkel adhatjuk meg. A Doctrine támogatja az összes kapcsolatot az egyszerű one-to-one idegenkulcsos kapcsolattól a self-referencing kapcsolatig bezárólag.

Az oszlopdefiníciókkal ellentétben a Doctrine_Record::hasMany és a Doctrine_Record::hasOne a setUp()-on belül helyezkednek el. Mindkettőnek két argumanentuma van: az első az osztály nevét és esetleg egy álnevet tartalmaz, a második argumentum egy tömb, ami a kapcsolat beállításait tartalmazza. Ezek a következők lehetnek:

Nézzünk egy példát: Van két osztályunk, a Forum_Thread és a Forum_Board. A Forum_Board több Forum_Thread-et tartalmaz, ezért one-to-many kapcsolatot használunk. Figyeljük meg, hogy a Forum_ előtagot elhagyva létrehozunk egy Threads nevű aliast.

Először nézzük a Forum_Board osztályt. Három oszlopa van: name, description És egy id nevű elsődleges kulcs, amit a Doctrine automatikusan hozott létre.

Kapcsoljuk össze a Forum_Thread osztállyal, ezzek a hasMany() metódust használjuk. a local mező az osztály elsődleges kulcs id mezője, amit a Doctrine hozott létre, a foreign mező pedig a board_id mező a Forum_Thread osztály mezője.

// models/Forum_Board.php class Forum_Board extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('name', 'string', 100); $this->hasColumn('description', 'string', 5000); } public function setUp() { $this->hasMany('Forum_Thread as Threads', array( 'local' => 'id', 'foreign' => 'board_id' ) ); } }

A fenti példa YAML használatával:

// models/Forum_Board.php class Forum_Board extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('name', 'string', 100); $this->hasColumn('description', 'string', 5000); } public function setUp() { $this->hasMany('Forum_Thread as Threads', array( 'local' => 'id', 'foreign' => 'board_id' ) ); } }

Lássuk a Forum_Thread osztályt. Az oszlopok nem lényegesek, csak a kapcsolatok. Minden Thread-nek csak egy Board-ja van, ezért a hasOne() metódust használjuk. Itt is adunk állnevet, a Forum_Board helyett Board-ot. Itt a local oszlop a board_id, míg a foreign oszlop az id.

// models/Forum_Thread.php class Forum_Thread extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('user_id', 'integer'); $this->hasColumn('board_id', 'integer'); $this->hasColumn('title', 'string', 200); $this->hasColumn('updated', 'integer', 10); $this->hasColumn('closed', 'integer', 1); } public function setUp() { $this->hasOne('Forum_Board as Board', array( 'local' => 'board_id', 'foreign' => 'id' ) ); $this->hasOne('User', array( 'local' => 'user_id', 'foreign' => 'id' ) ); } }

Ugyanez YAML-al:

--- # schema.yml # ... Forum_Thread: columns: user_id: integer board_id: integer title: string(200) updated: integer(10) closed: integer(1) relations: User: local: user_id foreign: id foreignAlias: Threads Board: class: Forum_Board local: board_id foreign: id foreignAlias: Threads

Most már használhatjuk az osztályokat.

Hozzunk létre egy új board-ot

// test.php // ... $board = new Forum_Board(); $board->name = 'Some board';

Adjunk egy új thread-et a board-hoz:

// test.php // ... $board->Threads[0]->title = 'new thread 1'; $board->Threads[1]->title = 'new thread 2';

Minden Thread-hez tartozik egy felhasználó, hozzunk létre tehát egy új User-t, és kapcsoljuk a Thread-hez:

$user = new User(); $user->username = 'jwage'; $board->Threads[0]->User = $user; $board->Threads[1]->User = $user;

Most mentsünk ki mindent egyetlen metódushívással. Ezzel mindent mentünk adatbázisba

// test.php // ... $board->save();

Végezzünk ellenőrzést, nézzük meg az adatszerkezetet, ami létrejön a fenti kód hatására:

print_r($board->toArray(true));