使用Zend Framework 2将数据库列映射到域模型



我做了大量的研究,却找不到这个看似热门的问题的答案。

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

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

通过查看实现ZendStdlibHydratorAbstractHyrdrator接口的现有类的源代码,我看不到它们中有任何一个提供映射功能。

有人见过这样一种不会污染模型的清洁解决方案吗?

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

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

<?php
use ZendStdlibHydratorNamingStrategyUnderscoreNamingStrategy;
use ZendStdlibHydratorObjectProperty;
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)

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

编辑:这是Fowler对图案的描述

这里有一篇博客文章,其中有一个小例子,说明如何在PHP中实现它

使用ORM

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

就我个人而言,我喜欢教条。我过去通过docbloc注释将字段与数据库映射,因为这更容易,但我同意关于如何存储数据的信息不应该在模型中。幸运的是,您拥有用于数据映射的XML和YAML选项。

例如,如果你的模型中有这个实体:

<?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

您可以在配置文件中创建映射。使用表网关模式之后,可以将该配置注入到table类中。我将给你一个例子,从ZF2骨架应用程序中调整Album模块的例子(你可以将其调整到你的真实场景,我认为这比我必须研究你的系统更容易)

  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(
    'AlbumModelAlbumTable' =>  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('ZendDbAdapterAdapter');
    $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');
    }
    }
    }
    

列名的任何地方都是一样的。

我认为你可以很容易地将其适应你的系统。

相关内容

  • 没有找到相关文章

最新更新