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

Zend Framework PHP Discussion :

ZF2 : table avec prefix


Sujet :

Zend Framework PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    79
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mars 2007
    Messages : 79
    Par défaut ZF2 : table avec prefix
    Bonjour à tous,

    Je suis en train me former à Zend Framework 2 en suivant le tutoriel officiel (http://framework.zend.com/manual/2.1...plication.html). Et j'essaie de l'adapter à ma base de données.

    Mon problème vient du fait que toutes mes tables sont préfixées par deux lettres suivies d'un underscore (configurable à l'instalation de la base de données). Et j'aimerai que la classe AlbumTable garde ce nom au lieu de xx_AlbumTable.

    Voici la classe AlbumTable (reprise du tuto) située dans le répertoire module/Album/src/Album/Model :
    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
    ?php
    namespace Album\Model;
     
    use Zend\Db\TableGateway\TableGateway;
     
    class AlbumTable
    {
        protected $tableGateway;
     
        public function __construct(TableGateway $tableGateway)
        {
            $this->tableGateway = $tableGateway;
        }
     
        public function fetchAll()
        {
            $resultSet = $this->tableGateway->select();
            return $resultSet;
        }
     
        public function getAlbum($id)
        {
            $id  = (int) $id;
            $rowset = $this->tableGateway->select(array('id' => $id));
            $row = $rowset->current();
            if (!$row) {
                throw new \Exception("Could not find row $id");
            }
            return $row;
        }
     
        public function saveAlbum(Album $album)
        {
            $data = array(
                'artist' => $album->artist,
                'title'  => $album->title,
            );
     
            $id = (int)$album->id;
            if ($id == 0) {
                $this->tableGateway->insert($data);
            } else {
                if ($this->getAlbum($id)) {
                    $this->tableGateway->update($data, array('id' => $id));
                } else {
                    throw new \Exception('Form id does not exist');
                }
            }
        }
     
        public function deleteAlbum($id)
        {
            $this->tableGateway->delete(array('id' => $id));
        }
    }
    J'ai donc essayé de modifier le nom de la table dans le constructor en faisant un set table, mais j'ai l'erreur suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Invalid magic property access in Zend\Db\TableGateway\AbstractTableGateway::__set()
    A priori, la classe TableGateway n’autorise pas la modification de la table.

    Je vois pour l'instant qu'une solution qui est de créée une classe qui hérite de TableGateway en modifiant dans son construtor le nom de la table. Mais ça me plait pas trop car il faudra faire ça pour toutes mes tables !

    Je voudrais savoir s'il y avait une solution "plus élégante", c'est-à-dire plus une solution avec moins de code à écrire.

  2. #2
    Membre chevronné

    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2003
    Messages
    253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2003
    Messages : 253
    Par défaut
    Bonjour,

    Petit disclaimer, je débute totalement sur ZF2 je n'ai pour le moment même pas posé une ligne de code... Par contre j'ai pas mal lu dessus, aussi j'en profite pour me former un peu

    Si j'en crois cette étape : http://framework.zend.com/manual/2.1...the-albumtable

    Le ServiceManager a une entrée AlbumTableGateway qui configure le nom de la table à passer au TableGateway. Donc soit tu modifies le premier paramètre du constructeur en rajoutant ton préfixe... soit tu veux que le préfixe soit automatiquement ajouté pour toutes tes tables si je comprends bien ?

    Si oui je pense que la solution se trouve du côté de créer ta propre factory, dont le rôle sera de préfixer le nom des tables par ton préfixe, et de configurer le ServiceManager pour l'utiliser.

    Je proposerais bien un morceau de code, mais je n'ai pas d'environnement de travail pour tester... si tu recherches encore j'essayerais de faire un poc rapide, si personne avec des compétences ZF2 ne passe avant

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    79
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mars 2007
    Messages : 79
    Par défaut
    merci Nighty pour ton aide.

    J'ai modifié dans le fichier Module.php le nom de la table en dur pour tester et ça fonctionne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return new TableGateway('xx_album', $dbAdapter, null, $resultSetPrototype);

    Maintenant, reste à aller chercher le préfixe dans la conf. (je pense mettre ça dans le fichier config/autoload/global.php dans la section 'db') et à automatiser le préfixage, je vais me diriger vers ta piste de la Factory. Mais, il me reste encore pas mal de notions de ZF2 à appréhender avant de developper cette factory.

  4. #4
    Membre chevronné

    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2003
    Messages
    253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2003
    Messages : 253
    Par défaut
    J'ai fait un petit test qui pourrait être concluent dans le contexte du tutorial (pour ma part je me suis contenté d'un die pour valider ma théorie...).

    Très possible que ce code soit une erreur d'interprétation de ma part, mais voilà l'idée.

    D'abord on met en place une AbstractFactory :
    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
    <?php
    namespace Album\Factory;
     
    class TableGateway implements \Zend\ServiceManager\AbstractFactoryInterface
    {
        const TABLE_PREFIX = 'xx_';
        const TABLE_GATEWAY_SUFFIX = 'tablegateway';
     
        public function canCreateServiceWithName(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, $name, $requestedName)
        {
            return strpos($name, self::TABLE_GATEWAY_SUFFIX) !== false;
        }
     
        public function createServiceWithName(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, $name, $requestedName)
        {
            $tableName = substr($name, 0, strpos($name, self::TABLE_GATEWAY_SUFFIX));
            $prefixedName = self::TABLE_PREFIX . $tableName;
            $dbAdapter = $serviceLocator->get('Zend\Db\Adapter\Adapter');
            $resultSetPrototype = new \Zend\Db\ResultSet\ResultSet();
            $resultSetPrototype->setArrayObjectPrototype(new \Application\Model\TableAlbum());
            return new TableGateway($prefixedName, $dbAdapter, null, $resultSetPrototype);
        }
    }
    L'abstract factory part du principe que le nom dans le ServiceManager se construit comme ${NomTable}TableGateway, ce qui est très light et mériterait de creuser...

    Ensuite dans la configuration du module :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    'service_manager' => array(
        'factories' => array(
            'Album\Model\AlbumTable' =>  function($sm) {
                $tableGateway = $sm->get('AlbumTableGateway');
                $table = new AlbumTable($tableGateway);
                return $table;
            }
        ),
        'abstract_factories' => array(
            'AlbumTableGateway' => 'Album\Factory\TableGateway',
        )
    ),
    A priori, ça devrait marcher... je suis pas contre un retour

  5. #5
    Membre chevronné

    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2003
    Messages
    253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2003
    Messages : 253
    Par défaut
    Petit up car je viens de finir le tutorial, j'ai donc une solution qui marche que je te propose ici.

    Dans modules/Album/Module.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
        public function getServiceConfig()
        {
            return array(
                'invokables' => array(
                    'AlbumModel' => 'Album\Model\Album'
                ),
                'factories' => array(
                    'Album\Model\AlbumTable' => function (ServiceLocatorInterface $sm) {
                        $tableGateway = $sm->get('AlbumTableGateway');
                        $table = new AlbumTable($tableGateway);
                        return $table;
                    }
                ),
                'abstract_factories' => array(
                    'TableGateway' => 'Application\Factory\TableGatewayFactory'
                )
            );
        }
    Dans modules/Application/Factory/TableGatewayFactory.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
    42
    43
    44
    45
    46
    47
    <?php
    namespace Application\Factory;
    use Zend\ServiceManager\AbstractFactoryInterface,
        Zend\ServiceManager\ServiceLocatorInterface,
        Zend\Db\TableGateway\TableGateway,
        Zend\Db\ResultSet\ResultSet;
     
    class TableGatewayFactory implements AbstractFactoryInterface
    {
        const TABLE_PREFIX = 'my_';
        const TABLE_GATEWAY_SUFFIX = 'tablegateway';
     
        public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
        {
            return strpos($name, self::TABLE_GATEWAY_SUFFIX) !== false;
        }
     
        public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
        {
            $tableName = $this->getTableNameFromServiceName($name);
            $modelClassName = $this->getModelClassNameFromTableName($tableName);
            $prefixedTableName = self::TABLE_PREFIX . $tableName;
            return $this->createTableGateway($serviceLocator, $prefixedTableName, $modelClassName);
        }
     
        protected function getTableNameFromServiceName($serviceName)
        {
            $suffixPos = strpos($serviceName, self::TABLE_GATEWAY_SUFFIX);
            $tableName = substr($serviceName, 0, $suffixPos);
            return $tableName;
        }
     
        protected function getModelClassNameFromTableName($tableName)
        {
            $modelClassName = ucfirst($tableName) . 'Model';
            return $modelClassName;
        }
     
        protected function createTableGateway(ServiceLocatorInterface $serviceLocator, $tableName, $modelClassName)
        {
            $dbAdapter = $serviceLocator->get('Zend\Db\Adapter\Adapter');
            $model = $serviceLocator->get($modelClassName);
            $resultSetPrototype = new ResultSet();
            $resultSetPrototype->setArrayObjectPrototype($model);
            return new TableGateway($tableName, $dbAdapter, null, $resultSetPrototype);
        }
    }
    Honnêtement je trouve ça beaucoup trop intimement lié à une convention de nommage pour être fiable... Je ne l'utiliserais personnellement pas en production, mais c'était pour le fun !

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    79
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mars 2007
    Messages : 79
    Par défaut
    Je viens de mettre en place ton code et j'ai une erreur 500
    J'ai regardé dans les logs et j'ai l'exception suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    PHP Fatal error:  Uncaught exception 'Zend\\ServiceManager\\Exception\\InvalidArgumentException' with message 'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' in /home/nicolas/zendTest/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:302
    Stack trace:
    #0 /home/nicolas/zendTest/vendor/zendframework/zendframework/library/Zend/ServiceManager/Config.php(126): Zend\\ServiceManager\\ServiceManager->addAbstractFactory('Application\\Fac...')
    #1 /home/nicolas/zendTest/vendor/zendframework/zendframework/library/Zend/ModuleManager/Listener/ServiceListener.php(223): Zend\\ServiceManager\\Config->configureServiceManager(Object(Zend\\ServiceManager\\ServiceManager))
    #2 [internal function]: Zend\\ModuleManager\\Listener\\ServiceListener->onLoadModulesPost(Object(Zend\\ModuleManager\\ModuleEvent))
    #3 /home/nicolas/zendTest/vendor/zendframework/zendframework/library/Zend/EventManager/EventManager.php(460): call_user_func(Array, Object(Zend\\ in /home/nicolas/zendTest/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php on line 302
    Je suis aller voir dans le code et cette exception est déclenchée lorsque la factory ne satisfait pas ces conditions :
    Zend\ServiceManager\ServiceManager.php
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     public function setFactory($name, $factory, $shared = null)
        {
            $cName = $this->canonicalizeName($name);
     
            if (!is_string($factory) && !$factory instanceof FactoryInterface && !is_callable($factory)) {
                throw new Exception\InvalidArgumentException(
                    'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.'
                );
            }
    		...

    J'ai tout d'abord pensé à un mauvais formatage du nom de la classe (mauvais copier/coller, problème de casse, ...). Mais a priori ce n'est pas ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    ll -R /home/nicolas/zendTest/module/
    ...
    /home/nicolas/zendTest/module/Application/Factory:
    total 4
    -rw-r--r-- 1 nicolas nicolas 1815  4 mars  10:25 TableGatewayFactory.php
    ...
    J'ai ensuite pensé un problème d'import dans mon fichier module/Album/Module.php, j'ai donc rajouté un use sur TableGatewayFactory. Mais ce n'est pas ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    use Application\Factory\TableGatewayFactory;
    C'est certainement une erreur à la ***, mais laquelle ?

  7. #7
    Membre chevronné

    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2003
    Messages
    253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2003
    Messages : 253
    Par défaut
    Pour l'occasion, je m'auto-quote :

    Citation Envoyé par Nighty Voir le message
    Honnêtement je trouve ça beaucoup trop intimement lié à une convention de nommage pour être fiable... Je ne l'utiliserais personnellement pas en production, mais c'était pour le fun !
    Sur cette base, à toi de transformer la Factory pour coller à tes besoins, tu peux utiliser la variable $requestedName qui elle conserve la casse plutôt que $name... par contre si tes tables ne suivent jamais le même schéma de nommage, tu risques d'avoir du mal à faire fonctionner l'ensemble

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [MySQL-5.0] Dupliquer des tables avec prefixe
    Par jeremy059 dans le forum Requêtes
    Réponses: 0
    Dernier message: 09/10/2013, 11h59
  2. [MySQL] Exporter les tables avec un prefix défini
    Par Invité dans le forum PHP & Base de données
    Réponses: 0
    Dernier message: 29/01/2011, 16h04
  3. Réponses: 0
    Dernier message: 19/03/2010, 14h31
  4. [Access] Nom d'une table avec un espace dans SQL
    Par Corsaire dans le forum Langage SQL
    Réponses: 7
    Dernier message: 21/04/2006, 15h50
  5. Création de table avec index
    Par Seb7 dans le forum Requêtes
    Réponses: 2
    Dernier message: 10/04/2003, 16h11

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