Partage
  • Partager sur Facebook
  • Partager sur Twitter

POO: Objets imbriqués les uns dans les autres

    30 mars 2017 à 14:41:54

    Bonjour,

    J'ai un petit souci de conception quand je me retrouve à devoir gérer des objets imbriqués les uns dans les autres.

    Si j'ai une classe composée d'attributs non objets, j'arrive à faire ce que je veux, c'est à dire définir ma classe:

    <?php
    
    namespace Locations\Model\Entities;
    
    class Environnement {
        
        private $id;
        private $nom;
        
        function getId() {
            return $this->id;
        }
        
        function getNom() {
            return $this->nom;
        }
    
        function setId($id) {
            $this->id = $id;
        }
    
        function setNom($nom) {
            $this->nom = $nom;
        }
        
    }

    ... définir mon manageur d'objets:

    <?php
    
    namespace Locations\Model\Dao;
    
    require_once(dirname(__FILE__).'/DB.php');
    require_once(dirname(__FILE__).'/../entities/Environnement.php');
    
    use \PDO as PDO;
    use \Locations\Model\Entities\Environnement;
    
    class DAOEnvironnement {
        
        /**
         * Récupérer annonces
         * @return Annonce $annonces
         */
           static function recupererEnvironnements(){
            $pdo = DB::getConnection();
    			
    	$stmt = $pdo->query("SELECT * FROM environnement ORDER BY nom ASC");
    	
            $environnements = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, Environnement::class);
    		   
    	return $environnements;
        }
    }

    ... J'ai plus dans le contrôleur qu'à appeler la méthode statique recupererEnvironnements et transmettre le résultat à la vue par le biais d'une variable

    ... Dans la vue, je fais une boucle pour afficher chaque résultat ainsi

    <h1>Environnements possibles</h1>
    <div class="diaporama">
    
    <?php
        foreach ($data['environnements'] as $environnement) {
            ?>
                <div class="vignette">
                    <!--<div class="image">
                        <img src="assets/img/documents/<?php //echo $annonce->getImage(); ?>" >
                    </div>-->
                    <div class="legende">
                      <h3><?php echo $environnement->getNom(); ?></h3>
                      <p>description </p>
                    </div>
                </div>
            <?php
        }
    ?>
    
    </div>

    et j'obtiens ainsi ma liste de 4 environnements possibles: plage (id=1, nom=plage), campagne (id=2, nom=campagne), montagne (id=3, nom=montagne), ville (id=4, nom=ville).

    Supposons maintenant que je souhaite utiliser ma classe Environnement comme type de données pour une nouvelle classe "Location". C'est ce que j'appelle des objets imbriqués... Mes locations auront comme attributs un id, une adresse, un code postal, une ville et un environnement. Ces attributs seront donc de type primaires (string, int ou bool) sauf l'environnement qui sera de type Environnement...

    Du coup je définirais ma nouvelle classe ainsi:

    class Location {
        
        private $id;
        private $adresse;
        private $code_postal;
        private $ville;
        private $environnement;
    
    }

    Par la suite on aura aussi un attribut "private $proprietaire" qui sera de type Personne... 

    Je pense que ma classe est bonne (encore que...) mais c'est pour le manageur que j'ai un souci.

    Si ma table Location était composée uniquement de types primaires, je ferais ainsi et cela fonctionnerait:

    <?php
    
    namespace Locations\Model\Dao;
    
    require_once(dirname(__FILE__).'/DB.php');
    require_once(dirname(__FILE__).'/../entities/Location.php');
    
    use \PDO as PDO;
    use \Locations\Model\Entities\Location;
    
    class DAOLocation {
        
        /**
         * Récupérer les locations
         * @return Location $locations
         */
           static function recupererLocations(){
            $pdo = DB::getConnection();
    			
    	$stmt = $pdo->query("SELECT * FROM location");
    	
            $locations = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, Location::class);
    		   
    	return $locations;
        }
    }

    Mais ici avec 2 tables différentes et des données à récupérer sous forme de 2 objets différents à chaque fois, je suis un peu perdu. Sur le concept il faudrait faire ma requête pour récupérer toutes les locations. Ensuite il faudrait remplacer chaque clé étrangère "environnement_id" qui est de type int par l'objet Environnement correspondant et seulement à ce moment récupérer les données dans la variable $locations.

    Le problème, c'est que je ne vois pas comment faire.

    Du coup un petit coup de main serait accepté volontiers... :)

    • Partager sur Facebook
    • Partager sur Twitter
      30 mars 2017 à 14:45:34

      Salut,

      avant d'attaquer le code, je te conseilerai d'apprendre les différentes conception de relations entre les classes en UML.

      Ce que tu décris est :

      • Soit une composition
      • Soit une agregation

      Mais il faut avoir un peu de bagage pour gérer tout en même temps. Donc commence par vriament savoir ce que tu veux, puis UML puis le code ;)

      Sur le concept, tu peux très bien monter la première classe, puis monter les classes qui sont composées/agrégées.

      Si tu montes tout en même temps, tu risques de créer des grappes d'objet difficilement manipulable / maintenables, et tu risques de saturer la mémoire.

      ++

      -
      Edité par christouphe 30 mars 2017 à 14:49:33

      • Partager sur Facebook
      • Partager sur Twitter
        30 mars 2017 à 17:01:53

        Hello,

        Je viens d'essayer de capter le concept de composition/agrégation en uml sur http://uml.free.fr/cours/p15.html. 

        J'avoue que tout n'est pas clair pour moi

        Bon après, la conception je l'ai fait en Merise (modèle entités/associations) mais ça ne m'aide pas beaucoup au niveau du code :/

        Si je reprends tes 2 dernières phrases, cela veut dire que l'attribut $environnement de ma classe Location devrait plutôt contenir une clé étrangère de type int de ma table Environnement, et non comme je l'imaginais au début une instance de la classe Environnement?  

        -
        Edité par Xt0f 30 mars 2017 à 17:24:23

        • Partager sur Facebook
        • Partager sur Twitter
          30 mars 2017 à 18:50:20

          Tout dépend de la relation:

          • A1 - 1B => FK = PKA dans B ou PKB dans A
          • A1 - NB => FK = PKA dans B
          • AN - NB => Nouvelle table de relation A_B(id,PK1,PKB)

          Dans quel cas es-tu ?

          ++

          • Partager sur Facebook
          • Partager sur Twitter
            30 mars 2017 à 20:39:18

            C'est une relation 1-1, j'ai donc une table Location contenant comme tu le dis une clé étrangère environnement_id qui est aussi la clé primaire de ma table Environnement

            Après mon souci débute lorsque je construis mes classes...

            class Location {
                 
                private $id;
                private $adresse;
                private $code_postal;
                private $ville;
                private $environnement;
             
            }
            class Environnement {
                 
                private $id;
                private $nom;
            }


             

            Est ce que les objets construits à partir de ma classe Location devraient contenir au niveau de l'attribut $environnement une valeur de type int qui serait juste l'identifiant d'un objet de type Environnement (en somme la construction de l'objet en php serait à l'identique du schéma en base de données):

            Ou est-ce que la bonne pratique serait plutôt que l'attribut $environnement contienne un objet tout entier de type Environnement? ... ce qui pourrait alors revenir à pouvoir faire un truc du genre:

            $environnement1 = new Environnement("campagne");
            $location1= new Location("rue de Strasbourg", 75000, "Paris", $environnement1);

            -
            Edité par Xt0f 30 mars 2017 à 20:47:36

            • Partager sur Facebook
            • Partager sur Twitter
              30 mars 2017 à 22:04:44

              Perso je fais comme ceci dans mon application (mais c'est MA solution ;) ) je veux dire que tu peux très bien en avoir une différente qui te conviendra. Je te donne ça pour t'inspirer ;)

              <?php
              	class Unite {
              		protected $_id; /* int(11) unsigned */
              		//autre code
              	
              		/* identifiant composite Equipement */
              		protected $_equipement; /* bigint(20) unsigned */
              		/* liste des objets Equipement */
              		protected $_oEquipement; /* equipementarmegauche Object*/
              		
              		public function __construct($id=0,$equipement=0) {
              			//Generated by ObjectGenerator::generateConstruct()
              			$this->_id = $id;
              			$this->_equipementet = $equipement;
              		
              		}
              		
              		public function getEquipement() {
              			//Generated by ObjectGenerator::generateGet()
              			return $this->_equipementarmedroite;
              		}
              		
              		public function getEquipementObject() {
              			//Generated by ObjectGenerator::generateGet()
              			if (empty($this->_oEquipement)) {
              				$this->setEquipementObject();
              			}
              			if ((empty($this->_oEquipement) || is_null($this->_oEquipement))&& $this->_equipement > 0) {
              				$this->setEquipementObject();
              			}	
              			return $this->_oEquipement;
              		}
              		
              		protected function setEquipementObject() {
              			//Generated by ObjectGenerator::generateSet()
              			if ($this->_equipement > 0) {
              				//ma factory == acces DAO
              				$this->_oEquipement = FactoryEquipement::getFromTableEquipement($this->_equipement);
              			}
              		}
              	}
              	
              	class Equipement {
              		protected $_id; /* bigint(20) */
              		
              		public function __construct($id=0) {
              			//Generated by ObjectGenerator::generateConstruct()
              			$this->_id = $id;
              		}
              	}
              	
              	//utilisation
              	
              	$oUnite = new Unite(15,152);
              	//chargement de l'equipement
              	$oUnite->getEquipementObject();
              ?>

              ++

              • Partager sur Facebook
              • Partager sur Twitter
                2 avril 2017 à 22:44:42

                Si jamais ça intéresse quelqu'un, je poste la solution que j'ai finalement trouvée.

                Il fallait changer la classe DAOLocation ainsi:

                <?php
                 
                namespace Locations\Model\Dao;
                 
                require_once(dirname(__FILE__).'/DB.php');
                require_once(dirname(__FILE__).'/../entities/Location.php');
                 
                use \PDO as PDO;
                use \Locations\Model\Entities\Location;
                 
                class DAOLocation {
                     
                    /**
                     * Récupérer les locations
                     * @return Location $locations
                     */
                    static function recupererLocations(){
                        $pdo = DB::getConnection();
                             
                        $stmt = $pdo->query("SELECT l.id, l.adresse, l.code_postal, l.ville, e.id AS environnement_id, e.nom AS environnement_nom FROM location AS l INNER JOIN environnement AS e ON l.environnement_id = e.id");
                        $stmt->setFetchMode(PDO::FETCH_OBJ);
                        $res = $stmt->fetchAll();
                            
                        $locations = array();
                        foreach ($res as $donnee)
                        {
                	        $environnement = new Environnement($donnee->environnement_id, $donnee->environnement_nom);
                	        $location = new Location($donnee->id, $donnee->adresse, $donnee->code_postal, $donnee->ville, $environnement);
                	        array_push($locations, $location);
                        }		   
                        return $locations;
                    }
                }

                Du coup ça me permet de récupérer mes données de la base de données sous la forme d'objets complexes comme je le souhaitais ("objets imbriqués les uns dans les autres" comme je disais...)

                Je peux alors passer mon tableau de locations ($locations) à ma vue et boucler dessus pour faire des trucs du genre $location->getEnvironnement()->getNom(); pour afficher le nom de l'environnement de la location ou $location->getEnvironnement()->getId(); pour en afficher l'identifiant.

                -
                Edité par Xt0f 2 avril 2017 à 22:46:33

                • Partager sur Facebook
                • Partager sur Twitter
                  3 avril 2017 à 8:58:04

                  ^^ content que cela fonctionne, mais à ce que je comprends tu remonte systématiquement des grappes d'objets complexes importants en mémoire ==> attention aux memory fault ;)

                  ++

                  • Partager sur Facebook
                  • Partager sur Twitter
                    3 avril 2017 à 9:57:43

                    Merci de ton avis.

                    Si j'évite de récupérer l'ensemble de mes locations mais que je les récupère 10 par 10 ou 20 par 20 par le biais d'un système de pagination et d'un "LIMIT" dans la requête sql, ça devrait éviter ce genre de souci non?

                    -
                    Edité par Xt0f 3 avril 2017 à 9:58:24

                    • Partager sur Facebook
                    • Partager sur Twitter
                      3 avril 2017 à 10:41:03

                      Oui, mais en fait derrière ma question était: as-tu réellement besoin de tout au même moment / même endroit ?

                      Tu risques de faire des chargement successifs inutiles.

                      ++

                      • Partager sur Facebook
                      • Partager sur Twitter

                      POO: Objets imbriqués les uns dans les autres

                      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                      × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                      • Editeur
                      • Markdown