Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Class with Array of object - assigning methods to the array?

Status
Not open for further replies.

sugarflux

Technical User
Aug 14, 2003
111
GB
Hi All

I'm a brand spanking noobie to PHP but here's what I'm up to...

I have a class that contains an array of instances of another class and I'm wondering whether it's possible to convert this array into a further class that contains methods.

Example:
PHP:
Class Topping{
  private $sID;
  private $sName;
  
  public function ID($pID){
    if(empty($pID)){
      return $this->sID;
    }else{
      $this->sID=$pID;
    };
  }

  public function Name($pName){
    if(empty($pName)){
      return $this->sName;
    }else{
      $this->sName=$pName;
    };
  }
}

Class Pizza{
  private $sID;
  private $sName;
  private $sToppings;
  
  public function AddTopping($ID,$Name){
    $Topping=new Topping();
    $Topping->ID=$ID;
    $Topping->Name=$Name;
    $this->Toppings['Topping_'.$ID]=$Topping;
  }

  public function ID($pID){
    if(empty($pID)){
      return $this->sID;
    }else{
      $this->sID=$pID;
    };
  }

  public function Name($pName){
    if(empty($pName)){
      return $this->sName;
    }else{
      $this->sName=$pName;
    };
  }

  public function Topings($pTopping){
    if(empty($pTopping)){
      return $this->sTopping;
    }else{
      $this->sTopping=$pTopping;
    };
  }
}

Setup like this:
PHP:
$myPizza=new Pizza();
$myPizza->ID=1;
$myPizza->Name='Margarita';
$myPizza->AddTopping(1,'Cheese');
$myPizza->AddTopping(2,'Tomato');

And later used like this:
PHP:
echo 'A '.$myPizza->Name.' pizza has: <br />';
foreach($myPizza->Toppings as $Topping){
  echo $Topping->Name.';<br />';
};

Producing:
A Margarita pizza has:
Cheese
Tomato


If you haven't yet realised this is a highly simplified version of my code!! What I'm wondering is if there is any way of storing the Toppings array property of the Pizza class as another class (Class Toppings) rather than an array so that I'm able to define methods for the array such as:
PHP:
$Number_Of_Toppings=$Pizza->Toppings->Count;
$Pizza->Toppings->Add('Peppers');

Hope this makes some sense but please let me know if not!

Thanks

~S~
 
Yup. No problem.

Create the class as normal.
Instantiate the toppings object in the constructor of your pizza class.
Change the addtopping method of pizza to add the topping object to the toppings object and that should be it. You will need to add a getter to the pizza object to in order to make it easier to access the topping objects. Create a count method too as simply counting am object won't work the way you want it to.
 
Thanks for getting back jpadie - and you make this sound dead easy!

However, I'm not sure I completely follow...

So I now have three classes:
Pizza
Topping
Toppings

PHP:
Class Toppings{
  public function Add($ID,$Name){
    
  }
}
Class Topping{
  private $sID;
  private $sName;
  
  public function ID($pID){
    if(empty($pID)){
      return $this->sID;
    }else{
      $this->sID=$pID;
    };
  }

  public function Name($pName){
    if(empty($pName)){
      return $this->sName;
    }else{
      $this->sName=$pName;
    };
  }
}

Class Pizza{
  private $sID;
  private $sName;
  private $sToppings;
  
  /* No Longer needed as I can now call directly from the Toppings class
  public function AddTopping($ID,$Name){
    $Topping=new Topping();
    $Topping->ID=$ID;
    $Topping->Name=$Name;
    $this->Toppings['Topping_'.$ID]=$Topping;
  }
  */
  
  public function __construct{
    $this->Toppings=new Toppings();
  }

  public function ID($pID){
    if(empty($pID)){
      return $this->sID;
    }else{
      $this->sID=$pID;
    };
  }

  public function Name($pName){
    if(empty($pName)){
      return $this->sName;
    }else{
      $this->sName=$pName;
    };
  }

  public function Toppings($pTopping){
    if(empty($pTopping)){
      return $this->sTopping;
    }else{
      $this->sTopping=$pTopping;
    };
  }
}


So, if I understand correctly, I can now create thusly:
PHP:
$myPizza=new Pizza();
$myPizza->ID=1;
$myPizza->Name='Margarita';
$myPizza->Toppings->Add(1,'Cheese');
$myPizza->Toppings->Add(2,'Tomato');

What code would I put in the Add class of Toppings to be able to add a new Topping to itself?

Thanks again

~S~
 
yes - that would work with the way that you have assigned a Toppings property to the pizza object in its class constructor.

but typically you will want to abstract this to the pizza class.

Code:
public function AddTopping($ID,$Name){
    $Topping=new Topping();
    $Topping->ID=$ID;
    $Topping->Name=$Name;
    $this->Toppings->add($Topping);
}

so you can simply call the add property like this
Code:
$pizza->addTopping(1, 'cheese');

note that I do not agree with assigning ID properties like this. let your database do that for you. also don't assign a 'cheese' topping to a pizza. instead create a cheese topping in your database table and then assign that record to the pizza.

to have the toppings class be able to assign toppings 'to itself' use this code

Code:
Class Toppings{
	public $toppings = array();

	public function Add($topping, $Name = null){
		if(is_null($Name)):
			$this->toppings['Topping_'.$topping->ID] = $topping;
		else:
			$Topping=new Topping();
			$Topping->ID=$topping;
			$Topping->Name=$Name;
			$this->toppings['Topping_'.$Topping->ID] = $Topping;
		endif;
  }
  
  public function count(){
  	return count($this->toppings);
  }
  	
  public function getToppings(){
  	return $this->toppings;
  }
}

but in general, I don't recommend this as an overall approach to the problem, if you are using a database as a storage engine.
i recommend instead that you create a base class that handles all database requests and then make sure that each of your entity classes (toppings,topping, and pizza - although toppings probably is not a true entity class) inherits from the base class.
If you will need to search on toppings, then you must create a join table for pizzas and toppings. If not, then just store the toppings in a serialized array in a column of the pizza table. For normalisation purposes the first is purer.
if you need examples of this I can provide.
 
Thanks jPadie

That makes sense - and believe me the pizza/toppings scenario is purely an example to get the classes working. My actual model is using an extensive database consisting of around 40 (noramlised) tables with classes that mimic the database structure (and are nothing to do with pizza unfortunately!) As i say, currently I'm storing the "arrays" of each class as an actual array object within the parent class, but it will be much cleaner to convert these child arrays into classes of their own that can have Add, Update, Get, Delete, Create and Render methods.

So instead of having my $Toppings array in the pizza class and a multitude of functions such as toppingsGet, toppingAdd, toppingDelete, toppingCreate, toppingReorder etc. as well as a whole host of rendering functions to spit out the toppings in tables, divs, visually as images and so on it will be nicer to call the Toppings class from within the Pizza class to handle these functions.

Thanks for all your help and I'll post back here if i run into difficulties!

~S~
 
Hmmm... I've run into difficulties already!

My classes (although might be slightly unorthodox) are actually laid out like this:

PHP:
Class Pizza{
  private $sID;
  private $sName;
  private $sToppings;
  
  function __construct(){
    $this->Toppings=new Toppings();
  }

  public function ID($sID){
    if(empty($pID)){
      return $this->sID;
    }else{
      $this->sID=$pID;
    };
  }

  public function Name($sName){
    if(empty($pName)){
      return $this->sName;
    }else{
      $this->sName=$pName;
    };
  }

  public function Toppings($sToppings){
    if(empty($pToppings)){
      return $this->sToppings;
    }else{
      $this->sToppings=$pToppings;
    };
  }
}

Class Topping{
  private $sID;
  private $sName;
  
  public function ID($sID){
    if(empty($pID)){
      return $this->sID;
    }else{
      $this->sID=$pID;
    };
  }

  public function Name($sName){
    if(empty($pName)){
      return $this->sName;
    }else{
      $this->sName=$pName;
    };
  }
}

Class Toppings{
  private $sToppings;
  
  public function Toppings($sToppings){
    if(empty($pToppings)){
      return $this->sToppings;
    }else{
      $this->sToppings=$pToppings;
    };
  }
}

Laying out my classes with a private container property and a public method as the name of the property allows me to simply call the method directly rather than having a separate GET and SET routine (similar to how most OO languages work).

For example:
PHP:
$Pizza=new Pizza;
$Pizza->Name='Margarita';
echo $Pizza->Name;

Now what I have been talking about above I want to convert the Toppings class to action all my methods for the toppings, starting with a GET routine:

PHP:
Class Toppings{
  private $PizzaID;
  private $sToppings;
  
  public function Get(){
    // Connect to DB and retrieve all toppings fr this PizzaID
    while($row=$stmt->fetch()){
      $tmpTopping=new Topping();
      $tmpTopping->ID=$row['ToppingID'];
      $tmpTopping->Name=$row['ToppingName'];
      $this->Toppings['Topping_'.$row['ToppingID']]=$tmpTopping;
    }
  }
  
  public function PizzaID($sPizzaID){
    if(empty($pPizzaID)){
      return $this->sPizzaID;
    }else{
      $this->sPizzaID=$pPizzaID;
    };
  }
  
  public function Toppings($sToppings){
    if(empty($pToppings)){
      return $this->sToppings;
    }else{
      $this->sToppings=$pToppings;
    };
  }
}

Calling the routine (or at least how I would ideally like to call the routine:

PHP:
$Pizza=new Pizza;
$Pizza->Get($myPizzaID); // method not detailed above but basically gets details from db
$Pizza->Toppings->Get();
foreach($Pizza->Toppings as $Topping){
  echo 'Topping: '.$Topping->Name'<br />';
};

So this isn't working. I think because $Pizza->Toppings is storing an instance of the $Toppings class itself rather than returning the array within the class.

This could be to do with the way I'm using the function to return it. Any ideas how I could make this work in the way I'd like?

Thanks again,

~S~
 
Code:
$Pizza->Toppings->Get();
foreach($Pizza->Toppings as $Topping){
  echo 'Topping: '.$Topping->Name'<br />';
};

Well you would have to choose whether you want Toppings to be an object that contains an array of elements or if you want it to be an Array of objects.

You can't have it both ways.

Personally I would have a getToppings() method in the Pizza object build me an array of toppings. Then I can use that array anyway I want to.

In pseudo code
Code:
public function getToppings()
{

rowLoop
{
  $this->toppings[] = new Topping(row['data']...);
}
}

then

Code:
$Pizza->Toppings as $Topping)
{
  echo $Topping->Name;
}

----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
That's a pain...

As i say at the moment my 'toppings' are stored as an array of objects within the Pizza class.

But really I would like to move some of the functionality out of the pizza class. Mainly for rendering the toppings in different ways.

At the moment i have to call various functions in the pizza class such as:
RenderToppingsAsTable
RenderToppingsAsVisual
RenderToppingsAsDropdown

...amongst many others.

My plan was to move the Toppings to a class of their own and call a function "Render" with just one parameter for how to display them.

That is as well as the Add, Update, Create, Delete etc. which i think would be nicer to be called by $Pizza->Toppings->Add();.

Perhaps there may be a better way to change my rendering handler rather than have lots of different functions in the pizza class?

Any advice or ideas?

Thanks

~S~
 
Then Have Toppings be an object that contains an array of said toppings.

For instance:
Code:
class Toppings
{
	private $Toppings=array();
	
	public function addTopping($name,$value)
	{
		$num = count($this->Toppings);
		$this->Toppings[$num]['name'] = $name;
		$this->Toppings[$num]['val'] = $value;
	}

	public function getToppings()
	{
		return $this->Toppings;
	}

        ...
        

}

$theTop = new Toppings();

$theTop->addTopping('cheese',1);
$theTop->addTopping('pepperoni',2);
$theTop->addTopping('sausage',3);
$theTop->addTopping('anchovies',4);

_dump($theTop->getToppings());

Then all your Toppings are stored in the same Object and you can use the methods to manipulate the Array inside.



----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 

To clarify:
Your Pizza class will then contains a single Toppings object which in turn contains an array of the pizza toppings you can manipulate.



----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
the pizza metaphor is not working at this level as in fact the toppings would not be associated in this way with pizzas in any event.

can we drop the abstraction and address the real dataset and real business case? fwiw I think an add method on the toppings class should add a topping to the database rather than to a pizza. from a purist perspective because toppings are a property of pizza it makes more sense for pizza to have an addtopping method that would identify if a topping existed in the database. if it did then a join entry would be added to associate the topping with the pizza. if not then first the database entry for the topping would be added with an add method on the toppings class. so you see the metaphor is breaking down and we should drop it.

however I see no issue with moving the array of objects into a pointer to an object that holds an array of objects. thus creating a single toppings class that has a property of toppings that is an array of objects of class topping. that's how my code above was intended to be.

then to iterate over the toppings you would use code like

Code:
foreach($Pizza->Toppings-> as $Topping){


or implement a getter in the toppings class or the pizza class

Code:
function gettoppongs(){return $this>Toppings;}
and then use this in the for each
Code:
foreach($this-Toppings->getToppongs() as $Topping)

or implement an iterator in the Toppings object. this is probably closest to what you are looking for. implementing an iterator in the class is very simple.


also look into magic methods as there is an easier way to do your getters and setters.

Code:
public function __call($field, $val=null){
if (isnull($val) && isset ($this->$field)) return $this->$field;
$this->$field=  $val;

put this in a base class and you can use it to get and set all properties by using the property name as a method.


 
the pizza metaphor is not working at this level as in fact the toppings would not be associated in this way with pizzas in any event.
I believe they could be if you instantiate a different Toppings object for each pizza.

Each Toppings object would then hold the toppings for the specific pizza it belongs to.

In any case, you are right, the actual business case would help us understand the requirements a little better and come up with a better solution.

It would also help me not want a pizza now with pepperonni, anchovies, mushrooms and extra cheese now. [cook]



----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
possibly, Phil - but that's not the way I would do it.

I'd associate the ID of the topping in the database with the pizza. then perhaps associate a full object on render time, but more likely I would use a factory class to do the rendering. Because the pizza itself never needs to know what its topping might be, simply that it has one.

When the pizza object was loaded, I would call a method that looked like this
Code:
public function loadToppings(){
 if(empty($this->id)):
   $this->toppings = array(); 
   return;
 endif;
 global $pdo;
 $sql = "SELECT id from pizzaToppingsJoin where pizzaID = ?";
 $s = $pdo->prepare($sql);
 $s->execute($this->id);
 $this->toppings = $s->fetchAll(PDO::FETCH_COLUMN, 0);
}

then to add a topping I would do something like this

Code:
 public function AddTopping($topping){
	$toppingObj = new topping;
	if(!is_numeric($topping)):
	    $toppingObj->searchByName($topping);
	    $toppingID = $toppingsObj->getPrimary();
	    if($toppingID == 0): //we know the topping does not exist in the database yet
			$toppingObj->Name($topping);
			$toppingObj->save();
		endif;
		$toppingID = $toppingObj->getPrimary();
	else:
		$toppingObj->searchByID($topping);
	    $toppingID = $toppingsObj->getPrimary();
	    if($toppingID != $topping): //we know the topping does not exist in the database yet
			return false; // we cannot do anything as we don't know the name of the topping
		endif;
	endif;
	$this->toppings[] = $toppingID; //add the topping ID to the list of current toppings
  }

for rendering, if I had to I'd most likely construct a factory class that looked like this.

Code:
class renderTopping{
  public function renderDropDown($toppingID){}
  public function renderList{$toppingID){}
  /etc
}

I would not add the renderer into the main class structure however. I used to do this a few years ago and found that it just wasn't efficient when coming back and altering code later on. The renderer should be part of the template or view. the object definition should be part of the model. if you're following a design paradigm like MVC, for example.

Depending on whether my app were to allow searching for pizzas by toppings, I would either store the association of pizza ID and topping ID in a join table or, if no searching were needed, in a serialized array. However 9 times out of ten, even if you think searching won't be needed upfront, there comes a time later when you wish you'd gone the extra step and had a join table...

so I would use this as a save routine inside the pizza class (the load routine is above)
Code:
public function save(){
	parent::save(); //save this record
	//then deal with toppings
	global $pdo;
	$sql = "delete from pizzatoppingsjoin where pizzaID=?";
	$s = $pdo->prepare($sql);
	$s->execute(array($this->getPrimary());
	$sql = "Insert into pizzatoppingsjoin (pizzaID, toppingID) values (?,?)";
	$s = $pdo->prepare($sql);
	$pizzaID = $this->getPrimary();
	foreach($this->toppings as $toppingID):
		$s->execute(array($pizzaID, $toppingID));
	endforeach;
}

and just in case people are wondering about the getPrimary() method - this is a standard method i use in my inherited classes. This is because all entity classes inherit from a baseclass which intermediates database access. An example of this base class is as follows

Code:
<?php

class base {
	
	public function __construct(){
		$this->load(); //instantiate variables
	}
	
	public function getPrimary(){
		return isset($this->{$this->primary}) ? $this->{$this->primary} : false;
	}
	
	public function save(){
		return $this->getPrimary() === false ? $this->insert() : $this->update();
	}
	
	public function insert(){
		global $pdo;
		$params = array();
		$sql = "INSERT into {$this->table} SET ";
		foreach($this->fields as $key=>$field):
			$fields[] = "$field = :{$field}";
			$params[$field] = $this->{$field}; 
		endforeach;
		$query = $sql . implode(',', $fields);
		
		$s = $pdo->prepare($query);
		if($s === false) $this->bail($pdo->errorInfo());
		$r = $s->execute($params);
		if($r === false) $this->bail($s->errorInfo());
		
		return $s->rowCount(); 
	}
	
	public function update(){
		global $pdo;
		$params = array();
		$sql = "UPDATE {$this->table} SET ";
		foreach($this->fields as $key=>$field):
			if($field === $this->primary) continue;
			$fields[] = "$field = :{$field}";
			$params[$field] = $this->{$field}; 
		endforeach;
		
		$query = $sql . implode(',', $fields) . " WHERE {$this->primary} = :{$this->primary}";
		$params[$this->primary] = $this->getPrimary();
		$s = $pdo->prepare($query);
		if($s === false) $this->bail($pdo->errorInfo());
		$r = $s->execute($params);
		if($r === false) $this->bail($s->errorInfo());
		
		return $s->rowCount();
	}
	
	public function delete(){
		global $pdo;
		$sql = "delete from {$this->table} where {$this->primary} = ?";
		$params = array($this->getPrimary());
		$s = $pdo->prepare($sql);
		if ($s === false) $this->bail($pdo->errorInfo());
		$r = $s->execute($params);
		if($r === false) $this->bail($s->errorInfo());
		return $s->rowCount();
	}
	
	public function loadFromID($id){
		global $pdo;
		$sql = "Select * from {$this->table} where {$this->primary} = ?";
		$s = $pdo->prepare($sql);
		if ($s === false) $this->bail($pdo->errorInfo());
		$r = $s->execute(array($id));;
		if($r === false) $this->bail($s->errorInfo());
		$row = $s->fetchObject();
		$this->load($row);
	}
	
	public function searchByID($id){
		return $this->loadFromID($id);
	}
	
	public function load($row){
		if(is_array($row)) $row = (object) $row;
		foreach ($this->fields as $field):
			if(isset($row->{$field})) $this->{$field} = $row->{$field};
		endforeach;	
	}
	
	public function bail($array){
		echo '<pre>' . print_r($array, true) . '</pre>';
		die;	
	}
}
?>

so taking the pizza class as an example

Code:
<?php
Class Pizza extends base{
	public $id, $name;
	public $toppings = array();
	public $fields =  array('id','name'); // NB no toppings
	public $table = 'pizzas';
	public $primary = 'id';


	public function __construct{
		parent::__construct();
	}

	public function load($array = array()){
		parent::load($array);
		$this->loadtoppings();
	}
	public function save(){
		parent::save(); //save this record
		//then deal with toppings
		$this->saveToppings();
	}
	
	public function saveToppings(){
		global $pdo;
		$sql = "delete from pizzatoppingsjoin where pizzaID=?";
		$s = $pdo->prepare($sql);
		$s->execute(array($this->getPrimary());
		$sql = "Insert into pizzatoppingsjoin (pizzaID, toppingID) values (?,?)";
		$s = $pdo->prepare($sql);
		$pizzaID = $this->getPrimary();
		foreach($this->toppings as $toppingID):
		$s->execute(array($pizzaID, $toppingID));
		endforeach;
	}

	public function AddTopping($topping){
		$toppingObj = new topping;
		if(!is_numeric($topping)):
		$toppingObj->searchByName($topping);
		$toppingID = $toppingsObj->getPrimary();
		if($toppingID == 0): //we know the topping does not exist in the database yet
		$toppingObj->Name($topping);
		$toppingObj->save();
		endif;
		$toppingID = $toppingObj->getPrimary();
		else:
		$toppingObj->searchByID($topping);
		$toppingID = $toppingsObj->getPrimary();
		if($toppingID != $topping): //we know the topping does not exist in the database yet
		return false; // we cannot do anything as we don't know the name of the topping
		endif;
		endif;
		$this->toppings[] = $toppingID; //add the topping ID to the list of current toppings
	}

	public function loadToppings(){
		if(empty($this->id)):
			$this->toppings = array();
			return;
		endif;
		global $pdo;
		$sql = "SELECT id from pizzaToppingsJoin where pizzaID = ?";
		$s = $pdo->prepare($sql);
		$s->execute($this->id);
		$this->toppings = $s->fetchAll(PDO::FETCH_COLUMN, 0);
	}
}

?>

and you would use this like so

Code:
$pizza = new pizza;
$pizza->loadFromID($pizzaID);
if($pizza->getPrimary() == $pizzaID): //record loaded
  //do something with the pizza
endif;

// or if you were submitting a form with new pizza information ... 
[code]
$pizza = new pizza;
$pizza->loadFromID($_POST['pizzaID']);
//override the current data with new data
$pizza->load($_POST); //note that the base object will automatically filter the POST superglobal
// deal with toppings
//delete all existing toppings
$pizza->toppings = array();
foreach($_POST['pizzaToppings'] as $toppingID): // assumes an enumerated list of toppings.
 $pizza->addTopping($toppingID);
endforeach;
$newToppings = explode(',',$_POST['newToppings']);
foreach($newToppings as $newTopping):
 $pizza->addTopping( trim($newTopping) );
endforeach;
//now save the pizza
$pizza->save();

//and maybe display it ... 
require_once BASEDIR . 'templates/pizzas/pizzaDisplay.php';
exit;

Just to revisit an earlier post too - my auto getter/setter using magic methods was not quite right (I was typing on a phone). it should look like this

Code:
	public function __call($name, $arguments=NULL){
		if(is_null($arguments) || ( is_array($arguments) && count($arguments) == 0):
			return isset($this->$name) ? $this->$name : NULL;
		else:
			if(count($arguments) > 1 ):
				$this->$name = $arguments;
			else:
				$this->$name = $arguments[0];
			endif;
			return true;
		endif;
	}

i have not tested this overloaded call and i note that unexpected behaviour may occur if you try to use it to assign an empty array to a property. but other than that, it is (or looks) quite neat.

because this is a fully abstracted function then I would definitely put this in the base inherited class.

and to revisit my earlier reference to the iterator with an example (although I have not used an intermediate toppings object in my example of the pizzas class above, it remains possible)

Code:
Class Toppings implements Iterator {
	public $pizzaID;
	public $toppings = array();
	
	
	public function Add($topping, $Name = null){
		if(is_null($Name)):
			$this->toppings['Topping_'.$topping->ID] = $topping;
		else:
			$Topping=new Topping();
			$Topping->ID=$topping;
			$Topping->Name=$Name;
			$this->toppings['Topping_'.$Topping->ID] = $Topping;
		endif;
	}
  
	public function count(){
		return count($this->toppings);
	}
  	
	/* iterator logic */
	private $pos = 0;
	public function rewind(){$this->pos = 0;}
	public function current(){return $this->toppings[$this->pos];}
	public function key(){return $this->pos;}
	public function next(){++$this->pos;}
	public function valid(){return isset($this->toppings[$this->pos]);}	  
}

then with the above in the class you can just do this from the pizza class
Code:
foreach($pizza->Toppings as $toppingID=>$toppingObj):
 // do stuff
endforeach;

note as well that objects naturally implement iterator too, however the iterator that is natively implemented iterates over an implicit associative array of all public properties.

one thing to be aware of is that I am not certain whether a class and extend another class and implement an iterator. It may well be that the class must be left only extending another class and then an iterface to that class would be built that implements the iterator. I will investigate and mention this only for fullness as the toppings class is not an entity in itself, but a holder; so it would not need to inherit from base anyway.

 
just as a follow up, seemingly no problem in a class extending another and implementing an iterator

Code:
class base{
 public function test(){ echo "test inheritance\n";}
}
class test extends base implements iterator{
 public $toppings = array('apples','oranges','lemons','bananas');
 public $pos = 0;
         /* iterator logic */
        private $pos = 0;
        public function rewind(){$this->pos = 0;}
        public function current(){return $this->toppings[$this->pos];}
        public function key(){return $this->pos;}
        public function next(){++$this->pos;}
        public function valid(){return isset($this->toppings[$this->pos]);}
}
$t = new test;
$t->test();
foreach($t as $key=>$val):
 echo "key $key has value $val\n";
endforeach;

gives output as expected
Code:
test inheritance
key 0 has value apples
key 1 has value oranges
key 2 has value lemons
key 3 has value bananas
 
Without knowing the exact business requirements its hard to say whether I would actually recommend instantiating a different object Toppings for each pizza over any other approach, simply based on what sugarflux has said he wants to do is what comes to mind as an maybe easier way of doing it when taken at face value.

Also with the requirement to connect to a DB that holds the pizzas and toppings, I'm not entirely sure and object manager for the toppings is all that necessary.

Probably a more manageable approach is to let the DB have a ToppingsforPizza table to join pizzas to toppings, and then have a factory class generate a Toppings object with the toppings for the current pizza. As you load a different pizza you get a new toppings object to overwrite the existing one.

Basically I fail to see the need of having more than one Pizza with its toppings instantiated at any one time.

I would simply save the Pizza and the toppings array to the DB as you have, and instantiate the new Pizza and get its toppings from the DB.

Like you, using a factory class for DB access and an extender class to construct the Toppings object for the selected pizza.

Whether we can use an implicit iterator or not for the class would be more a choice to make based on the operational needs of the Toppings object usage later on.

At the end if I need to manage a Pizza and its toppings I would need to manage one at a time and thus instantiate a single Pizza object and Toppings object with in it, If I'm displaying Pizzas and their toppings in tabular form for instance I would probably just get the direct data from the DB without instantiating an object for each pizza as I have no need to create them yet. This is were a table for ToppingsForPizzas would help manage that relationship between the Pizza itself and the Topping list you need.

It all comes down to what actually needs to be done with the objects and DB data at the time.








----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
Yup!

I had a spare few minutes earlier and borrowed from some other projects to write this pizza toppings app. I will bung it on a website for amusement value later this evening.
 
jpadie said:
i have not tested this overloaded call and i note that unexpected behaviour may occur if you try to use it to assign an empty array to a property.

I have tested it and I was talking rubbish/not thinking. It works fine like this
Code:
        public function __call($name, $arguments = NULL){
		if(count($arguments) == 0):
			return isset($this->$name) ? $this->$name : null;
		endif;
		$this->$name = $arguments[0];
	}

jpadie said:
I had a spare few minutes earlier and borrowed from some other projects to write this pizza toppings app. I will bung it on a website for amusement value later this evening.

completely forgot last night. I know it does not help the OP as it is not an answer to his original question (which as been answered in the thread however); however it may serve either:

1. as a fun thing for people to create and document their own pizzas with (and give me some inspiration)
2. to help people learn how to build a simple database drive web-app using object oriented php

the source is available here
and a demo is up here

notes

1. this app does not use traits, which remain relatively new in php. I would normally use traits instead of an inherited class when I could control the version of php that the app would be installed on. But using inherited classes is not a problem when the class hierarchy is likely to be flat.

2. this app does not degrade gracefully if javascript is turned off. there are a number of functions (like adding new toppings) that are not available without javascript. this could be remedied but would involve building new html templates and I wasn't bothered enough in the short time I allocated.

3. the app requires PDO and sqlite. these will be standard on most distros but not all. rewrite the base class yourselves to use native mysql (hint - it should transfer very easily into mysqli OOP)

4. the structure is not pure MVC. But I have found over the years that it is much more accessible and easier to maintain and structure than pure MVC and suffers no negatives (imo).

5. in more complex apps, the templates would be in a logical hierarchical folder structure

6. in more complex apps, or those destined for public consumption, I would use permalinks and mod_rewrite instead of ?action=... links.

7. note the use of nonces to prevent form resubmission (and thus duplicate pizzas)

8. it would be very simple to extend this app to include pizza and topping prices to allow ordering.

9. I have not bothered creating routines for deleting/changing toppings from the database. To do so would be an extension of what is already there. But of course you can remove and add toppings to a pizza as you wish.

10. A nicer user experience might be to use drag and drop. very easy to implement in jQuery but beyond the scope of the half hour I gave this.

have fun!
 
Very nice. [2thumbsup]

----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top