Object Relational Mapping en PHP

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Object Relational Mapping en PHP

bof

par foxdiecs » 28 juil. 2009, 19:54

ah nan, ce qui m'importe c'est de conserver un maximum de souplesse au sein de mon application.
En effet, certains membres ne doivent pas être initialisés betement à partir de données récupérées dans la base, d'autres sont des collection d'objets, etc...
Les fichers XML me permettent d'autre part de définir les relations existant entre les différentes tables de la base.
Par contre l'introspection me paraît très intéréssante, pour d'autres choses, je vais y regarder de plus près, merci pour l'info. :lol:

par Hywan » 28 juil. 2009, 13:52

Ah ok, tu le récupères depuis l'XML. Bah tu triches aussi. Avec l'intropsection (voir http://php.net/reflection) tu peux le savoir. C'est plus propre, car ça différencie bien tes couches. Tu peux te passer de ton XML et ton système fonctionne encore.

par Invité » 28 juil. 2009, 13:42

je n'ai pas mis tout le code dans l'exemple mais la classe MappedObject possède une méthode abstraite qui oblige ses filles à retourne une instance de Map. Il s'agit d'une classe qui décrit la structure l'objet, la liste des membres/champs dans la base est chargée depuis un fichier XML ( comme dans beaucoup d'ORM ).

Ca donne ça
abstract class MappedObjet{

   /**
    * @return map
   **/
   public abstract function getMap();

}
dans la classe User, la méthode devient :
class User extends MappedObjet{

   public function getMap(){ return new Map( "chemin_de_config/user.xml" ); }

}
La classe, chez moi, hérite de DOMDocument :
class Map extends DomDocument{

   public function __contruct( $filename ){

      parent::loadXML( $filename );

   }

   //puis une méthode qui retourne un itérateur vers la liste des membres/champ dans la base

}
Le fichier XML est un truc du genre :

Code : Tout sélectionner

<?xml version="1.0" encoding="UTF-8"?> <class name="User"> <tables> <table name="user"> <columns> <column name="iduser" ... /> </colums> </table> </tables> <relationships> ... pour éventuellements mapper des objets membres ou des collections d'objets </relationships> </class>

par Hywan » 28 juil. 2009, 11:06

Et comment scannes-tu les membres privés d'une classe ?

soit

par foxdiecs » 27 juil. 2009, 19:35

soit pour le Visiteur, je n'insiste pas, je viens de découvrir le pattern, je relirai ça tranquilou.

Pour ce qui est de la manipulation des membres privés passés par références voila un petit exemple de ce qu'autorise le langage aujourd'hui :

//-------------------------------------------------------------------------------------------------

abstract class MappedObject{

   //méthode qui permet de donner accès à un membre privé à une instance de Mapper
   public function setDataMemberAccess( Mapper &$mapper, $dataMember ){

      $mapper->setCurrentDataMember( $this->$dataMember );

   }

}

//-------------------------------------------------------------------------------------------------

class User extends MappedObject{ //la classe User peut donner accès à ses membres privés à une instance de Mapper car elle dérive de MappedObject

   private $id;
   private $login;

   public function __construct( $id ){ 

      $this->id = intval( $id ); 

   }

}

//-------------------------------------------------------------------------------------------------

class Mapper{

   private $currentDataMember; //utilisé pour récupéré les accès vers les membres privés objets à mapper

   public function map( MappedObject &$instance ){ //initialiser les membres privés d'un MappedObject

     //je ne détaille pas comment, mais on supposera qu'à ce stade le mapper connaît la structure
     //de l'objet User et l'emplacement des infos à récupérer dans la base de données
     
       foreach( ....as dataMember => $value ){ //pour chaque attribut à initialiser

         //demander à l'objet User un accès privilégié au membre privé concerné
         //l'objet User appelera la méthode Mapper::setCurrentDataMember() pour donner une référence vers son membre privé
         $instance->setDataMemberAccess( $this, $dataMember ); //récupérer l'accès

         //à ce moment là, $this->currentDataMember est une référence vers l'attribut privé de l'objet User. On peut donc le manipuler
         $this->currentDataMember = $value;

      }

   }

   public function setCurrentDataMember( &$dataMember ){

      $this->currentDataMember =& $dataMember;

   }

}

//-------------------------------------------------------------------------------------------------

//pour éxécuter tout ça :

$user = new User( 1 );
$mapper = new Mapper();
$mapper->map( $user );

//-------------------------------------------------------------------------------------------------
[/php]

par Hywan » 27 juil. 2009, 09:55

Mais arrêtez de parler de visiteur, ça n'a rien à voir avec le sujet … On ne visite pas une classe mais un arbre, c'est tout à fait différent. Ce n'est pas le bon terme. Tu peux dire que tu inspectes une classe à la limite.

Tu arrives à casser la visibilité en utilisant les références ? J'aimerais bien voir ça. Un exemple ?

merci

par foxdiecs » 26 juil. 2009, 22:49

lol, je ne savais pas qu'il était possible d'accéder aux membres protégés d'une classe en passant par sa mère ( sous-entendu que la mère elle ne possède pas ces membres ).

C'est une bonne astuce, cela me force à faire un héritage que je souhaitais éviter au début mais tant pis.

Pour ce qui est pattern Visitor ( que je ne connaissais pas avant ), c'est assez proche de ce que j'avais ebauché : la classe User était visitée par la classe Mapper.
J'ai testé une autre chose intéréssante et qui fonctionne à merveille :

