我做了大量的研究,却找不到这个看似热门的问题的答案。
我有一个域模型,它包含一些属性,比如firstName
和lastName
,但在我的数据库中,我将它们存储为fname
和lname
。
根据好的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模块的例子(你可以将其调整到你的真实场景,我认为这比我必须研究你的系统更容易)
您可以在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', ) ), //... );
然后,当您在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); }, ), ); } }
然后,您只需要调整您的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'); } } }
列名的任何地方都是一样的。
我认为你可以很容易地将其适应你的系统。