PHP mysqli_result:使用带有fetch_object的Traversable接口

时间:2012-11-16 13:50:27

标签: php mysqli

PHP mysqli_result类现在实现Traversable接口已有一段时间了。 这提供了一些有趣的可能性,如下面的代码:

<?php

$db = new mysqli( '...' );

$result = $db->query( '...' );

foreach( $result as $row ) {
   //$row is an associative array containing the fetched values
}

现在,我非常喜欢这个,但是在我正在处理的库中,我希望获取对象,而不是关联数组。我也希望能够将一个类传递给它,所以我可以用它创建一个简单的模型系统

为此,我将其扩展为:

<?php

class mysqli_result_extension extends mysqli_result {

    protected $result_class;

    public function set_result_class( $class ) { $this->result_class = $class; }

    public function function_to_overwrite() {

        return $this->fetch_object( $this->result_class );
    }
}

我的问题和我的问题:

如何立即从 fetch_assoc 切换到 fetch_object ? 我想覆盖Traversable接口使用的方法以返回结果,但它没有定义任何(http://php.net/manual/en/class.traversable.php

那么为了从fetch_assoc切换到fetch_object以及能够将$ result_class传递给它,我需要覆盖什么才能做到这样的事情:

<?php

$db = new mysqli_extension( '...' );

$posts = $db->query( 'select * from posts' );
//$result is object of type mysqli_result_extension

$posts->set_result_class( 'post' );

foreach( $posts as $post ) {
   //$post is an instance of class 'post'
}

我不想要一些肮脏的东西,比如覆盖fetch_assoc并只返回其中的对象

提前感谢您的帮助和输入

3 个答案:

答案 0 :(得分:3)

mysqli_result 的遍历获取模式(我们称之为)遍历获取模式不会受到影响。因此,默认情况下,关联数组(列名为键,值为值)与mysqli_result::fetch_assoc()保持一致。

因此,这需要一个自己的Iterator才能完成工作。那么,Mysqli结果的非foreach迭代传统上看起来如何?是的,while()循环:

while($row = $result->fetch_assoc())
{
   ...
}

其中读取:虽然fetch-method不返回NULL继续。这是创建迭代的常用形式。

对于这种迭代我通常使用FetchingIterator,它是一个具体的迭代器,它将PHP特定的Iterator接口(倒带,有效,当前,密钥,下一个)转换为更简化的接口。简化的一个是:下一个是倒带,下一个是下一个有效直到NULL - 这与while循环几乎相同,可以用于类似的工作API,如弃用的mysql甚至数组(比较array_shift等)。它也类似于不同编程语言中的其他迭代器实现,特别是Java(比较Iterator Design Pattern (Source Making Guide on Design Patterns) article)。

我在去年的文章Some PHP Iterator Fun (Feb 2012)中对此进行了简要概述,并且对此进行了大量改进,标准化的实施可以找到FetchingIterator in Iterator Garden,其中很容易允许做什么你想做什么。

让我们看一个示例应用程序:

class ObjectFetchIterator extends FetchingIterator
{
    public function __construct(mysqli_result $result, $class) {

        parent::__construct(function() use ($result, $class) {
            return $result->fetch_object($class);
        });
    }
}

以前位于mysqli_result::fetch_object()内的while(...)方法现已移至 FetchingIterator 的构造函数中,用于mysqli结果。这是一个相当直接的实现。或者,也可以覆盖受保护的fetchNext()方法,但我只注意它,因为这里不需要它。

然后使用示例非常简单:

$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
}

正如这个例子所示,没有必要使用PDO,这听起来像一个非常懒惰的借口:只为这个小细节更改整个数据库层?没有!情况恰恰相反:在foreach中使用迭代器允许您将代码从具体的数据库访问库中解耦为泛型类型(因为之前的数组也是如此,但是数组是到目前为止)并不像对象类型那样明显。)

希望这有助于即使答案有点晚。刚引起我的注意,到目前为止还没有真正回答。

答案 1 :(得分:0)

注意:

这是一个内部引擎接口,无法在PHP脚本中实现。必须使用IteratorAggregate或Iterator。在实现扩展Traversable的接口时,请确保在implements子句中的名称前面列出IteratorAggregateIterator

即。您可以在http://docs.php.net/class.iterator和/或http://docs.php.net/class.iteratoraggregate

找到界面定义

答案 2 :(得分:0)

正如我在评论中所述,使用PDO很容易做到你想要的。只需设置PDO::ATTR_DEFAULT_FETCH_MODE属性,当您执行foreach循环时,它将执行适当的提取。

您还可以设置PDO::ATTR_STATEMENT_CLASS属性以定义要使用的类。

$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS);
$pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS,array('classname',$constructorArgs));

有关详细信息,请参阅http://php.net/manual/en/pdo.setattribute.php

使用该解决方案,您需要更改每个查询的ATTR_STATEMENT_CLASS属性,具体取决于您要实例化的类。

或者,您可以将其设置为在查询中指定类名:

$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE);
$stmt = $db->query("SELECT 'classname', * FROM `table` WHERE `id` = 1;");

只需将相关的类名作为要选择的第一个字段添加到查询中。

有关此问题的更多信息,请参阅此相关问题In PHP, How do I set default PDO Fetch Class?的其中一个答案。