2.4. Mapeador de dados (Data Mapper)¶
2.4.1. Objetivo¶
Um mapeador de dados é uma camada de acesso à dados que realiza transferências bidirecionais de dados entre um armazenamento de dados persistente (frequentemente um banco de dados relacional) e uma representação em memória dos dados (a camada de domínio). O objetivo do padrão é manter a representação em memória e o armazenamento de dados persistente independente um do outro e do próprio mapeador de dados. A camada é composta de um ou mais mapeadores (ou Objetos de Acesso à Dados - Data Access Objects), realizando a tranferência dos dados. Implementações de mapeadores variam em escopo. Mapeadores genéricos irão manipular muitos tipos de entidades de domínio diferentes e mapeadores dedicados irão manipular um ou alguns.
O ponto-chave deste padrão é que, diferente do padrão de registro ativo (Active Record pattern), os modelos de dados seguem o princípio de responsabilidade simples (Single Responsibility Principle).
2.4.2. Exemplos¶
- Mapeamento objeto-relacional do bando de dados (ORM - Object Relational Mapper) : Doctrine2 usa um objeto de acesso a dados (DAO) nomeado como “EntityRepository”
2.4.3. Diagrama UML¶

2.4.4. Código¶
Você também pode encontrar este código no GitHub
User.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <?php
declare(strict_types=1);
namespace DesignPatterns\Structural\DataMapper;
class User
{
public static function fromState(array $state): User
{
// validate state before accessing keys!
return new self(
$state['username'],
$state['email']
);
}
public function __construct(private string $username, private string $email)
{
}
public function getUsername(): string
{
return $this->username;
}
public function getEmail(): string
{
return $this->email;
}
}
|
UserMapper.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <?php
declare(strict_types=1);
namespace DesignPatterns\Structural\DataMapper;
use InvalidArgumentException;
class UserMapper
{
public function __construct(private StorageAdapter $adapter)
{
}
/**
* finds a user from storage based on ID and returns a User object located
* in memory. Normally this kind of logic will be implemented using the Repository pattern.
* However the important part is in mapRowToUser() below, that will create a business object from the
* data fetched from storage
*/
public function findById(int $id): User
{
$result = $this->adapter->find($id);
if ($result === null) {
throw new InvalidArgumentException("User #$id not found");
}
return $this->mapRowToUser($result);
}
private function mapRowToUser(array $row): User
{
return User::fromState($row);
}
}
|
StorageAdapter.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php
declare(strict_types=1);
namespace DesignPatterns\Structural\DataMapper;
class StorageAdapter
{
public function __construct(private array $data)
{
}
/**
* @return array|null
*/
public function find(int $id)
{
if (isset($this->data[$id])) {
return $this->data[$id];
}
return null;
}
}
|
2.4.5. Teste¶
Tests/DataMapperTest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?php
declare(strict_types=1);
namespace DesignPatterns\Structural\DataMapper\Tests;
use InvalidArgumentException;
use DesignPatterns\Structural\DataMapper\StorageAdapter;
use DesignPatterns\Structural\DataMapper\User;
use DesignPatterns\Structural\DataMapper\UserMapper;
use PHPUnit\Framework\TestCase;
class DataMapperTest extends TestCase
{
public function testCanMapUserFromStorage()
{
$storage = new StorageAdapter([1 => ['username' => 'domnikl', 'email' => 'liebler.dominik@gmail.com']]);
$mapper = new UserMapper($storage);
$user = $mapper->findById(1);
$this->assertInstanceOf(User::class, $user);
}
public function testWillNotMapInvalidData()
{
$this->expectException(InvalidArgumentException::class);
$storage = new StorageAdapter([]);
$mapper = new UserMapper($storage);
$mapper->findById(1);
}
}
|