2

我一直在做大量的研究,但无法找到这个看似流行的问题的答案。

我有一个包含一些属性的域模型 - 比如说firstNamelastName- 但在我的数据库中我将​​它们存储为fnameand lname

根据良好PoEAA的规则,模型应该不知道它是如何存储的,如果有的话。所以我的问题是,这些字段的映射到哪里去了?

查看实现接口的现有类的源代码,Zend\Stdlib\Hydrator\AbstractHyrdrator我看不到它们中的任何一个提供映射功能。

有没有人看到一个不污染模型的干净解决方案?

更新:更好更简单的解决方案

自从我问这个问题以来已经有一段时间了,但是这里将是一个非常简单的解决方案,可以将列映射到对象而无需太多开销。使用 ZF2 的内置命名策略功能可以实现所需的效果。

<?php
use Zend\Stdlib\Hydrator\NamingStrategy\UnderscoreNamingStrategy;
use Zend\Stdlib\Hydrator\ObjectProperty;

class Person {
    public $fname;
    public $lastName;
}
class MyNamingStrategy extends UnderscoreNamingStrategy {
    protected $map = [
        'fname' => 'first_name'
    ];

    public function hydrate($name) {
        $map = array_flip($this->map);
        if(isset($map[$name])) {
            return $map[$name];
        }
        return lcfirst(parent::hydrate($name));
    }

    public function extract($name) {
        $map = $this->map;
        if(isset($map[$name])) {
            return $map[$name];
        }
        return parent::extract($name);
    }


}
$p = new Person();
$h = new ObjectProperty();
$h->setNamingStrategy(new MyNamingStrategy());
$h->hydrate([
    'first_name' => 'john',
    'last_name' => 'Doe'
],$p);
var_dump($p);
var_dump($h->extract($p));

输出:

    object(Person)[4]
      public 'fname' => string 'john' (length=4)
      public 'lastName' => string 'Doe' (length=3)

    array (size=2)
      'first_name' => string 'john' (length=4)
      'last_name' => string 'Doe' (length=3)
4

2 回答 2

3

你看过 Data Mapper 设计模式吗?我之前在 PHP(几个 ZF1 应用程序)中实现了这一点,我认为它可以满足您的需求。您将了解数据库的责任交给映射器层,而模型(实体)对象不知道它的任何信息。它的实现非常简单,您不需要使用像 Doctrine 这样更复杂的 ORM。

编辑:这是福勒对模式的描述

这是一篇博客文章,其中包含一个小示例,说明如何在 PHP 中实现它

于 2014-02-11T05:47:34.687 回答
1

使用 ORM

我会使用任何ORM,它允许您在模型类之外配置映射。

就个人而言,我喜欢教义。我使用docbloc 注释将字段映射到数据库,因为它更容易,但我同意有关数据如何存储的信息不应该在模型内部。幸运的是,您有用于数据映射的XMLYAML选项。

例如,如果您的模型中有此实体:

<?php
class Message
{
    private $id;
    private $text;
    private $postedAt;
}

您可以在 XML 中配置此映射

<doctrine-mapping>
  <entity name="Message"  table="message">
    <field name="id" type="integer" />
    <field name="text" length="140" />
    <field name="postedAt" column="posted_at" type="datetime" />
  </entity>
</doctrine-mapping>

因此,映射位于配置文件中,因此模型本身不知道它是如何持久化的。

你有DoctrineORMModule模块可以轻松地将 Doctrine 集成到 ZF2 中:

如果您不想使用 ORM

您可以在配置文件中创建映射。使用表网关模式后,您可以将该配置注入表类。我会给你一个例子,从 ZF2 框架应用程序中改编专辑模块的例子(你可以将它改编成你的真实场景,我认为这比我必须研究你的系统容易)

  1. 您在 module.config.php 文件中创建映射。您将类名映射到一个表,并将类上的每个字段映射到一个列名

    'mappings' => array (
    
    'AlbumTable' => array ( 
    
            'table'=> 'TABLENAME',
    
            'fields' => array (
                'field1_en_clase' => 'field1_en_db',
                'field2_en_clase' => 'field2_en_db',
    
                ) 
        ),
    
        'OTHER_ENTITY' =>   array ( 
    
                    'table'=> 'TABLENAME',
    
                    'fields' => array (
                            'field1_en_clase' => 'field1_en_db',
                            'field2_en_clase' => 'field2_en_db',
    
    
                    )
    ), 
        //...   
    );
    
  2. 然后,当您在 Module.php 中配置服务时,将该信息发送给需要它们的类:

    class Module
    {
    
    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'Album\Model\AlbumTable' =>  function($sm) {
    
                    $config = $sm->get('AlbumTableGateway');
                        $tableGateway = $sm->get('AlbumTableGateway');
    
                                    //when you create the AlbumTable class, you send the field mappings
                                    $table = new AlbumTable($tableGateway, $config ['mappings']['AlbumTable']['fields']);
                            return $table;
                },
    
                'AlbumTableGateway' => function ($sm) {
    
                    $config = $sm->get('AlbumTableGateway');
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Album());
    
                                     //when you create the TableGateway, you also take the table name from the config.
                    return new TableGateway($config ['mappings']['AlbumTable']['table'] , $dbAdapter, null, $resultSetPrototype);
                      },
                ),
        );
    }
    }
    
  3. 然后,您只需要调整 AlbumTable 类,以便它接收并使用该映射。

    class AlbumTable
    {
    protected $tableGateway, $mappings;
    
    //First, in the constructor, you receive and save the mappings
    
        public function __construct(TableGateway $tableGateway, $mappings)
    {
        $this->tableGateway = $tableGateway;
        $this->mappings = $mappings;
    }
    
    
    //then in every function where you before hardcoded the column names, now you take it from the mappings, for instance:
    
    public function saveAlbum(Album $album)
    {
    
            /*
             here you had
            $data = array(
                 'artist'  => $album->artist,
                 'title'   => $album->title,
                   );
    
             */
            // now you change it to:
             $data = array(
                $mappings['artist'] => $album->artist,
                $mappings['title']  => $album->title,
        );
    
       $id = (int) $album->id;
            if ($id == 0) {
                $this->tableGateway->insert($data);
            } else {
                if ($this->getAlbum($id)) {
                    $this->tableGateway->update($data, array('id' => $id));
                } else {
                    throw new \Exception('Album id does not exist');
                }
            }
     }
    

列名被硬编码的任何地方都是一样的。

我认为您可以轻松地将其适应您的系统。

于 2014-02-10T22:24:15.880 回答