美文网首页
PHP 设计模式 - 结构型 - 数据映射模式(Data Map

PHP 设计模式 - 结构型 - 数据映射模式(Data Map

作者: SylviaYuan95 | 来源:发表于2021-01-15 13:11 被阅读0次

    1. 模式定义

    数据映射:是持久化数据存储层(通常是关系型数据库)和驻于内存得数据表现层直接进行双向数据传输得数据访问层
    数据映射模式的目的:让持久化数据存储层、驻于内存的数据表现层、以及数据映射本身三者相互独立、互不依赖。
    这个数据访问层由一个或多个映射器(或者数据访问对象)组成,用于实现数据传输。通用的数据访问层可以处理不同的实体类型,而专用的则处理一个或几个。

    2. 数据映射模式 ( Data Mapper ) VS 活动记录模式(Active Record)

    首先我们将 ORM 模型拆分开来就是两个功能

    1. 数据操作 - 对数据对象做变更,就是我们常说的业务逻辑。
    2. 数据持久化 - 将数据落地,比如存储到 MySQL,MongoDB 等不同的数据库。

    数据映射模式 ( Data Mapper ):主张两个功能必须分开,扩展灵活,逼格高,数据模型遵循单一职责原则(Single Responsibility Principle)。使用Data Mappers的框架数量相比ActiveRecord要少很多,主要有Java Hibernate,PHP Doctrine,SQLAlchemy in Python,EntityFramework for Microsoft .NET。

    <?php
    $model = new User();
    $model->setId(1);
    $model->setAccount('it2048');
    $model->setPassword('123456'); 
    
    $result = (new UserMapper())->save($user);
    

    $model 对象属性的修改属于业务逻辑UserMapper涵括持久化逻辑

    活动记录模式 (Active Record) : 主张把两个功能合在一起,简单方便,易上手。用ActiveRecord ORM的PHP框架有Laravel, Yii, CodeIgniter, CakePHP等。其他语言用的有 Ruby on Rails,Django等。

    <?php
    $model = new User();
    $model->user_id = 1;
    $model->name = 'Sylvia';
    $model->save();
    

    $model 属性的修改属于业务逻辑,调用save()方法属于持久化逻辑。使用者完全不用关心save()方法执行后数据是存储到MySQL还是MongoDB,在开发过程中可以将精力全部放到业务逻辑,开发速度非常快。

    3. UML类图

    image.png

    4. 示例代码

    业务逻辑 User

    <?php
    
    namespace DesignPattern\Structural\DataMapper;
    
    /**
     * 数据库记录在内存的表现层
     */
    class User
    {
        /**
         * @var int
         */
        protected $userId;
    
        /**
         * @var string
         */
        protected $name;
    
    
        /**
         * @param null $id
         * @param null $name
         */
        public function __construct($id = null, $name = null)
        {
            $this->userId = $id;
            $this->name = $name;
        }
    
        /**
         * @return int
         */
        public function getUserId()
        {
            return $this->userId;
        }
    
        /**
         * @param int $userId
         */
        public function setUserID($userId)
        {
            $this->userId = $userId;
        }
    
        /**
         * @return string
         */
        public function getName()
        {
            return $this->name;
        }
    
        /**
         * @param string $name
         */
        public function setName($name)
        {
            $this->name = $name;
        }
    }
    

    持久化逻辑 UserMapper

    <?php
    
    namespace DesignPattern\Structural\DataMapper;
    
    use Doctrine\DBAL\DBALException;
    use Doctrine\DBAL\DriverManager;
    
    /**
     * 数据映射类
     * 数据库的连接与修改,这里用的是扩展包DBAL适配器,用于连接各种数据库并进行操作  composer require doctrine/dbal:3.0.0 进行下载安装
     */
    class UserMapper
    {
        protected $table = 'users';
        protected $adapter;
    
        /**
         * UserMapper constructor.
         * @throws DBALException
         */
        public function __construct()
        {
            $connectionParams = array(
                'dbname' => 'design_pattern',
                'user' => 'root',
                'password' => '123456',
                'host' => '127.0.0.1:3316',
                'driver' => 'pdo_mysql',
            );
    
            $adapter = DriverManager::getConnection($connectionParams);
            $this->adapter = $adapter;
        }
    
    
        /**
         * 将用户对象保存到数据库
         *
         * @param User $user
         * @return bool
         * @throws DBALException
         */
        public function save(User $user)
        {
            // $data的键名对应数据库表字段
            $data = array(
                'user_id' => $user->getUserId(),
                'name' => $user->getName(),
            );
            // 如果没有指定ID则在数据库中创建新纪录,否则更新已有记录
            if (null === ($id = $user->getUserId())) {
                unset($data['user_id']);
                $this->adapter->insert($this->table, $data);
                return true;
            } else {
                $this->adapter->update($this->table, $data, array('user_id ' => $id));
                return true;
            }
        }
    
        /**
         * 基于ID在数据库中查找用户并返回用户实例
         * @param $id
         * @return mixed
         * @throws DBALException
         * @throws \InvalidArgumentException
         */
        public function findById($id)
        {
            $result = $this->adapter->executeQuery("select * from ".$this->table." where user_id = ".$id)->fetch();
    
            if (empty($result)) {
                throw new \InvalidArgumentException("User #$id not found");
            }
            return $this->mapObject($result);
        }
    
        /**
         * 获取数据库所有记录并返回用户实例数组
         * @return array
         * @throws DBALException
         */
        public function findAll()
        {
            $resultSet = $this->adapter->executeQuery("select * from ".$this->table)->fetchAll();
            $entries = array();
    
            foreach ($resultSet as $row) {
                $entries[] = $this->mapObject($row);
            }
    
            return $entries;
        }
    
        /**
         * 映射表记录到对象
         *
         * @param array $row
         *
         * @return User
         */
        protected function mapObject(array $row)
        {
            $entry = new User();
            $entry->setUserID((int)$row['user_id']);
            $entry->setName($row['name']);
    
            return $entry;
        }
    
    }
    
    

    单元测试:

    <?php
    
    namespace DesignPattern\Tests;
    
    use DesignPattern\Structural\DataMapper\User;
    use DesignPattern\Structural\DataMapper\UserMapper;
    use PHPUnit\Framework\InvalidArgumentException;
    use PHPUnit\Framework\TestCase;
    
    /**
     * 测试数据映射模式
     * Class DataMapperTest
     * @package Creational\Singleton\Tests
     */
    class DataMapperTest extends TestCase
    {
    
        public function getNewUser()
        {
            return array(array(new User(null, 'Sylvia')));
        }
    
    
        public function getExistingUser()
        {
            return array(array(new User(1, 'Sylvia1')));
        }
    
        /**
         * @param User $user
         *
         * @dataProvider getNewUser
         *
         * @throws \Doctrine\DBAL\DBALException
         */
        public function testCreate(User $user)
        {
            $result = (new UserMapper())->save($user);
            $this->assertIsBool($result);
        }
    
        /**
         * @param User $user
         * @dataProvider getExistingUser
         * @throws \Doctrine\DBAL\DBALException
         */
        public function testUpdate(User $user)
        {
            $updateResult = (new UserMapper())->save($user);
            $this->assertIsBool($updateResult);
        }
    
        /**
         * @param User $existing
         * @dataProvider getExistingUser
         * @throws \Doctrine\DBAL\DBALException
         */
        public function testFindById(User $existing)
        {
            $user = (new UserMapper())->findById(1);
            $this->assertEquals($existing, $user);
        }
    
    
        /**
         * @dataProvider getExistingUser
         * @throws \Doctrine\DBAL\DBALException
         */
        public function testFindAll()
        {
    
            $users = (new UserMapper())->findAll();
            $this->assertIsArray($users);
            foreach ($users as $user) {
                $this->assertIsObject($user);
            }
        }
    
    }
    

    参考文档:https://laravelacademy.org/post/2739.html
    教程源码:https://github.com/SylviaYuan1995/DesignPatternDemo

    相关文章

      网友评论

          本文标题:PHP 设计模式 - 结构型 - 数据映射模式(Data Map

          本文链接:https://www.haomeiwen.com/subject/pyyeaktx.html