Ankündigung

Einklappen
Keine Ankündigung bisher.

PHP Array-Klasse für tabellenartige Datenstrukturen

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

  • PHP Array-Klasse für tabellenartige Datenstrukturen

    Vorgestellt und diskutiert werden soll das Konzept einer PHP-Klasse für die Selection und Sortierung für mehrdimensionale Arrays mit Tabellenstrukturen. Nützlich kann eine solche Klasse bei der Verarbeitung von Daten sein welche z.B. als JSON aus einer API(Webservice) oder als CSV daherkommen. Mit tabellenartigen Datenstrukturen sind Arrays ähnlich wie dies hier gemeint:
    PHP-Code:
    $data = [ 
      [
    'id' => 1'val' => 23.333333333], 
      [
    'id' => 2'val' => 13.7777777777], 
    ]; 
    Bei der Notation der Methoden fällt sofort die Ähnlichkeit zu SQL auf.
    PHP-Code:
    $newData tableArray::create($data
      ->
    select('id, FORMAT("%6.2f",val) AS rval'
      ->
    orderBy('val ASC')
      ->
    fetchAll(); 
    Das ist beabsichtigt. Die Funktionsweise ist jedoch schon etwas anders. Beispiel select-Methode.

    Die mit Komma getrennten Ausdrücke werden nacheinander abgearbeitet. Ein alleinstehender Feldname(Schlüssel) wird nur für einen Abruf der Daten mit FetchAll() vorgemerkt. Ein Ausdruck mit AS erzeugt intern eine neue Spalte. Diese neue Spalte steht "sofort" für nachfolgende Ausdrücke im select oder nachfolgende Methoden zur Verfügung. Durch die Nutzung von Funktionen für select und orderBy wird die Klasse extrem vielseitig. Die obige FORMAT-Funktion entspricht der sprintf-Funktion von PHP.

    Folgende Funktionen sind aktuell verfügbar:
    UPPER, LOWER, TRIM, FORMAT, DATEFORMAT, REPLACE, SUBSTR, LIKE, INTVAL, FLOATVAL

    Ist keine dieser Funktionen für den konkreten Anwendungsfall nutzbar, können eigene Funktionen mit der Methode addSqlFunction der Klasse bekannt gemacht und benutzt werden.
    PHP-Code:
    $newData tableArray::create($data)
      ->
    addSqlFunction('mul',function($v1,$v2){return $v1*$v2;})
      ->
    select('id,val,factor,mul(val,factor) as product')
      ->
    fetchAll(); 
    Diese Funktionen sind alle auch für die Methode orderBy nutzbar.
    PHP-Code:
      ->orderBy("LIKE(pos,'%CEO%') DESC, pos ASC, name"
    Eine Vergleichsfunktion wie bei usort muss vom Nutzer nicht erstellt werden, diese wird in der Klasse dynamisch aus dem Sortierausdruck erstellt. Damit wird das Sortieren eines Arrays mit mehreren Kriterien stark vereinfacht.
    Das jetzige Konzept verlangt für jede Tabellenzeile einen gleiche Struktur. Leider wird das in der Praxis von den Datenlieferanten nicht immer eingehalten.
    Die Frage wie damit umgegangen werden soll, wenn beispielsweise ein Feldelement fehlt, ist noch ein offenes Problem.

    Die Feldelemente können selbst wieder Arrays, Objekte oder ein Gemisch aus Arrays und Objekten der StdClass sein. Typisch sind da solche Strukturen
    PHP-Code:
    $data = [ 
      [
    'id' => 1'@attributes' => ['currency' => "USD",'rate' => "1.1370"]], 
      [
    'id' => 2'@attributes' => ['currency' => "JPY",'rate' => "1.28"]], 
    ]; 
    wenn die Daten aus einer XML kommen. Um Zugriff auf untere Ebenen zu haben gibt es 2 Methoden um die Daten unterhalb der 2.Ebene zu "flatten".
    Mit
    PHP-Code:
      ->flatten() 
    wird das Array in der 2.Ebene flach gemacht. Das Resultat
    PHP-Code:
    $newData = [
      [
    'id' => 1'@attributes.currency' => "USD"'@attributes.rate' => "1.1370"],
      [
    'id' => 2'@attributes.currency' => "JPY"'@attributes.rate' => "1.28",]
    ]; 
    Mit der Methode addFlatKeys() bleiben die tieferen Strukturen erhalten und die flachen Elemente werden zugefügt. Dieses Konzept erscheint für mich als ein gangbarer Weg tiefe Strukturen zu verarbeiten und gleichzeitig den Aufwand des internen Parsers für select und orderBy klein zu halten.
    Die einzelnen Methoden sind auch mehrfach nutzbar. Sortierungen wie die ältesten 5 Mitglieder zu ermitteln und diese dann den Namen nach sortiert auszugeben stellt auch kein Problem dar.
    PHP-Code:
      ->orderBy('alter DESC')
      ->
    limit(5)
      ->
    orderBy('name ASC'
    Zum Schluss möchte ich noch etwas klarstellen:
    Die Klasse ist nicht dafür gedacht Daten roh aus einer Datenbank zu holen um diese dann zu Filtern und zu Sortieren. Das kann mit einer Datenbank besser und schneller erledigt werden. Auch bei größeren Datenmengen (>1000 Datensätze) und wenn diese nicht sofort verarbeitet werden ist abzuwägen, ob es nicht Sinn macht die Daten in eine Datenbank zu bringen.

    LG jspit

    PHP-Klassen auf github


  • #2
    PHP-Code:
    ->select('id, FORMAT("%6.2f",val) AS rval'
    Wäre es nicht sinnvoller, einen Parameter für jeden Key zu nehmen?
    PHP-Code:
    ->select('id''FORMAT("%6.2f",val) AS rval'
    oder auch als array...
    PHP-Code:
    ->select(['id''rval' => 'FORMAT("%6.2f",val)']) 

    Kommentar


    • #3
      Sieht für mich so ähnlich aus wie:

      https://github.com/Athari/YaLinqo

      Kommentar


      • #4
        Oder ähnlich wie: https://github.com/ReactiveX/RxPHP (http://reactivex.io/) auch wenn es natürlich eine komplett andere Syntax hat.

        Kommentar


        • #5
          Dormilich : Inwiefern sinnvoller? Deine Vorschläge sind mehr PHP-Like und dadurch womöglich auch einfacher umzusetzen. Den für die Umsetzung meiner Syntax notwendigen Parser habe ich bereits im Griff. Hatte Anfangs die Befürchtung das dadurch zuviel Zeit draufgeht, das hat sich jedoch als unbegründet erwiesen. Eine an SQL angelehnte Syntax, die ohne viel zu überlegen hingeschrieben werden kann ohne sich Gedanken über die Reihenfolge von Argumenten oder etwa notwendige Arraystrukturen zu machen möchte ich nicht aufgeben.
          To do a common thing uncommonly well brings success.
          Links wie es andere Entwickler gemacht haben sind immer willkommen. Hab aber beides bisher nur überflogen. Beim Link von hellbringer kann ich bestimmt noch was nützliches entdecken.
          Die Syntax beim Link #3 ist für mich jedoch nicht auf den ersten Blick verständlich. Es werden Bezeichner eingeführt wie $cat für die eine Zuordnung zu den Daten nicht erkennbar ist.
          Das Ziel dort so wie ich das sehe, beliebige Wunschstrukturen von Arrays zu erzeugen, geht weit über das hinaus was ich mit meiner Klasse möchte. Bei mir ist erstmal immer eine reguläre Tabelle das Ziel.

          Edit: Der Link #3 hat ein schönes Beispiel, das ich als Test mal mit meinem Ansatz und der Einschränkung Resultat = Tabelle umgesetzt habe (dabei noch einen Bug gefunden)

          Datenbasis:
          PHP-Code:
          $products = array(
              array(
          'name' => 'Keyboard',    'catId' => 'hw''quantity' =>  10'id' => 1),
              array(
          'name' => 'Mouse',       'catId' => 'hw''quantity' =>  20'id' => 2),
              array(
          'name' => 'Monitor',     'catId' => 'hw''quantity' =>   0'id' => 3),
              array(
          'name' => 'Joystick',    'catId' => 'hw''quantity' =>  15'id' => 4),
              array(
          'name' => 'CPU',         'catId' => 'hw''quantity' =>  15'id' => 5),
              array(
          'name' => 'Motherboard''catId' => 'hw''quantity' =>  11'id' => 6),
              array(
          'name' => 'Windows',     'catId' => 'os''quantity' => 666'id' => 7),
              array(
          'name' => 'Linux',       'catId' => 'os''quantity' => 666'id' => 8),
              array(
          'name' => 'Mac',         'catId' => 'os''quantity' => 666'id' => 9),
          );
          $categories = array(
              array(
          'name' => 'Hardware',          'id' => 'hw'),
              array(
          'name' => 'Operating systems''id' => 'os'),
          ); 
          Aufgabe (Orginalkommentar):
          // Put products with non-zero quantity into matching categories;
          // sort categories by name;
          // sort products within categories by quantity descending, then by name.

          Lösung:
          PHP-Code:
          $newData tableArray::create($products)
            ->
          innerJoinOn($categories,'t2','id','catId')
            ->
          filter() 
            ->
          orderBy('t2.name,quantity DESC,name')
            ->
          select('t2.name AS Categorie,name,quantity')
            ->
          fetchAll();

          echo 
          html::table('border=1',HTML::KEYTITLE,$newData); 
          Resultat:
          result_tablearray.png
          Die Aufgabenstellung oben kann für diesen Fall fast 1:1 umgesetzt werden. Das SELECT um die id's wegzulassen ist eine Zugabe von mir.

          LG jspit
          PHP-Klassen auf github

          Kommentar


          • #6
            Ich habe mir das Projekt eben angesehen und finde es grundsätzlich gut.

            Bisher habe ich es immer so gehalten, CSVs etc. in eine SQLite Memory DB zu packen und damit zu arbeiten. Das ging recht flott.

            Ich weiß nicht so recht, ob Deine Lösung einen erheblichen Geschwindigkeitsvorteil bietet und werde es testen.

            Kommentar


            • #7
              Den Test hab ich bereits gemacht. Am Ende von diesen Test wird das für ein Array mit 1000 Zeilen gemacht. Daher kommt auch die Zahl 1000 im Startbeitrag.
              SQLite (im RAM) braucht dort viel mehr Zeit beim Insert und ist schneller bei der Auswertung. Die Zeit/Geschwindikkeit steht für mich bei dem Projekt jedoch nicht an erster Stelle, da die nach meinem Ermessen für beide Varianten traumhaft sind.

              Bei einem Test bei dem eine API ca 260K Daten als JSON liefert kam ich auf solche Zeiten:
              Webservicezugriff : 1 Sek.
              Json-Decode : 100ms
              Verarbeitung mit tablearray: 10ms


              Anmerkung: Der Test läuft in Echtzeit, daher ergeben sich bei jeden Aufruf geringfügig andere Zeiten.
              Anmerkung2: Die Klasse ist noch nicht ausgetestet, den letzten Bock ha ich heute früh gefunden.
              PHP-Klassen auf github

              Kommentar


              • #8
                Zitat von #5
                PHP-Code:
                  ->filter() 
                Was macht das "nackte" filter() hier?
                Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
                PHP.de Wissenssammlung | Kein Support per PN

                Kommentar


                • #9
                  Zitat von hausl Beitrag anzeigen
                  Was macht das "nackte" filter() hier?
                  Arbeitet mit dem Defaultparameter NULL ähnlich wie array_filter() , nur das hier alle Zeilen rausgefiltert werden die in einer Spalte den Wert null, 0 oder "" haben. Im Beispiel ist das der Monitor.
                  Im Normalfall wird dort als Parameter eine anonyme Funktion übergeben.
                  PHP-Klassen auf github

                  Kommentar


                  • #10
                    Cool, gefällt mir sehr gut. Habe sowas bisher auch immer über den SQLite Umweg gemacht.
                    Hast du evt. geplant, noch einfache Aggregatfunktionen und GroupBy einzubauen? Oder funktioniert das sogar schon irgendwie mit fetchGroup und addSqlFunction?

                    Kommentar


                    • #11
                      Zitat von Meister1900 Beitrag anzeigen
                      Habe sowas bisher auch immer über den SQLite Umweg gemacht.
                      Ist ja auch keine schlechte Lösung. Wenn es mit den Daten passt spricht so einiges für eine Datenbanklösung.
                      Eben ein wenig mehr Aufwand durch die notwendige Erstellung der Tabellen und dem Insert der Daten.
                      Es gibt jedoch Anwendungsfälle, da wird dies für eine Datenbanklösung sehr aufwendig (Stichwort Normalisierung).
                      So ist immer abzuwägen, was für den konkreten Anwendungsfall der bessere Weg ist.

                      Zitat von Meister1900 Beitrag anzeigen
                      Hast du evt. geplant, noch einfache Aggregatfunktionen und GroupBy einzubauen?
                      Wohl eher nicht, da dies in meinen Augen Aufgaben sind, die Datenbanken sozusagen auf den Laib geschneidert sind. Hab in der Richtung mir auch noch keine Gedanken gemacht.
                      Will mich bezüglich Erweiterungen da zum jetzigen Zeitpunkt nicht festlegen, denn eine solche Notwendigkeit zeigt sich erst bei der Benutzung der Klasse in konkreten Anwendungen.
                      Zudem steht es ja jeden frei, die Klasse nach eigenen Anforderungen zu erweitern oder umzubauen.

                      LG jspit





                      PHP-Klassen auf github

                      Kommentar

                      Lädt...
                      X