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 :

Constructeur entité & formType


Sujet :

Symfony PHP

  1. #1
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut Constructeur entité & formType
    Salut à tous,

    Voilà j'ai une relation OneToOne Unidirectionnel entre deux entitiés : rubrique & image.

    Cependant je souhaite un argument modulable pour l'entité inverse : Image (je veux un path différent) du coup j'ai créer un constructor et passe par la fonction : setDefaultOptions du formType associé pour passer la variable ainsi cela donne çà :

    buildForm du formType de l'entité Rubrique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('nom')
            ->add('image', new ImageType('rubrique'), array('required' => true))
        ;
    }

    setDefaultOptions du formType de l'entité Image :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    use Echyzen\NewsBundle\Entity as Entity;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setDefaults(array(
                'empty_data' => new Entity\Image($this->someDependency),
            ));
        }

    Et le constructeur de l'entité image :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public function __construct($path = null) {
     
            if(empty($path)) $this->path = '';
            else {
     
            $this->path = $path;
     
            }
    }
    Alors bien sûr avant que je rajoute çà tout fonctionnait mais plus maintenant au moment de la soumission j'ai ce genre d'erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Index "file" cannot be read from object of type "Echyzen\NewsBundle\Entity\Image" because it doesn't implement \ArrayAccess
    Il faut dire un truc les erreur remonter par symfony sont incompréhensible pour l'être humain moyen XD


    Si quelqu'un a une piste ou a déjà été voulu faire un truc comme moi mais a trouver une autre solution pour rendre modulable une entité?

    Merci d'avance de votre réponse

  2. #2
    Membre habitué
    Ingénieur d'études et de développement
    Inscrit en
    Juin 2009
    Messages
    112
    Détails du profil
    Informations professionnelles :
    Activité : Ingénieur d'études et de développement
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2009
    Messages : 112
    Points : 154
    Points
    154
    Par défaut
    Bonjour,

    Qu'est ce que tu entends par rendre modulable ton entité ?

  3. #3
    Membre éprouvé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    725
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juin 2011
    Messages : 725
    Points : 1 050
    Points
    1 050
    Par défaut
    l'erreur dit que à un moment donné du programme on execute ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    $uneEntiteImage['file']
    Est-ce l'erreur complète que tu as fourni?

  4. #4
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Alors en faite je souhaite que le path soit modulable (le path ou je stocke mes images). Et comme j'utilise l'image en tant qu'entité faire un service pour çà n'est peut être pas très cohérent... Enfin je sais pas car je suis totalement débutant.

    Sinon oui c'est l'erreur en entier...
    Vous voulez la trace ou du code en supplémentaire?

    et l'entité image possède bien une données $file.

    En tout cas merci de votre aide

  5. #5
    Membre habitué
    Ingénieur d'études et de développement
    Inscrit en
    Juin 2009
    Messages
    112
    Détails du profil
    Informations professionnelles :
    Activité : Ingénieur d'études et de développement
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2009
    Messages : 112
    Points : 154
    Points
    154
    Par défaut
    Désolé mais je ne comprends toujours pas le 'modulable' du path.

    Tu veux uploader un fichier et avoir une entité qui gère ton fichier plus potentiellement d'autres champs ?

    Peux expliquer fonctionnellement ce que tu cherches à obtenir ?

  6. #6
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Je souhaite juste pouvoir modifier l'endroit ou je move l'image selon les cas.

    Par exemple, si j'ai une image pour une news je souhaite enregistrer le nom de l'image en BDD et faire un move de l'image dans web\uploads\img\news

    Et pour, un article web\uploads\img\articles

    C'est pour cela que j'ai commencé a modifier la données membres path de mon entité image mais peut être que tu as une meilleurs solution je suis preneur
    Sachant qu'un service c'est pas possible car l'entité est en relation OneToOne et ManyToOne avec plusieurs autres entités... enfin, je sais pas si c'est génant...

    Aurait tu une autre solution ou alors tu pense que ce que j'ai fais c'est correct?

    En tout cas merci de l’intérêt que tu me porte

  7. #7
    Membre habitué
    Ingénieur d'études et de développement
    Inscrit en
    Juin 2009
    Messages
    112
    Détails du profil
    Informations professionnelles :
    Activité : Ingénieur d'études et de développement
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2009
    Messages : 112
    Points : 154
    Points
    154
    Par défaut
    Je reviens vers toi ce soir. Ma réponse sera longue

  8. #8
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Ne t’inquiète pas j'ai tout mon temps cela m'empêche pas de continuer a faire sans pour le moment mais même si c'est pas très pratique pour moi

  9. #9
    Membre habitué
    Ingénieur d'études et de développement
    Inscrit en
    Juin 2009
    Messages
    112
    Détails du profil
    Informations professionnelles :
    Activité : Ingénieur d'études et de développement
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2009
    Messages : 112
    Points : 154
    Points
    154
    Par défaut
    Ok donc si j'ai bien compris tu veux sur une rubrique, pouvoir uploader un document qui peut être de plusieurs types possibles en fonction de cette rubrique.

    La solution "un peu compliquée mais très propre" est de faire des services d'upload avec héritage et de gérer ton upload par EntityListener.

    Je t'explique comment je vois la chose et comment je l'ai fait sur notre plateforme.

    Tu auras besoin de plsieurs choses : créer un EntityListenerResolver qui te permettra d'injecter des services dans les listeners de persistance de tes objets, d'une entité (elle existe déjà) liée à ton upload et enfin de services pour gérer le métier de tes uploads.

    Tu crées un service d'upload dans lequel tu feras tes traitements communs à tous tes types d'uploads (récupération des slugs, des thumbnails, des upload dir…)
    Puis pour chaque type d'upload tu crées un service associé (ImageService, PdfService, VideoService …) dans ces services tu y mettras tout ce qui concerne ton type de fichier par surcharge (les répertoire d'upload par exemple, ou les traitments d'image, de vidéos, de pdf …).

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <service id="wf.asset_service" class="%wf.asset_service.class%">
                <argument type="service" id="doctrine.orm.default_entity_manager"/>
                <argument type="service" id="translator.default"/>
                <argument id="kernel_root_dir">%kernel.root_dir%</argument>
                <argument id="platform">%platform%</argument>
                <argument id="url_static">%url_static_site%</argument>
            </service>
            <service id="wf.asset_image_service" class="%wf.asset_image_service.class%" parent="wf.asset_service">
            </service>
            <service id="wf.asset_flash_video_service" class="%wf.asset_flash_video_service.class%" parent="wf.asset_service">
            </service>

    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
    class AssetService
    {
    //…
     
    function __construct(EntityManagerInterface $entity_manager,
                             TranslatorInterface $translator,
                             $kernel_root_dir,
                             $platform,
                             $url_static)
        {
            $this->entity_manager = $entity_manager;
            $this->translator     = $translator;
            $this->kernel_root_dir            = $kernel_root_dir;
            $this->platform                   = $platform;
            $this->url_static                 = $url_static;
     
            $this->upload_dir = $this->kernel_root_dir . "/../../web/uploads/" . $this->platform . "/";
     
            $this->thumbnail_dir = $this->kernel_root_dir . "/../../web/" . $this->platform . "/thumbnails/";
        }
     
    //…
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class AssetImageService extends AssetService
    {
    //…
    // ici je surcharger la méthode de récupération du chemin d'upload pour les Images
     public function getUploadDir()
        {
            return $this->kernel_root_dir . "/../../web/uploads/" . $this->platform . "/MON_NOUVEAU_PATH/MES_BELLES_IMAGES/";
        }
     
    //…
     
    }
    Dans ton entité Image tu y place un attribut $file qui récupérera l'uploadedFile de symfony.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        /**
         * Champ non mappé pour gérer l'upload du fichier
         */
        private $file;
     
        public function setFile(UploadedFile $file = null)
        {
            $this->file = $file;
        }
     
        public function getFile()
        {
            return $this->file;
        }
    Pour la suite il te faut la version de Doctrine 2.4 au minimum. Dans cette version ils ont amélioré la gestion des listener d'entité. On peut désormais attacher un listener par entité (bien plus propre) mais on ne peut lui fournir de service.
    C'est pourquoi je te montre comment palier à ce problème.


    Dans service.xml
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
            <!-- Entity listener -->
            <parameter key="wf.entity_listener_resolver.class">PATH\EntityListenerResolver</parameter>
     
            <service id="wf.entity_listener_resolver" class="%wf.entity_listener_resolver.class%">
                <argument type="service" id="service_container"/>
            </service>
    La classe associée
    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
    use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
    use Symfony\Component\DependencyInjection\ContainerInterface;
     
    class EntityListenerResolver extends DefaultEntityListenerResolver
    {
     
        private $container;
        private $mapping;
     
        public function __construct(ContainerInterface $container)
        {
            $this->container = $container;
            $this->mapping   = array();
        }
     
        public function addMapping($className, $service)
        {
            $this->mapping[$className] = $service;
        }
     
        public function resolve($className)
        {
            if (isset($this->mapping[$className]) && $this->container->has($this->mapping[$className])) {
                return $this->container->get($this->mapping[$className]);
            }
     
            return parent::resolve($className);
        }
     
    }
    Dans ta config
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    # Doctrine Configuration
    doctrine:
      orm:
     
      # Resolver perso des listener d'entité, pour la gestion des event de lifecycle (prePersist...)
        entity_listener_resolver: wf.entity_listener_resolver
    Cette classe te permet tout simplement de définir des listener (ce qui suit) et de leur injecter n'importe quel service.

    Dans ton entité image, définie lui un listener
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     * @ORM\Table(name="image")
     * @ORM\Entity(repositoryClass="TON_PATH_VERS_TON_ENTITE")
     * @ORM\EntityListeners({"TON_PATH_VERS_TON_ENTITE\Listener\ImageListener"})
     */
    class ImageListener
    {
    //…
    }
    Définis ce listener comme service et injecte lui tes service de gestion d'uploads créé précédemment et n'oublie pas le TAG.
    C'est ce tag qui lancera la passe de compilation de ton entityListenerResolver
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     <service id="wf.entity_listener.image" class="%wf.entity_listener.image.class%">
                <argument type="service" id="wf.asset_image_service"></argument>
                <argument type="service" id="wf.asset_flash_video_service"></argument>
                <tag name="wf.entity_listener"/>
    </service>
    Dans ta classe de bundle Core par exemple, tu lances la passe de compilation:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class WFCoreBundle extends Bundle
    {
     
        public function build(ContainerBuilder $container)
        {
            parent::build($container);
     
            $container->addCompilerPass(new DoctrineEntityListenerPass());
        }
    Ta passe de compilation

    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
    namespace WF\CORE\CoreBundle\DependencyInjection\CompilerPass;
     
    use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\DependencyInjection\Reference;
     
    class DoctrineEntityListenerPass implements CompilerPassInterface
    {
     
        public function process(ContainerBuilder $container)
        {
            $ems = $container->getParameter('doctrine.entity_managers');
            foreach ($ems as $name => $em) {
                $container->getDefinition(sprintf('doctrine.orm.%s_configuration', $name))
                    ->addMethodCall('setEntityListenerResolver', [new Reference('wf.entity_listener_resolver')]);
            }
     
            $definition = $container->getDefinition('wf.entity_listener_resolver');
            $services   = $container->findTaggedServiceIds('wf.entity_listener');
     
            foreach ($services as $service => $attributes) {
                $definition->addMethodCall(
                    'addMapping',
                    [$container->getDefinition($service)->getClass(), $service]
                );
     
            }
        }
    }

    Dans ton listener d'entité tu définies deux méthodes de gestion d’événements de persistance

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
        public function prePersist($asset, LifecycleEventArgs $event)
        {
            $this->upload($asset, $event);
        }
     
        public function preUpdate($asset, PreUpdateEventArgs $event)
        {
            $this->upload($asset, $event);
        }
    Puis les méthode métier

    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
     protected function upload($asset, $event)
        {
            $asset_service = $this->getAssetService($asset);
     
            if ($asset->getFile())
            {
                $file_name = $asset_service->generateFilePathFilename($asset->getProduct(), pathinfo($asset->getFile()->getClientOriginalName(), PATHINFO_EXTENSION));
     
                // grâce au service dynamique on récupère ici le path que tu cherchais, en fonction du type d'asset uploadé
                $asset->getFile()->move($asset_service->getUploadDir() . '/' . $asset->getProduct()->getImagePath(), $file_name);
     
                // ton path est donc modulable
                $asset->setFilePath($file_name);
     
                $asset->setFile(null);
            }
     
                $infos = getimagesize($asset_service->getUploadDir() . $asset->getFilePath());
     
                $asset->setHeight($infos[1]);
                $asset->setWidth($infos[0]);
                $asset->setMimeType($infos[2]);
     
        }
     
    // méthode qui récupère le service de gestion des asset en fonction du type choisi dans le formulaire, ces services ont été injecté dans la définition du service (voir plus haut)
        public function getAssetService($asset)
        {
     
            switch ($asset->getType()) {
                case BaseAsset::_IMAGE:
                    return $this->asset_image_service;
                    break;
                case BaseAsset::_FLASH_VIDEO:
                    return $this->asset_flash_video_service;
                    break;
                case BaseAsset::_NOTICE:
                    return $this->asset_notice_service;
                    break;
                case BaseAsset::_PDF:
                    return $this->asset_pdf_service;
                    break;
                case BaseAsset::_SCHEMA:
                    return $this->asset_schema_service;
                    break;
            }
        }

    Voila pour la réponse un peu longue

    J'espère ne rien avoir oublié. Si tu as des questions n'hésite pas. Je pars en vacances ce soir pour deux semaines mais je checkerai de temps en temps

    A+

  10. #10
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Bon honnêtement j'ai essayer de comprendre mais là je sèche c'est trop compliqué pour moi j'ai vraiment pas le niveau la lol...

    Et puis je suis pas sûr que tu a réellement compris mon soucis.

    Je cherche juste a avoir un attribut path (la tu ma fais carrément un système pour al gestion des différents types...).

    Alors pour que cela soit propre j'avais voulu pouvoir faire un constructor sauf que cela ne fonctionne pas...

    Du coup, plutôt que de faire ce que tu me propose qui semble long pourquoi ne pas faire tout simplement un attribut et l'hydrater seulement dans la partie controller et donner une valeur par défaut sinon?

    Je sais pas si je suis bien clair? Mais j'aimerais quand même que tu m'explique exactement ce que fait ta solution car la je comprends pas tout :s

  11. #11
    Membre habitué
    Ingénieur d'études et de développement
    Inscrit en
    Juin 2009
    Messages
    112
    Détails du profil
    Informations professionnelles :
    Activité : Ingénieur d'études et de développement
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2009
    Messages : 112
    Points : 154
    Points
    154
    Par défaut
    Le problème avec la gestion du path dans le controller c'est que ce n'est pas flexible et ne correspond pas aux bonnes pratiques MVC. Tu vas mettre de la logique dans ton controller au lieur de la mettre dans tes services 'métier'.

    Et puis si demain tu as 20 nouveau types d'uploads, tu auras une méthode de controller à rallonge.

    Ma méthode est certe complexe mais très propre.

    En fait, elle te permet de gérer tes uploads en fonction de leur type (ou tout autre critère) et d'effectuer automatiquement des actions lors de leur upload ainsi que de profiter de l'héritage pour avoir des spécificités sur tel ou tel type de document (si besoin)

    Si demain tu as 20 nouveaux type d'uploads qui ont chacun besoin d'un path spécifique. Tu crées 20 services avec la propriété path surchargée, et hop c'est fini. Automatiquement toute la logique d'upload reste la même mais le path lui change.

    Le côté complexe de ma solution vient de la passe de compilation pour les listeners d'entité je pense. Mais c'est une méthode que j'ai développée qui permet de vraiment tiré parti du potentiel des services de SF2 avec la persistance de Doctrine 2.
    Sinon tu te retrouve bloqué à ne rien pouvoir faire dans ton listener.

    Essaye de mettre en place cette solution avec juste un seul type d'upload. Vas-y pas à pas. Je suis revenu de vacances et pourrais t'aider.

Discussions similaires

  1. [DOM] [Xerces] Insertion d'une entité
    Par Traroth dans le forum Format d'échange (XML, JSON...)
    Réponses: 10
    Dernier message: 19/05/2008, 09h28
  2. Générer automatiquement un schéma entité/association
    Par worldchampion57 dans le forum Outils
    Réponses: 3
    Dernier message: 03/06/2003, 17h11
  3. pb constructeurs classes dérivant classe abstraite
    Par Cornell dans le forum Langage
    Réponses: 2
    Dernier message: 10/02/2003, 19h02
  4. [MSXML] Comment empécher la conversion des entités ?
    Par nima dans le forum XSL/XSLT/XPATH
    Réponses: 3
    Dernier message: 08/11/2002, 14h14

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