22
Ce bon vieux Propel RDV AFUP, 30 mars 2011 François Zaninotto

Ce bon vieux propel

Embed Size (px)

DESCRIPTION

Introduction to Propel, the PHP ORM, during the ORM meetup organized by AFUP in Paris on March 2011.

Citation preview

Page 1: Ce bon vieux propel

Ce bon vieux PropelRDV AFUP, 30 mars 2011

François Zaninotto

Page 2: Ce bon vieux propel

ActiveRecord

$author = new Author();$author->setFirstName('Leo');$author->setLastName('Tolstoi');$author->save();

$book = new Book();$book->setTitle('War And Peace');$book->setISBN('0143039997');$book->setPrice(12.99);$book->setAuthor($author);$book->save();

authoridlast_namefirst_name

bookidtitleauthor_id

*

Propel est une implémentation d’ActiveRecord Un enregistrement d’une table = Un objetOn abstrait le SQL derrière la manipulation d’objets en PHPGetter et Setter pour chaque colonne, et pour les FK aussiPersistance via save() - et delete() bien sûrPropel génère les classes Author et Book à partir de la BDD - ou à partir d’un schema

Page 3: Ce bon vieux propel

INSERT INTO `author` (`first_name`, `last_name`) VALUES ('Leo', 'Tolstoi');

INSERT INTO `book` (`title`, `ISBN`, `price`, `author_id`) VALUES ('War And Peace', '0143039997', 12.99, 123);

Mais le SQL est bien là, généré par Propel.Il est toujours disponible à un clic de souris (dans un fichier de log, ou dans une propriété de l’objet de connexion).Notez que Propel gère automatiquement la clé étrangère author_id, et que les identifiants primaires n’apparaissent pas dans le code PHP.Pour la quincaillerie DBAL, c’est PDO qui est aux commandes - un PDO surboosté

Page 4: Ce bon vieux propel

