ORM or not ORM faut-il les utiliser ?

Avec l’arrivée des frameworks php, nous avons appris à évoluer entre ces nouveaux acronymes: MVC, DAO, DRY et ORM
Mais qu’est-ce qu’un ORM ?
Un ORM (Object Relation Mapping) est un ensemble de librairies qui vous permettent d’interagir avec vos données via des objets.
La plupart des frameworks respectant le design MVC* intègrent leurs propres ORM plus ou moins performants.
note: pour les exemples de syntaxes d’ORM j’utiliserai l’ORM du mkFramework.

*MVC: Model View Controller (séparation de la couche modèle de l’affichage et du controlleur)

Un ORM pourquoi faire ?
Pourquoi utiliser un ORM plutôt que d’écrire simplement des requêtes SQL dans pdo ?
Si vous voulez coder propre, vous allez dans un premier temps écrire toutes vos requêtes dans un fichier php.
Si vous souhaitez organiser un peu mieux, vous allez regrouper les requêtes par table…
Si vous souhaitez éviter de vous répéter, vous aller créer un fichier/une classe pour les éléments récurrents (connexion, requête…) : principe de DRY*
Le fait de faire ceci, vous avez créé un pseudo début d’ORM ;)

*DRY Don’t Repeat Yourself (créer des fonctions/classes pour éviter d’écrire plusieurs fois le même code)

Un des avantages des ORM se situe concernant la manipulation des données.
Prenons un exemple ou dans plusieurs pages vous devez modifier uniquement le titre, l’auteur ou le nombre de vue d’un article.
En pdo simple vous écririez:

‘UPDATE article SET titre=? WHERE id=?’,$titre,$îd
‘UPDATE article SET auteur=? WHERE id=?’,$auteur,$îd
‘UPDATE article SET nbVue=nbVue+1 WHERE id=?’,$îd

Avec l’ORM vous n’avez pas besoin d’écrire à l’avance vos différents types de requêtes

$oArticle=model_Article::getInstance()->findById($id);
$oArticle->titre=$titre;
$oArticle->save();
//ou
$oArticle->auteur=$auteur;
$oArticle->save();
//ou
$oArticle->nbVue=($oArticle->nbVue+1);
$oArticle->save();

note: vous pouvez, si vous le souhaitez écrire des requêtes update
Exemple:

class model_article extends abstract_model{
(…)
public function updateNbVue($id){
$this->execute(‘UPDATE article SET nbVue=nbVue+1 WHERE id=?’,$id);
}
}

Les avantages sont multiples:

  • Organiser vos requêtes
  • Gagner du temps à l’écriture
  • Bénéficier des avantages du design MVC*

Tous les ORMs n’offrent pas les mêmes performances
Pour argumenter un post dans un topic sur ce même site, j’avais à l’époque procédé au benchmark suivant:
100 000 enregistrements en base de donnée mysql, et l’affichage de ces lignes dans un tableau html.
5 types différents:
– php simple en utilisant pdo
– une application en Zend Framework (version 1.11.12)
– une application en Yii (version yii-1.1.14)
Рune application en MkFramework (m̩thode findMany() )
Рune application en MkFramework en utilisant la r̩cup̩ration rapide (m̩thode findManySimple() )

Résultat des courses:

benchmark

– 0.50s : pdo simple
Р0.58s : MkFramework en utilisant la r̩cup̩ration rapide
– 0.68s : Yii (version yii-1.1.14)
– 1.97s : MkFramework (normal)
– 11.20s : Zend Framework (version 1.11.12):

note : j’ai fait un benchmark sur 100 000 entrées pour avoir un volume assez important pour mettre en évidence les différences de performances, si j’avais fait un findById(4)
Sur un seul enregistrement on aurait
benchmark2

– 0.007s : pdo simple
Р0.015s : MkFramework en utilisant la r̩cup̩ration rapide
– 0.043s : MkFramework (normal)
– 0.049s : Zend Framework

note 2 : pour obtenir de bonnes performances avez yii j’ai écrit les requêtes sans passer par les classes « ORM »

$sql=’SELECT * FROM Article';
$connection=Yii::app()->db; // vous devez avoir une connection « db »
$command=$connection->createCommand($sql);
$articles=$rows=$command->queryAll();

Les ORMs offrent de la flexibilité
J’ai lu à plusieurs reprise que les ORM étaint lourd, qu’ils retournaient toujours toutes les colonnes même ceux non utilisées, que pour faire une jointure on se retrouvait à récupérer une ligne entière sur les deux tables liés…
Premièrement, vous pouvez choisir de récupérer uniquement certains champs de votre table. Beaucoup d’ORM proposent d’écrire sa requête SQL
Par exemple:

class model_article extends abstract_model{
(…)
public function findListAuteurName(){
return $this->findMany(‘SELECT auteur.name FROM auteur’);
}
}

Idem pour les fameuses jointures, vous avez le choix de récupérer par exemple un objet article puis de lui demander de nous retourner son auteur pour afficher son nom ainsi

$oArticle=model_article::getInstance()->findById(4);
print $oArticle->findAuteur()->nom;

Mais vous pouvez également écrire une méthode qui vous retournera les articles avec leur auteurs ;)

class model_article extends abstract_model{
(…)
public function findArticleWithAuteur(){
return $this->findOne(‘SELECT article.titre, auteur.name FROM article INNER JOIN auteur ON article.auteur_id=auteur.id WHERE article.id=?’,(int)$id);
}
}

Une flexibilité aussi concernant les performances: selon les parties de vos sites vous avez besoin de récupérer plus ou moins d’élements et ceci plus ou moins rapidement.
La encore vous avez le choix: si vous devez récupérer un auteur, ses articles et ceci sans trop regarder les performances vous pouvez utiliser la méthode de récupération d’objets riches findMany/findOne
Si au contraire vous souhaitez afficher un bon nombre d’article d’auteurs, ou autre, vous pouvez utiliser la méthode rapide retournant des objets « simple » findManySimple/findOneSimple

Vous avez le choix :)

Conclusion
Comme vous avez pu le lire ici, tous les ORM ne sont pas des usines à gaz, ils sont très pratiques et vous permettent dans l’esprit du MVC de bien organiser votre couche modèle.
Les ORM sont une bonnes choses que vous codiez une petite application ou une application importante: l’essentiel c’est de penser à long terme (la maintenabilité).
Il sera plus simple pour vous ou un collègue d’intervenir sur votre application avec ORM que sans ORM ;)
Certains ORM sont plus ou moins flexible et performant comme les frameworks, mais ne les mettez pas tous dans le même panier, essayez en plusieurs avant de vous faire un avis.

note: je n’ai pas inclus Symfony 2 (pour tester doctrine) dans le benchmark car malgré mes efforts d’optimisation de cache, mode production… je n’arrivais pas à passer sous la barre des 23 secondes :(
Si vous pouvez m’envoyer un exemple d’application affichant ces 100 000 enregistrements en moins de 2 secondes je suis preneur ;)

note2: retrouvez dans mon post d’origine les éléments du benchmarks (sauf celui de yii fait ici pour l’occasion)
http://www.developpez.net/forums/d1252983-2/club-professionnels-en-informatique/actualites/etes-vous-pour-contre-orm-blogueur-invite-a-tenir-baton-par-milieu/#post6847792