collection d'objets

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 : collection d'objets

par rufus_ » 21 juil. 2008, 17:05

merci sadeq pour cette version très complète

j'utilise en effet une table hiérarchisée similaire id_menu, id_menuSup, menu plus quelques colonnes pour des cas particuliers comme lecture d'un sous menu ailleurs dans une liste

par sadeq » 21 juil. 2008, 16:09

Voici une version plus développée qui crée une collection d'objets hiérarchisés. Dans notre exemple, un menu peut avoir plusieurs sous-menus qui à leur tour peuvent en avoir d'autre à l'infini (fini).

C'est au moment du Add() dans la collection que cette méthode détecte s'il s'agit d'un menu parent ou enfant et affecte automatiquement dans la bonne branche.

Nouveauté aussi, pour supprimer un menu ou sous-menu par son key quelque soit son emplacement dans la hiérarchie.
<pre>
<?php
mysql_connect("localhost", "root", ""); mysql_select_db("test");

//
class MenuItems
{
   // Propriétés de la classe
   private $id_menu;
   private $text;
   private $id_parent;
   private $sous_menus; // menus enfants
   
   // Propriétés de la collection
   private $currentIndex;
   private $collection;
   
   // Constructeur de la classe
   public function MenuItems($id_menu=null, $text=null, $id_parent=null, $sous_menus=null){ // Params facultatifs
	$this->id_menu 		= $id_menu;
	$this->text 		= $text;
   	$this->id_parent 	= $id_parent;
	$this->sous_menus 	= array();
   }
   // Méthodes SET pour les propriétés privées
   public function setId_menu($id_menu){
   	$this->id_menu 	= $id_menu;
   }  
   public function setMenu($text){
   	$this->text = $text;
   }  
   public function setid_parent($id_parent){
   	$this->id_parent = $id_parent;
   } 
   public function setSous_menus($key, $sous_menus){
   	$this->sous_menus[$key] = $sous_menus;
   } 
   public function setCollection($collection){
   	$this->collection = $collection;
   } 
   // Méthodes GET pour les propriétés privées
   public function getId_menu(){
   	return $this->id_menu;
   }  
   public function getMenu(){
   	return $this->text;
   }  
   public function getid_parent(){
   	return $this->id_parent;
   } 
   public function getSous_menus(){
   	return $this->sous_menus;
   } 
   public function getCollection(){
   	return $this->collection;
   } 
   // Méthodes : Ajout, Recherche,  Suppression dans la collection
   public function exists($id_menu){
   	if (count($this->collection)>0 && $this->collection[$id_menu]) return true; else return false;
   }  
   public function getObject($id_menu){
   	if ($id_menu && $this->exists($id_menu)) return $this->collection[$id_menu];
   }
   public function addToParent(&$collection, $object) {
   	if (count($collection)>0){
	 foreach($collection as $key=>$text){
		if ($object->id_parent == $text->id_menu) { // Parent trouvé
			$collection[$key]->setSous_menus($object->id_menu, $object);
			return;
		}
		else $this->addToParent($collection[$key]->sous_menus, $object); // Recherche de parent dans les branches
	 }
	}
	return;
   }
   public function add($textObject){
   	if ($textObject->id_menu && ! $this->exists($textObject->id_menu)){
		$o = new MenuItems($textObject->id_menu, $textObject->text, $textObject->id_parent);
		//
		if ($o->id_parent != 0){ // sous-menu
			$this->addToParent($this->collection, $o); // place le sous-menu hiérachiquement
		}
		else $this->collection[$o->id_menu] = $o; // menu parent
	}
   }
   public function remove($id_menu, &$collection=null){
   	if (!is_array($collection) && $collection == null) $collection = &$this->collection;
	if ($id_menu && count($collection)>0){
		if ($collection[$id_menu]) { // Suppression d'un parent
			unset($collection[$id_menu]);
			return true;
		}
		else { // Suppression dans les sous-menus enfants
			foreach ($collection as $key=>$text){
				if ($this->remove($id_menu, $text->sous_menus)) return true;
			}
		}
	}
	return false;
   }
   // iterator : Naviguer dans la collection
   public function getCollectionKeys(){
   	return is_array($this->collection) ? array_keys($this->collection) : 0;
   }
   public function getCollectionSize(){
   	return count($this->collection);
   }
   public function current(){
	$keys = $this->getCollectionKeys();
	$currentKey = $this->currentIndex >= 0 ? $keys[$this->currentIndex] : null;
   	if ($currentKey && $this->collection[$currentKey]){
		$this->id_menu 		= $this->collection[$currentKey]->id_menu;
		$this->text 		= $this->collection[$currentKey]->text;
		$this->id_parent 			= $this->collection[$currentKey]->id_parent;
		$this->sous_menus 	= $this->collection[$currentKey]->sous_menus;
		//		
		return $this->collection[$currentKey];
	}  
	else return null;
   }  
    public function first(){
	$this->currentIndex = 0;
   }  
   public function next(){
	$this->currentIndex ++;
   }  
   public function previous(){
	$this->currentIndex --;
   }  
   public function last(){
	$this->currentIndex = $this->getCollectionSize()-1;
   }
}
//
$mi = new MenuItems();
//
$result = mysql_query("select id_menu, text, id_parent from menu");
while($result && $row = mysql_fetch_object($result))
{
  if ($row) {
       // Alimentation de la collection d'objets MenuItem
       $mi->add($row);
  }
}
//
$mi->first();
while ($mi->current()) {
	
   echo "<b>Id Menu: </b>", $mi->getId_menu(),"<br />", "<b>Text Menu: </b>",$mi->getMenu(),"<br />", "<b>Id Parent: </b>",$mi->getid_parent(),"<br />";
   echo "<b>Sous Menus : </b>"; print_r($mi->getSous_menus());
   $mi->next();
}
?>
</pre>
La base de données d'exemple:

Code : Tout sélectionner

-- phpMyAdmin SQL Dump -- version 2.11.6 -- http://www.phpmyadmin.net -- -- Serveur: localhost -- Généré le : Lun 21 Juillet 2008 à 16:22 -- Version du serveur: 5.0.51 -- Version de PHP: 5.2.6 SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; -- -- Base de données: `test` -- -- -------------------------------------------------------- -- -- Structure de la table `menu` -- CREATE TABLE `menu` ( `id_menu` int(11) NOT NULL, `text` varchar(50) NOT NULL, `id_parent` int(11) default '0', PRIMARY KEY (`id_menu`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -- Contenu de la table `menu` -- INSERT INTO `menu` (`id_menu`, `text`, `id_parent`) VALUES (1, 'Menu 1', 0), (2, 'Menu 2', 0), (11, 'Sous-Menu 1.1', 1), (12, 'Sous-Menu 1.2', 1), (21, 'Sous-Menu 2.1', 2), (22, 'Sous-Menu 2.2', 2), (111, 'Sous-Menu 1.1.1', 11), (121, 'Sous-Menu 1.2.1', 12);

par ouckileou » 21 juil. 2008, 14:22

Modération :
rufus_, si ta question est résolue, pense à ajouter le tag [Résolu]
pour indiquer aux personnes qui voudront consulter ce sujet qu'il contient une solution.
Tu peux réaliser cette opération en cliquant sur le bouton Image en haut à gauche de ce sujet.

par rufus_ » 21 juil. 2008, 13:07

pardon autant pour moi, les deux versions fonctionnent !
une variable manquant dans un include car j'avais copie le code dans un second fichier afin de tout garder

merci a vous deux

par Hywan » 21 juil. 2008, 12:51

En lisant rapidement :
while(null !== $current = $mi->current()) {

    echo '<strong>Id Menu</strong>&nbsp;:'  . $current->getId_menu() . "<br />\n";
    $mi->next();
}
Ça me semble plus cohérent.

Je ne sais pas si null est interprété comme false par while, d'où ma condtion null !==, sinon un simple $current = $mi->current() suffirait.

par rufus_ » 21 juil. 2008, 12:43

cette derrière version est très proche du code que j'utilise en .net c'est quasiment pareil
a part qu'il faut me familiariser avec cette nouvelle syntaxe et ce qu'on peut faire ou pas en php 5

