Ankündigung

Einklappen
Keine Ankündigung bisher.

PHP Array-Klasse für tabellenartige Datenstrukturen

Einklappen

Neue Werbung 2019

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

  • #16
    Zitat von Meister1900 Beitrag anzeigen

    verwenden kannst du sie aber doch trotzdem in deinem projekt, wenn du ihr einfach den namespace deiner app verpasst für das autoloaing, oder?
    zumindest würde ich das so machen. aber über composer ist natürlich einfacher und man bekommt auch automatisch updates
    Natürlich kann man sie auch so verwenden.
    Aber wenn man alle anderen Abhängigkeiten eines Projektes über Composer pflegt, macht das händische verwenden es unnötig aufwendig.

    Kommentar


    • #17
      Zitat von Zeichen32 Beitrag anzeigen
      Was mir noch fehlt, damit man die Klasse in Projekten einsetzen kann, wäre eine Composer Datei und das die Klasse ein Namespace nutzt.
      Ich habe im Code gesehen, dass du den Namespace bereits vorbereitet hast (aber auskommentiert).
      Das klingt ein wenig so als wäre das mit dem Erstellen einer composer.json erledigt. Ich nutze Composer nicht und auch keine Versionsverwaltung wie Git.
      Da ist eben für mich nicht in 5 Minuten erledigt eine Klasse composerlike zu präsentieren.
      Meine Projekte haben eine überschaubare Anzahl von Dateien. Dazu keine Abhängigkeiten zu externen Ressourcen, also alles eigener Code.
      Die eigenen Klassen sind jeweils in einer einzigen Datei. Ein Update einer Klasse ist mit dem Kopieren der Datei erledigt. Composer ist für mich mehr Aufwand.
      Zudem halte ich es aus Sicherheitsaspekten nicht für unbedenklich, sich blind ohne Kenntnis der Materie per Composer fremden PHP-Code in eigene Projekte zu ziehen.

      Ähnlich verhält sich das mit dem Namespace. Ich selbst nutze kein Namespace. Die auskommentierte Zeile mit dem Namespace resultiert aus einen Experiment die Klasse für ein Namespace fit zu machen. Mit dem Namespace in der Klasse läuft mein Test mit einem
      PHP-Code:
      use Jspit\TableArray
      vorab zumindtest fehlerfrei. Da ich einen eigenen Autoloader nutze habe ich keinen Dunst wie der vom Composer tickt noch wie die Klasse eingebunden werden kann.
      Zitat von Zeichen32 Beitrag anzeigen
      PS: Die Dokumentation ist scheinbar gerade defekt: jspit.de/tools/classdoc.php?class=tableArray
      Danke für den Hinweis. Es war der Link in der Readme veraltet. Die Dokumentation wird bei Aufruf immer automatisch aus der Klasse erstellt.
      Diese nutze ich selbst jedoch nur wenig, da für alle Methoden der Klasse reproduzierbare kopierfähige Beispiele aus dem Klassentest existieren. Finde ich anschaulicher.

      Kommentar


      • #18
        Zitat von jspit Beitrag anzeigen
        Die eigenen Klassen sind jeweils in einer einzigen Datei.
        Ein wenig Struktur schadet nicht. Klassen mit 2000 Zeilen und 150 Methoden können dann auch schon unübersichtlich werden. Es wird ja auch nicht weniger sondern mehr. Beim nächsten Problem kommen die nächsten Methoden dazu und immer so weiter. PHP bietet genung Möglichkeiten das auch anders zu strukturieren.

        Zitat von jspit Beitrag anzeigen
        Ein Update einer Klasse ist mit dem Kopieren der Datei erledigt. Composer ist für mich mehr Aufwand.
        Auch mit einer composer.json kannst du die Datei(en) noch per Hand kopieren. Die composer.json würde nur einen alternativen Weg bieten.
        Autoloading über Composer wäre in dem Fall über eine classmap möglich. Du gibst in der composer.jsom das Verzeichnis mit den Dateien an (oder direkt die eine Datei) und der Autoloader von Composer übernimmt den Rest. (Mehrere Klassen in einer Datei wären damit auch möglich... Struktur und so. Mit mehreren Klassen in einer Datei stößt dein Autoloader aber dann an Grenzen.) Namespaces sind dafür nicht nötig.


        Kommentar


        • #19
          erc hat eigentlich schon alles gesagt. Das Anbinden von Composer ist wirklich nicht sehr aufwendig.

          Zitat von jspit Beitrag anzeigen
          Zudem halte ich es aus Sicherheitsaspekten nicht für unbedenklich, sich blind ohne Kenntnis der Materie per Composer fremden PHP-Code in eigene Projekte zu ziehen.
          Sachen die man per Composer installiert, sollten so wie jeder anderen Code den man installiert, vorher überprüft werden. Der Vorteil bei Composer ist allerdings dann, dass Updates etc. sehr viel einfacher durchzuführen sind.
          Wenn ich die Abhängigkeiten per Hand pflege muss ich das kopieren und ggf. anpassen des Autoloaders selber machen. Was bei einer Klasse noch relativ einfach geht, wird bei komplexeren Libraries schon zu einer Tagesaufgabe.

          Darüber hinaus habe ich bei mir in der Build Pipeline z.B. ein Schritt, der die Composer, NPM, etc. Abhängigkeiten automatisch auf bekannte Sicherheitslücken untersucht und dann entsprechend eine Warnung ausgibt.
          GitHub - FriendsOfPHP/security-advisories: A database of PHP security advisories


          Kommentar


          • #20
            Zitat von jspit Beitrag anzeigen
            Die eigenen Klassen sind jeweils in einer einzigen Datei.

            Zitat von erc Beitrag anzeigen
            Ein wenig Struktur schadet nicht. Klassen mit 2000 Zeilen und 150 Methoden können dann auch schon unübersichtlich werden.
            Der Aussage stimme vom Grundsatz zu, aber nicht als Gegenargument zu meiner obigen Aussage. Man erreicht nicht unbedingt eine Strukturverbesserung indem eine Klasse in viele Dateien gesplittet wird. Habe nur wenige Klassen bisher gesehen, wo dies gut gelungen ist. Eine gute Struktur kann auch mit einer großen Datei erreicht werden. Als Vorbild und Positivbeispiel sehe ich da den PHPMailer. Übersichtlich und relativ wenige gut strukturierte Dateien. Die Kernklasse PHPMailer.php zählt immerhin stolze 5000 Zeilen. Musste da schon öfters mal reinschauen und habe mich gut zurechtgefunden.

            Zitat von Zeichen32 Beitrag anzeigen
            erc
            Sachen die man per Composer installiert, sollten so wie jeder anderen Code den man installiert, vorher überprüft werden. [/URL]
            Darauf wollte ich hinaus. Und das genaugenommen nach jeden Update und für die gesamte Kette der Abhängigkeiten. Und da ich das nicht überblicke wenn sich da einige Klassen zig. Framework-Komponenten hinterherziehen lasse ich das für den Fall dann lieber sein.

            Zitat von Zeichen32 Beitrag anzeigen
            erc
            Was bei einer Klasse noch relativ einfach geht, wird bei komplexeren Libraries schon zu einer Tagesaufgabe.
            Ich würde es noch etwas schärfer formulieren. Bei komplexeren Libraries ist das manuell nicht mehr machbar.


            Zitat von Zeichen32 Beitrag anzeigen
            erc
            Darüber hinaus habe ich bei mir in der Build Pipeline z.B. ein Schritt, der die Composer, NPM, etc. Abhängigkeiten automatisch auf bekannte Sicherheitslücken untersucht und dann entsprechend eine Warnung ausgibt.
            GitHub - FriendsOfPHP/security-advisories: A database of PHP security advisories
            Interssante Lösung. Um so etwas zu händeln muss man aber schon recht tief in der Materie stecken.


            Kommentar


            • #21
              Zitat von jspit Beitrag anzeigen

              Interssante Lösung. Um so etwas zu händeln muss man aber schon recht tief in der Materie stecken.

              Geht, in dem von mir verlinkten Repository ist auch der Verweis auf das entsprechende Command Line Tool.
              GitHub - fabpot/local-php-security-checker: PHP security vulnerabilities checker

              Bei NPM (Die Paketverwaltung für JavaScript) ist es sogar noch einfacher, da ist es bereits eingebaut mit "npm audit"

              Kommentar


              • #22
                Vor einigen Tagen wurde hier im Forum eine für diese Klasse interessante Aufgabe gepostet. Gegeben ist eine CSV-Datei mit Tiersichtungen mit folgender Struktur:
                PHP-Code:
                /* CSV Datei Aufbau
                Datum,Tiername
                2017-08-16,Hase
                2021-03-09,Igel
                */ 
                Es sollen nun alle Tiere und die Monate dazu ausgefiltert werden welche im Monat mehr als einmal gesichtet wurden. Die Lösung mit der Klasse ist relativ kurz:
                PHP-Code:
                TableArray::setCsvDefaultOptions(['title' => true]);
                $tarr TableArray::createFromCsvFile(__DIR__.'/test/tiere.csv')
                  ->
                select('Tiername,Datum as Quantity,DATEFORMAT("Y-m",Datum) as TimePeriod,Datum as Dates')
                  ->
                filterGroupAggregate(['Quantity' => 'COUNT','Dates' => 'ARRAY'],['Tiername','TimePeriod'])
                  ->
                filter(function($row){return $row["Quantity"]  >= 2;})
                  ->
                orderBy('Quantity DESC,TimePeriod DESC')
                  ->
                fetchAll()

                Als Ergebnis bekommen wir ein Array wie dieses:
                PHP-Code:
                $tarr = [
                  
                => [ 'Tiername' => 'Wolf''Quantity' => 3'TimePeriod' => '2021-11''Dates' => [ => '2021-11-01'=> '2021-11-08'=> '2021-11-09', ],],
                  
                => [ 'Tiername' => 'Fuchs''Quantity' => 2'TimePeriod' => '2021-11''Dates' => [ => '2021-11-26'=> '2021-11-30', ],],
                  
                => [ 'Tiername' => 'Reh''Quantity' => 2'TimePeriod' => '2021-10''Dates' => [ => '2021-10-12'=> '2021-10-25', ],],
                ]; 
                Erläuterungen:
                Mit createFromCsvFile() bekommen wir ein 2-dimensionales Array mit den Schlüsseln Datum und Tiername. Die Option 'title' => true sorgt dafür das die erste Zeile der CSV-Datei als Schlüssel verwendet wird. Liegt die Datei als UTF-8 mit BOM vor dann wird der BOM vorab entfernt.

                Mit select werden neue Felder erzeugt: Quantity und TimePeriod, TimePeriod bekommt mittels interner DATEFORMAT-Funktion einen Inhalt wie '2021-11'. Diese Spalte benötigen wir zur Gruppierung.

                filterGroupAggregate gruppiert nun nach Tiername und TimePeriod und aggregiert in Quantity die Anzahl und in Dates die Einzelwerte als Array.Für ARRAY kann hier auch JSON oder CONCAT Sinn machen.

                filter mit einer eigenen Funktion sorgt dafür das nur Einträge bleiben deren Quantity >= 2 ist.

                Mit orderBy wird das ganze so sortiert das zuerst die höchsten Werte von Quantity auftauchen. Bei Gleichheit der Quantity sollen dann die neuesten TimePeriod Einträge zuerst erscheinen.

                fetchAll liefert alles was im select angegeben wurde dann als Array aus.




                Kommentar


                • #23
                  Zitat von jspit Beitrag anzeigen
                  Die Version 2.0 der Klasse bekommt jetzt doch einfache Aggregatefunktionen wie MIN,MAX,SUM und AVG. Diese Funktionen können auch in Verbindung mit einen 'GroupBy' genutzt werden können.
                  Die Realisierung erfolgt mit der universellen Filterfunktion filterGroupAggregate(). Abweichend von der bekannten SQL-Syntax wird hier als erster Parameter ein Array der Form ['feldname' => 'Aggregatfunktionen',..]
                  übergeben
                  Hey, erstmal vielen Dank das du die Aggregatefunktionen und auch Composer-Support noch mit eingebaut hast. Ich habe es heute in einer App von uns ausprobiert und bin über eine Kleinigkeit gestolpert.
                  Und zwar wurden bei mir zunächst die Felder nicht korrekt SUMmiert. Ursache war, dass ich select() mit Alias-Namen verwende und filterGroupAggregate() dann scheinbar auch diese ALIAS-Namen dort erwartet, statt den ursprünglichen Array-Keys.
                  Leider ist keine Exception geflogen; es wurde nur einfach aggregiert aber nicht summiert. Bei SQL nimmt man ja auch eher die ursprünglichen Feldnamen statt den Aliasen.
                  Vielleicht habe ich es aber auch einfach nur überlesen irgendwo in der Doku. Aber ansonsten ist die Library echt top

                  P.S: Oder liegt es vielleicht einfach nur an der Reihenfolge? Weil ich erst select() und dann filterGroupAggregate() aufrufe?
                  sorry, shift-taste kaputt

                  Kommentar


                  • #24
                    Danke erstmal für das Feedback.

                    select mit 'as newField' ist kein echter Alias, sondern erstellt nur eine zusätzliche Spalte (Kopie) mit den neuen Namen. Die 'alten' Feldnamen bleiben intern erhalten. Select kann hier auch mehrfach zur Anwendung kommen. Beispiel. select multiple times.

                    Mit fetchAll() werden aber nur die Felder ausgeliefert die im select auftauchen. Steht das select vor der Aggregatefunktion sollten dort auch die Aliasnamen benutzt werden. Ein Beispiel s. #22.

                    Soweit erstmal zur grundsätzlichen angedachten Funktionsweise. Werde in Kürze mal ein paar Test's mit den Aggregatemethoden in Verbindung mit select machen und dann berichten.

                    Edit: Meister1900 Denke die kurze Erklärung reichte dir schon, zumal du mit deinen Vermutungen nicht falsch gelegen hast. Bringe dennoch mal Beispiele dazu.:
                    PHP-Code:
                    $data = [ 
                      [
                    'Name' => 'Max''Punkte' => 3, ],
                      [
                    'Name' => 'Moritz''Punkte' => 4, ],
                      [
                    'Name' => 'Max''Punkte' => 1, ],
                      [
                    'Name' => 'Moritz''Punkte' => 2, ],
                    ];
                    $data TableArray::create($data)
                      ->
                    select('Name as Vorname,Punkte as Points')
                      ->
                    filterGroupAggregate(['Punkte' => 'SUM'],['Name'])
                      ->
                    fetchAll()
                    ;
                    /*
                    array (
                      0 => 
                      array (
                        'Vorname' => "Max",
                        'Points' => 3,
                      ),
                      1 => 
                      array (
                        'Vorname' => "Moritz",
                        'Points' => 4,
                      ),
                    )
                    */ 
                    Points enthalten die ersten Werte, aber nicht die Summe. Mit fetchRaw() anstelle von fetchAll() bekommen wir die internen Rohdaten angezeigt.
                    PHP-Code:
                    array (
                      
                    => 
                      array (
                        
                    'Name' => "Max",
                        
                    'Punkte' => 4,
                        
                    'Vorname' => "Max",
                        
                    'Points' => 3,
                      ),
                      
                    => 
                      array (
                        
                    'Name' => "Moritz",
                        
                    'Punkte' => 6,
                        
                    'Vorname' => "Moritz",
                        
                    'Points' => 4,
                      ),

                    Die gesuchte Summe ist nur in Punkte zu finden. Es muss also entweder für fetchAll nach einem select mit den as-werten gearbeitet werden:
                    PHP-Code:
                    $data TableArray::create($data)
                      ->
                    select('Name as Vorname,Punkte as Points')
                      ->
                    filterGroupAggregate(['Points' => 'SUM'],['Vorname'])
                      ->
                    fetchAll()

                    Result
                    PHP-Code:
                    array (
                      
                    => 
                      array (
                        
                    'Vorname' => "Max",
                        
                    'Points' => 4,
                      ),
                      
                    => 
                      array (
                        
                    'Vorname' => "Moritz",
                        
                    'Points' => 6,
                      ),

                    oder das selcect kommt nach dem ilterGroupAggregate:
                    PHP-Code:
                    $data TableArray::create($data)
                      ->
                    filterGroupAggregate(['Punkte' => 'SUM'],['Name'])
                      ->
                    select('Name as Vorname,Punkte as Points')
                      ->
                    fetchAll()

                    mit dem identischen Ergebnis.

                    Kommentar

                    Lädt...
                    X