MB: Kann man mit PDO Klassen-Arrays aus der Database fetchen

moin community,

ich wurde durch einen Thread drauf hingewiesen, dass man auch Klassen als return type aus einer Datenbank in PDO fetchen kann. Ich habe bisjetzt nur gefetchte primitive Datentypen in Klassen gesehen aber keine arrays. Aber ich möchte aus der Datenbank ein Klassenbasiertes Array herausgezogen bekommen wie $foobar->tok[]. Ist das möglich oder muss ich mir mit PDO:FETCH_ASSOC ein array in eine Klasse basteln?

lgmb

akzeptierte Antworten

  1. Tach!

    ich wurde durch einen Thread drauf hingewiesen, dass man auch Klassen als return type aus einer Datenbank in PDO fetchen kann.

    Ach ja? Kann man aber nicht. Klassen sind Baupläne. Um konkret Daten transportieren zu können, braucht man Objekte. Objekte sind Instanzen von Klassen.

    Ich habe bisjetzt nur gefetchte primitive Datentypen in Klassen gesehen aber keine arrays.

    Weil es nicht geht. Es gibt keinen Datentyp Array in beispielsweise MySQL. Wozu auch? Es gibt ja Tabellen für die Massenhaltung von Daten.

    Aber ich möchte aus der Datenbank ein Klassenbasiertes Array herausgezogen bekommen wie $foobar->tok[]. Ist das möglich oder muss ich mir mit PDO:FETCH_ASSOC ein array in eine Klasse basteln?

    Keine Ahnung, was du konkret möchtest, aber Arrays aus Datenbankabfrageergebnissen musst du selbst erstellen.

    dedlfix.

    1. nabend dedlfix,

      ich wurde durch einen Thread drauf hingewiesen, dass man auch Klassen als return type aus einer Datenbank in PDO fetchen kann.

      Ach ja? Kann man aber nicht. Klassen sind Baupläne. Um konkret Daten transportieren zu können, braucht man Objekte. Objekte sind Instanzen von Klassen.

      ich weis. Sry, wenn ich es anders rübergebracht habe

      Ich habe bisjetzt nur gefetchte primitive Datentypen in Klassen gesehen aber keine arrays.

      Weil es nicht geht. Es gibt keinen Datentyp Array in beispielsweise MySQL. Wozu auch? Es gibt ja Tabellen für die Massenhaltung von Daten.

      also ich habe in ERM z.B. in der 3. Normalform durch SQL gedacht wie so etwas gehen würde. Ich weis aber nicht, obs dazu ne function gibt. Daher die Frage. Ich kann mir auch selbst was zusammen friemeln.

      Aber ich möchte aus der Datenbank ein Klassenbasiertes Array herausgezogen bekommen wie $foobar->tok[]. Ist das möglich oder muss ich mir mit PDO:FETCH_ASSOC ein array in eine Klasse basteln?

      Keine Ahnung, was du konkret möchtest, aber Arrays aus Datenbankabfrageergebnissen musst du selbst erstellen.

      Danke Dir. Werds machen.

      lgmb

  2. Hallo MB,

    wenn dein Datenmodell so aussieht, dass Du irgendein Dingsbums 0-n mal hast, dann gehört es in der DB "untereinander", nicht "nebeneinander". Also auf Rows verteilt, nicht auf Columns. Das nennt man das Einhalten von Normalformen im relationalen Modell.

    Wenn Du nun eine Objektklasse hast, die nur ein Attribut als Array hat, dann ist das in der DB natürlich lästig. Um das zu normalisieren, brauchst Du eine weitere Tabelle. Und wenn Du es als JOIN liest, liest Du eine Menge redundanter Daten; deshalb fällt dafür eine weitere Query an. Das KANNST Du ggf. optimieren, indem Du über eine Stored Procedure liest die mehrere Rowsets liefert; keine Ahnung ob MySQL das kann.

    Solche Extratabellen nur wegen eines n-fach Attributs sind ärgerlich. Aber man sollte sich nicht verleiten lassen, sie "weg zu optimieren". Man könnte ja das PHP Array vor dem Speichern in der DB in eine kommaseparierte Liste umwandeln und die in einem langen String speichern. Klar. Geht. Das ist dann eine vorsätzliche Denormalisierung und es ist deine Entscheidung, alle Nachteile davon in Kauf zu nehmen. Eine Lieferung des Attributs als PHP-Array bekommst Du aber auch damit nicht.

    Rolf

    --
    sumpsi - posui - clusi
    1. nabend RolfB,

      wenn dein Datenmodell so aussieht, dass Du irgendein Dingsbums 0-n mal hast, dann gehört es in der DB "untereinander", nicht "nebeneinander". Also auf Rows verteilt, nicht auf Columns. Das nennt man das Einhalten von Normalformen im relationalen Modell.

      habe ich zu dedlfix oben erwähnt

      Wenn Du nun eine Objektklasse hast, die nur ein Attribut als Array hat, dann ist das in der DB natürlich lästig. Um das zu normalisieren, brauchst Du eine weitere Tabelle. Und wenn Du es als JOIN liest, liest Du eine Menge redundanter Daten; deshalb fällt dafür eine weitere Query an.

      ja ist mir auch klar geworden 😕. Daher frage ich Fachmänner 😉.

      Solche Extratabellen nur wegen eines n-fach Attributs sind ärgerlich. Aber man sollte sich nicht verleiten lassen, sie "weg zu optimieren".

      Ok werde ich beherzigen! Danke.

      Man könnte ja das PHP Array vor dem Speichern in der DB in eine kommaseparierte Liste umwandeln und die in einem langen String speichern.

      klingt gut und wäre auch meine idee gewesen hätte ich auch gemacht wenn kein assoziatives array entstehen würde. Ich brauch ja einen anderen zugriff aufs Array: foo;fu;fuz;bar;baz; wenn ich das in nen array umwandeln will muss ich das obligatorisch mit nem numerischen array machen: $arr[ 0 ] = 'foo'. Ich brauch aber zwei werte die referenzieren n-mal. Klar irgendwie würde ich das bestimmt hin bekommen aber ich nehme den Rat den du mir unterbreitet an.

      danke dafür.

      lgmb

      1. Hallo MB,

        Ich brauch aber zwei werte die referenzieren n-mal.

        Hab ich nicht kapiert, aber wenn Du geholfen bist, dann ist's ja gut 😀

        Rolf

        --
        sumpsi - posui - clusi
    2. wenn dein Datenmodell so aussieht, dass Du irgendein Dingsbums 0-n mal hast, dann gehört es in der DB "untereinander", nicht "nebeneinander". Also auf Rows verteilt, nicht auf Columns. Das nennt man das Einhalten von Normalformen im relationalen Modell.

      Mag so sein und seine Richtigkeit haben. Aber:

      • ENUM 1 oder 2 Bytes ('val1', 'val2', ...) Liste von Werten (val1, val2, ...). 65.535 eineindeutige Elemente sind maximal möglich.
      • SET x Bytes ('val1', 'val2', ...) String-Objekt mit verschiedenen Variablen. 64 sind maximal möglich. Speicherbedarf: x ist 1, 2, 3, 4 oder 8.

      Und wenn es das gibt, dann sollte man das auch auswerten können. Allerdings "befürchte" ich, dass hier seitens PDO Strings und nicht Arrays zurückgeliefert werden.

      1. Tach!

        wenn dein Datenmodell so aussieht, dass Du irgendein Dingsbums 0-n mal hast, dann gehört es in der DB "untereinander", nicht "nebeneinander". Also auf Rows verteilt, nicht auf Columns. Das nennt man das Einhalten von Normalformen im relationalen Modell.

        Mag so sein und seine Richtigkeit haben. Aber:

        • ENUM 1 oder 2 Bytes ('val1', 'val2', ...) Liste von Werten (val1, val2, ...). 65.535 eineindeutige Elemente sind maximal möglich.

        Ein Enum-Feld enthält exakt einen Wert, ist also skalar / primitiv.

        • SET x Bytes ('val1', 'val2', ...) String-Objekt mit verschiedenen Variablen. 64 sind maximal möglich. Speicherbedarf: x ist 1, 2, 3, 4 oder 8.

        Und wenn es das gibt, dann sollte man das auch auswerten können. Allerdings "befürchte" ich, dass hier seitens PDO Strings und nicht Arrays zurückgeliefert werden.

        Schon seitens MySQL kommt hier ein kommaseparierter String bei einer Abfrage als Ergebnis. Zudem sind beide Typen nicht umsonst im Abschnitt String-Typen des MySQL-Handbuchs aufgeführt.

        dedlfix.

      2. Hallo Regina,

        ein auf 2 Byte gestützter Enum kann 65536 aufgezählte Werte repräsentieren, von denen aber nur einer gleichzeitig aktiv ist. Das ist also kein 1:n. Verwendest Du ein Enum als Flag-Leiste, sind es 16 Werte (Flag-Bits). Die Du beim Tabellendesign festlegen musst; du kannst nicht irgendwann sagen, dass Bit 4 für "Regina" stehen soll, während es anfangs für "Fastix" stand.

        Ein SET ist, wenn ich das richtig verstanden habe, das gleiche in Grün und mit größeren Integers als Speicher-Repräsentation, nur dass MYSQL hier die Decodierung in eine String-Liste übernimmt. Welche Werte im Set möglich sind, legst Du ebenso bei der Tabellendefinition fest.

        Hat alles seine Existenzberechtigung. Ob es für den OP passt, ist dann zu prüfen. Ob man effizient alle Rows finden kann, in denen ein Enum das 4. Bit gesetzt hat, oder in denen ein Set einen bestimmten Eintrag enthält, weiß ich nicht. Angesichts der Speicherung als Bitmap würde das für mich einen Full Index- oder Table-Scan erfordern.

        Rolf

        --
        sumpsi - posui - clusi
        1. Tach!

          ein auf 2 Byte gestützter Enum kann 65536 aufgezählte Werte repräsentieren, von denen aber nur einer gleichzeitig aktiv ist. Das ist also kein 1:n. Verwendest Du ein Enum als Flag-Leiste, sind es 16 Werte (Flag-Bits).

          Enum ist ein String-Typ. Konkret gesagt kann der genau einen Wert von einer vordefinierten Liste repräsentieren. Die interne Speicherung als Byte oder ähnliches, hat keine weitere Bedeutung für den Verwender. Man kann da nicht auf Bits zugreifen. Man liest und schreibt einen Wert aus der Liste als String.

          Die Du beim Tabellendesign festlegen musst; du kannst nicht irgendwann sagen, dass Bit 4 für "Regina" stehen soll, während es anfangs für "Fastix" stand.

          Also, man kann Einträgen der Liste einen anderen Wert geben, aber wenn Datensätze diesen Wert hatten, wird der dabei zum Defaultwert geändert.

          Ein SET ist, wenn ich das richtig verstanden habe, das gleiche in Grün

          Nö, ein Set ist sozusagen eine Kombination aus Bit-Positionen. Da braucht man etwas mehr Platz, weil jeder Wert durch eine einzelne Bitposition repräsentiert wird. Der ENUM kann 256 Werte in einem Bye darstellen, das SET nur 8, die aber in beliebigen Kombinationen.

          Ob man effizient alle Rows finden kann, in denen ein Enum das 4. Bit gesetzt hat, oder in denen ein Set einen bestimmten Eintrag enthält, weiß ich nicht. Angesichts der Speicherung als Bitmap würde das für mich einen Full Index- oder Table-Scan erfordern.

          Man kann sowieso nicht über Bits oder Bytes auf ENUM oder SET zugreifen.

          dedlfix.

          1. Hallo dedlfix,

            ok, hätte wohl besser erstmal die Doku gelesen.

            Dann ist das SET die Bitmap (Flags-Enum) und ENUM die reine Aufzählung. Jedenfalls beide nicht oder nur in Ausnahmefällen geeignet, um eine 1:n Relation zu denormalisieren.

            Man kann sowieso nicht über Bits oder Bytes auf ENUM oder SET zugreifen.

            Äh doch, beim SET schon. Gerade erst gelesen, kurz vor den User-Comments.

            Rolf

            --
            sumpsi - posui - clusi
  3. moin community,

    ich wurde durch einen Thread drauf hingewiesen, dass man auch Klassen als return type aus einer Datenbank in PDO fetchen kann.

    Wo hast Du das gelesen?

    Ich habe bisjetzt nur gefetchte primitive Datentypen in Klassen gesehen aber keine arrays.

    PDOStatement::fetchAll() returns an array containing all of the remaining rows in the result set.

    Also kriegst Du somit ein Array.

    Aber ich möchte aus der Datenbank ein Klassenbasiertes Array herausgezogen bekommen

    Was verstehst Du denn unter Klassenbasiertes Array ?

    MfG

    1. moin pl,

      ich wurde durch einen Thread drauf hingewiesen, dass man auch Klassen als return type aus einer Datenbank in PDO fetchen kann.

      Wo hast Du das gelesen?

      Hier: zugriff auf stdClass

      Aber ich möchte aus der Datenbank ein Klassenbasiertes Array herausgezogen bekommen

      Was verstehst Du denn unter Klassenbasiertes Array ?

      Ich vermute von mir ein Neologismus und ich hoffe es ist verständlich geworden. Ein Beispiel hoffe ich klär dies:

      class Foobar {
        public $baz;
        public $tok = [];
      }
      

      Ich hoffte aus der DB eine definierte Klasse zu extrahieren aber ich wurde eines besseren belert.

      lgmb

      1. Tach!

        Ich hoffte aus der DB eine definierte Klasse zu extrahieren aber ich wurde eines besseren belert.

        Das geht prinzipiell, dass du dir Objekte einer bestimmten Klasse von der Fetch-Funktion PDOStatement::fetchObject() für jeden Datensatz der Ergebnismenge erzeugen lässt. Aber die erzeugt keine Eigenschaften vom Typ Array aus irgendwelchen Daten. Und dann gibt es auch noch das bereits erwähnte PDOStatement::fetchAll(), dass dir die while-Schleife beim Fetchen mit PDOStatement::fetchObject() spart und ein komlettes Array mit je einem Objekt der angegebenen Klasse pro Datensatz der Ergebnismenge liefert.

        Falls es das ist, was du eigentlich meinst. In jedem Fall liefern die Fetch-Funktionen (außer fetchAll) einen komplexen Datentyp für jede Ergebniszeile mit je einem primitiven Typ pro Feld der Ergebnismenge.

        dedlfix.

      2. Hi MB,

        danke für Deine Rückmeldung. Für mich war es überhaupt nicht klar, was Du eigentlich erreichen wolltest, nun ist es klar 😉

        Ich hoffte aus der DB eine definierte Klasse zu extrahieren

        Ist FETCH_CLASS gesetzt, kriegst Du eine neue Instanz; so stehts in der Doku. Wie ich die Doku verstehe, ist das eine Instanz der PDO Klasse bzw. einer derer Erweiterung an welche die Daten des Abfragestatements gebunden sind. Instanzen können Methoden aufrufen und wenn die Daten in der Instanz vorliegen, stehe die dann über $this in jeder Methode zur Verfügung -- Müssen also nicht als Funktionsargumente übergeben werden.

        Soweit zu dieser Idee, ich mach maln Beispiel zum besseren Verständnis.

        Bis dann 😉

        1. Tach!

          Ist FETCH_CLASS gesetzt, kriegst Du eine neue Instanz; so stehts in der Doku. Wie ich die Doku verstehe, ist das eine Instanz der PDO Klasse bzw. einer derer Erweiterung an welche die Daten des Abfragestatements gebunden sind.

          Nein, die Objekte sind entweder von einer anonymen Klasse, also stdClass oder vom angegebenen Typ. Bei PDOStatement::fetchObject() und PDOStatement::fetchAll() gibt man den Typ als Parameter an, bei PDOStatement::fetch() muss man ihn mittels separatem PDOStatement::setFetchMode() angeben. Diese Objekte haben nichts mit PDO-Klassen zu tun.

          dedlfix.

        2. Hi MB,

          Soweit zu dieser Idee, ich mach maln Beispiel zum besseren Verständnis.

          Hier isses:

          class Dumper{
              public function dump(){
                  print_r( $this );
              }
          }
          
          $m = new MySQL();
          $q = "SELECT * FROM log LIMIT 2";
          $sth = $m->DBH->prepare($q);
          $sth->execute();
          $r = $sth->fetchAll(PDO::FETCH_CLASS, 'Dumper');
          
          # Ausgaben
          print_r($r); zeigt das da
          Array
          (
              [0] => Dumper Object
                  (
                      [url] => /login.html
                      [ref] => localhost
                      [hugo] => 3cb5c03fb01057bdf3fdea7062b49d7d
                      [dat] => 2015-10-06 07:35:32
                      [lang] => 
                      [name] => 
                      [title] => 
                  )
          
              [1] => Dumper Object
                  (
                      [url] => /
                      [ref] => http://rolfrost/login.html
                      [hugo] => 3cb5c03fb01057bdf3fdea7062b49d7d
                      [dat] => 2015-10-06 07:35:45
                      [lang] => 
                      [name] => 
                      [title] => 
                  )
          
          )
          
          $r[0]->dump(); zeigt das da
          Dumper Object
          (
              [url] => /login.html
              [ref] => localhost
              [hugo] => 3cb5c03fb01057bdf3fdea7062b49d7d
              [dat] => 2015-10-06 07:35:32
              [lang] => 
              [name] => 
              [title] => 
          )
          
          

          Du siehst also im ersten Dump daß die Ergebnismenge aus einzelnen Objekten besteht, diese Objekte sind Instanzen der Klasse Dumper die weiter oben deklariert und namentlich übergeben wurde. Mit jeder Instanz der Ergebnismenge ist es nun möglich, Methoden der Klasse Dumper aufzurufen, das zeigt $r[0]->dump();

          Machs Beste daraus 😉

      3. hi MB,

        das Beispiel hast ja schon gesehen.

        Im Prinzip ist das so, daß man eine Referenz auf die Ergebnismenge mit dem Namen einer Klasse segnet, damit man mit dieser Referenz Methoden aufrufen kann.

        $r = $sth->fetchAll(PDO::FETCH_CLASS, 'Dumper'); segnet zwar nicht die gesamte Datenmenge sondern nur jeden darin enthaltenen Record, aber das Prinzip sollte verständlich sein.

        In Perl würde dasselbe etwa so aussehen:

        use strict;
        use warnings;
        require dd;
        
        my $r = \%ENV;    # Referenz erzeugen
        bless $r, 'main'; # Referenz mit dem Namen der Klasse main segnen
        
        $r->dd();         # Methode der Klasse main aufrufen
        

        Wobei hier der Einfachheit halber eine Referenz auf den Hash %ENV erzeugt wurde. In meiner Praxis mache ich prinzipiell dasselbe nur andersherum: Die Daten/Ergebnismenge werden in einer Instanz referenziert die bereits vorhanden ist. I.d.R. ist es diejenige Instanz die auch für die Ausgabe der Daten zuständig ist, z.B. ein FW-Instanz welche die Response erzeugt.

        In Perl kann man übrigens auch FileHandles blessen weil das auch Referenzen sind. Mit IO::File erstellte Handles sind mit ebendiesem Namen der Klasse gesegnet und gestatten es, Methoden wie print(), seek(), `truncate()´ usw. aufzurufen.

        Also allesamt ist das Alles nüscht Neues, aber ein Neuer sollte schonmal bereits beim Lesen der Doku darüber nachdenken. Nämlich darüber wie er das alles zweckmäßg verwenden kann. Der Rest ergibt sich in der Praxis 😉

        MfG