2013-01-01 31 views
3

在我的项目中,我想构建对多个数据库引擎的支持。我通过放置在模型层中的datamappers来实现这一点。这方面的一个简单的例子看起来像(抱歉的代码墙,如果你想获得的要点跳到结尾):如何防止在为不同的数据库引擎实现数据加载器时出现重复?

用户

namespace Application\Model; 

use Application\Model\Mapper; 

class User 
{ 
    private $mapper; 

    private $id; 

    public function __construct(Mapper $mapper, $id) 
    { 
     $this->mapper = $mapper; 
    } 

    public function setPassword($password) 
    { 
     $this->mapper->updatePassword($this->id, $password); 
    } 
} 

映射接口

namespace Application\Model; 

interface UserMapper 
{ 
    public function updatePassword($id, $password); 
} 

MySQL的映射

namespace Application\Model; 

use Application\Model\Mapper; 

class UserMysqlMapper implements Mapper 
{ 
    private $connection; 

    public function __construct(\PDO $connection) 
    { 
     $this->connection = $connection; 
    } 

    public function updatePassword($id, $password) 
    { 
     $stmt = $this->connection->prepare('UPDATE user SET password = :password WHERE userid = :userid'); 
     $stmt->execute(['password' => $password, 'userid' => $id]); 
    } 
} 

PostgreSQL的映射

namespace Application\Model; 

use Application\Model\Mapper; 

class UserPgsqlMapper implements Mapper 
{ 
    private $connection; 

    public function __construct(\PDO $connection) 
    { 
     $this->connection = $connection; 
    } 

    public function updatePassword($id, $password) 
    { 
     $stmt = $this->connection->prepare('UPDATE user SET password = :password WHERE userid = :userid'); 
     $stmt->execute(['password' => $password, 'userid' => $id]); 
    } 
} 

加载的东西

$connection = new \PDO(dsn stuff); 
$mapper = \Application\Model\UserPgsqlMapper($connection); 
$user = \Application\Model\User($mapper, 1); 
$user->setPassword('new password'); 

正如你可以看到我有基本与重复代码的两个映射器(该查询是两个引擎相同)。这有点“强奸”DRY的原则,但我只是没有看到一个好的/干净/正确的方法来防止这种情况。请注意,这当然只是一个简单的例子,通常会有不同的数据库引擎中的查询。

我曾经想过让映射器延长一些映射与基本的查询,但是这种感觉更脏,因为根本不可能对某事物的基本查询。

我昨天也在PHP聊天中提出这个问题,结论基本上是“假设重复并继续你的生活”,我越想越多,我认为这是我唯一的真正选择。

但可以肯定我不是缺少一些干净和智能解决方案,我想我会在这里发表的问题。

回答

1

如果示例代码代表,复制是一个真正的代码的气味。您不仅复制查询,而且PHP代码也是相同的;你在这里写了很多重复的代码,它并没有赢得它的保留。

我会考虑处理查询作为一种资源,而不是代码。

例如,创建一个名为“queries.php”的文件,并设置每个查询作为一个变量:

$update_password= ["default" => "UPDATE user SET password = :password WHERE userid = :userid"] 
$create_user = ["default" => "insert into blabla" 
       "mysql" => "insert into wibble"] 

当执行查询,检查是否有一个特定的数据库版本,否则使用默认。

您可能还会考虑映射器类是否真的获得了保留 - 如果他们所做的只是执行稍微不同的SQL语句,那么您可能会摆脱它们,或者至少将大量代码一个超级班。

例如,而不是一个接口,您可以创建一个DataMapper的类,默认行为,并以适合当前数据库中查询拉。如果特定数据库确实需要不同的方法实现,则可以在数据库特定的子类中重写该方法。

喜欢的东西:

namespace Application\Model; 

use Application\Model\Mapper; 

class UserDefaultMapper 
{ 
    private $connection; 

    public function __construct(\PDO $connection) 
    { 
     $this->connection = $connection; 
    } 

    public function updatePassword($id, $password) 
    { 
     $query = getQueryForDB("updatePassword", $connection); 

     $stmt = $this->connection->prepare(query); 
     $stmt->execute(['password' => $password, 'userid' => $id]); 
    } 
    public function createUser($name){ 
    ... 
    } 
} 

如果 “的createUser()” 需要一个特定的数据库实现(例如,检索用户ID),您将创建一个覆盖:

namespace Application\Model; 

use Application\Model\Mapper; 

class UserMySQLMapper extends UserDefaultMapper 
{ 
    public function createUser($name){ 
    ... 
    } 
} 

这给了你更少的代码,更少的重复代码(连接管理,语句的执行等),允许最常见的变化(不同的查询不同的发动机)通过一个资源文件,而不是继承来管理,但仍然给你的时候,你需要重写功率至。

+0

不知道你的代码重复的意思,因为实在是没有太多的代码在那里回事。你能详细说明一下吗?此外,映射器类可以将持久层与其他层分开。或者那不是你的意思? – PeeHaa

+0

据我所知,这两个数据库映射器的每一行是重复的 - 这很可能是不可收拾的相当快。如果你决定改变你建立连接的方式 - 你将需要修改每个映射器文件。我已经更新了答案,以便更清楚。 –

0

我曾经想过让映射器延长一些映射器与 基础查询,但是这种感觉更脏,因为根本 不能对某事物的基本查询。

从PHP 5.4.0起,PHP实现了一种代码重用方法,称为Traits。性状旨在通过使开发人员能够重用集的方法自由地生活在不同的类层次结构几个独立的类,以减少单继承的一些限制。

另外,Flourish PHP Unframework可以在不同的数据库(MySQL,PostgreSQL,SQLite,MSSQL,Oracle,DB2)上运行。它支持跨所有数据库类型的one dialect of SQL。您可以直接在您的项目中使用它,或者绘制其通用SQL方言的想法。

最后,你甚至可以结合这两种方法(性状和SQL方言)。对于简单查询,使用SQL的子集,而对于高级查询,则从不同特征“混入”查询。

顺便说一句,我一直在使用兴旺的数据库如MySQL和PostgreSQL,SQLite和MS SQL Server的许多不平凡的项目。我建议直接使用它。


UPDATE:您也可以参考MediaWiki内的database abstraction layer。它为不同的后端构建SQL查询,但保持应用程序使用的相同接口。

+0

我要去看看蓬勃发展的感谢。尽管我不喜欢“特质”,因为它只是另一种引入紧密耦合的方式。 – PeeHaa

+0

@PeeHaa我还没有试过特质,在现实世界的项目。 :-)然后尝试蓬勃发展。 –

+0

刚才看着蓬勃发展,我不是很喜欢我所看到的(抱歉)。 ''static's遍布整个地方,充满了坏习惯:( – PeeHaa

相关问题