data-mapper Перетворювач Даних (англ. Data Mapper) — це шаблон, який виступає в ролі посередника для двоспрямованої передачі між постійним сховищем даних (часто, реляційної бази даних) і подання даних у пам’яті (шар домену, що вже завантажено і використовується для логічної обробки).

Мета шаблону в тому, щоб тримати подання даних у пам’яті та постійне сховище даних незалежними один від одного та від самого перетворювача даних. Шар складається з одного або більше mapper‘а (або об’єктів доступу до даних), відровідальних за передачу даних. Реалізації mapper‘ів різняться за призначенням. Загальні mapper‘и можуть обробляти різні типи сутностей доменів, а виділені mapper‘и будуть обробляти один або кілька конкретних типів.

  • Storage.php
                  
    <?php
    
    namespace DesignPatterns\Structural\DataMapper;
    
    /**
     * Class Storage
     * @package DesignPatterns\Structural\DataMapper
     */
    class Storage
    {
        /**
         * @var array
         */
        private $data = [];
    
        /**
         * Storage constructor.
         *
         * @param array $data
         */
        public function __construct(array $data)
        {
            $this->data = $data;
        }
    
        /**
         * @param int $id
         *
         * @return array|null
         */
        public function find(int $id)
        {
            if (isset($this->data[$id])) {
                return $this->data[$id];
            }
    
            return null;
        }
    }
    
                  
  • User.php
                  
    <?php
    
    namespace DesignPatterns\Structural\DataMapper;
    
    /**
     * Class User
     * @package DesignPatterns\Structural\DataMapper
     */
    class User
    {
        /**
         * @var string
         */
        private $username;
        /**
         * @var string
         */
        private $email;
    
        /**
         * @param array $state
         *
         * @return User
         */
        public static function fromState(array $state): User
        {
            return new self(
                $state['username'],
                $state['email']
            );
        }
    
        /**
         * User constructor.
         *
         * @param string $username
         * @param string $email
         */
        public function __construct(string $username, string $email)
        {
            $this->username = $username;
            $this->email = $email;
        }
    
        /**
         * @return string
         */
        public function getUsername()
        {
            return $this->username;
        }
    
        /**
         * @return string
         */
        public function getEmail()
        {
            return $this->email;
        }
    }
    
                  
  • UserMapper.php
                  
    <?php
    
    namespace DesignPatterns\Structural\DataMapper;
    
    use InvalidArgumentException;
    
    /**
     * Class UserMapper
     * @package DesignPatterns\Structural\DataMapper
     */
    class UserMapper
    {
        /**
         * @var Storage
         */
        private $storage;
    
        /**
         * UserMapper constructor.
         *
         * @param Storage $storage
         */
        public function __construct(Storage $storage)
        {
            $this->storage = $storage;
        }
    
        /**
         * @param int $id
         *
         * @return User
         */
        public function findById(int $id): User
        {
            $result = $this->storage->find($id);
    
            if ($result === null) {
                throw new InvalidArgumentException('User #' . $id . ' not found');
            }
    
            return $this->mapRowToUser($result);
        }
    
        /**
         * @param array $row
         *
         * @return User
         */
        private function mapRowToUser(array $row): User
        {
            return User::fromState($row);
        }
    }
    
                  
  • Test case:

    UserMapperTest.php
      
    <?php
    
    namespace DesignPatterns\Tests\Structural\DataMapper;
    
    use DesignPatterns\Structural\DataMapper\Storage;
    use DesignPatterns\Structural\DataMapper\UserMapper;
    use InvalidArgumentException;
    use PHPUnit_Framework_TestCase;
    
    class DataMapperTest extends PHPUnit_Framework_TestCase
    {
        public function testCanMapUserFromStorage()
        {
            $storage = new Storage(
                [
                    1 => [
                        'username' => 'zyuzka',
                        'email'    => 'zyuzka@mail.com',
                    ],
                ]
            );
            $mapper = new UserMapper($storage);
    
            $user = $mapper->findById(1);
    
            $this->assertInstanceOf('DesignPatterns\Structural\DataMapper\User', $user);
        }
    
        public function testWillNotMapInvalidData()
        {
            $this->expectException(InvalidArgumentException::class);
            $storage = new Storage([]);
            $mapper = new UserMapper($storage);
    
            $mapper->findById(1);
        }
    }