Skip to content

DataMapper

Muhammet Şafak edited this page May 24, 2026 · 1 revision

DataMapper

InitORM\DBAL\DataMapper\DataMapper wraps a single PDOStatement and exposes a small, fluent API for binding values and fetching results.

You rarely instantiate it directly — Connection::query() hands you one already prepared and executed. The factory is documented in Factories and DI.

At a glance

$mapper = $db->query('SELECT id, name FROM users');

// pick a fetch mode (sticky)
$mapper->asAssoc();

// pull rows
$first  = $mapper->row();    // array|object|null
$all    = $mapper->rows();   // array (possibly [])
$count  = $mapper->numRows();

Every method returning self is chainable:

$db->query('SELECT * FROM users WHERE active = :a', ['a' => true])
   ->asClass(User::class)
   ->rows();

Fetch modes

Method PDO constant Each row is…
asAssoc() FETCH_ASSOC An associative array, column ⇒ value.
asObject() FETCH_OBJ A stdClass instance.
asObject($t) FETCH_INTO Populates the supplied object $t.
asClass($c) FETCH_CLASS A new instance of $c per row.
asArray() FETCH_BOTH Both numeric and named keys.
asBoth() alias of asArray()
asLazy() FETCH_LAZY A PDORow lazy proxy.

The fetch mode is set on the underlying statement, so it persists across multiple row() / rows() calls on the same mapper.

asAssoc()

$row = $db->query('SELECT id, name FROM users WHERE id = :id', ['id' => 1])
          ->asAssoc()
          ->row();

// ['id' => 1, 'name' => 'Alice']

asObject()

$row = $db->query('SELECT id, name FROM users WHERE id = :id', ['id' => 1])
          ->asObject()
          ->row();

echo $row->name;   // stdClass

asObject($target) — fill an existing instance

FETCH_INTO writes columns directly onto a single object that you provide. Useful for re-using an instance per row:

$buf = new \stdClass();
$mapper = $db->query('SELECT id, name FROM users')->asObject($buf);

while ($mapper->row() !== null) {
    echo $buf->name, "\n";
}

asClass($class)

final class User
{
    public int    $id   = 0;
    public string $name = '';
}

$users = $db->query('SELECT id, name FROM users')
            ->asClass(User::class)
            ->rows();   // User[]

Behavioural note. FETCH_CLASS writes properties before the constructor runs, so any setup logic in __construct sees populated state. If you need the constructor first, use FETCH_CLASS | FETCH_PROPS_LATE directly via getStatement().

asLazy()

FETCH_LAZY returns a special PDORow object that fetches each column on demand:

$row = $db->query('SELECT * FROM big_users WHERE id = :id', ['id' => 1])
          ->asLazy()
          ->row();

echo $row->name;   // each property access lazily reads from the row

Pulling results

row() — next row or null

$row = $mapper->row();

if ($row === null) {
    // no more rows
}

rows() — every remaining row

foreach ($mapper->rows() as $r) {
    // ...
}

2.x change. rows() returns [] (empty array) when there are no rows. In 1.x it returned null. The return type is now non-nullable array.

numRows() — affected row count

$mapper = $db->query('UPDATE users SET active = :a WHERE id = :id',
    ['a' => false, 'id' => 1]);

echo $mapper->numRows();   // 1

Forwards to PDOStatement::rowCount(). For SELECT queries this is driver-dependent — reliable on MySQL/PostgreSQL with buffered results, unreliable on SQLite. When in doubt, run a separate SELECT COUNT(*).

Binding manually

If you obtained a mapper outside Connection::query() (rare, but possible), bind values yourself:

$stmt   = $db->getPDO()->prepare('SELECT id FROM users WHERE id = :id');
$mapper = (new DataMapperFactory())->createDataMapper($stmt);

$mapper->bindValue('id', 1);       // single, type chosen by bind()
$mapper->bindValues(['id' => 1]);  // bulk
$mapper->execute();

bind($value) returns the matching PDO::PARAM_* constant:

PHP $value Returns
bool PARAM_BOOL
int PARAM_INT
null PARAM_NULL
anything else PARAM_STR

Forwarding to PDOStatement

Any method or property that isn't defined on DataMapper is forwarded to the underlying PDOStatement:

$mapper->closeCursor();      // PDOStatement::closeCursor()
$mapper->columnCount();      // PDOStatement::columnCount()
$mapper->getColumnMeta(0);   // PDOStatement::getColumnMeta()

echo $mapper->queryString;   // PDOStatement::$queryString

When the forwarded method returns $this (the statement), the wrapper re-wraps it as the DataMapper — so fluent chains still span the wrapper boundary.

Getting the raw statement

$stmt = $mapper->getStatement();

Useful for handing off to code that expects a PDOStatement.

What's next

Clone this wiki locally