开发者

In PHP, How do I set default PDO Fetch Class?

Using PDO::setAttribute, how do I provide the class name when setting PDO::ATTR_DEFAULT_FETCH_MODE to PDO::FETCH_CLASS.

This is the code I am using.. I would like to set it so all of my rows are returned as an instance of DB_Row:

class DB_Row extends ArrayObject {}

$db = new PDO('mysql:dbname=example;host=localhost', 'user', 'pass');
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS);

$stmt = $db->query("SELECT * FROM `table` WHERE `id` = 1;");

$row = $stmt->fetch(); // I want a DB_Row by default!

The code above results in a PDOException since the DB_Row class name was not assigned.

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General    error: No fetch class specified

How would I go about this?

Thanks in advance..

SOLUTION: I used fireeyedboy's answer. It worked the best for my situation as I was already extending PD开发者_如何学编程OStatement for logging purposes...

class DB extends PDO {
    public function __construct($host = null, $user = null, $pass = null, $db = null) {
        try {
            parent::__construct('mysql:dbname=' . $name .';host=' . $host, $user, $pass);
            $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS);
            $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DB_Query', array('DB_Row')));
        } catch (PDOException $e) {
            die('Database Error');
        }
    }
}

class DB_Query extends PDOStatement {
    private $class;
    protected function __construct ($class = 'DB_Row') {
        $this->class = $class;
        $this->setFetchMode(PDO::FETCH_CLASS, $this->class);
    }
}

class DB_Row extends ArrayObject {
    public function __set($name, $val) {
        $this[$name] = $val;
    }
    public function __get($name) {
        return $this[$name];
    }
}


I think it should return a stdclass instance so this would be a bug - but would have to look it up in the code to verify. will do if there is no accepted answer till then.

What will work is to use PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE and then provide a class name as first column. While this is a hack:

$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE);

$stmt = $db->query("SELECT 'classname', * FROM `table` WHERE `id` = 1;");

EDIT: As promised here the code. The relevant code is here http://lxr.php.net/xref/PHP_5_4/ext/pdo/pdo_stmt.c#940 So setting the for using fetch() is only possible with FETCH_CLASSTYPE. The alternative is using PDOStatement::fetchObject()


Another kind of hack would be to extend PDOStatement, override its fetch methods and let your PDO instance use that as the default statement class.

As an example I'll just demonstrate overriding fetch()1 and leave fetchAll() and what have you up to you, if you wanna go this route:

class Db_Row
{
}

class PDOStatementWithClass
    extends PDOStatement
{
    private $fetch_class;

    // PHP complained when I tried to make this public
    protected function __construct( $fetch_class = 'StdClass' )
    {
        // internally set the fetch class for later use
        $this->fetch_class = $fetch_class;
    }

    // let $fetch_style default to PDO::FETCH_CLASS in stead of PDO::FETCH_BOTH
    public function fetch( $fetch_style = PDO::FETCH_CLASS, $cursor_orientation = PDO::FETCH_ORI_NEXT, $cursor_offset = 0 )
    {
        // make sure we're really dealing with the correct fetch style
        if( $fetch_style == PDO::FETCH_CLASS )
        {
            // then automatically set the fetch mode of this statement
            parent::setFetchMode( $fetch_style, $this->fetch_class );
        }

        // go ahead and fetch, we should be good now
        return parent::fetch( $fetch_style, $cursor_orientation, $cursor_offset );
    }
}

$db = new PDO( /* etc... */ );
// set default fetch mode to FETCH_CLASS
$db->setAttribute( PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_CLASS );
// override what statement class to use, and provide constructor arguments (found out by trial and error)
$db->setAttribute( PDO::ATTR_STATEMENT_CLASS, array( 'PDOStatementWithClass', array( 'Db_Row' ) ) );

This has the added benefit that you'll only have to define your PDO::FETCH_CLASS once in your application, and not in every query.


1) I'm surprised PHP didn't complain about overriding the signature of the method by the way.


public function fetchAll($fetch_style = \PDO::FETCH_CLASS, $fetch_argument = null, $ctor_args = null) {
    if ($fetch_style == \PDO::FETCH_CLASS){
        parent::setFetchMode( $fetch_style, 'App\Core\DBFetch' );
    }
    $fetchedData = call_user_func_array(array('parent', __FUNCTION__), func_get_args());
    return $fetchedData;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