j'utilise les property set get et les iterator de la même manière sinon

mais la je ne comprend pas pourquoi les valeurs ne sont pas retournée

par Hywan » 21 juil. 2008, 12:32

Je ne sais pas si tu comprends tout ce que fait Sadeq en fait.
Il n'implémente pas les interfaces, mais c'est tout comme. En fait, on pourrait ajouter : implements Iterator, par exemple.
Si tu regardes un des premiers liens que je t'avais donné sur ArrayObjet, on peut voir que cette classe implémente ArrayIterator, Iterator, et des choses du genre. C'est de là que viennent les méthodes next, previous, last, etc.

par rufus_ » 21 juil. 2008, 12:24

je viens de retester , en copiant collant tout ton code
mais l'echo me donne rien , une page blanche

mais bon la c'est une caisse de biere que je vais te devoir !!

par sadeq » 21 juil. 2008, 12:13

lol, j'ai opéré quelques modifs et notamment le test sur array_keys qui ne supporte que les tableaux.

J'ai modifié aussi le comportement de l'itterator : next(), previous() ... pour que la lecture de la collection soit séquentiel du début jusqu'à la fin
<pre>
<?php
mysql_connect("localhost", "root", ""); mysql_select_db("test");

//
class MenuItems
{
   // Propriétés de la classe
   private $id_menu;
   private $menu;
   private $sm;
   
   // Propriétés de la collection
   private $currentIndex;
   private $collection;
   
   // Constructeur de la classe
   public function MenuItems($id_menu=null, $menu=null, $sm=null){ // Params facultatifs
	$this->id_menu 	= $id_menu;
	$this->menu 	= $menu;
   	$this->sm 		= $sm;
   }
   // Méthodes SET pour les propriétés privées
   public function setId_menu($id_menu){
   	$this->id_menu 	= $id_menu;
   }  
   public function setMenu($menu){
   	$this->menu = $menu;
   }  
   public function setSm($sm){
   	$this->sm = $sm;
   } 
   // Méthodes GET pour les propriétés privées
   public function getId_menu(){
   	return $this->id_menu;
   }  
   public function getMenu(){
   	return $this->menu;
   }  
   public function getSm(){
   	return $this->sm;
   } 
   // Méthodes : Ajout, Recherche,  Suppression dans la collection
   public function exists($id_menu){
   	if (count($this->collection)>0 && $this->collection[$id_menu]) return true; else return false;
   }  
   public function add($menuObject){
   	if ($menuObject->id_menu && ! $this->exists($menuObject->id_menu)){
		$this->collection[$menuObject->id_menu] = new MenuItems($menuObject->id_menu, $menuObject->menu, $menuObject->sm);
	}
   }
   public function getObject($id_menu){
   	if ($id_menu && $this->exists($id_menu)) return $this->collection[$id_menu];
   }  
   public function remove($id_menu){
   	if ($id_menu && $this->exists($id_menu)) unset($this->collection[$id_menu]);
   }
   // iterator : Naviguer dans la collection
   public function getCollectionKeys(){
   	return is_array($this->collection) ? array_keys($this->collection) : 0;
   }
   public function getCollectionSize(){
   	return count($this->collection);
   }
   public function current(){
	$keys = $this->getCollectionKeys();
	$currentKey = $this->currentIndex >= 0 ? $keys[$this->currentIndex] : null;
   	if ($currentKey && $this->collection[$currentKey]){
		$this->id_menu 	= $this->collection[$currentKey]->id_menu;
		$this->menu 	= $this->collection[$currentKey]->menu;
		$this->sm 		= $this->collection[$currentKey]->sm;
		//		
		return $this->collection[$currentKey];
	}  
	else return null;
   }  
    public function first(){
	$this->currentIndex = 0;
   }  
   public function next(){
	$this->currentIndex ++;
   }  
   public function previous(){
	$this->currentIndex --;
   }  
   public function last(){
	$this->currentIndex = $this->getCollectionSize()-1;
   }  
}
//
$mi = new MenuItems();
//
$result = mysql_query("select id_menu, menu, sm from menu");
while($result && $row = mysql_fetch_object($result))
{
  if ($row) {
       // Alimentation de la collection d'objets MenuItem
       $mi->add($row);
  }
}
//
$mi->first();
while ($mi->current()) {
   echo "<b>Id Menu: </b>", $mi->getId_menu(),"<br />", "<b>Menu: </b>",$mi->getMenu(),"<br />", "<b>Sm: </b>",$mi->getSm(),"<br />";
   $mi->next();
} 
?>
</pre>

par rufus_ » 21 juil. 2008, 12:08

ta premier champ

Code : Tout sélectionner

marche très bien j'ai un problème sur le second (développement possible ect ...) [b]Warning: array_keys() [function.array-keys]: The first argument should be an array in C:\wamp\www\rufus\menuTest.php on line 67[/b] qui correspond a [b]return array_keys($this->collection);[/b]

par sadeq » 21 juil. 2008, 11:41

Ok, en fait, en l'absence du casting sous PHP, ce dernier ne fait pas la conversion de type à type mais retype (recrée l'objet sujet de l'affectation) en l'occurence, il écrase l'objet MenuItems et le remplace par la définition de celui affecté ($row). Ce qui explique la prise en compte du champ XXX.
La preuve est que le type de l'objet reste : stdClass. Ce qui veut dire classe standard inconnue.

Pour palier à ce problème il faut toujours instancier correctement l'objet MenuItem en lui affectant les valeurs de $row et non l'objet $row lui-même.
if ($row) {
       // Instanciation de l'objet MenuItem et Stockage dans la collection indéxée par id_menu
       $key = $row->id_menu;
       $mi[$key] = new MenuItems();
       $mi[$key]->sm = $row->sm;
       $mi[$key]->id_menu = $row->id_menu;
       $mi[$key]->menu = $row->menu;
  } 
Mais voici un développement possible de ton script vers une vision globale de collection d'objets:
<pre>
<?php
mysql_connect("localhost", "root", ""); mysql_select_db("test");

//
class MenuItems
{
   // Propriétés de la classe
   private $id_menu;
   private $menu;
   private $sm;
   
   // Propriétés de la collection
   private $currentIndex;
   private $collection;
   
   // Constructeur de la classe
   public function MenuItems($id_menu=null, $menu=null, $sm=null){ // Params facultatifs
	$this->id_menu 	= $id_menu;
	$this->menu 	= $menu;
   	$this->sm 		= $sm;
   }
   // Méthodes SET pour les propriétés privées
   public function setId_menu($id_menu){
   	$this->id_menu 	= $id_menu;
   }  
   public function setMenu($menu){
   	$this->menu = $menu;
   }  
   public function setSm($sm){
   	$this->sm = $sm;
   } 
   // Méthodes GET pour les propriétés privées
   public function getId_menu(){
   	return $this->id_menu;
   }  
   public function getMenu(){
   	return $this->menu;
   }  
   public function getSm(){
   	return $this->sm;
   } 
   // Méthodes : Ajout, Recherche,  Suppression dans la collection
   public function exists($id_menu){
   	if (count($this->collection)>0 && $this->collection[$id_menu]) return true; else return false;
   }  
   public function add($menuObject){
   	if ($menuObject->id_menu && ! $this->exists($menuObject->id_menu)){
		$this->collection[$menuObject->id_menu] = new MenuItems($menuObject->id_menu, $menuObject->menu, $menuObject->sm);
	}
   }
   public function getObject($id_menu){
   	if ($id_menu && $this->exists[$id_menu]) return $this->collection[$id_menu];
   }  
   public function remove($id_menu){
   	if ($id_menu && $this->exists[$id_menu]) unset($this->collection[$id_menu]);
   }
   // iterator : Naviguer dans la collection
   public function getCollectionKeys(){
   	return array_keys($this->collection);
   }
   public function getCollectionSize(){
   	return count($this->collection);
   }
   public function current(){
	$keys = $this->getCollectionKeys();
	$currentKey = $this->currentIndex > 0 ? $keys[$this->currentIndex] : null;
   	if ($currentKey && $this->collection[$currentKey]){
		$this->id_menu 	= $this->collection[$currentKey]->id_menu;
		$this->menu 	= $this->collection[$currentKey]->menu;
		$this->sm 		= $this->collection[$currentKey]->sm;
		//		
		return $this->collection[$currentKey];
	}  
	else return null;
   }  
    public function first(){
	$this->currentIndex = 0;
	return $this->current();
   }  
   public function next(){
	$this->currentIndex ++;
	return $this->current();
   }  
   public function previous(){
	$this->currentIndex --;
	return $this->current();
   }  
   public function last(){
	$this->currentIndex = $this->getCollectionSize()-1;
	return $this->current();
   }  
}
//
$mi = new MenuItems();
//
$result = mysql_query("select id_menu, menu, sm from menu");
while($result && $row = mysql_fetch_object($result))
{
  if ($row) {
       // Alimentation de la collection d'objets MenuItem
       $mi->add($row);
  }
}
//
while ($mi->next()) {
   echo $mi->getId_menu(), $mi->getMenu(), $mi->getSm();
} 
?>
</pre>