- on demande à la classe Mapper de mapper une instance de User
- la classe Mapper connait la structure de la classe User ( comme dans tous les ORM, soit grâce à un fichier XML, ou autre,... )
- pour chaque attribut à initialiser, la classe Mapper visite la classe User ( exactement comme je l'ai écrit tout au début ) en demandant l'accès au member à l'aide de MappedObject::setDataMemberAccess()
- à ce moment-là la classe User appele la méthode Mapper::setCurentDataMember() en passant son attribut privés ou protégé à mapper par référence
Donc ca y est, la classe Mapper peut manipuler les membres privés des objets à mapper.

Merci pour votre aide :D [/b]

par Hywan » 26 juil. 2009, 20:59

Hey :-),

Je ne sais pas comment Hibernate fonctionne mais voici ce qu'il est possible de faire.
Tout d'abord, on ne peut pas définir des valeurs définies comme privées en dehors de la classe. Donc même avec un héritage, c'est impossible. Tu peux essayer toutes les circonvolutions possibles, tu ne pourras pas.
Par contre, pour ce qui est des valeurs protégées, c'est tout à fait possible. Un bête héritage, __get(), et le tour est joué. Un truc du genre :
class Map {

    public function query ( ) {

        // quand on lance la requête, cette méthode est exécutée.
        foreach($fields as $i => $field)
            $field = ...;

        // on peut récupérer les champs (attributs) des enfants (ici dans $fields) depuis la classe mère.
        // on peut également attribuer une valeur, même si c'est protégé.
    }
}

class Table extends Map {

    public $id;
    protected $name;
}
Il existe plusieurs moyens pour récupérer les attributs des enfants. C'est un peu galère, mais c'est possible. Tu as beaucoup de façon de faire.

Pour donner l'exemple de mon framework, Hoa_Database permet de faire ce genre de chose (la documentation est en cours). Mais on ne peut pas accéder aux attributs privés, car ils sont ... privés !
L'astuce pour les attributs protégés, c'est de les atteindre depuis la classe Mère (c'est l'inverse de ce qu'on fait d'habitude). C'est la seule façon de le faire.

Sinon Mojorisin, le modèle de conception visiteur n'a rien à voir avec ce genre de problème ;-).

par mojorisin » 25 juil. 2009, 20:45

Bonjour,
vous pouvez vous orienter du coté du pattern Visiteur peut être ?

re

par foxdiecs » 25 juil. 2009, 16:57

merci mais je me suis peut être mal exprimé :?
En fait je ne souhaite pas qu'un développeur ou qu'une autre classe puisse accéder aux membres privés des objets afin d'en modifier le contenu. Seule la classe de mapping doit y être autorisée. C'est cette restriction que j'essaie de modéliser.

par fab » 25 juil. 2009, 16:01

Object Relational Mapping en PHP

par foxdiecs » 25 juil. 2009, 11:28

salut!

Il existe différents ORM en PHP ( Doctrine, PHPMyObject, ... ) mais après jetté un oeil sur leur façon de fonctionner je me rends compte que ceux-ci violent les principes fondamentaux de la programmation orientée objet.

En effet, lorsqu'il s'agit d'initialiser les attributs d'une instance d'une classe, les classes de mapping accèdent directement aux membres de la classe à mapper qui doivent être déclarés publiques ou bien énumérés dans un tableau qui lui sera déclaré publique.

Au contraire, en java, Hibernate à la possibilité d'accéder directement aux membres déclarés private ou protected d'une classe lors du mapping, ce qui à ma connaissance, n'existe pas en PHP.

Je suis à la recherche d'un pattern qui permettrait à une classe de ne donner accès à ses attributs private ou protected qu'à une classe de mapping donnée. J'ai imaginé le code suivant :

Code : Tout sélectionner

interface MappedObject{ //retourne la map qui décrit les propriétés de l'objet et leur emplacement dans la bdd public function Map getMap(); //donner accès aux attributs privés à la classe de mapping public function setDataMemberAccess( Mapper &$mapper, $dataMember ); }

Code : Tout sélectionner

class User implements MappedObject{ private $id; private $login; public function __construct( $id ){ $this->id = intval( $id ); } public function getMap(à{ return new UserMap(); } public function setDataMemberAccess( Mapper &$mapper, $dataMember ){ //l'attribut privé sera récupéré par référence par le mappeur, //je n'ai pas testé si cela était possible $mapper->setCurrentDataMember( $this->$dataMember ); } }

Code : Tout sélectionner

class Mapper{ //récupérer une référence vers l'attribut privé de la classe à mapper //aucune idée si cela fonctionne public function setCurrentDataMember( &$dataMember ){ $this->currentDataMember = $dataMember; } public function map( $instance ){ $map = $instance->getMap(); //pour chaque attributs définit dans la map on récupère la valeur dans la bdd //et on inititalise l'attribut de la classe foreach( ....as $dataMember => $value ){ $instance->setDataMemberAccess( $this, $dataMember ); //récupérer l'accès $this->currentDataMember = $value; //initialiser } } }
finalement le mappage s'effectuerait comme ceci :

Code : Tout sélectionner

$user = new User( 1 ); $mapper = new Mapper(); $mapper->map( $user );
Bref, la méthode est n'est peut être pas très élégante. Si quelqu'un a une meilleur approche je suis prenneur :lol: