So I have an item class as follows:
class Item { private $db; private $data = array( 'AltItem1' => null, 'AltItem2' => null, 'BaseUOM' => null, 'Category1' => null, 'Category2' => null, 'Category3' => null, 'Category4' => null, 'Iden' => null, 'IsHCS' => null, 'ItemDesc' => null, 'ItemNmbr' => null, 'ItemType' => null, 'MSDS' => null, 'NoteText' => null, 'NonStock' => null, 'PrcLevel' => null, 'TipPrice' => null, 'DTM_UpdType' => null, 'DTM_UpdDateTime' => null, 'DTM_DownloadDateTime' => null, 'DTM_UploadDateTime' => null ); public function __construct(mysqli $db, $id = null){ $this->db = $db; if(!empty($id)){ $id = (int)$id; $this->populate($id); } } public function __get($key) { if(array_key_exists($key, $this->data)){ return $this->data[$key]; } error_log("Invalid key '$key'"); return null; } public function __set($key, $value) { if(array_key_exists($key, $this->data)){ $this->data[$key] = $value; return true; } return false; } public function populate($id) { $sql = sprintf( "SELECT %s FROM ItemMaster WHERE id = ?", implode(", ", array_keys($this->data)) ); $stmt = $this->db->stmt_init(); $stmt->prepare($sql) or die ("Could not prepare statement:" . $stmt->error); $stmt->bind_param('i', $id); $stmt->execute() or die('exec'); $stmt->store_result(); if($stmt->num_rows == 1) { $params = array(); foreach($this->data as $key => $val){ $params[] = &$this->data[$key]; } call_user_func_array(array($stmt, 'bind_result'), $params); $stmt->fetch(); $return = true; } else{ user_error("No rows returned for id '$id'"); $return = false; } return $return; } public function insert() { $params = $this->data; $values = array(); foreach($params as $param){ $values[] = "?"; } $sql = sprintf( "INSERT INTO recurrence (%s) VALUES (%s)", implode(", ", array_keys($params)), implode(", ", $values) ); $stmt = $this->db->stmt_init(); $stmt->prepare($sql) or die ("Could not prepare statement:" . $stmt->error); $types = str_repeat("s", count($params)); array_unshift($params, $types); call_user_func_array(array($stmt, "bind_param"), $params); $stmt->execute(); $stmt->store_result(); $result = $stmt->result_metadata(); } public function update() { $sql = "UPDATE recurrence SET "; $params = array(); foreach($this->data as $key => $value){ $params[] = "$key = ?"; } $sql .= implode(", ", $params) . " WHERE id = ?"; $stmt = $this->db->stmt_init(); $stmt->prepare($sql) or die ("Could not prepare statement:" . $stmt->error); $params = $this->data; $params[] = $this->data['id']; $types = str_repeat("s", count($params)); array_unshift($params, $types); call_user_func_array(array($stmt, "bind_param"), $params); $stmt->execute(); $stmt->store_result(); $result = $stmt->result_metadata(); } }
My question is what would be the best way to extend this class with the data structure the way I have it? I basically want another class for an item in a shopping cart. So some extra fields would be quantity, cart id, etc. Or is there a better way to do this without extending the class?
On another note, say I have another variable $price thats not store directly in the database. So I make it a public variable, but I would have to make helper methods to access it wouldn't I? If that's the case, is my $data array the best solution of this type of item?
Thanks in advance.
I'm not 100% sure what kind of usage of the private $data
variable you might be doing, so my tendency here would be to take a slightly different approach.
Instead of grouping all your data fields inside a single private variable of the object, I would make each field a private variable itself, ie:
class Item
{
private $db;
private $AltItem1;
private $AltItem2;
...
etc.
This would immediately solve your problem with having publicly available data fields as well, as you could simply declare such fields as a public member. Public members don't require a getter and a setter, so you wouldn't have to worry about that... you could just access them through $this->price
(internally), or $item->price
(externally). Saves you some code. And it would be a quick modification of your populate()
function to set all your new properties, as all you'd have to do would be to set $this->$$key
instead of $this->data[$key]
.
Now, with your use of __set()
and __get()
, it looks like you want to be able to access the private $data
member even from outside the object. There's no reason you can't continue that by having each field declared separately private as well. __set()
and __get()
will operate exactly the same way, you'd just need a minor adjustment, ie:
public function __get($varname)
{
if ($this->$varname !== null) return $this->varname;
error_log("Invalid key '$key'");
return null;
}
As a final bonus, extending the class becomes easier, because you don't have to redeclare all the fields in their entirety if you want to override the $data
property. You simply add the new fields of your children as new private members.
So I'm not sure if that makes your life easier, but I think that would be my approach.