Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/82.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 在为不同的数据库引擎实现数据映射器时,如何防止重复?_Php_Sql_Oop_Datamapper_Data Mapping - Fatal编程技术网

Php 在为不同的数据库引擎实现数据映射器时,如何防止重复?

Php 在为不同的数据库引擎实现数据映射器时,如何防止重复?,php,sql,oop,datamapper,data-mapping,Php,Sql,Oop,Datamapper,Data Mapping,在我的项目中,我想构建对多个数据库引擎的支持。我通过放置在模型层中的数据映射器来实现这一点。这方面的一个简单示例如下所示(抱歉,代码太多,如果您想了解要点,请跳到末尾): 用户 namespace Application\Model; use Application\Model\Mapper; class User { private $mapper; private $id; public function __construct(Mapper $mapper,

在我的项目中,我想构建对多个数据库引擎的支持。我通过放置在模型层中的数据映射器来实现这一点。这方面的一个简单示例如下所示(抱歉,代码太多,如果您想了解要点,请跳到末尾):

用户

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;

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]);
    }
}
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]);
    }
}
映射器接口

namespace Application\Model;

interface UserMapper
{
    public function updatePassword($id, $password);
}
MySQL映射器

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;

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]);
    }
}
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]);
    }
}
PostgreSQL映射器

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;

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]);
    }
}
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');
正如您所看到的,我有两个映射器,基本上都有重复的代码(两个引擎的查询是相同的)。这有点“强奸”了干燥原理,但我只是没有看到一个好的/干净的/正确的方法来防止这种情况。请注意,这当然只是一个简单的示例,通常在不同的数据库引擎中会有不同的查询

我曾经考虑过让映射器用基本查询扩展一些映射器,但是这感觉更脏,因为根本不可能有某个东西的基本查询

我昨天在PHP聊天中也问过这个问题,结论基本上是“放弃重复,继续你的生活”,我想得越多,我就越觉得这是我唯一的选择


但是为了确保我没有遗漏一些干净而聪明的解决方案,我想我会在这里发布一个问题。

如果示例代码具有代表性,那么重复就是真正的代码味道。您不仅复制了查询,而且PHP代码也完全相同;你在这里写了很多重复的代码,但这并不能让你保持下去

<>我将把查询当作资源,而不是代码。 例如,创建一个名为“querys.php”的文件,并将每个查询设置为一个变量:

$update_password= ["default" => "UPDATE user SET password = :password WHERE userid = :userid"]
$create_user = ["default" => "insert into blabla"
                "mysql"   => "insert into wibble"]
执行查询时,请检查是否存在特定于数据库的版本,否则使用默认版本

你也可以考虑映射器类是否真的赚取他们的保留——如果他们所做的只是执行稍微不同的SQL语句,那么你就可以去掉它们,或者至少把很多代码拉到超级类中。p> 例如,您可以创建具有默认行为的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){
    ...
    }
}
这样可以减少代码,减少重复代码(连接管理、语句执行等),允许通过资源文件而不是继承来管理最常见的变体(针对不同引擎的不同查询),但仍然可以在需要时进行覆盖

我考虑过让制图员用 但是,基本查询感觉更脏,因为 不能是某事物的基本查询

从PHP5.4.0开始,PHP实现了一种称为的代码重用方法。Trait旨在通过使开发人员能够在不同类层次结构中的几个独立类中自由地重用方法集来减少单个继承的一些限制

此外,PHP取消框架能够在不同的数据库(MySQL、PostgreSQL、SQLite、MSSQL、Oracle、DB2)上运行。它包括对所有数据库类型的支持。您可以直接在项目中使用它,也可以从中得出通用SQL方言的概念

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

顺便说一句,我已经在MySQL、PostgreSQL、SQLite和MS SQL Server等数据库上使用了很多非平凡的项目。我建议直接使用它



更新:您也可以参考MediaWiki中的。它为不同的后端建立SQL查询,但为应用程序使用保留相同的界面。

不确定代码重复是什么意思,因为那里实际上没有多少代码。你能详细说明一下吗?此外,映射器类用于将持久性层与其他层分开。或者这不是你的意思吗?据我所知,两个数据库映射器的每一行都是重复的——这很可能很快就会失控。如果您决定更改设置连接的方式,您将需要在任何地方修改每个映射器文件。我已经更新了答案,让它更清晰。我将调查一下,谢谢。不过我不喜欢Traits,因为这只是引入紧耦合imho的另一种方式。@PeeHaa我也没有在现实项目中尝试Traits.:-)然后试着发福。只是看看发福,我并不真的喜欢我所看到的(对不起)
static
s到处都是,而且都是坏习惯:(@PeeHaa,没关系。然后你可以根据它的SQL方言风格推出你自己的:-D