class Author extends BaseAuthor{ public function getFullName() { if ($this->getFirstName() && $this->getLastName()) { return $this->getFirstName().' '.$this->getLastName(); } if ($this->getLastName()) { return $this->getLastName(); } if ($this->getFirstName()) { return $this->getFirstName(); } return 'Anonymous Coward';}

echo $author->getFullName(); // Leo Tolstoi

Les classes ActiveRecord sont l’endroit idéal pour stocker de la logique métier.Cela rend les méthodes réutilisables, testables, lisibles.

Page 5: Ce bon vieux propel

ActiveRecord est un Design Pattern

http://martinfowler.com/eaaCatalog/activeRecord.html

Propel n’a rien inventé. ActiveRecord est un pattern connu depuis longtemps, et utilisé dans 80% des ORM.Ces n’est pas ça qui fait la différence entre Propel et les autres ORM

Page 6: Ce bon vieux propel

Rapide

Professionnel

Ergonomique

IDE friendly

Il y a quatre éléments qui font vraiment la différence. Si vous ne devez retenir qu’une seule slide.1 - Rapide: pour installer, pour exécuter, et pour apprendre les bases de Propel, il vous faudra une demi journée.2 - Professionnel, car après plus de 7 ans d’existence et 5000 unit tests, Propel tient bon. Seul Doctrine arrive au même niveau de professionnalisme ; les autres ORMs sont loin derrière.3 - Ergonomique, car Propel fait toujours le pari de la facilité d’utilisation. Propel prend en charge toutes les tâches évidentes, et gère sans configuration particulière les 80% des cas les plus courants.4 - IDE Friendly, car la génération de code donne aux IDE la visibilité de toutes les méthodes, et le phpDoc a été spécialement pensée pour aider les développeurs.

Page 7: Ce bon vieux propel

ActiveQuery

Comme pour les objets ActiveRecord, Propel génère une classe de requête par table.Cela permet de proposer des raccourcis de requêtage, et d’encapsuler la logique métier dans des classes de requêtage.On a donc une véritable API objet pour requêter un entrepôt... une BDD OO?

Page 8: Ce bon vieux propel

// trouver un objet avec sa clé primaire$author = AuthorQuery::create()->findPk(123);

// conditions sur les colonnes$book = BookQuery::create() ->filterByTitle('War And Peace') ->findOne();

// raccourcis$book = BookQuery::create() ->findOneByTitle('War And Peace');

create() est une factory qui renvoie un objet déterminé, ça aide beaucoup l’IDE pour la complétion.findPk() et findOne() renvoient un objet ActiveRecord. find() renvoie une collection.filterByXXX() est la façon de dire WHERE à Propel. On peut combiner plusieurs filtres.Les classes de requêtes sont truffées de raccourcis qui facilitent la vie, comme le findOneByXXX() qui est une méthode magique... mais qui apparait dans l’IDE.

Page 9: Ce bon vieux propel

// conditions intelligentes$author = AuthorQuery::create() ->filterByTitle('War%') ->findOne();

$book = BookQuery::create() ->filterByAuthor($author) ->find();

$books = BookQuery::create() ->filterByPrice(array('min'=>10, 'max'=>20)) ->find();

La connaissance du type de la colonne au moment de la génération des méthodes de filtre permet de leur ajouter des capacités particulières.On peut ainsi filtrer sur une chaine avec des jokers, sur une intervalle pour un nombre.Les clés étrangères sont vues comme des relations, et Propel génère des raccourcis supplémentaires. On peut ainsi filtrer sur des objets, sans connaitre les colonnes impliquées dans une clé étrangère.

Page 10: Ce bon vieux propel

SELECT * FROM `book` WHERE `book.title` LIKE 'War%';

SELECT * FROM `book` WHERE `book.author_id` = 123;

SELECT * FROM `book` WHERE `book.price` > 10 AND `book.price` < 20;

Les SQL généré est toujours à un clic de souris.Le binding est fait via PDO, en fonction du type de la colonne.Vous êtes donc à l’abri des injections SQLPetit détail: le SQL montré ici est celui que Propel génère pour MySQL. Pour PostgreSQL, SQLite, SQLServer, et Oracle, Propel génère un code différent et optimisé pour chaque de base.

Page 11: Ce bon vieux propel

class BookQuery extends BaseBookQuery{ const CHEAP_LEVEL = 8; public function cheap($maxPrice = self::CHEAP_LEVEL) { $this->filterByPrice(array('max' => $maxPrice)); return $this; }}

// trouver les éditions bon marché de 'War And Peace',// classées par titre$books = BookQuery::create() ->filterByTitle('War%') ->cheap() ->orderByTitle() ->find();

Les classes ActiveQuery sont l’endroit idéal pour stocker de la logique métier.Cela rend les méthodes réutilisables, testables, lisibles.On crée un DSL, un Domain-Specific Language, pour son modèle.Ca ne vous rappelle rien ? Eh oui, ce sont les mêmes principes que pour ActiveRecord.

Page 12: Ce bon vieux propel

// trouver les auteurs ayant le prénom 'Leo'$authors = AuthorQuery::create() ->filterByFirstName('Leo') ->find();$books = BookQuery::create() ->filterByAuthor($authors) ->find();

// la même chose, en une seule requête$books = BookQuery::create() ->useAuthorQuery() ->filterByFirstName('Leo') ->endUse() ->orderByTitle() ->find();

Queries embarquées: filterByFirstName() est une méthode de l’objet AuthorQuery. On peut l’utiliser dans l’objet BookQuery par une query embarquée, que Propel traduit par un bon vieux Join.Cela vous permet donc d’encapsuler la logique métier dans la bonne classe, et de la rendre réutilisable dans les objets liés

Page 13: Ce bon vieux propel

SELECT * FROM `book`LEFT JOIN `author` ON `book.author_id` = `author.id`WHERE `author.first_name` = 'Leo'ORDER BY `book.title` ASC

Propel connait la relation Book <-> Author, il sait donc écrire le join tout seul.En fait, toute requête SQL peut être écrite comme une suite d’appels de méthode sur un objet ActiveQuery. Mais ce n’est pas là l’essentiel: Dans Propel, on ne traduit pas du SQL en objet, on manipule directement des objets. A partir de maintenant, fini le SQL (même si vous savez qu’il est toujours derrière).

Page 14: Ce bon vieux propel

Full Featured

Hydratation combinée

Hydratation sur demande

Validators

Héritage de table

Import/Export XML, YAML, JSON, CSV

Types Blob, Enum, Boolean, Array, Object

Colonnes Lazy loadées

Moteur d’évènements

Liaisons 1-1 et n-n

Collections

Pager

Migrations

Namespaces

FK virtuelles

Transactions imbriquées

Réplication master-slave

Reverse engineering

Clés composites

Indexes

Vues SQL

Autoloading

Connections multiples

Query log

Profiler

Introspection au runtime

ActiveRecord et ActiveQuery sont la partie immergée de l’iceberg. Propel offre bien plus - voyez cette liste non exhaustive, qui montre que Propel est un ORM full-featured. Seul Doctrine 1 arrive à ce niveau.L’hydratation, c’est le fait de peupler un objet avec des données qui viennent de la base. L’hydratation transforme un objet vide (sec) en objet imbibé de données venant d’un resultset.

Page 15: Ce bon vieux propel

Hydratation combinée

Hydratation sur demande

Validators

Héritage de table

Import/Export XML, YAML, JSON,

CSV

Types Blob, Enum, Boolean, Array,

Object

Colonnes Lazy loadées

Moteur d’évènements

Liaisons 1-1 et n-n

Collections

Pager

Migrations

Namespaces

FK virtuelles

Transactions imbriquées

Réplication master-slave

Reverse engineering

Clés composites

Indexes

Vues SQL

Autoloading

Connections multiples

Query log

Profiler

Introspection au runtime

Page 16: Ce bon vieux propel

Behaviorstimestampable

sluggable

sortable

versionable

soft_delete

nested_set

i18n

concrete_inheritance

aggregate_column

query_cache

alternative_coding_standards

... et contributions tierces

Propel offre ce que tout bon ORM doit offrir, une bibliothèque de «comportements» d’objets métiers prédéveloppés, que l’on peut activer à loisir.query_cache et alternative_coding_standards ne sont pas vraiment des «comportements», mais ça montre que le système de behaviors de Propel est plus un système de plugins, qui permet de modifier le code généré à loisir

Page 17: Ce bon vieux propel

Propel est RAPIDE0x 2,5x 5,0x 7,5x 10,0x 12,5x

PDO

Propel 1.6

Doctrine2

Doctrine 1.2

Scénario 1: findPkScénario 2: hydrate

http://code.google.com/p/php-orm-benchmark/

Selon les scénarios, Propel est entre 1,5 fois et 5 fois plus lent que pdo seul. Ca peut paraître beaucoup, mais c’est à comparer au code boilerplate que vous avez pu écrire dans vos applications pour effectuer le binding des variables, tester les cas limites, chercher la bonne connexion, logguer les résultats... Etes-vous sûrs que ce code est rapide ? Propel, lui, cherche avant tout la rapidité. Et ça se voit, quand on le compare aux autres ORMs phares en PHP.Le benchmark qui permet d’arriver aux résultats ci-dessus est public, testez-le !

Page 18: Ce bon vieux propel

Propel est PROFESSIONNEL

Tests unitaires

BC depuis la 1.3

Configurable

Documentation

Ecosystème (behaviors, DBDesigner, plugins)

Communauté

Maj tous les mois

Bug tracker public

Utilisé par des professionnels

License MIT

Propel a tout ce qu’il faut pour faire une application qui tient, une application qui dure, une application qui grandit. PHP n’est pas forcément le langage dans lequel on trouve le plus de librairies professionnelles. Propel en est une. Il a été choisi par de nombreuses sociétés parce qu’il est professionnel.Chaque patch contient des tests et de la doc - c’est du DDD, documentation-driven development (cherchez le terme pour rire un peu). La doc, c’est mon dada - j’ai déjà écrit deux bouquins. Propel, c’est sept ans d’efforts.

Page 19: Ce bon vieux propel

Propel est ERGONOMIQUE

$book->setCreatedAt(time());$book->setCreatedAt('now');$book->setCreatedAt('2011-03-30 20:15');$book->setCreatedAt(new DateTime());

C’est à Propel de s’adapter à vous, pas à vous de vous adapter à Propel. Zéro configuration pour commencer. Une seule dépendance externe: Phing. Mais si vous utilisez symfony ou Symfony2, le setup est instantané et Phing est livré avec. Peu d’objets à manipuler (ActiveRecord, ActiveQuery, connection), d’où un apprentissage très rapide. Pas de magie: le code est généré et accessible. phpDoc complète.

Page 20: Ce bon vieux propel

Propel est IDE FRIEDNLY

Les IDE avec autocomplétion tirent parti des information générées par Propel au format phpDoc pour faciliter encore davantage la vide des développeurs.

Page 21: Ce bon vieux propel

ActiveRecord Not Dead

ActiveRecordDataMapper

N’écoutez pas ceux qui vous disent qu’ActiveRecord est mauvais. C’est juste une histoire de goût. ActiveRecord permet de tester son modèle. La preuve: Propel a plus de 5000 tests. L’héritage n’est pas un problème puisque les behaviors permettent la réutilisation horizontale. Quant à la performance, ça n’a jamais été un problème que pour les ActiveRecord n’utilisant pas de génération de code.Pour comparer, EntityManager, c’est le goût de la pureté de Java. ActiveRecord, c’est le goût de la facilité de Ruby et Rails. Les deux viennent de Fowler.

Page 22: Ce bon vieux propel

Merci

François Zaninotto, lead dev Propel

Site: http://www.propelorm.org

Blog: http://propel.posterous.com

Twitter: @francoisz

eTF1 recrute, contactez-moi !