par rufus_ » 21 juil. 2008, 10:18

effectivement ça marche
mais si je fais

Code : Tout sélectionner

class MenuItems { public $sm; public $id_menu; public $menu; } $mi = array(); $result = mysql_query("select sm, id_menu, menu, XXX from ...."); while($row = mysql_fetch_object($result)) { if ($row) { // Instanciation de l'objet MenuItem $o = new MenuItems(); // Affectation de contenu $o = $row; // Stockage dans la collection indéxée par id_menu $key = $row->id_menu; $mi[$key] = $o; } } foreach ($mi as $menuItem) { echo $menuItem->sm, $menuItem->id_menu, $menuItem->menu, $menuItem->XXX; } exit();
la valeur XXX contenue dans la requête($result) et appelée dans le foreach est trouvée $menuItem->XXX alors qu'elle n'existe pas dans la class MenuItems

il faut juste que je m'habitue bien sur , mais la je comprend pas comment une valeur non définie dans la class MenuItems est trouvée , ou alors a quoi sert la class dans ce cas puisque c'est censé faire un genre de collection d'objets et qu'un intrus s'y ajoute sans générer d'erreur

par sadeq » 21 juil. 2008, 10:13

Non, j'ai supprimé le casting (conversion) de $row vers $o qui est un objet dument instancié au paravant.
Le casting sert simplement pour convertir un objet en un autre en utilisant une classe apparentée. Les objets sujets au casting par contre, doivent être instanciés (créés) au paravant selon le modèle objet. Si PHP n'a pas supporté cette forme de casting les langages objets le supporte et donc PHP le fait automatiquement par affectation.

Voici le récap du code :
class MenuItems
{
   public $sm;
   public $id_menu;
   public $menu;
}

$mi = array();

$result = mysql_query("select sm, id_menu, menu from ....");

while($result && $row = mysql_fetch_object($result))
{
  if ($row) {
       // Instanciation de l'objet MenuItem
       $o = new MenuItems();
       // Affectation de contenu 
       $o = $row;
       // Stockage dans la collection indéxée par id_menu
       $key = $row->id_menu;
       $mi[$key] = $o;
  }
}

foreach ($mi as $menuItem) {
    echo $menuItem->sm, $menuItem->id_menu, $menuItem->menu;
}
exit(); 

par rufus_ » 21 juil. 2008, 10:08

mais tu as supprime l'instanciation $o = new MenuItems(); car elle plantait
et en ce cas ma class MenuItems n'est pas utilisee

en tout cas ton code marche nikel sans la class menuitem et donc sans instanciation
avec

while($row = mysql_fetch_object($result))
{
$mi[$row->id_menu] = $row;
}

par sadeq » 21 juil. 2008, 10:08

Oui, tu peux utiliser cette clé comme ça bien sûr en écrivant :
$key = $row->id_menu;
$mi[$key] = $row; 
Mais si tu enlève l'instanciation de la classe MenuItems tu ne peux pas utiliser ses méthodes. Si ta classe ne contient pas de méthodes (fonctions) spéciales et se limite à des propriétés (champs) alors là tu peux ne pas l'utiliser et utiliser directement $row qui est déjà un objet issu de mysql_fetch_object().