Methode auf Objekt anwenden

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Methode auf Objekt anwenden

    Hi zusammen,

    ich habe gerade leider ein ziemlich kniffliges Problem. Bei der Lösungssuche im Internet bin ich auch schon über Begriffe wie Strategy Pattern oder Decorator Pattern gestolpert, bin mir aber nicht sicher, ob das wirklich das Mittel zum Zweck ist.

    Vorab, als Vergleich, was ich erreichen will: Auf php.net gibt es zu mysqli_fetch_array einen kleinen Code Snippet, siehe (ich habs etwas gekürzt):

    PHP-Code:
    $mysqli = new mysqli("localhost""my_user""my_password""world");
    $query "SELECT Name, CountryCode FROM City ORDER by ID LIMIT 3";
    $result $mysqli->query($query);
    $row $result->fetch_array(); 
    Es wird in diesem Beispiel eine Methode fetch_array() auf das Objekt $result angewendet. Das ist letzten Endes genau das, was ich auch erreichen will. Ich will eine Methode, die ich jederzeit auf beliebig viele Objekte anwenden.

    Was ich bisher gebastelt habe:

    Klasse:

    PHP-Code:
    class mysqldb extends mysqli
    {
        
    /**
         * DB Verbindung herstellen und Charset setzen
         */
        
    public function __construct()
        {    
            
    // DB Verbindung
        
    }
        
        
    /**
         * Führt die Datenbankanfrage durch
         */
        
    public function query ($sql)
        {
            
    $this->result parent::query($sql);
            return 
    $this;
        }
        
        
        
    /**
         * Liefert alle Datensätze der abgefragten SQL Query
         */
        
    public function getAll ()
        {        
            while (
    $row $this->result->fetch_array())
            {
                
    $data[] = $row;
            }
            return 
    $data;
        }

    So, jetzt kommts zu dem Problem-Part:

    PHP-Code:
    $mysqldb = new mysqldb();

    $result_a $mysqldb->query('SELECT bla');
    $result_b $mysqldb->query('SELECT blub');

    $data_a $result_a->getAll();
    $data_b $result_b->getAll();

    var_dump($data_a);
    var_dump($data_b); 
    Nur $data_b ist mit Inhalten gefüllt. $data_a ist leer. Das liegt an der Funktionsweise von getAll(); Eigentlich soll die Methode alle Datensätze der Datenbankabfrage liefern, über das Objekt, über das es aufgerufen wird.
    Also:
    PHP-Code:
    $result_a->getAll(); // soll alle Datensätze der Query A liefern.
    $result_b->getAll(); // soll alle Datensätze der Query B liefern. 
    Es wird aber aufgrund der Implementierung von $this->result->fetch_array() in der Methode getAll() logischerweise immer nur das Result der zuletzt durchgeführten Datenbankanfrage verwendet, nicht aber des Results, auf das ich mittels dem Pfeil -> verweise.

    Meine Frage ist: Wie muss ich die Methode getAll() umgestalten, dass ich sie nach dem gleichen Prinzip verwenden kann, wie die php-Entwickler das in ihrem Beispiel auf der Php-Seite mittels folgendem Code machen:
    PHP-Code:
    $row $result->fetch_array(); 
    Aus meiner Sicht erlaubt solch eine Arbeitsweise eine enorme Flexibilität und ich will in jedem Fall eine saubere Lösung. Klar gibt es Workaround (ich könnte die betroffene Query z.B. als Argument der Methode übergeben), aber ich will in diesem Fall mal ausnahmsweise was gescheites auf die Beine stellen

    Ich hab mir schon einige Tutorials bezüglich Decorator Patterns angesehen, aber da werden plötzlich viele Klassen ins Leben gerufen und ich bin mir nicht ganz sicher, ob das auch die Korrekte arbeitsweise in diesem Fall ist. Weil in diesem Fall müsste ich ja dann pro Query ebenfalls eine eigene Klasse erstellen, oder? a la:

    PHP-Code:
    $mysqldb_a = new mysqldb;
    $result_a $mysqldb_a->query ("SELECT bla...");

    $mysqldb_b = new $mysqldb;
    $result_b $mysqldb_b->query("SELECT blubb..."); 
    Dadurch würden dann auch etliche DB-Connects durchgeführt werden, weil der Konstruktor der Klasse mysqldb logischerweise die Datenbankverbindung herstellt.

    Fragen über Fragen, über jegliche Hilfe, sei es auch nur ein kleiner Denkanstoß, wäre ich wahnsinnig dankbar.

    Vielen Dank soweit!
    Zuletzt geändert von Chrissi007; 24.08.2013, 16:51.
    Grüße, Chrissi
    Our Dreams are Wings

  • #2
    Zitat von Chrissi007 Beitrag anzeigen
    Nur $data_b ist mit Inhalten gefüllt. $data_a ist leer. Das liegt an der Funktionsweise von getAll()
    Nein, das liegt viel mehr daran, dass du mit dem zweiten Aufruf von query() die Variable $this->query überschrieben hast.

    Es wird aber aufgrund der Implementierung von $this->result->fetch_array() in der Methode getAll() logischerweise immer nur das Result der zuletzt durchgeführten Datenbankanfrage verwendet, nicht aber des Results, auf das ich mittels dem Pfeil -> verweise.
    Da du immer auf der selben Objektinstanz arbeitest, verweisen auch deine „Pfeile“ immer auf die gleiche Query bzw. das gleiche result set.

    Meine Frage ist: Wie muss ich die Methode getAll() umgestalten
    Nicht diese eine Methode ist das Problem, sondern die ganze Klasse, bzw. wie du sie benutzt.

    Du erzeugst nur eine einzige Instanz der Klasse, und gibst dann auch noch diese selber zurück (in query() mit return $this), was ungewöhnlich und auch nicht zielführend ist.

    Was ist denn der Rückgabetyp der Methode mysqli::query laut Handbuch …?

    Weil in diesem Fall müsste ich ja dann pro Query ebenfalls eine eigene Klasse erstellen, oder?
    Keine eigene Klasse, sondern mehrere Instanzen der Klasse, also mehrere Objekte.

    Dadurch würden dann auch etliche DB-Connects durchgeführt werden, weil der Konstruktor der Klasse mysqldb logischerweise die Datenbankverbindung herstellt.
    Dann lass ihn vorher prüfen, ob er überhaupt eine neue Verbindung herstellen muss, oder schon eine geöffnete vorliegen hat.

    aber ich will in diesem Fall mal ausnahmsweise was gescheites auf die Beine stellen
    Wozu das ganze überhaupt? Das einzige, was deine Klasse momentan macht, ist MySQLi zu „kapseln“ und eine getAll-Methode hinzu zu fügen. Wenn diese der Hauptzweck des ganzen ist – warum nimmst du dann nicht gleich PDO, das bringt bereits eine fetchAll-Methode mit …?

    (Außerdem hast du die Absicherung gegen SQL Injection noch vernachlässigt in deiner Klasse – prepared statements nehmen dir auch das ab.)
    I don't believe in rebirth. Actually, I never did in my whole lives.

    Kommentar


    • #3
      Hey,

      vielen Dank für deine Antwort!

      Was ist denn der Rückgabetyp der Methode mysqli::query laut Handbuch …?
      Laut Handbuch handelt es sich um ein Objekt. Deswegen war dort auch mein Gedanke, dass ich eine Methode auf das Objekt anwenden möchte.

      Wozu das ganze überhaupt? Das einzige, was deine Klasse momentan macht, ist MySQLi zu „kapseln“ und eine getAll-Methode hinzu zu fügen. Wenn diese der Hauptzweck des ganzen ist – warum nimmst du dann nicht gleich PDO, das bringt bereits eine fetchAll-Methode mit …?
      Bei mir gings in diesem Fall jetzt eher so ums Prinzip. Das Handbuch zu mysqli_result hat mich da so ein bisschen in Versuchung geführt. Dort wird auch mittels
      PHP-Code:
      $row $result->fetch_array() 
      auf ein Objekt zugegriffen. Und, wenn ich es richtig verstehe, wurde dieses Objekt via
      PHP-Code:
       $result $mysqli->query($query); 
      erzeugt. Also es handelt sich bei $result nicht um eine Klasse, die instanziert wurde(?), sondern nur um ein Objekt. Ich würde soetwas auch gerne mit einer eigenen Klasse und Methoden hinkriegen, nur fehlt mir hierzu der Gedanke, wie man soetwas zu realisieren hat? Wenn ich meinen Code von oben direkt nur mit der mysqli-Klasse aufrufe, dann funktioniert das Script problemlos (also quasi nicht meine eigene Klasse ins Spiel bring...).



      Was meinst du, wäre es sinnvoll für jede Query eine eigene Instanz der Klasse aufzurufen (für den Fall, dass dort noch weitere, brauchbare Methoden hinterlegt sind)?

      Wäre es möglich, in diesem Fall die Datenbankverbindung in der Klasse aufbauen zu lassen (z.B. im Konstruktor) und im Fall einer Instanzierung der Klasse zu überprüfen, ob die Datenbankverbindung bereits durch eine andere Instanz eröffnet würde? (Ist es möglich quasi ein Objekt einer anderen Instanz automatisch in die nächste Instanz der selben Klasse zu importieren?) Oder sollte ich in diesem Fall die DB-Verbindung außerhalb der Klasse aufbauen und via global die DB-Verbindung in die Klasse reinholen?
      Entspräche das noch dem Sinn des objektorientierten Programmierens?

      Sorry für die vielen Fragen...
      Grüße, Chrissi
      Our Dreams are Wings

      Kommentar


      • #4
        dass ich eine Methode auf das Objekt anwenden möchte.
        Man kann keine "Methoden auf Objekte anwenden"!
        Zumindest ist das eine sehr verschrobenen Sicht auf die Dinge.


        Methoden sind Bestandteile eines Objektes.
        Und diese kann man aufrufen!

        Eine Bessere Sicht wäre:
        Durch den Methodenaufruf sendet man dem Objekt eine Nachricht.


        Wäre es möglich, in diesem Fall die Datenbankverbindung in der Klasse aufbauen zu lassen (z.B. im Konstruktor) und im Fall einer Instanzierung der Klasse zu überprüfen, ob die Datenbankverbindung bereits durch eine andere Instanz eröffnet würde? (Ist es möglich quasi ein Objekt einer anderen Instanz automatisch in die nächste Instanz der selben Klasse zu importieren?) Oder sollte ich in diesem Fall die DB-Verbindung außerhalb der Klasse aufbauen und via global die DB-Verbindung in die Klasse reinholen?
        Entspräche das noch dem Sinn des objektorientierten Programmierens?
        Mache dich mit dem "Dependency Injection Design Pattern" vertraut. Das wird deine Sorgen lösen/mildern.
        Wir werden alle sterben

        Kommentar


        • #5
          Hi,

          vielen Dank euch beiden soweit. Ich werd mich mal auf die Suche begeben...
          Grüße, Chrissi
          Our Dreams are Wings

          Kommentar


          • #6
            Für andere, die evtl. mal an einem ähnlichen Problem hängen: Bin gerade über ein interessantes Tutorial gestolpert, zum Nachlesen: The Dependency Injection Design Pattern in PHP 5 - PHP
            Grüße, Chrissi
            Our Dreams are Wings

            Kommentar


            • #7
              Hi Chrissi007,

              ich denke mal, es liegt daran, dass du nicht auf eine klasseneigene Variable verweist. Statt . . .
              PHP-Code:
              . . .
              $data[] = $row;
              . . .
              return 
              $data
              . . . solltest du es so formulieren . . .
              PHP-Code:
              . . .
              $this->data[] = $row;
              . . .
              return 
              $this->data
              Die Variable $data sollte im Funktionsaufruf vor einer Neubelegung auch gelöscht werden, sonst sind die Abfragergebnisse
              einer vorherigen Abfrage auch noch darin enthalten. Der Vollständigkeit halber würde ich diese Variablen auch in der Klasse deklarieren.
              PHP-Code:
              <?php
              class mysqldb extends mysqli
              {
                  private 
              $result;
                  private 
              $data = array();
                
              /**
                   * DB Verbindung herstellen und Charset setzen
                   **/
                  
              public function __construct()
                  {    
                      
              parent::__construct('localhost','guenni','guenni','test');
                  }
                  
                  
              /**
                   * Führt die Datenbankanfrage durch
                   **/
                  
              public function query ($sql)
                  {
                      
              //Vor (neuer) Abfrage $result löschen
                      
              $this->result NULL;
                      
              $this->result parent::query($sql);
                      return 
              $this->result;
                  }
                  
                  
                  
              /**
                   * Liefert alle Datensätze der abgefragten SQL Query
                   **/
                  
              public function getAll ()
                  {        
                      
              //Vor (neuer) Abfrage $data löschen, sonst sind vorher abgefragte Daten
                     //auch noch im Array enthalten
                     
              $this->data NULL;
                     
              //Konstante MYSQLI_ASSOC liefert ein assoziatives Array
                     
              while ($row $this->result->fetch_array(MYSQLI_ASSOC))
                      {
                          
              $this->data[] = $row;
                      }
                      return 
              $this->data;
                  }
              }  
               
              ?>
              PHP-Code:
              <?php 
              $db 
              = new mysqldb;

               
              //Abrage Tabelle A
              $result_a $db->query('select * from tabelle2 where id = 5');
              $data_a $db->getAll();

               
              //Abrage Tabelle B
              $result_b $db->query('select * from tabelle5 where id > 8 and id < 12');
              $data_b $db->getAll();

              echo 
              '<pre>';
              echo 
              'var_dump data_a<br>';
              var_dump($data_a);
              echo 
              'var_dump data_b<br>';
              var_dump($data_b);
              ?>
              Gruß
              Günni

              PS.: Das funktioniert zwar so, wie du es erhofft hast, ob es allerdings sinnvoll (praktikabel) ist, muß ich mich den Vorrednern anschließen.

              Kommentar

              Lädt...
              X