Instanz einer Klasse in anderer Klasse verwenden

L

Lunatic

Hallo liebe tutorials.de Community,

eine Sache vorweg, ich habe bereits gegoogled aber keine zufriedenstellende Lösung gefunden.

Es geht um folgendes:
Ich habe eine User Klasse. Die hält verschiedene Methoden bereit, z.B. selectUser($id), getUserData($key) etc...
u.a habe ich allerdings auch noch eine MySQL Klasse für Dinge die eine MySQL Klasse eben braucht. Queries, Fetching etc...

In der index.php lade ich erstmal die benötigten Klassen und instanziere sie dann:

PHP:
$mysql = new MySQL('localhost', 'user', 'pass', 'db');
$core = new Core();
$user = new User();
echo $user->selectUser(1);

Soa, dass wäre das Debakel. Die selectUser Methode ruft eine Methode der mySQL Klasse auf. Die Instanz der MySQL Klasse ist in der User Klasse allerdings nicht gültig weshalb ich die "call to a member function" Fehlermeldung erhalte.

Denkbare Möglichkeiten wären nun eben entweder die MySQL Klasse als Referenz zu übergeben, in jede Methode der Userklasse ein global $mysql; zu bomben oder die MySQL Klasse statisch zu machen. Darüber hinaus könnte ich die MySQL Klasse statisch machen. Allerdings misfallen mir aber alle o.g. Möglichkeiten und von ein paaren habe ich auch schon mitgekriegt, sie seien unsauber.

Meine Frage wäre nun, wie sähe denn eine saubere Möglichkeit aus die MySQL Klasse in der User- und ggf. noch anderen Klassen verfügbar zu machen?
Ich danke schonmal im Voraus,
Lunatic
 
Du findest Singletons unsauber? Ich würde es darüber erledigen. Ist ganz einfach zu implementieren und beschleunigt wahrscheinlich sogar deinen Code etwas. Du brauchst deine Klasse MySQL noch nicht mal anpassen. Du abstrahierst sie einfach:

PHP:
class Database extends MySQL
{
  private static $_instance;

  public static function getInstance()
  {
     if( self::$_instance == null )
     {
       self::$_instance = new Database('localhost', 'user', 'pass', 'db');
     }
     return self::$_instance;
  }
}

Jetzt fragst du dich vielleicht, warum das deinen Code beschleunigen soll. Weil du immer nur eine Instance von MySQL (bzw. Database) hast. Das ganze verwendest du dann so:

PHP:
$user = Database::getInstance()->query("SELECT * FROM users WHERE id = " . $userid);
$userdetails = Database::getInstance()->query("SELECT * FROM userdetails WHERE userdetailid = " . $user->detailid);

// usw. usf.

Was daran unsauber sein soll, verstehe ich noch nicht ganz. Es gibt zwar eine Methode, bei der bewiesen werden kann, das ein Singleton nicht immer ein Singleton ist, aber das ist bei PHP so gut wie unmöglich anzuwenden, da PHP in sich nicht threaded ist. Wenn man aber auf Nummer sicher gehen will, implementiert man in ein Singleton auch noch Mutexes. Dann hat man prinzipiell gesehen sogar die Critical-Sections-Implementierung von Java implementiert.
 
Kann mich Saftmeister nur anschließen. Das Singleton Pattern bietet sich insbesondere bei Datenbank Instanzen an.

Grüße BN
 
Kann mich Saftmeister nur anschließen. Das Singleton Pattern bietet sich insbesondere bei Datenbank Instanzen an.

Wenn man die Verbindung zu nur einer Datenbank gleichzeitig herstellen will...
 
Du könntest auch das globale Array $GLOBALS verwenden:
PHP:
$GLOBALS['DB'] = new MySQL('localhost', 'user', 'pass', 'db'); 

public function selectUser($id)
{
  $GLOBALS['DB']->Query("...");
}
 
Wie wäres es den mit dem Registry-Pattern: Registry

