我一直在进行大量研究,无法找到这个看似热门问题的答案。
我有一个包含一些属性的域模型 - 让我们说firstName
和lastName
- 但在我的数据库中,我将它们存储为fname
和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)
答案 0 :(得分:2)
您是否看过Data Mapper设计模式?我以前在PHP(几个ZF1应用程序)中实现了这一点,我认为它可以满足您的需求。您有责任了解数据库到映射器层,而模型(实体)对象不知道它的任何信息。它实现起来非常简单,您不需要像Doctrine那样使用更复杂的ORM。
编辑:这是Fowler's description of the pattern
这是一个blog post with a small example of how you could implement it in PHP
答案 1 :(得分:1)
使用ORM
我会使用任何ORM,它允许您配置模型类之外的映射。
就个人而言,我喜欢doctrine。我使用docbloc annotations通过XML来映射字段,因为它更容易,但我同意有关如何存储数据的信息不应该在模型中。幸运的是,您有YAML和DoctrineORMModule数据映射选项。
例如,如果你的模型中有这个实体:
<?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>
因此,映射在配置文件中,因此模型本身不知道它是如何持久化的。
你有{{3}}模块可以轻松地将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(
'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);
},
),
);
}
}
然后,您只需要调整您的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');
}
}
}
在列名称被编码的每个地方都是如此。
我认为您可以轻松地将其调整到您的系统中。