Autor wpisu: Tomasz Kowalczyk, dodany: 12.06.2011 22:20, tagi: doctrine, framework, php, symfony, symfony2
Autor wpisu: Tomasz Kowalczyk, dodany: 26.04.2011 23:56, tagi: mysql, symfony, framework, doctrine
Autor wpisu: singles, dodany: 09.04.2011 23:18, tagi: php, zend_framework, doctrine
Zend Framework składa się z wielu komponentów, a jednym z nich jest ten odpowiedzialny za komunikację z bazą danych – Zend_Db.
Jednakże, celem tego wpisu nie jest opisywanie Zend_Db
– to zostało zrobione już w dokumentacji. Celem tego wpisu jest wyrażenie mojej opinii na temat tego co mi się w Zend_Db podoba, a co nie – jest to oczywiście wpis czysto subiektywny. Traktuje ten wpis także jako przyczynek do dyskusji na temat, czy Zend_Db
jest naprawdę tak złym komponentem, za jakiego ma go wielu programistów PHP – m.in patrz komentarze do wpisu u batmana. Chciałbym się także odnieść do podanych tam „zarzutów”. Niektóre z przedstawionych przeze mnie zalet bądź wad mogą Wam się wydawać śmieszne bądź banalne, ale kilka takich rzeczy połączonych ze sobą wpływa na wygodę korzystania z danego rozwiązania. Tak więc zaczynamy!
Zalety
Oparty głównie na PDO
Coraz więcej frameworków idzie tą drogą, co uważam za niewątpliwy plus. PDO de facto jest standardem w PHP i bardzo dobrze, że twórcy zdecydowanej większości frameworków się tego trzymają. Dzięki temu, kiedy przychodzi potrzeba napisania bardziej zaawansowanego zapytania możemy skorzystać ze znanych nam metod i oczekiwać tego samego, czego oczekiwaliśmy korzystając z czystego PDO.
Wygoda podczas stosowania klauzuli WHERE
Kwestia oczywiście w pełni subiektywna. Mam na myśli korzystanie głównie z metody fetchAll
i automatycznego bindowania parametrów jako odpowiedni typ danych. Przykład:
$model->fetchAll(array( 'item_id = ?' => 12, //bind as PDO::PARAM_INT, cast it using (string)12 to automatically bind as PDO::PARAM_STR 'name = ?' => 'foobar', //PDO::PARAM_STR 'category_id IN (?)' => arrray(1, 2, 4, 7) // bind each array element separately as PDO::PARAM_INT -> gives you: category_id IN (?, ?, ?, ?) ));
Stosunkowo mała liczba plików
Zend_Db w obecnej wersji frameworka (1.11.5) waży ok. 600KB i mieści się w 53 plikach – z czego 17 z nich to puste klasy wyjątków Exception.php
. Dla porównania – Doctrine2 DBAL (czyli sama warstwa abstrakcji bazy danych) to 144 pliki, w tym 10 od wyjątków. Oczywiście, w obu przypadkach można te liczby jeszcze bardziej zmniejszyć, wyrzucając niepotrzebne adaptery.
ActiveRecord
Podoba mi się fakt, że mogę zdefiniować własne metody odnoszące się bezpośrednio do rekordu
O ile twórcy ZF piszą o Zend_Db_Table_Row
jako o implementacji Row Data Gateway, jednakże z punktu widzenia używającego go programisty niedaleko mu do ActiveRecord. Mam na myśli możliwość definiowania własnych metod odnoszących się do konkretnego rekordu. Banalny przykład:
// /application/models/Product.php class Model_Product extends Zend_Db_Table_Abstract { protected $_name = 'product'; protected $_rowClass= 'Model_ProductRow'; //use Model_ProductRow instead Zend_Db_Table_Row } // application/models/ProductRow.php class Model_ProductRow extends Zend_Db_Table_Row_Abstract { public function getPriceWithTax() { return $this->price * 0.23; //where price is column in database defining price without VAT tax rate } }
To samo tyczy się obiektów kolekcji – w przypadku Zend Frameworka dziedziczących po klasie Zend_Db_Table_Rowset
. Mogę ustawić własną klasę, gdzie zaimplementuje potrzebne mi metody kolekcji.
Wspracie dla interfejsów ArrayAccess oraz Iterable w przypadku obiektów i kolekcji
Kolejna rzecz, która wpływa na wygodę. Mogę odnosić się do właściwości obiektu używając notacji tablicowej. Mogę także iterować po obiekcie kolekcji używając foreach
, bez wykonywania za każdym razem toArray()
.
Zend_Db_Select == QueryBuilder
O ile na początku korzystanie z Zend_Db_Select sprawiało mi sporą trudność (moim kolegom z zespołu także), tak po pewnym czasie jest to dla nas natywny sposób pisania bardziej skomplikowanych zapytań podczas korzystania z ZF. Właśnie dzięki jego obiektowej naturze i wykorzystaniu fluent interface mogę dowolnie łączyć warunki zapytania bądź zmieniać je korzystając z metody reset()
. Przykład:
class Model_Foo extends Zend_Db_Table_Abstract { private $_name = 'foo'; // use fluent interface and add condition public function fetchWithLeftJoinWhereBarIsntNull() { $select = $this->_getBaseSelect(); $select->where('bar IS NOT NULL'); return parent::fetchAll(); } // user reset() and remove LEFT JOIN public function fetchWithoutLeftJoin() { $select = $this->_getBaseSelect(); $select->reset(Zend_Db_Select::LEFT_JOIN); return parent::fetchAll($select); } private function _getBaseSelect() { $select = $this->select(); $select->from('table1') ->setIntegrityCheck(false) // allow to join another tables ->joinInner('table2', 'table1.id = table2.table1_id') ->joinLeft('table3', 'table1.id = table3.table1_id'); return $select; } }
Wady
Brak nazwanych relacji
O używaniu relacji w Zend Framework pisał już batman we wpisie Zend_Db i relacje. Jednak mi nie odpowiada fakt, że korzystając domyślnej metody opisywanej praktycznie wszędzie muszę odnosić się do rekordów podrzędnych po nazwie klasy, a nie nazwie zdefiniowanej relacji. Chciałbym, abym mógł napisać tak:
$params = $userRow->findDependentRowset('UserParamsRelationWithCustomName'); //instead of specifying model class
Nazwa klasy zawsze może się zmienić, a to ułatwia refactoring. Dodatkowo, chciałbym móc w ramach relacji zdefiniować dodatkowe parametry dotyczące połączenia – nie tylko kolumnę, po jakiej ma się ono odbyć, ale także warunki do kolumn dodatkowych. Istnieje co prawda Zend_Db_Table_Definition
, jednak rozwiązanie to ma dwie wady. Po pierwsze wymaga osobnej definicji dla każdej klasy modelu, co jest niewygodne. Po drugie, nadal nie pozwala na definiowanie dodatkowych warunków dla połączeń.
Autor wpisu: sokzzuka, dodany: 18.01.2011 00:13, tagi: php, doctrine
Ostatnio wiele mówi się o bazach danych NoSQL (NotOnlySQL). Jako, że nie zauważyłem nic w polskim światku PHP na ten temat, stwierdziłem, że pora wypełnić tę lukę i zagłębić się w temat.
Czym są bazy NoSQL ?
Jak podpowiada nam ciotka wikipedia. Bazy NoSQL są to bazy danych, które nie są klasycznymi relacyjnymi bazami danych jakie znamy (MySQL,PostgreSQL etc). Termin NoSQL nie odpowiada żadnym wspólnym cechom tych baz, a raczej temu, że na pewno nie są one RDBMS-ami.
Popularne bazy NoSQL są magazynami danych. W przeciwieństwie do klasycznych odpowiedników nie posiadają stałego schematu, a dane przechowują jako pary klucz – wartość.
Jako przykład dla naszych rozważań wybrałem bazę MongoDB. Dlaczego akurat tą? Istnieją dwa główne powody. Pierwszym z nich jest to, że twórcy projektu Doctrine, stworzyli również ORM w wersji dla tej bazy. Drugim natomiast to, że baza jest szczególnie przyjazna dla webdeveloperów.
Jej przyjazność objawia się tym, że baza żyje i oddycha Javascriptem. Shell przyjmuje komendy tylko w tym języku. Natomiast odpowiedź na zapytania zwracana jest w formacie JSON. Ciekawą rzeczą, może mniej dla webdeveloperów a bardziej dla zwolenników funkcyjnego podejścia do programowania, jest to, że baza wspiera rozproszony model obliczeń oparty na paradygmacie map-reduce. Na roztrząsanie tego tematu czas przyjdzie później. Natomiast teraz przejdźmy do części praktycznej.
Instalacja
- Na stronie projektu, wyszukujemy odpowiednią dla nas paczkę i ściągamy
- Rozpakowujemy ją w dowolnym katalogu
- W głównym katalogu dysku tworzymy folder data/db
- W katalogu gdzie rozpakowaliśmy paczkę z bazą odpalamy plik bin/mongod.exe
- Z sourceforge ściągamy driver do php w postaci rozszerzenia (w archiwum są dll-e dla odpowiednich wersji php)
W tej chwili mamy już działającą bazę MongoDB. Kolejnym krokiem będzie zaprzęgnięcie Doctrine’a do pracy.
Struktura projektu:
|--application |---model |----MailBox |------Message.php |-----User.php |--lib |---vendor |----Doctrine |--proxies |--index.php
Do katalogu Doctrine ściągamy ODM (Object Document Mapper).
Autor wpisu: batman, dodany: 22.12.2010 19:27, tagi: doctrine, php
Ekipa odpowiedzialna za Doctrine poinformowała o wydaniu pierwszej stabilnej wersji Doctrine 2. Spośród wszystkich zmian jakie zaszły w projekcie, najważniejszą zdaje się być brak kompatybilności z poprzednią wersją oraz fakt, iż Doctrine 2 działa tylko na PHP 5.3.
Pełną informację na temat zmian znajdziecie na blogu Doctrine. Zachęcam również do zapoznania się z tutorialem oraz dokumentacją.
Autor wpisu: Damian Rusinek, Piotr Wierzgała, dodany: 02.07.2010 12:23, tagi: doctrine, symfony
Overriding methods should not be a problem, but it can make you confused if you don’t know how magic functions are used in Symfony 1.4 models.
In this short article I will explain why usual overriding does not work in Symfony models and how to override methods handled by magic functions.
Suppose, we have a base model class BaseTopic:
abstract class BaseTopic extends sfDoctrineRecord { public function setTableDefinition() { $this->setTableName('topic'); $this->hasColumn('name', 'string', 255, array( 'type' => 'string', 'notnull' => true, 'length' => 255, )); $this->hasColumn('updates_count', 'integer', null, array( 'type' => 'integer', )); } }and our class:
public class Topic extends BaseTopic { }Now we want to update updates_count field each time we modify name field.
First guess is to do the following:
public class Topic extends BaseTopic { public function setName($name) { $this->updates_count++; return parent::setName($name); } }However it will not work unless you have declared method setName in BaseTopic literally (magical __call does not count).
Upper method will do the following:
- Increment updates_count field
- Call setName method from BaseTopic (which does not exist literally) so it will look for this method in parents
- Because there is no setName in any parent class, it will go to Doctrine_Record and call __call method which will find setName method in Topic class
- Call setName from Topic class again
To solve this problem you have to use _set function from Doctrine_Record class: