3

私のプロジェクトでは、複数のデータベースエンジンのサポートを組み込みたいと思っています。これは、モデルレイヤーに配置されたデータマッパーを介して実現しています。この簡単な例は次のようになります(コードの壁については申し訳ありませんが、要点を知りたい場合は最後までスキップしてください)。

ユーザー

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');

ご覧のとおり、基本的に重複コードを持つ2つのマッパーがあります(クエリは両方のエンジンで同じです)。これはDRYの原則をいくらか「レイプ」しますが、これを防ぐための良い/クリーン/正しい方法がわかりません。もちろん、これは単なる例であり、通常、異なるデータベースエンジン間で同じではないクエリが存在することに注意してください。

マッパーにベースクエリを使用してマッパーを拡張させることを考えましたが、何かのベースクエリが存在しないため、これはさらに汚い感じがします。

昨日のPHPチャットでもこれを聞いたところ、結論は基本的に「複製を偽ってあなたの人生を続ける」でした。考えれば考えるほど、それが唯一の本当の選択肢だと思います。

しかし、クリーンでスマートなソリューションを見逃していないことを確認するために、ここに質問を投稿すると思いました。

4

2 に答える 2

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ステートメントを実行しているだけの場合は、それらを取り除くか、少なくとも多くのコードをスーパークラスにプルアップできる可能性があります。

たとえば、インターフェイスの代わりに、デフォルトの動作でデータマッパークラスを作成し、現在のデータベースに適切なクエリをプルすることができます。特定のデータベースで実際にメソッドの別の実装が必要な場合は、データベース固有のサブクラスでそのメソッドをオーバーライドできます。

何かのようなもの:

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()」がデータベース固有の実装を必要とする場合(たとえば、userIDを取得するため)、オーバーライドを作成します。

namespace Application\Model;

use Application\Model\Mapper;

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

これにより、コードが減り、コードの重複(接続管理、ステートメント実行など)が減り、最も一般的なバリエーション(エンジンごとに異なるクエリ)を継承ではなくリソースファイルで管理できるようになりますが、それでもオーバーライドすることができます。必要なときに。

于 2013-01-01T22:52:50.867 に答える
0

マッパーに基本クエリを使用して一部のマッパーを拡張させることを考えましたが、何かの基本クエリが存在できないため、これはさらに汚いと感じます。

PHP 5.4.0 以降、PHP はTraitsと呼ばれるコード再利用の方法を実装しています。特性は、開発者が異なるクラス階層に存在するいくつかの独立したクラスでメソッドのセットを自由に再利用できるようにすることで、単一継承のいくつかの制限を軽減することを目的としています。

また、Flourish PHP Unframework はさまざまなデータベース (MySQL、PostgreSQL、SQLite、MSSQL、Oracle、DB2) で実行できます。これには、すべてのデータベース タイプにわたるSQL の 1 つのダイアレクトのサポートが含まれます。プロジェクトで直接使用することも、ユニバーサル SQL ダイアレクトのアイデアを引き出すこともできます。

最後に、これら 2 つのアプローチ (特性と SQL 方言) を組み合わせることもできます。単純なクエリの場合は SQL のサブセットを使用し、高度なクエリの場合はさまざまな特性のクエリを「ミックス」します。

ところで、私は、MySQL、PostgreSQL、SQLite、MS SQL Server などのデータベースに関する多くの重要なプロジェクトに Flourish を使用してきました。直接使用することをお勧めします。


更新: MediaWikiのデータベース抽象化レイヤーを参照することもできます。さまざまなバックエンドの SQL クエリを構築しますが、アプリケーションで使用するために同じインターフェイスを維持します。

于 2013-01-02T09:58:48.117 に答える