IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

Relation entité et/ou design pattern


Sujet :

Symfony PHP

  1. #1
    Nouveau Candidat au Club
    Femme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2014
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2014
    Messages : 4
    Points : 1
    Points
    1
    Par défaut Relation entité et/ou design pattern
    Hello,

    Je désire créer un bundle pour la gestion de Tag.

    Admettons que l'on ne sache pas le nombre et le nom des entités qui seront lié à cette entité Tag. C'est à dire qu'elle pourra être lié a une seule entité, comme à plusieurs entités.

    Le truc c'est que je ne sais pas quel type de relation ou/et design pattern adopter. Si j'ai bien compris ce que j'ai lu dans les recherches que j'ai faites c'est que l'on ne peut pas faire plusieurs MANYTOONE sur un seule champ de la table Tag, et un ONETOMANY vers le champ du entité qui n'a pas de manytoone n'est pas possible non plus.

    J'ai pensé a une MANYTOMANY mais cela impliquerais la création d'une table dans la base de données pour chaque entité liés.

    Ce qui aurait été idéal c'est une entité intermédiaire genre TagRelation de ce type:

    - id : classique.

    -tag: qui contiendrai l'id du tag.

    -model: le nom de l'entité liée. par exemple "Article".

    -model_id: l'id de l'entité lié.

    Je sais qu'avec certains ORM cela est possible mais avec doctrine2 je n'arrive pas a mettre cela en oeuvre. Ou peut être que ce n'est pas la bonne façon de penser cette appli'.

    Si quelqu'un pouvais m'éclairer sur ce sujet, ça serai vraiment cool car je bloque sur cette idée depuis un bon moment.

    Merci d'avance

  2. #2
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Hello !

    Pense objet !

    - Interface "Taggable".
    - Classe abstraite qui l'implémente avec une relation vers l'objet tag.
    - Discriminator pour faire le lien avec chacune des entités taggable.
    - Les entités finales étendent la classe abstraite.

    Si tu veux vraiment savoir à quoi ressemblera ton schema de BDD :
    Table A
    Table B
    Table TableToTag : elle possède les champs table_id (qui peut être l'id de A ou de B), tag_id, et discriminator (unique par table).
    Table Tag

    Doctrine (si je ne dit pas de bêtise) doit être capable avec ce schéma de récupérer tous les tags d'une entité grâce au discriminator qui le renseignera sur l'entité/la table possédant cette relation.

    De cette manière il est très simple d'ajouter ou de supprimer une entité Taggable sans toucher à la BDD. Toutes tes entités possèdent une relation ManyToMany avec Tag et tu n'as qu'une seule table intermédiaire.

    Have fun =)
    ++

  3. #3
    Nouveau Candidat au Club
    Femme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2014
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2014
    Messages : 4
    Points : 1
    Points
    1
    Par défaut
    Hello Nico_F et merci pour ton message et pour m'avoir mise sur la piste.
    j'y vois un peu plus claire
    je reviens vers toi si je coince.
    Merci encore et bonne journée ^.^

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 168
    Points : 219
    Points
    219
    Par défaut
    Moi j'ai une solution qui passe super bien

    Imaginons donc que tu as un bundle TagBundle et l'entité Tag

    TagBundle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    php app/console generate:bundle --namespace="App\TagBundle" --dir=src --bundle-name="TagBundle" --no-interaction
    php app/console doctrine:generate:entity --entity=TagBundle:Tag --format=annotation --fields="name:string(255)" --with-repository --no-interaction
    Dans notre application nous avons aussi deux autres bundles :

    BlogBundle :
    un petit bundle qui va nous fournir une entité Article qui devra etre lié en manyToMany a notre entité Tag.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    php app/console generate:bundle --namespace="App\BlogBundle" --dir=src --bundle-name="BlogBundle" --no-interaction
    php app/console doctrine:generate:entity --entity=BlogBundle:Article --format=annotation --fields="title:string(255) body:text" --with-repository --no-interaction
    ShopBundle :
    un petit bundle qui va nous fournir une entité Article qui devra etre lié en manyToMany a notre entité Tag.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    php app/console generate:bundle --namespace="App\ShopBundle" --dir=src --bundle-name="ShopBundle" --no-interaction
    php app/console doctrine:generate:entity --entity=ShopBundle:Article --format=annotation --fields="title:string(255) body:text" --with-repository --no-interaction
    Maintenant que nous avons tout en place nous allons pouvoir gérer nos relations.

    L'idée est donc de pouvoir specifier les Entités cible.
    Dans notre fichier de configuration de l'application (app/config.yml ) nous allons écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    # Tag Configuration
    tag:
        targets:
            - App\BlogBundle\Entity\Article
            - App\ShopBundle\Entity\Article
    Ensuite dans la class Configuration de notre bundle (app/App/TagBundle/DependencyInjection/Configuration.php)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
     /**
         * {@inheritdoc}
         */
        public function getConfigTreeBuilder()
        {
            $treeBuilder = new TreeBuilder();
            $rootNode = $treeBuilder->root('tag');
     
            $rootNode
                ->children()
                    ->arrayNode('targets')
                        ->defaultValue(array())
                        ->prototype('scalar')->end()
                    ->end()
                ->end()
            ;
     
            return $treeBuilder;
        }
    Puis dans la class TagExtension (app/App/TagBundle/DependencyInjection/TagExtension.php)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
        public function load(array $configs, ContainerBuilder $container)
        {
            $configuration = new Configuration();
            $config = $this->processConfiguration($configuration, $configs);
     
            $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
            $loader->load('services.xml');
     
     
            $container->setParameter('tag_bundle.targets', isset($config['targets'])?$config['targets']:array());
        }
    Pour les relations nous allons écrire une class DoctrineEventSubscriber (app/App/TagBundle/EventListener/DoctrineEventSubscriber.php) qui écoutera l'événement loadClassMetadata et nous allons lui passeront la liste des entités que nous souhaitons lié a notre entité Tag.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    <?php
     
    namespace App\TagBundle\EventListener;
     
    use Doctrine\Common\EventSubscriber;
    use Doctrine\ORM\Events;
    use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
     
    class DoctrineEventSubscriber implements EventSubscriber
    {
        /**
         * @var array
         */
        protected $targets;
     
        /**
         * @param array $targets
         */
        public function __construct(array $targets = array())
        {
            $this->targets = $targets;
        }
     
        /**
         * @param LoadClassMetadataEventArgs $event
         */
        public function loadClassMetadata(LoadClassMetadataEventArgs $event)
        {
            /** @var \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetaData */
            $classMetaData = $event->getClassMetadata();
     
            if (in_array($classMetaData->getName(), $this->targets)) {
     
                $className = explode('\\', $classMetaData->getName());
                $tableName = strtolower($className[count($className) - 3] . '_' . $className[count($className) - 1]);
     
                $classMetaData->mapManyToMany(
                    array(
                        'targetEntity' => 'App\\TagBundle\\Entity\\Tag',
                        'fieldName' => 'tags',
                        'joinTable' => array(
                            'name' => $tableName . 's_tags',
                        )
                    )
                );
            }
        }
     
        /**
         * @return array
         */
        public function getSubscribedEvents()
        {
            return array(
                Events::loadClassMetadata
            );
        }
    }
    Il nous reste plus qu'a enregistre notre subscriber dans le fichier de configuration du bundle (app/App/TagBundle/Resources/config/services.xml)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    <?xml version="1.0" ?>
     
    <container xmlns="http://symfony.com/schema/dic/services"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
     
        <parameters>
            <parameter key="tag_bundle.event_listener.doctrine_event_subscriber.class">App\TagBundle\EventListener\DoctrineEventSubscriber</parameter>
        </parameters>
     
        <services>
            <service id="tag_bundle.event_listener.doctrine_event_subscriber" class="%tag_bundle.event_listener.doctrine_event_subscriber.class%">
                <tag name="doctrine.event_subscriber" connection="default"/>
                <argument>%tag_bundle.targets%</argument>
            </service>
        </services>
     
    </container>
    A ce stade on a plus qu'a specifier des noms pour nos table et ajouter une variable tags dans nos entités ShopBundle:Article et BlogBundle:Article.

    BlogBundle:Article (app/App/BlogBundle/Entity/Article.php) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
    <?php
     
    namespace App\BlogBundle\Entity;
     
    use Doctrine\ORM\Mapping as ORM;
     
    /**
     * Article
     *
     * @ORM\Table(name="blog_article")
     * @ORM\Entity(repositoryClass="App\BlogBundle\Entity\ArticleRepository")
     */
    class Article
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
     
        /**
         * @var string
         *
         * @ORM\Column(name="title", type="string", length=255)
         */
        private $title;
     
        /**
         * @var string
         *
         * @ORM\Column(name="body", type="text")
         */
        private $body;
     
        private $tags;
     
    }

    ShopBundle:Article (app/App/ShopBundle/Entity/Article.php) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
    <?php
     
    namespace App\ShopBundle\Entity;
     
    use Doctrine\ORM\Mapping as ORM;
     
    /**
     * Article
     *
     * @ORM\Table(name="shop_article")
     * @ORM\Entity(repositoryClass="App\ShopBundle\Entity\ArticleRepository")
     */
    class Article
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
     
        /**
         * @var string
         *
         * @ORM\Column(name="title", type="string", length=255)
         */
        private $title;
     
        /**
         * @var string
         *
         * @ORM\Column(name="body", type="text")
         */
        private $body;
     
        private $tags;
     
    }
    Puis on va rajouter une function __toString à notre entité Tag (app/App/TagBundle/Entity/Tag.php) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    <?php
     
    namespace App\TagBundle\Entity;
     
    use Doctrine\ORM\Mapping as ORM;
     
    /**
     * Tag
     *
     * @ORM\Table()
     * @ORM\Entity(repositoryClass="App\TagBundle\Entity\TagRepository")
     */
    class Tag
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
     
        /**
         * @var string
         *
         * @ORM\Column(name="name", type="string", length=255)
         */
        private $name;
     
     
        public function __toString()
        {
            return $this->name;
        }
     
    }
    ?>
    On va régénérer les entités :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    php app/console doctrine:generate:entities App
    puis construire notre base et nos tables :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    php app/console doctrine:database:create
    php app/console doctrine:schema:update --force
    En BDD maintenant nous avons :
    Nom : tag-bundle.png
