Live geek or die tryin'

Symfony2: Utiliser Plusieurs Bases De Données

Dans le cas où votre site développé avec Symfony2 utiliserait plusieurs bases de données, il faudra déclarer autant de connexions et d’Entity Managers que de bases de données.

Un Entity Manager est tout simplement une interface qui inter-réagi avec une base de donnée.

Pour illustrer mon exemple, je vais utiliser une bdd site et une autre forum.

Déclarer les connexions dans config.yml

doctrine:
  dbal:
    default_connection: site # précise la connexion utilisée par défaut
    connections:
      site:
        driver:   %database_driver%
        host:     %database_host%
        port:     %database_port%
        dbname:   site
        user:     site_usr
        password: site_pwd
        charset:  UTF8
      forum:
        driver:   %database_driver%
        host:     %database_host%
        port:     %database_port%
        dbname:   forum
        user:     forum_usr
        password: forum_pwd
        charset:  UTF8

Déclarer les EM, dans le même fichier

Tout d’abord désactivez l’auto-mapping, comme ça vous pourrez choisir quel EM va gérer les entités de quel bundle.

doctrine:
  orm:
    auto_mapping: false

Note: Si vous avez une erreur relative à cette ligne, supprimez-la carrément.

Ensuite, juste en bas, déclarez les EM en précisant les mappings avec chaque bundle.

doctrine:
  orm:
    auto_mapping: false
    default_entity_manager: web # précise l'em utilisé par défaut (lors de l'utilisation de la ligne de commande par exemple)
    entity_managers:
      web:
        connection: web
        mappings:
          DinduksMachinBundle : ~
          DinduksTrucBundle : ~
          FOSUserBundle: ~
      forum:
        connection: forum
        mappings:
          DinduksForumBundle : ~

Pour faire simple, les mappings font le lien entre un EM et les entités d’un bundle spéficique. C’est eux qui appellent, si j’ose dire, l’EM X quand on veut manipuler l’entité Y.

Attention: N’oubliez pas de toujours mapper vos bundles fraîchement créés avec un EM, sinon vous aurez un erreur de bundle inexistant lors de l’utilisation de la commande doctrine:generate:entities.

Utilisation

De la même manière que vous appelez votre EM par défaut, appelez l’EM dont vous avez besoin en prenant soin de passer son nom en paramètres à la fonction getEntityManager().

$forumEm = $this->get('doctrine')->getEntityManager('forum');

J’espère que ce tutoriel vous sera utile. A bientôt. :)

Symfony2: Utiliser Des Types De Données Non Supportés Par Doctrine

Doctrine2, en tant qu’ORM, doit assurer la compatibilité des types de données (varchar, integer, datetime, etc…) entre différents SGBD, et ne supporte donc pas certains types propres à MySQL (dans mon cas).
Il est cependant possible de lui ajouter des types manuellement, mais la procédure est assez longue et n’en vaut pas la peine à mon avis.

Si vous êtes sûr que votre site n’utilisera qu’un seul SGBD et que ça ne va pas changer plutard, vous pouvez spécifier, dans vos annotations, une partie du code SQL qui servira à générer le champ.

Exemple

Pour générer un champ tinyint qui est NOT NULL et qui a une valeur par défaut de 10, voici le code nécessaire:

/**
* @ORM\Column(columnDefinition="tinyint(4) NOT NULL DEFAULT '10'", name="foo")
*/
protected $foo;

A vous d’adapter ce code pour votre cas. ;)

Symfony2: Executer Les Commandes Console Sur Un Entity Manager Spécifique

Ce qui suit n’est pas une science exacte mais ça peut vous éviter 10 minutes de recherche.
C’est cet argument qui vous sauvera la vie: --em.

Exemple:

Dans le cas où vous voudrez générer les informations sur le mapping à partir de l’Entity Manager “client”:

php app/console doctrine:mapping:convert yml /src/Acme/ClientBundle/Resources/config/doctrine/metadata/orm --from-database --force --em="client

Symfony2: Personnaliser Les Champs De La Table Générée Par FOSUserBundle

Bonjour,

Mon premier vrai projet sous Symfony 2 est un site pour un serveur World Of Warcraft.
Ce serveur, sous MaNGOS, utilise ses propres tables, avec leur propre structure. Je voulais donc, pour des raisons de stabilité et pour éviter les emmerdes, générer une table User avec les mêmes champs et propriétés que celles de MaNGOS.

Pour comprendre la suite de cet article vous devez avoir une idée basique sur le fonctionnement de FOSUserBunde. Si ce n’est pas le cas, lisez la doc: Getting Started With FOSUserBundle.

