1
votes

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.

2

2 Answers

2
votes

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.

1
votes

If you need specifics, I can't help you much, but in terms of the logic:

From a purely object-oriented design point of view, I think this is best solved by creating a Cart class to keep track of items. It could be basically a wrapper class for a list of an appropriate sort (language-specific, and I don't know much PHP =P).

I don't see a particular reason to make an item keep track of the cart it's in - in most situations, having the cart keep track makes more sense. (In terms of real-world modeling: do items keep track of the cart they might or might not be in? Nah. But carts are basically just containers for items.)

I'm not quite sure what you're asking in your second question -- could you elaborate, please? Sorry I can't be of more help.

David