PHP richtig aufbauen

f1nc

Grünschnabel
Hallo zusammen

Ich habe schon mehere Applikationen mit PHP aufgebaut und immer wieder bin ich an Probleme gestossen, welche ich "unschön" gelöst habe, da ich nicht wusste wie man dies sinnvoll umsetzt.

Da ich tutorials.de schon länger kenne versuche ich es erst mal hier zu fragen.

Für eine neue Applikation brauche ich folgendes:
- Benutzer (Anmeldung, Login, Verwaltung)
- Einträge (jeder Benutzer kann Einträge erstellen, etc.)
- Kategorien verwalten
- Artikel verwalten
- Kommentare eintragen

Das Problem was ich immer habe ist welche Klasse erbt von welcher und wie verlinke ich Klassen untereinander.

Für die Datenbankabfragen (Insert, Update, Select, ...) habe ich eine Klasse "class.database.php".

Die Benutzerklasse enthält Methoden (Add, Edit, Delete). Für jede dieser Aktionen braucht es aber eine Datenbankverbindung.
Deshalb habe ich in der Konstruktor Methode:

global $db;
$this->db = $db;

Jetzt möchte ich das ein Benutzer eine eigene Kategorie erstellen kann.

Dazu habe ich die Kategorieklasse mit Methoden (Add, Edit, Delete). Für jede dieser Aktionen braucht es wieder eine Datenbankverbindung und zusätzlich den Benutzer.

Wie mache ich das jetzt?

so:
class categories {
...
global $db, $user;
$this->db = $db;
$this->user = $user;
}

oder
class categories extends base {

}
und
class base extends users {

}

Dann möchte ich das ein Benutzer einen Artikel erfassen kann.

Dazu habe ich die Artikelklasse und die braucht Kategorieinformationen, Datenbankinformationen und Benutzerinformationen.

Wie läuft es dann?

Bei mir endet das leider immer in grossem "Gebastel", da ich nicht weiss wie man dies am besten umsetzt.


Ich hoffe ihr könnt mir auf die Sprünge helfen, damit ich dies sauber "umbauen kann". Mir würde auch schon helfen, wenn ich wüsste was ich online suchen muss und das nachlesen kann.

Freue mich auf eure Antwort.
 
Wie wäre es so?
PHP:
class System{
  private  $mysql = null;
  public function getMysqlInstance(){
    if( $this->mysql === null ){
      $this->mysql = new Mysql('host', 'user', 'pass','dtbs');
    }
    return $this->mysql;
  }
}
class Mysql{
  private $link;
  public function __construct($host, $user, $pass, $dtbs){
    $this->link = mysql_connect($host, $user, $pass);
    mysql_select_db($dtbs, $this->link);
  }
  public function query($qry){
    return mysql_query($qry, $this->link);
  }
}

$result = System::getMysqlInstance()->query('SELECT * FROM table');

Natürlich von den Klassen weiter ausbaubar. Aber es geht ja um die Idee ;)
Global finde ich immer sehr unschön.
 
Zuletzt bearbeitet:
Du solltest dir mal das prinzip der Dependency Injection anschauen. Dabei wird nicht eifnach imemr weiter vererbt, was zu immer mehr abhängigkeiten und weniger austauschbarkeit führen würde.
Was du hier mit "global" implementiert hast wird mittlerweile durch das wohl bekannteste Entwurfsmuster gelöst: Das sogenannte Singletone-Pattern. Dieses verhindert, dass von einer Klasse mehrere Objekte instanziiert werden. (Logischerweise brauchst du nur eine geöffnete Datenbankressource) Implementierungen zu diesem Pattern gibts hier: http://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)#Implementierung_in_PHP_.28ab_Version_5.29

Für den Datenzugriff gib es natürlich noch weitere Muster, aber ich denke, das sollte erstmal reichen.

Zu den konkreten Problemen:
Dazu habe ich die Kategorieklasse mit Methoden (Add, Edit, Delete). Für jede dieser Aktionen braucht es wieder eine Datenbankverbindung und zusätzlich den Benutzer.

