Concepteur / Développeur PHP5 / Symfony.
Mangeur de bières.
Technophile.
Intégrer la fonction MySQL RAND() à Doctrine2 (Dans ce cas, Random() en DQL) :
use Doctrine\ORM\Query\AST\Functions\FunctionNode,
Doctrine\ORM\Query\SqlWalker,
Doctrine\ORM\Query\Parser,
Doctrine\ORM\Query\Lexer;
class Random extends FunctionNode
{
public function getSql(SqlWalker $sqlWalker)
{
return 'RAND()';
}
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
Un article ? Ça faisait longtemps !
Problème bête, dans Symfony2, j’ai besoin d’utiliser un Repository comme service. Pourquoi ? Pour l’injecter comme Provider dans une classe de gestion de calendrier dans mon cas.
La solution est très simple, et se généralise à la création d’une entité via une méthode de factory.
Donc, dans notre config.yml, on a :
# config.yml
services:
my_repository:
class: My\MainBundle\Entity\EventRepository
factory_service: doctrine.orm.default_entity_manager
factory_method: getRepository
arguments: ['MyMainBundle:Event']
C’était pas sorcier, non ?
I have just published the first Twig 1.2 release candidate. This version is fully compatible with previous versions of Twig (just don’t forget to clear the cache after upgrading).
In this post, I want to highlight some of the changes we have made to Twig to make it even better and more flexible…
Intégration firefox terminée, c’est ok. Bon allez, on teste.
Chrome ? rien à faire cool.
Safari ? idem, génial.
IE ? Ah oui mais, comment je teste ça moi, j’ai pas de pc windows sous la main… Un virtualbox en local ? Mouai, pourquoi pas.
Ou alors… Un virtualbox sur mon serveur de développement avec remote desktop !
C’est la petite solution toute simple que j’ai trouvée à mes tests IE, toujours très fastidieux…
Pour ceux que ça interesse, ça m’a pris environ 2h pour installer un virtualbox 4.1 sur mon serveur (attention, sous debian ne pas utiliser le paquet virtualbox-ose fourni, vraiment vieux, un .deb tout neuf est dispo sur le site de virtualbox).
Et du coup je me retrouve avec 4 machines virtuelles sur mon serveur (XP-IE6, XP-IE7, XP-IE8, 7-IE9), accessibles via rdesktop… Simple, efficace, et très pratique !
Pensez-y ;)
Dans le cadre d’un projet symfony 1.4 / Doctrine 1.2, avec un back-office constitué essentiellement d’admin generator, avec des tables i18n et tout ce qui va avec, je me sui frotté à un problème… assez sympa :
Avec ce schema :
Category:
actAs:
NestedSet:
hasManyRoots: true
rootColumnName: thematic_id
Timestampable: ~
I18n:
fields: [ name ]
columns:
id: { primary: true, autoincrement: true, type: integer }
thematic_id: { type: integer, notnull: true }
name: { type: string, length: 255, notnull: true }
relations:
Thematic:
onDelete: CASCADE
L’admin generator me sortait ce genre de requete :
SELECT c.id AS c__id, c.thematic_id AS c__thematic_id, c.name AS c__name, c.lft AS c__lft, c.rgt AS c__rgt, c.level AS c__level, c.created_at AS c__created_at, c.updated_at AS c__updated_at FROM category c
Sauf que du coup, le champ c.name, il pouvait toutjours le chercher dans la table de base !
Après de longues heures de recherches, voici la solution (ridicule) à ce problème, située dans mon generator.yml :
generator:
class: sfDoctrineGenerator
param:
model_class: category
theme: admin
non_verbose_templates: true
with_show: false
singular: ~
plural: ~
route_prefix: category
with_doctrine_route: true
actions_base_class: sfActions
...
et plus précisément, ici :
model_class: category
Mon generator.yml a été généré avec un c minuscule à category, ce qui générait ces problèmes de requêtes à la première exécution, maintenant on le saura !
Dans le cadre de manipulation de grandes quantités de données avec doctrine, il arrive souvent que l’hydratation des objets pose problème, dans le sens ou l’occupation mémoire devient vite énorme.
Exemple :
avec ce schema :
Category:
columns:
name: { type: string(255) }
Product:
columns:
category_id: { type: integer }
name: { type: string(255) }
relations:
Category: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: Products }
et ces fixtures :
Category:
<?php for ($i = 0 ; $i < 100 ; $i++): ?>
Category_<?php echo $i ?>:
name: 'Catégorie <?php echo $i ?>'
<?php endfor ?>
Product:
<?php for ($i = 0 ; $i < 100 ; $i++): ?>
<?php for ($j = 0 ; $j < 100 ; $j++): ?>
Product_<?php echo $i ?>_<?php echo $j ?>:
Category: Category_<?php echo $i.PHP_EOL ?>
name: 'Produit <?php echo $i ?>_<?php echo $j ?>'
<?php endfor ?>
<?php endfor ?>
Soit 100 catégories contenant chacune 100 produits, ce code :
$q = Doctrine_Query::create()
->from('Product p'); foreach ($q->execute() as $product)
{
$name = $product->getName();
}
à une empreinte mémoire de plus de 30Mo. Pas vraiment problématique, mais imaginez la même chose avec des “vrais” objets, contenant autre chose qu’un simple nom…
Deux solutions à ce problème :
1er cas : Ne pas Hydrater les objets
Si vous n’utilisez pas de méthodes métier particulières, et que vous n’avez pas besoin de récuperer d’objets liés à vos produits, vous pouvez vous contenter de ne pas hydrater vos objets doctrine. Ce code :
$q = Doctrine_Query::create()
->from('Product p');
foreach ($q->execute(array(), Doctrine_Core::HYDRATE_ARRAY) as $row)
{
$name = $row['name'];
}
a une empreinte mémoire de seulement 1.9 Mo, 15 fois moins.
2ème cas : Vous avez besoin des méthode de votre objet, utilisation du poids mouche.
L’autre solution, consiste à instancier un seul objet Product, que l’on hydratera à chaque itération de la boucle, avec de nouvelles données, remplaçant les anciennes.
Ce code :
$q = Doctrine_Query::create()
->from('Product p');
$product = new Product;
foreach ($q->execute(array(), Doctrine_Core::HYDRATE_ARRAY) as $row)
{
$product->hydrate($row);
$name = $product->getName();
}
a une empreinte mémoire de 2.1Mo, c’est un peu plus que la méthode précédente, mais on garde l’accès à nos méthodes métier, et à nos relations à conditions de prévoir les jointures dans le requête.
A noter que l’execution du script est également beaucoup plus rapide