25

ページ付けクラスを作成し、クラスの外部から変数を使用しようとしています。

しかし、それは私に致命的なエラー「非オブジェクトでのメンバー関数query()の呼び出し」を与えています。

これはインデックスファイルです:

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new pagi();
$records = $pagination->get_records("SELECT * FROM `table`");

そしてこれはpagi.phpファイルです:

class pagi {

    public function get_records($q) {
        $x = $db->query($q);
        return $db->fetch($x);
    }

}

クラス内で新しい変数を作成せずに、クラス内のクラス外からこの変数を使用することは可能ですか?

4

4 に答える 4

69

これを解決する正しい方法は、データベース オブジェクトを他のクラスに注入することです (依存性注入)。

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new Paginator($db);
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`");

class Paginator
{    
    protected $db;

    // Might be better to use some generic db interface as typehint when available
    public function __construct(DB_MySQL $db)
    {
        $this->db = $db;
    }

    public function get_records($q) {
        $x = $this->db->query($q);
        return $this->db->fetch($x);
    }

}

それを解決する別の方法は、データベース クラスのインスタンスを、それを使用するメソッドに挿入することです。

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new Paginator();
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`", $db);

class Paginator
{
    public function get_records($q, DB_MySQL $db) {
        $x = $db->query($q);
        return $db->fetch($x);
    }

}

どちらの方法を選択するかは、状況によって異なります。1 つのメソッドだけがデータベースのインスタンスを必要とする場合は、それをメソッドに挿入できます。それ以外の場合は、クラスのコンストラクターに挿入します。

pagiまた、クラスの名前を からに変更したことにも注意してくださいPaginator。Paginator は、他の人がコードを (再) 表示することが明確であるため、クラスの名前としては私見の方が適切です。また、最初の文字を大文字にしたことに注意してください。

私が行ったもう1つのことは、「ワイルドカード」を使用する代わりに、使用しているフィールドを選択するようにクエリを変更することです*。これは、クラス名を変更したのと同じ理由です。コードを (再) 表示する人は、データベースや結果を確認しなくても、どのフィールドが取得されるかを正確に知ることができます。

アップデート

回答により、 object を宣言する代わりに依存性注入ルートを使用する理由についての議論が生じたためglobal、キーワードで依存性注入を使用する理由を明確にしたいと思いglobalます。次のようなメソッドがある場合:

function get_records($q) {
    global $db;

    $x = $db->query($q);
    return $db->fetch($x);
}

上記のメソッドをどこかで使用している場合、使用するクラスまたはメソッドが に依存していることは明確ではありません$db。したがって、これは隠れた依存関係です。上記が悪いもう1つの理由は、$dbインスタンス(したがってDB_MySQL)クラスをそのメソッド/クラスに密結合しているためです。ある時点で 2 つのデータベースを使用する必要がある場合はどうでしょうか。global $dbに変更するには、すべてのコードを実行する必要がありますglobal $db2。別のデータベースに切り替えるためだけにコードを変更する必要はありません。このため、次のことを行うべきではありません。

function get_records($q) {
    $db = new DB_MySQL("localhost", "root", "", "test");

    $x = $db->query($q);
    return $db->fetch($x);
}

DB_MySQL繰り返しますが、これは隠れた依存関係であり、クラスをメソッド/クラスに緊密に結合します。Paginatorこのため、クラスを適切に単体テストすることもできません。ユニット (クラス) だけをテストする代わりに、同時にクラスPaginatorもテストしています。DB_MySQLまた、複数の密接に結合された依存関係がある場合はどうなるでしょうか? ここで、いわゆる単体テストで突然いくつかのクラスをテストしています。したがって、依存性注入を使用すると、別のデータベース クラスに簡単に切り替えることができます。また、テスト目的でモック化されたクラスに切り替えることもできます。1 つのユニットのみをテストする利点 (依存関係のために間違った結果が得られることを心配する必要はありません) に加えて、テストが迅速に終了することも保証されます。

シングルトン パターンがデータベース オブジェクトへのアクセスを取得する正しい方法だと考える人もいるかもしれませんが、上記のすべてを読んで、シングルトンは基本的に物事を作成する別の方法であることは明らかglobalです。見た目は違うかもしれませんが、 とまったく同じ特性を持っているため、 と同じ問題がありglobalます。

于 2012-08-12T15:29:58.180 に答える
6

依存関係モデルが優れていることには同意しますが、データベースについては、データベース クラスのすべてのインスタンスで利用できる静的接続を個人的に使用し、必要なときにインスタンスを作成してクエリを実行します。次に例を示します。

<?php
//define a database class
class DB {
    //the static connection.
    //This is available to all instances of the class as the same connection.
    private static $_conn;

    //store the result available to all methods
    private $result;
    //store the last query available to all methods
    private $lastQuery;

    //static connection function. connects to the database and stores that connection statically.       
    public static function connect($host, $user, $pass, $db){
        self::$_conn = mysqli_connect($host, $user, $pass, $db);
    }

    //standard function for doing queries. uses the static connnection property.
    public function query($query){
        $this->lastQuery = $query;
        $this->result = mysqli_query(self::$_conn, $query);
        //process result, return expected output.
    }
}

//create connection to the database, this connection will be used in all instances of DB class
DB::connect('local', 'DB_USER', 'DB_PASS');

//create instance to query
$test = new DB;
//do query
$test->query("SELECT * FROM TABLE");

//test function
function foo(){
    //create instance to use in this function
    $bar = new DB;
    //do query
    $bar->query("SELECT * FROM OTHER_TABLE");
    //return results
    return $bar->fetchArray();
}

そうすれば、任意の関数、メソッドなどで DB に必要なすべてのインスタンスを作成し、そのクラスのローカル インスタンスを使用してすべてのクエリを実行できます。すべてのインスタンスが同じ接続を使用します。

ただし、これにより、定義されたクラスごとにデータベースへの接続が1つしか許可されないことに注意してください。ただし、私は1つしか使用しないため、これは問題ではありません。

于 2012-08-21T00:02:57.600 に答える
3

メソッド$dbの呼び出しにdb-connection ( ) を追加できます。get_records

関連するコード行のみを次に示します。

最初のファイル:

$records = $pagination->get_records("SELECT * FROM `table`", $db);

2 番目のファイル:

public function get_records($q, $db) {
于 2012-08-12T15:19:21.960 に答える
0

これまでの他の回答は、カプセル化が台無しになるため、グローバルを使用するよりも間違いなく望ましいです(たとえば、そのメソッドを呼び出す前にそのオブジェクトを定義する必要があります)。

メソッドの署名でそれを強制するか、クラスを使用しない方がはるかに優れています。

于 2012-08-12T15:22:44.473 に答える