Wie mache ich das jetzt?
Dieser Klasse würde ich im Konstruktor das benutzerobjekt mitgeben, in dem dann im idealfall auf alle berechtigungen sowie weitere Details gekapselt sind. An die datenbankverbindung kommst du dann durch zB DB::getInstace();


Dann möchte ich das ein Benutzer einen Artikel erfassen kann.

Dazu habe ich die Artikelklasse und die braucht Kategorieinformationen, Datenbankinformationen und Benutzerinformationen.

Wie läuft es dann?
Mh, Artikel und Kategorien hängen denke ich stark zusammen, sodass hier die verebung tatsächlich sinnvoll zu seins cheint. class Artikel() erweitert die Informationen von class Kategorie(); , oder nicht? Bei Datenbank und Benutzer würde ich gleich vorgehen wie beim vorherigen Beispiel.

edit: Was mir grade auffällt. Für besonders schönes Softwaredesign kannst du für die Klassen Artikeln, Kategorien und Einträge (wenn das alles Klassen sind) noch eine Schnittstelle basteln. Also ein Interface, dass die Methoden add, edit und delete schon vorgibt.

Hoffentlich ist das so einigermaen richtig, bin auch noch neu im OOP Geschäft.
 
Zuletzt bearbeitet:
Du könntest außerdem bereits genannten Entwurfsmuster Singleton auch das Design-Pattern "Registry" verwenden. Vom Prinzip her ist ist das eine Klasse, die eine Setter-Funktion mit zwei Parametern (Identifikator, Objekt) und eine Getter-Methode (Identifikator) besitzt. Alle Objekte in der Registry werden in einem internen Array abgelegt. Theorethisch kann man dafür sogar das ArrayObject intern verwenden. Hier ein Beispiel:

PHP:
class Registry
{
  /**
   * @var ArrayObject
   */
  private static $_objects;

  // Wir erlauben keine Instanz von Registry zu erstellen
  private function __construct() {}
  private function __clone() {}

  public static function set($identifier, $object)
  {
    self::_$objects->offsetSet($identifier, $object);
  }

  public static function get($identifier)
  {
    if( !self::$_objects->offsetExists($identifier) )
      throw new Exception("Registry beinhaltet das Objekt $identifier nicht");

    return self::$_objects->offsetGet($identifier);
  }
}

Das kann dann z.B. so verwendet werden:

PHP:
class Artikel
{
  public function __construct($id)
  {
     // Hier irgendwas mit der ID anstellen, z.B. aus der DB lesen
  }

  public function save()
    throws Exception
  {
    $currentUser = Registry::get('authorizedUser');
  }
}

Vorher muss dann in der Login-Klasse natürlich der Benutzer aus der DB gelesen und in der Registry abgelegt werden:

PHP:
class LoginController
{
  public function loginAction()
  {
    $user = DB::getInstance()->loadUser($loginId);  // Login-ID muss vorher irgendwie besorgt werden
    Regsitry::set('authorizedUser', $user);  // Benutzer in der Registry ablegen
  }
}

Warum über Registry? Nunja, eine Registry kann man einfach serialisieren, sprich in einer Session einhängen.
 
Ich würde dir UML empfehlen. Damit kannst du deine Gedanken ordnen und erstmal zu Papier bringen. (Das scheint dein Größtes Problem zu sein?)
http://de.wikipedia.org/wiki/Unified_Modeling_Language

An deiner Stelle würde ich damit die Datenbank planen und daraus dann ein Klassendiagramm ableiten.
http://de.wikipedia.org/wiki/Unified_Modeling_Language#Klassen

So kannst du auch Vorzeitig viele Probleme erkennen und umgehen.


Die Klassennamen hab ich mir von den Großen abgeguckt ;)

IDatabase <- Interface
MysqlDatabase <- Spezialisierung für MySQL
MysqliDatabase <- Spezialisierung für MySQLi
....
 
Zurück