Je vous explique le topo: pour créer une table utilisateur avec FOSUB, il faut créer une entité (un modèle Doctrine) User qui étend la classe User du fameux bundle. Cette classe contiendra, entre-autres, des variables (qui se traduisent niveau base de données par des champs) nécessaires à son fonctionnement (exemple: username, canonicalusername, etc…).
La table générée dépendra de votre entité User:

  • Si vous laissez votre entité User vide: Une table avec les champs par défaut de FOSUB sera générée.
  • Si vous définissez des variables dans cette classe: La table générée aura les champs par défaut + les champs portant les noms des variables définies dans la classe.
  • Si vous redéfinissez une variable de la classe User de FOSUserBundle: ERROR! - Doctrine ne supporte pas la redéfinition des variables protégées.

Arrivés là, vous comprenez que vous êtres dans la merde.

Je vous le dit tout de suite: il n’y a pas de solution propre et officielle pour ce problème. Stof, un des principaux contributeurs à FOSUserBundle me l’a confirmé lui même.

La solution est de hacker le code source de Doctrine, ou de FOSUserBundle. Vous avez même le choix. :)

Plan A: Bidouiller Doctrine

Cette solution est super simple. A mon avis, sa simplicité est proportionnelle à l’importance des dégâts qu’elle pourrait infliger plutard.

Rendez vous dans le fichier vendors/doctrine/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php, et commentez ces deux lignes:

throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);

et

throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);

Là, on annule carrément les exceptions balancées lors de la tentative de redéfinition d’une variable.
J’espère que vous comprenez maintenant pourquoi j’ai dit que ça peut vous créer des ennuis plutard.
Je pense que si les devs de Doctrine ont mis ces exceptions, c’est pour une raison.

N’oubliez pas que si vous mettez Doctrine à jour, il y a de fortes chances que le fichier ClassMetadataInfo.php sera écrasé. Pensez-y.

Plan B: bidouiller FOSUserBundle

Cette manip est moins suicidaire que la précédente, elle consiste à modifier le fichier FOS/UserBundle/Resources/config/doctrine/User.orm.xml.
Ce fichier gère les propriétés des champs générés par le bundle.
Je ne vais pas vous expliquer comment, sa structure est assez évidente.

Je vous rappelle que, comme pour le hack précédent, ce fichier peut être modifié par une màj.

Mon choix

Mon choix s’est porté sur la première solution parce qu’elle me permet de vraiment personnaliser mes champs: directement, elle me donne la possibilité de jouer avec mon entité User.
Indirectement elle me permet de:

  • Donner des types non supportés (par défaut) par Doctrine, tels que tinyint, timestamp ou encore longtext. Référez vous à mon article Symfony 2: Utiliser des types de données non supportés par Doctrine pour plus de détails.
  • Donner une valeur par défaut à un champ. Seul hic, lors de l’insertion d’un utilisateur, ces champs ne sont pas remplis automatiquement par la valeur par défaut. J’en parlerai en détails prochainement.

Pour finir, je voudrai donner mon avis à propos de FOSUserBundle: c’est un bundle utile lorsqu’on veut implémenter un gestionnaire d’utilisateurs (si on peut appeler ça comme ça) assez basique. Mais il peut vite tourner au cauchemar/mindfuck lorsqu’on veut faire un truc un peu plus poussé.
Ce bundle est en effet assez difficile à appréhender, le style “PHP5.3 n4m3sp4c3s r so OOP LOL” et le manque de documentation n’aident pas tout.

Bon après cet avis n’engage que moi, et est basé sur ma propre expérience. Je peux donc me tromper ou dire des bêtises.
Veuillez me faire part de vos remarques, corrections en commentaire.

Bonne arrachage de cheveux avec le couple FOSUserBundle/Doctrine, et à bientôt!

Git: Renommer Ou Changer L’adresse D’un Dépôt

Un p’tit coup de main qui vous fera gagner du temps si vous êtes nouveau à Git. ;)

Dans le répertoire local de votre projet, ouvrez le fichier .git/config, dans la catégorie [remote “origin”], modifier le paramètre url avec la nouvelle adresse ou le nouveau nom du dépôt.

OU

Utilisez la commande suivante:

git remote set-url origin <nouvelle adresse>

Note: Si vous utilisez GitHub, il faut impérativement renommer le projet sur la plateforme: allez sur la page de votre dépôt, cliquez sur le bouton Admin, et changez le nom.