In deiner index.php oder in deinem Core, wo die Datenbankverbdindung aufbaust setzt du sie in die Registry. Nun kannst du in jeder Datei wo du eine Verbindung brauchst sie abfragen.

So etwa:
PHP:
    try {
        $db = new PDO($dsn, $user, $password, $attributes);
    } catch(PDOException $e) {
        echo "An exception occured while trying to connect to Database. Error: ".$e->getMessage();
    } 

    // Datenbank registrieren
    Registry::set('db', $db);

Und dann in den Dateien/Klassen wo du sie brauchst:
PHP:
    class Model {

        private static $db = null;

        public static function getDB() {
            self::$db = Registry::get('db');
            return self::$db;
        }

        public static function getUser($id) {
            $stmt = self::getDB()->prepare("SELECT * FROM user WHERE ID=:id");
            $stmt->bindParam(':id', $id);
            $stmt->execute();
            
            return $stmt->fetch();
        }
    }

Das wars! :)
 
Wenn man die Verbindung zu nur einer Datenbank gleichzeitig herstellen will...

sagt wer?

Konfigurations-Klasse (kannste auch anders und ohne Klasse machen, ich möchte es nur andeuten):
PHP:
class DbConfig 
{
	/**
	 * KonfigurationsArray
	 * @var array $dbConfigs
	 */
	private static $dbConfigs = array (
		'db0' => array (
			'user' => 'root',
			'pass' => '',
			'host' => 'localhost',
			'port' => 3306,
			'dba'  => 'database1'
		),
		'db1' => array (
			'user' => 'root',
			'pass' => '',
			'host' => 'andererhost',
			'port' => 3306,
			'dba'  => 'database39'
		)
	);
	
	/**
	 * Getter
	 * @param string $instanceName
	 * @return array
	 */
	public static function get ($instanceName)
	{
		if (!isset (self::$dbConfigs [$instanceName]))
		{
			// Exception werfen oder andere Fehlerbehandlung...
		}
		
		return self::$dbConfigs [$instanceName];
	}
}

angedeutete Datenbank-Klasse (Singleton auch für mehrere Connections):
PHP:
class MyDatabase
{
	/**
	 * Intanzholder
	 * @var array
	 */
	private static $instances = array ();
	
	/**
	 * Getter für Datenbankinstanz
	 * @param string $instanceName
	 * @return MyDatabase
	 */
	public static function getInstance ($instanceName)
	{
		if (!isset (self::$instances [$instanceName]))
		{
			self::$instances [$instanceName] = new MyDatabase ($instanceName);
		}
		
		return self::$instances [$instanceName];
	}
	
	/**
	 * Konstruktor
	 * nicht public aufrufbar, da nur über getInstance instanzierbar
	 * @param string $instanceName
	 */
	private function __construct ($instanceName)
	{
		$this->connect (DbConfig::get ($instanceName));
	}
	
	/**
	 * Verbindungsaufbau
	 * @param array Verbindungsparameter
	 */
	private function connect ($dbConfig)
	{
		// mysql connection ...
		print_r ($dbConfig);
	}
}

kleiner Test:
PHP:
MyDatabase::getInstance ('db0');
MyDatabase::getInstance ('db0');
MyDatabase::getInstance ('db0');
MyDatabase::getInstance ('db1');
MyDatabase::getInstance ('db0');
MyDatabase::getInstance ('db1');
MyDatabase::getInstance ('db0');

Du bekommst lediglich 2x die Parameter gepromptet. Der Code ist so nicht zu verwenden, da Exceptions/Fehlerbehandlungen fehlen und auch sonst nützliche Dinge. Aber das erklärt sich von selbst. Dein Argument "geht nicht" zählt also nicht. Mit GLOBAL-Gedöns würde ich gleich mal gar nicht arbeiten. Das ist zwar schön einfach aber auch schön hässlich.

Grüße BN
 

Neue Beiträge

Zurück