webentwicklung-frage-antwort-db.com.de

Verwenden Sie globale Variablen in einer Klasse

Ich versuche, eine Paginierungsklasse zu erstellen und eine Variable außerhalb der Klasse zu verwenden.

Es gibt jedoch den schwerwiegenden Fehler "Aufruf einer Memberfunktion query () für ein Nichtobjekt".

Dies ist die Indexdatei:

$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`");

Und das ist die pagi.php-Datei:

class pagi {

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

}

Ist es möglich, diese Variable außerhalb der Klasse innerhalb der Klasse zu verwenden, ohne eine neue innerhalb der Klasse zu erstellen?

24
Marcoo

Der richtige Weg, dies zu lösen, wäre das Injizieren des Datenbankobjekts in die andere Klasse ( Dependency Injection ):

$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);
    }

}

Sie können das Problem auch lösen, indem Sie die Instanz der Datenbankklasse in die Methode einfügen, die sie verwendet:

$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);
    }

}

Welche Methode Sie wählen, hängt von der Situation ab. Wenn nur eine Methode eine Instanz der Datenbank benötigt, können Sie sie einfach in die Methode einfügen. Andernfalls würde ich sie in den Konstruktor der Klasse einfügen.

Beachten Sie auch, dass ich Ihre Klasse von pagi in Paginator umbenannt habe. Paginator ist ein besserer Name IMHO für die Klasse, da er für andere Personen (erneuten) Anzeigen des Codes eindeutig ist. Beachten Sie auch, dass ich den ersten Buchstaben in Großbuchstaben geschrieben habe.

Eine andere Sache, die ich getan habe, ist die Abfrage geändert, um die Felder auszuwählen, die Sie verwenden, anstatt den "Platzhalter" * zu verwenden. Dies ist aus dem gleichen Grund, aus dem ich den Klassennamen geändert habe: Personen, die Ihren Code (erneut) anzeigen, wissen genau, welche Felder abgerufen werden, ohne die Datenbank und/oder das Ergebnis zu überprüfen.

Update

Da die Antwort zu einer Diskussion geführt hat, warum ich die Abhängigkeitsinjektionsroute wählen würde, anstatt das Objekt global zu deklarieren, möchte ich klären, warum ich die Abhängigkeitsinjektion über das Schlüsselwort global verwenden möchte: Wenn Sie eine Methode wie:

function get_records($q) {
    global $db;

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

Wenn Sie die obige Methode irgendwo verwenden, ist nicht klar, dass die verwendete Klasse oder Methode von $db abhängt. Daher ist es eine versteckte Abhängigkeit. Ein weiterer Grund, warum das oben genannte schlecht ist, ist, dass Sie die Klasse $db-Instanz (also die DB_MySQL) Klasse eng mit dieser Methode/Klasse verbunden haben. Was ist, wenn Sie irgendwann 2 Datenbanken verwenden müssen? Jetzt müssten Sie den gesamten Code durchlaufen, um global $db in global $db2 zu ändern. Sie sollten niemals Ihren Code ändern müssen, um zu einer anderen Datenbank zu wechseln. Aus diesem Grund sollten Sie Folgendes nicht tun:

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

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

Auch dies ist eine versteckte Abhängigkeit und koppelt die DB_MySQL-Klasse eng mit der Methode/Klasse. Aus diesem Grund ist es auch unmöglich, die Paginator-Klasse ordnungsgemäß zu testen. Anstatt nur die Einheit (die Paginator-Klasse) zu testen, testen Sie auch die DB_MySQL-Klasse gleichzeitig. Und was ist, wenn Sie mehrere eng gekoppelte Abhängigkeiten haben? Jetzt testen Sie plötzlich mehrere Klassen mit Ihren sogenannten Unit-Tests. Wenn Sie also die Abhängigkeitseinspritzung verwenden, können Sie leicht zu einer anderen Datenbankklasse oder sogar zu einer verspotteten Datenbank zu Testzwecken wechseln. Neben dem Vorteil, dass nur ein Gerät getestet wird (Sie müssen sich keine Sorgen machen, dass aufgrund von Abhängigkeiten falsche Ergebnisse erzielt werden), wird auch sichergestellt, dass Ihre Tests schnell abgeschlossen werden.

Einige Leute denken vielleicht, dass das Singleton-Muster der richtige Weg ist, um auf ein Datenbankobjekt zuzugreifen, aber es sollte klar sein, dass, wenn Sie alle oben genannten Informationen gelesen haben, ein Singleton im Grunde nur eine andere Möglichkeit ist, Dinge global zu machen. Es sieht möglicherweise anders aus, hat aber genau dieselben Eigenschaften und damit die gleichen Probleme wie global.

67
PeeHaa

Obwohl ich damit einverstanden bin, dass das Abhängigkeitsmodell Nizza ist, verwende ich persönlich für die Datenbank eine statische Verbindung, die für alle Instanzen der Datenbankklasse verfügbar ist, und die Create-Instanzen, um sie abzufragen, wann immer ich sie brauche. Hier ist ein Beispiel:

<?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();
}

Auf diese Weise kann ich alle Instanzen, die ich von DB haben möchte, mit jeder Funktion, Methode usw. erstellen und diese lokale Instanz der Klasse verwenden, um alle meine Abfragen auszuführen. Alle Instanzen verwenden dieselbe Verbindung.

Zu beachten ist jedoch, dass dies nur eine Verbindung zur Datenbank pro definierter Klasse ermöglicht. Ich verwende jedoch nur eine, daher ist dies für mich kein Problem.

6
Jonathan Kuhn

sie können die Datenbankverbindung ($db) zum Aufruf der get_records-Methode hinzufügen:

Hier sind nur die relevanten Codezeilen:

Erste Datei:

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

Zweite Datei:

public function get_records($q, $db) {
3
Marcel Hebing

Die anderen bisherigen Antworten sind definitiv der Verwendung einer globalen Methode vorzuziehen, da dies Ihre Kapselung ruinieren wird (z. B. müssen Sie das Objekt vor dem Aufruf dieser Methode definieren).

Es ist viel besser, dies in der Methodensignatur durchzusetzen oder keine Klasse zu verwenden.

0
Colin Kroll