Affichages : 272
Taille : 25,3 Ko

    On peut générer des cruds pour voir si c'est bien ce que l'on souhaite:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    php app/console doctrine:generate:crud --entity=ShopBundle:Article --route-prefix=/admin/crud/shop/article --with-write --no-interaction
    php app/console doctrine:generate:crud --entity=BlogBundle:Article --route-prefix=/admin/crud/blog/article --with-write --no-interaction
    php app/console doctrine:generate:crud --entity=TagBundle:Tag --route-prefix=/admin/crud/tag --with-write --no-interaction

  5. #5
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    J'ai vraiment du mal à comprendre ta solution goabonga.

    Non seulement tu prends des chemins incroyablement compliqués pour faire ce qui me semble être une relation ManyToMany on ne peut plus classique, mais en plus, tu es obligé de faire une table intermédiaire à chaque fois que tu ajoutes une nouvelle entité taggable.

    Y a-t-il quelque chose qui m'a échappé ?

  6. #6
    Membre actif
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2012
    Messages
    133
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2012
    Messages : 133
    Points : 208
    Points
    208
    Par défaut
    J'ai vraiment du mal à comprendre ta solution goabonga.

    Non seulement tu prends des chemins incroyablement compliqués pour faire ce qui me semble être une relation ManyToMany on ne peut plus classique, mais en plus, tu es obligé de faire une table intermédiaire à chaque fois que tu ajoutes une nouvelle entité taggable.

    Y a-t-il quelque chose qui m'a échappé ?
    Oui en effet, comparé à une ManyToMany classique, ici on ne touche pas à l'entité partagé (Tag). Ce n'est pas un petit bonus c'est réellement intéressant.

    +

  7. #7
    Nouveau Candidat au Club
    Femme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2014
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2014
    Messages : 4
    Points : 1
    Points
    1
    Par défaut
    Hello goabonga,

    Merci pour cette solution, mais je l'avais déjà éssayé
    Le probleme comme l'a dis Nico_F c'est qu'une table de liaison sera généré a chaque fois que l'on voudra rendre une entité Taggable. Genre si tu veux ajouté des tags pour 10 entités cela créera 10 tables de liaisons en plus dans la bdd ce qui n'est pas top :/

    Le mieux est la solution de Nico_F mais sans discriminator ou "single table inheritance". Il faut faut gérer les liaisons à la main.

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 168
    Points : 219
    Points
    219
    Par défaut
    Je vois pas le problème avec la table de liaison ?

    Mais bon .... ce que je voulais mettre en avant c'est la possibilité de créer des mappings via l'event LoadClassMetadataEvent, maintenant reste plus qu'a changer la relation.... !

Discussions similaires

  1. design objet relations Entite/Repository
    Par elderion dans le forum MVC
    Réponses: 17
    Dernier message: 27/08/2012, 14h44
  2. Réponses: 4
    Dernier message: 24/02/2009, 12h06
  3. [Observateur] Précisions sur le design pattern Observer [UML]
    Par joquetino dans le forum Design Patterns
    Réponses: 2
    Dernier message: 07/10/2004, 22h35
  4. Les Designs Patterns Entreprise
    Par boulon dans le forum Design Patterns
    Réponses: 4
    Dernier message: 01/09/2004, 19h16
  5. [Design Patterns] Architecture 3 tiers
    Par HPJ dans le forum Design Patterns
    Réponses: 1
    Dernier message: 29/07/2003, 11h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo