the PHP mysqli_result class implements the Traversable interface for some time now. This gives some interesting possibilities like the following piece of code:
As I stated in the comments, it's easy to do exactly what you want with PDO. Just set the PDO::ATTR_DEFAULT_FETCH_MODE
attribute, and it will do the appropriate kind of fetch when you do the foreach loop.
You can also set the PDO::ATTR_STATEMENT_CLASS
attribute to define the class to use.
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS);
$pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS,array('classname',$constructorArgs));
See http://php.net/manual/en/pdo.setattribute.php for more info.
With that solution, you'd need to change the ATTR_STATEMENT_CLASS
attribute for each query, depending on what class you want to instantiate.
Alternatively, you can set it to specify the classname within the query:
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE);
$stmt = $db->query("SELECT 'classname', * FROM `table` WHERE `id` = 1;");
Simply add the relevant classname to your query as the first field to be selected.
See one of the answers on this related question In PHP, How do I set default PDO Fetch Class? for more on this.
Note:This is an internal engine interface which cannot be implemented in PHP scripts. Either IteratorAggregate or Iterator must be used instead. When implementing an interface which extends Traversable, make sure to list IteratorAggregate or Iterator before its name in the implements clause.
i.e. you'll find the interface definition at http://docs.php.net/class.iterator and/or http://docs.php.net/class.iteratoraggregate
The (let's call it) traversal-fetch-mode of the mysqli_result
can not be influenced. So as it is by default the associative array (column-name as key and it's value as value) like mysqli_result::fetch_assoc()
it stays with that.
So this requires an own Iterator
that does the work. So how does a non-foreach iteration of a Mysqli result traditionally look like? Right, the while()
loop:
while($row = $result->fetch_assoc())
{
...
}
Which reads: while the fetch-method does not return NULL
go on. This is a common form to create iteration.
For iterations of this kind I normally use a FetchingIterator
, it is a concrete iterator that turns the PHP specific Iterator
interface (rewind, valid, current, key, next) into a more simplified one. That simplified one goes: next on rewind, next on next and valid until NULL
- which is pretty much the same as the while
loop and can be used for similar working APIs like deprecated mysql or even arrays (compare array_shift etc.). It's also similar to other implementations of iterators in different programming languages, here specifically Java (compare Iterator Design Pattern
(Source Making Guide on Design Patterns) article).
I did outline it rudimentary back last year in my article Some PHP Iterator Fun (Feb 2012) and a much improved, standard implementation of this can be found as FetchingIterator in Iterator Garden which easily allows to do what you want to do.
Let's see an example application:
class ObjectFetchIterator extends FetchingIterator
{
public function __construct(mysqli_result $result, $class) {
parent::__construct(function() use ($result, $class) {
return $result->fetch_object($class);
});
}
}
The mysqli_result::fetch_object()
method which has been previously within the while(...)
has now been moved into the constructor of a FetchingIterator
for mysqli results. This is a pretty straight forward implementation of it then. Alternatively, it's also possible to override the protected fetchNext()
method but I only note it because it's not needed here.
The usage example then is pretty straight forward:
$db = new mysqli('...');
$result = $db->query('...');
$iterator = new ObjectFetchIterator($result, 'myClass');
/* @var $row myClass */
foreach ($iterator as $row)
{
// $row is a myClass now based on fetched values
}
As this example shows, there is no need to use PDO for that and it sounds like a pretty lazy excuse: Changing the whole database layer only for this little detail? No! The opposite is the case: Using an iterator here in the foreach
allows you to de-couple your code from the concrete database access library to the generic type (as it was possible with arrays earlier too, however arrays are by far not that distinct as object types).
Hope this helps even the answer comes a little late. Just came to my attention this wasn't really answered so far.