tags:

views:

31

answers:

2

I have a factory called ProductFactory

I can create a product like so:

$product = ProductFactory::getProduct($id);

Now depending on what type of product we are getting, this function may return a class of type Product or a descendant or Product.

But using the method above, the $product class would be responsible for connecting to the database and retrieving its data.


So if I were to select 20 products to be displayed on a category list, I would have to first get a list of all the ID's then call the above function 20 times. This would be 21 calls to the database.

So I added another function to my factory like so:

$product = ProductFactory::createProduct($data);

This does the same thing, except you pass in the data from the database, saving the $product class from having to make the trip. (depending on what data you pass, the factory will return the right type of class).

So now I want to make one call to select all products data and FETCH_INTO my factory to generate a $product object for each row.

How can I do that?

update

Here is what I tried that did not work:

$stmt->setFetchMode(PDO::FETCH_INTO,ProductFactory::createProduct);
foreach($stmt as $product)
{
    echo get_class($product) . '<br>';
}
A: 

According to the comments section on the PDOStatement->fetch() page, you should do this:

$stmt->setFetchMode(PDO::FETCH_INTO, new myClass());
$stmt->execute();
while ($object = $stmt->fetch())
{
  $result[] = clone $object;
}

So differences there are

  1. You're passing fetch_into a method rather than an existing object
  2. You're not executing the statement (in your example, at least)
  3. You didn't include a call to fetch() in your loop
  4. You're not cloning the object

Now I don't know if it'll work with a factory method instead of an existing object, but calling execute() and fetch() is the least you can do :)

Michael Clerx
Does calling foreach() over a statement execute it? I'd never noticed PDOStatement implements Traversable...
Michael Clerx
No and yes, execute() executes the statement and it implements Traversable ;-)
VolkerK
A: 

You could use PDO::FETCH_CLASSTYPE for something like

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
initDemo($pdo);

$result = $pdo->query("SELECT IF(x,'Product','ProductXVal'), id, x FROM soTest");
while ( $obj=$result->fetch(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE) ) {
  echo get_class($obj), "\n";
}

class Product {
  public function id() { return $this->id; }
}

class ProductXVal extends Product {
  public function x() { return $this->x; }
}

function initDemo($pdo) {
  $pdo->exec('CREATE TEMPORARY TABLE soTest (id int auto_increment, x int, primary key(id))');
  $pdo->exec('INSERT INTO soTest (x) VALUES (1),(NULL),(2),(NULL),(3),(NULL)');
}

which prints

Product
ProductXVal
Product
ProductXVal
Product
ProductXVal

but that seems rather ...odd to say the least.
I'd rather fetch the data as an array and then pass it to a factory that creates the appropriate object. You could do so in a class derived from PDO and/or PDOStatement.

VolkerK
I think I will just do what you recommend at the end.. `fetch the data as an array and then pass it to a factory` ..Seems the most strait forward.
John Isaacks