0

I have a quick one: I'm a beginner with PHP Object, and I have a product table with all my products.

I also have a class for my product (with a constructor that fills my instance with all the parameters of my product).

Say I want a list of all my product, shall I create a separate class for my list of products, or create a function that creates a new instance of my product class ?

I don't really get the benefits of it right now, because if I have to execute the exact same query as my class query, it's useless ?

Thank's for your tips !

UPDATE

Thank's folks ! It's still quite complicated for me, but here's what I did (talking about books) trying to follow your advices.

I created a DAO. Inside this DAO, I have two static functions that return me either a list of books or a single book, and one static that creates the book object.

With this method, I still have 2 queries (one for the list, one for the single book). I have a feeling this is wrong but any help is appreciated :)

class bookDAO {

    public static function getBooks() {
        $books = array();
        $db = databaseConnection::getInstance();
        $query = $db->query("SELECT * FROM books");
        while($row = $query->fetchObject()) {
            $book = self::createBookFromRow($row);
            $books[] = $book;
        }
        return $books;
    }

    public static function getBook($id){
        $db = databaseConnection::getInstance();
        $query = $db->prepare("SELECT * FROM books WHERE id=:id");
        $query->execute(array(":id"=>$id));
        $row = $query->fetchObject();
        $book = self::createBookFromRow($row);
        return $book;
    }

    private static function createBookFromRow($row) {
        $book = new book($row);
        return $book;
    }

}

4 Answers4

3

It's common to create an array of objects in this case.

For example:

$products = array();
foreach($results as $result) {
  $products[] = new Product($result);
}
print_r($products);
Jason McCreary
  • 71,546
  • 23
  • 135
  • 174
2

Your Product class is not suppose to execute any query, separate that into a DAO e.g. ProductDAO class

class ProductDAO
{
  function static getProducts()
  {
     //Your query here: PDO, MySQLi, MySQL etc
    $products = array(); //to store array of products

    while($row = mysql_fetch_assoc($result))
    {
       $product = createProductFromRow($row);

       $products[] = $product;
    }

    return $products;
}

   private static function createProductFromRow($row)
   {
   $product = new Product();
   $product->setId($row["id"]);
   $product->setName($row["Name"]);
   //etc

   return $product; 
   }
}

From your page

  $products = ProductDAO::getProducts();

  foreach($products as $p)
  {
     echo ($p->getName() + '<br />');
  }
codingbiz
  • 26,179
  • 8
  • 59
  • 96
2

You could simply add an array. Or if you want to add some methods to the productslist you could implement something like the following:

class Products
{
    private $productFactory;

    private $products = array();

    public function __construct(ProductFactory $productFactory) {
        $this->productFactory = $productFactory;
    }

    public function addProduct($product)
    {
        $this->products[] = $this->productFactory->build($product);
    }

    public function addProducts($products)
    {
        foreach ($products as $product) {
            $this->addProduct($product);
        }
    }

    // do soemthing with all products
    public function changeProducts()
    {
        foreach($this->products as $product) {
            $product->changeSpec('new spec value');
        }
    }
}

class Product
{
    private $someSpec;

    public function __construct($spec)
    {
        $this->someSpec = $spec;
    }

    public function changeSpec($spec) {
        $this->someSpec = $spec;
    }
}

class ProductFactory
{
    public function build($spec) {
        return new Product($spec);
    }
}

// build the productslist object
$productFactory = new ProductFactory();
$products = new Products($productFactory);
$products->addProducts(array(
    'some spec of product1',
    'some spec of product2',
));

note that I manually add products, but this may as well be a recordset from some persistent storage mechanism (e.g. a db). Or perhaps better inject some class which accesses the database to retrieve the products. Don´t do any queries inside the Product / Products classes. If you do you are tightly coupling something (the database) which makes it very hard to test. And besides that you also violate SRP.

Note that I have created a factory to create new instances of the Product class. This way you haven't tightly coupled the Product class to the Products class. This makes it far more easier to mock the Product class in case you want to do unit testing.

UPDATE

Another thing you could do with the Products class is let it implement Iterator interface so you could also loop through the products if you want.

Glavić
  • 42,781
  • 13
  • 77
  • 107
PeeHaa
  • 71,436
  • 58
  • 190
  • 262
1

Products class where all Product references will be saved

class Products {

    static private $store = array();

    static public function save(Product $object)
    {
        self::$store[] = $object;
    }

    static public function getAll()
    {
        return self::$store;
    }

}

Save every Product reference to the Products class

The easiest way is to do it in your Product constructor.

# your Product class
class Product {

    public function __construct()
    {
        # your stuff here..

        # save reference of current Product to the Products
        Products::save($this);
    }

}

Your initialization of the products

This is just for test, use your logic here to initialize the Product objects.

new Product;
new Product;
new Product;

Loop through all products

foreach (Products::getAll() as $product) {
    var_dump($product);
}
Glavić
  • 42,781
  • 13
  • 77
  • 107
  • 1
    I won't, but I do want to downvote this, because this will be very hard to unit test, because of all the `static` and tightly coupling. – PeeHaa Oct 02 '12 at 19:18
  • How are you going to mock out `Products::save($this);` in the constructor of the `Products` class? Also your constructor is doing work which is never a good idea (besides the tightly coupling going on). – PeeHaa Oct 02 '12 at 19:30
  • Also see my [semi related answer](http://stackoverflow.com/a/11923384/508666) for a more in depth explanation about why it isn't (imho) the best solution. – PeeHaa Oct 02 '12 at 19:31
  • http://sebastian-bergmann.de/archives/883-Stubbing-and-Mocking-Static-Methods.html ? Your are saying that "Static Methods are Death to Testability", and we sould not use them? – Glavić Oct 02 '12 at 19:33
  • 2
    +1 to what @PeeHaa is saying. I would run away from a design like this if I saw it. – Mahn Oct 02 '12 at 19:34
  • (and besides, using static is pretty much missing the point of OOP) – Mahn Oct 02 '12 at 19:35
  • Actually I didn't say that at all if you read my comment again. I say it makes it hard. That page you linked to contains all the good examples of "terrible" code. Have you read the link I shared in my previous comment? – PeeHaa Oct 02 '12 at 19:36
  • But long story short that's exactly what I'm saying :D – PeeHaa Oct 02 '12 at 19:40
  • @PeeHaa: Yep, I have read it, but there is talk about `global` keyword that I did not use, and I never use it in any of the code. Secondly, I agree with what you have wrote there about dependency injection. Probably my code is missing ProductsFactory, that would create Product and save it to Products, instead of saving self reference in the Product constructor to the Products. Code would be tha same if Products was singleton pattern. Only one array of products would exists. @Mahn: I think this code is a good example of the static use. Why would you run away? Just because it is static? – Glavić Oct 02 '12 at 19:50
  • If you have read my entire post you will see why singletons are bad and an anti pattern :) – PeeHaa Oct 02 '12 at 19:52
  • @PeeHaa: Singletons are **good** when they are used wisely. ;-) – Glavić Oct 02 '12 at 19:57