PhpNeuling: PHP SQL, Änderungen nach UPDATE Befehl visualisieren

Hallo zusammen,

ich bin noch Anfänger im PHP und SQL, und komme wahrscheinlich aus diesem Grund nicht zur Lösung eines eigentlich einfachen Problems.

Ich habe eine Übersicht erstellt, die Namen und Standorte zusammen darstellt. Die Werte daraus kommen aus einer SQL Datenbank. Jeder Datensatz besteht aus: ID, Name, Standort1, ..., ..., ... Sofern ein Standort zum Namen passt, wird im HTML eine checkbox gesetzt und abgesendet. Die DB erhält dann am treffenden Punkt eine "1". Dadurch kann die Checkbox dann gesetzt oder ungesetzt dargestellt werden.

Funktioniert soweit alles ganz toll. 2 Sachen ärgern mich aber noch.

Ich habe über PHPMailer eine Email-Funktion eingebaut, die mir eine Mail schickt sobald ein user die Form abschickt. Dabei lasse ich mir user-ID und username mitschicken. Das klappt soweit auch gut. Jetzt will ich aber gerne noch wissen, welche Änderungen der user vorgenommen hat, da ich diese visualisierten Änderungen praktisch in einer Active Directory umsetzen muss.

Das heisst, dass ich von der DB erfahren müsste, wie der Stand der DB VOR dem Update war, und wie der aktuelle Stand NACH dem Update ist

Ich habe hin und her SELECTed und Variablisiert, aber ich bekomm's cognitiv nicht auf die Reihe eine sinnige Lösung dafür zu finden :-/

Habt ihr einen Tipp für mich?

Viele Grüße, und bleibt gesund

  1. Hallo PhpNeuling,

    Du könntest zusätzlich in hidden-Feldern die Werte speichern, die der Nutzer vorgelegt bekommt.

    Bis demnächst
    Matthias

    --
    Du kannst das Projekt SELFHTML unterstützen,
    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
    1. Hallo Matthias, danke für den Tipp. Das könnte gehen. Hidden-feld mit anderem name (old.feld1), dann über $_POST in neue Variable (old.Var1) im mailkontext dann IF VAR1 != old.Var1 { echo VAR1 }

      Ist jetzt sehr aufwändig, aber sollte gehen :)

      SQL technisch gibt es da nichts ? Da ich mir auch, beim Anlegen mehrerer Einträge gleichzeitig über eine Schleife, die jeweilige zuletzt eingetragene neue ID abspeichern kann hätte ich gedacht das es auch dafür eine Funktion gibt ? Beim Googlen stieß ich nur auf TRIGGER und so Zeug. Das ist aber für meine Tabelle unnötig

      Viele Grüße, und Danke :)

      1. Hallo PHP-Neuling,

        Hallo Matthias, danke für den Tipp. Das könnte gehen. Hidden-feld mit anderem name (old.feld1), dann über $_POST in neue Variable (old.Var1) im mailkontext dann IF VAR1 != old.Var1 { echo VAR1 }

        Ist jetzt sehr aufwändig, aber sollte gehen :)

        nein, nicht umkopieren. Es landen alle Felder im $_POST-array.

        Du könntest auch das Array strukturieren. Etwa Name[0] und Name[1] Dann wäre [0] immer das alte und [1] der neue Eintrag. Dann kannst du über alle $_POST-Einträge mit foreach iterieren und die Werte vergleichen.

        foreach ($_POST as var) :
          if (var[0] != var[1]) :
            // geänderter Wert
          endif;
        endforeach;
        

        UNGETESTET!

        Bis demnächst
        Matthias

        --
        Du kannst das Projekt SELFHTML unterstützen,
        indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
        1. hm, okay Ich versuche das mal zu verarbeiten ;)

          Danke !

        2. Hallo PHP-Neuling,

          Hallo Matthias, danke für den Tipp. Das könnte gehen. Hidden-feld mit anderem name (old.feld1), dann über $_POST in neue Variable (old.Var1) im mailkontext dann IF VAR1 != old.Var1 { echo VAR1 }

          Ist jetzt sehr aufwändig, aber sollte gehen :)

          nein, nicht umkopieren. Es landen alle Felder im $_POST-array.

          Du könntest auch das Array strukturieren. Etwa Name[0] und Name[1] Dann wäre [0] immer das alte und [1] der neue Eintrag. Dann kannst du über alle $_POST-Einträge mit foreach iterieren und die Werte vergleichen.

          foreach ($_POST as var) :
            if (var[0] != var[1]) :
              // geänderter Wert
            endif;
          endforeach;
          

          UNGETESTET!

          Bis demnächst
          Matthias

          Nein, ich komm leider nicht hin :-/ ich weiß nicht wie ich mir das array aus der DB strukturieren soll, ohne die Form abzuschicken. Auch wie ich an die Werte komme ohne die explizit nochmal zu kopieren schnalle ich nicht Ich komm nicht dahinter, wie du das meinst :(

          1. Hallo PHP-Neuling,

            wie erstellst du denn das Formular?

            Bis demnächst
            Matthias

            --
            Du kannst das Projekt SELFHTML unterstützen,
            indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
            1. Das Formular ist eine table, deren tr dann über eine while schleife aus der datenbank erstellt werden die erste TD ist ein input type text, welches den username enthält, alle weiteren TDs sind input type checkboxen, denen ich jeweils einen Namen gebe optisch sieht das so aus

              http://prntscr.com/uw22z3

              TH und usernames habe ich jetzt weg gelassen aufgrund von Datenschutztrallalla

  2. Hallo PhpNeuling,

    wenn ich den Workflow richtig verstehe, dann bekommt ein User einen bestimmten Stand der DB präsentiert, kann ihn ändern und schickt das Form ab. Dein PHP Script ändert die DB und schickt Dir die Mail. Und du änderst manuell das Active Directory.

    Hinweis 1: über die LDAP Schnittstelle sollten diese Änderungen auch mit PHP machbar sein. Das setzt natürlich entsprechende Qualität der Daten voraus. Und Vertrauen in die Änderer.

    Matthias' Hinweise beziehen sich darauf, wie du beim POST des Formulars den alten Stand in der DB bekommen kannst. Das geht mit hidden fields. Statt dessen könntest du auch die DB lesen und vergleichen; das setzt aber voraus, dass es keine parallelen Änderungen durch andere User geben kann.

    Die Frage, wie der Barbeiter der Mail an den alten Stand kommt, ist offen.

    Idee 1 - Trigger: Mit einem Trigger kannst du Änderungen protokollieren. Wenn du eine Änderungsmail bekommst, kannst du ins Protokoll schauen und siehst, was geändert wurde.

    Idee 2 - Mail: wenn du die Mail erzeugst, hast du den alten Stand verfügbar. Siehe oben. Du kannst in die Mail dann hineinschreiben, was geändert wurde, mit allen und neuem Wert. Ob eine Sicherung gegen gefälschte Mails nötig ist, musst du für dich bewerten.

    Vision: Statt einer Mail kannst du, entweder via PHP oder Trigger, auch einen DB Satz mit den Änderungen erzeugen (oder mehr als einen, deine Daten könnten Normalisierungsbedarf haben). Statt der Mail rufst du Änderungsprotokolleinträge auf. Du (als Mensch) prüfst sie, korrigierst eventuell, gibst frei und dein Admin-Script ändert via LDAP das AD.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hi Rolf,

      die direkte Automatisierung über LDAP ist unerwünscht. Die Personen, die die Tabelle bearbeiten sind keine Techniker, IT Menschen oder sonstwas in der Richtung. Sie melden mir nur neue Bedarfe. Aktuell geschieht das alles in Excel, was mich ziemlich ank**t.

      Daher kam mir die Idee einer Tabelle mit WebIf, da kann keiner was falsch machen, und ich werde direkt benachrichtigt wenn etwas geändert wird.

      Die Mail selbst dient mir dann später auch als Nachweis, was warum von wem geändert worden ist. Diese soll also archiviert werden, für mich als History

      Nur sehe ich jetzt irgendwie den Wald vor lauter Bäumen nicht.

      Von Triggern habe ich schon gelesen. Hierfür wird meist eine temporäre Tabelle erstellt, was ich mit meinem "app" user nicht darf. Aufgrund von Richtlinien komme ich da auch über das App nicht weiter

      Ich könnte aber direkt in der DB eine neue, feste DB anlegen. Der App user darf diese dann füllen und leeren

      Richtig nachvollziehen kann ich das aber leider nicht.

      Ich hatte ehrlich gesagt erwartet, dass das ganz einfach wird Stand JETZT und Stand NACH UPDATE irgendwie zu vergleichen Aber selbst die Array Idee mit den hidden Feldern ist bei mir noch nicht ganz angekommen 😕 Vielleicht auch weil Freitag ist

      Grüße, und vielen Dank für alles

      1. Ich hatte ehrlich gesagt erwartet, dass das ganz einfach wird Stand JETZT und Stand NACH UPDATE irgendwie zu vergleichen

        Meine Idee:

        Vor dem UPDATE liest du den Datensatz mit der betr. ID ($row_alt). Dann änderst du und die DB verrät, ob irgend etwas zu ändern war:

        $q = "
        UPDATE lieder
        SET
         titel    = '".$row_neu['titel']."'
        ,komponist= '".$row_neu['komponist']."'
        WHERE id  = $row_alt['id']
        ";
        $res = @mysql_query( $q, $conn_id );
        if ( @mysql_affected_rows( $conn_id ) == 1 ) {
          foreach( $row_neu AS $key => $val ) {
            if ( $val != $row_alt[$key] ) {
              echo $key." neu=[".$val."] alt=[" .$row_alt[$key]."]<br>";
            }
          }
        }
        

        NICHT GETESTET

        Um Werte setze ich grundsätzlich Klammern für die Anzeige. So wird ein leerer Wert erkennbar, aber auch führende oder folgende Leerstellen. Ganz gemeiner Trick, wenn ein Passwort etwa mit Leerstellen beginnt.

        Gruß, Linuchs

        1. Hallo Linuchs,

          Um Werte setze ich grundsätzlich Klammern für die Anzeige. So wird ein leerer Wert erkennbar, aber auch führende oder folgende Leerstellen. Ganz gemeiner Trick, wenn ein Passwort etwa mit Leerstellen beginnt.

          Das Speichern von Passwörtern im Klartext ist mindestens grob fahrlässig, wenn nicht gar ein Verstoß gegen die DSGVO.

          Bis demnächst
          Matthias

          --
          Du kannst das Projekt SELFHTML unterstützen,
          indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
      2. Hallo PHP-Neuling,

        okay. Also nochmal Schritt für Schritt. Das Posting ist lang, weil es für jeden Teil diverse Optionen gibt und ich mehrere beleuchten möchte.

        Du hast 2 Aufgaben:

        (1) Erkennen, was der Anwender geändert hat
        (2) Dem Mail-Bearbeiter mitteilen, was geändert wurde.

        Teil 1 hängt auch davon ab, wie komplex dein Form ist. Ich habe nicht ganz verstanden, wie dein Form mit den DB-Sätzen korreliert. „Sofern ein Standort zum Namen passt“ schreibst Du, und das klingt so als würdest Du beim Aufbau des Forms bereits ordentlich mit DB-Sätzen jonglieren. Sehe ich das richtig? Oder enthält ein HTML Form, das der Anwender bekommt, nur Daten aus einem einzigen DB Satz?

        Ist aber letztlich egal. Du hast bspw. 47 Checkboxen im Form, und die kann der Anwender verändern. Jede Checkbox hat einen Namen oder eine laufende Nummer. Bei laufenden Nummern ist es am einfachsten, bei Namen etwas schwieriger, aber in jedem Fall kannst Du den "Alt" Datenstand in einem einzigen String codieren.

        Bei nummerierten Checkboxen von 1-47 ist es ein String mit 47 Positionen, jede Position entspricht einer Checkbox.

        Bei Namen machst Du so etwas wie "name1:0;name2:1;name3:0;...". Das erzeugst Du mittels implode und nimmst es mit explode wieder auseinander.

        Nun kannst Du den Stand, den Du dem Anwender als HTML geschickt hast, beim POST wiederherstellen und mit den Werten in $_POST vergleichen.

        Für Teil 2 wäre es aus meiner Sicht am einfachsten, die ermittelten Änderungen einfach in die Mail hineinzuschreiben. Wenn Du dann die Mail bekommst, brauchst Du keinen alten und neuen Stand der Datenbank, um deine AD Aktivitäten zu bestimmen, sondern machst das einfach auf Grund der Mail.

        Wenn Du den alten und neuen Stand unbedingt in der Datenbank sehen willst, dann hast Du wieder mehrere Möglichkeiten.

        • Historisieren der id,name,standort... Tabelle, in dem Du weitere Spalten wie "Last Update" oder "Version" hinzufügst. Im Ergebnis hast Du dann für jeden Eintrag nicht nur den aktuellen Stand, sondern auch alle Stände davor. Die Datenmenge wächst dadurch stetig mit jedem Update an - du musst für Dich entscheiden, ob das sinnvoll ist. Um das zu bekommen, machst Du an Stelle eines UPDATE einen INSERT mit der neuen Version. Beim Auslesen musst Du dann natürlich dafür sorgen, dass Du immer nur den letzten Satz verwendest. Das kann man durch eine weitere Spalte "Gültig" beschleunigen; das setzt aber auch voraus, dass Du beim Update den geänderten Satz als Ungültig markierst - du machst dann also einen Update und einen Insert.

        • Erzeugen einer Altdaten-Tabelle. Die Tabelle, die Du jetzt hast, zeigt den gültigen Datenstand. Bevor Du einen Update machst, duplizierst Du diesen Stand zusammen mit dem Änderungstimestamp in eine zweite Tabelle. Das geht mit einem INSERT (...) SELECT (...).

        Historisieren benötigt keinen Trigger, das steuerst Du über PHP und es bedeutet etliche Änderungen in deinem PHP. Die Altdaten-Tabelle kannst Du in einem Trigger befüllen. Die Idee, dass ein Trigger nur mit Temp-Tabellen hantiert, mag einem merkwürdigen Beispiel geschuldet sein; mit der Realität hat sie nichts zu tun. Du machst einen AFTER UPDATE Trigger auf diese Tabelle, und verwendest die OLD-Version der geänderten Row, um die Werte in die Altdatentabelle zu INSERTen. Was es mit OLD und NEW auf sich hat, findest Du hier.

        Aber wie gesagt; das sollte man nur tun wenn man auf historische Daten in der DB angewiesen ist. So, wie Du deinen Workflow beschreibst, sollte es genügen, wenn die Änderungen mit altem und neuem Wert in der Mail dokumentiert sind.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hi,

          und vielen Dank nochmal für alles. Ihr seid wirklich super

          Trotz allem hin und her, und überlegen, und lesen, und versuchen klug zu sein, ich habs nicht geschnallt. Eines der Arrays war mir zu viel. Ich komme da einfach nicht dahinter.

          Aber ich habe einen Weg gefunden. Eigentlich sogar einen sehr einfachen 🤦

          Ich erstelle beim klick auf "speichern" einfach 2 statements zu erst einen SELECT um die aktuellen Daten der verwendeten UserID zu bekommen, und anschließend das Update.

          Nach erfolgtem Update wird eine mail.php aufgerufen. In dieser steht ein 2. SELECT, um die neuen Daten der verwendeten UserID zu bekommen.

          Nach einigem hin und her habe ich auch rausbekommen, wie ich mit Schleifen im $body des phpmailers arbeiten kann. Daraus bastle ich mir 2 Tabellen mit einmal dem ersten Statement (alte daten) und einmal dem neuen Statement (neue Daten).

          So habe ich visualisiert, was geändert wird. Meine Checkboxen werden als [X] und [] angezeigt. Das liest sich sehr gut, und kann so auch abgelegt werden

          Manchmal, da wirst'e irre mit dem ganzen Zeug 😁

          Eine Sache ist aber etwas nervig. Die Verarbeitung des phpmailers dauert ganz schön. So lange bleibt das Formular noch im Browser stehen, bevor es dann neu geladen wird. Kann ich die mail.php auch irgendwie im Hintergrund "requiren" ?

          Viele Grüße

          1. Hallo,

            Trotz allem hin und her, und überlegen, und lesen, und versuchen klug zu sein, ich habs nicht geschnallt.
            Eines der Arrays war mir zu viel. Ich komme da einfach nicht dahinter.

            wenn du genauer beschreiben könntest, wo es mit deinem Verständnis klemmt, könnte man vielleicht auch gezielt erklären, was dahintersteckt. Oft ist es einfacher, als es auf den ersten Blick aussieht.

            Ich erstelle beim klick auf "speichern" einfach 2 statements zu erst einen SELECT um die aktuellen Daten der verwendeten UserID zu bekommen, und anschließend das Update.

            Nach erfolgtem Update wird eine mail.php aufgerufen. In dieser steht ein 2. SELECT, um die neuen Daten der verwendeten UserID zu bekommen.

            Okay, das geht natürlich auch. Also alten Stand lesen, aktualisieren, dann den neuen Stand lesen. Das geht bei dir gefahrlos, weil die Datensätze - so habe ich es jedenfalls verstanden - Nutzer-bezogen sind. Wären es jedoch x-beliebige Datensätze, müsste man hier überlegen, was schlimmstenfalls passieren könnte, wenn zwei Zugriffe fast zeitgleich erfolgen und die Datenbank-Queries zeitlich verzahnt auftreten. Könnte das zu inkonsistenten Daten führen?

            Manchmal, da wirst'e irre mit dem ganzen Zeug 😁

            Niemand hat gesagt, das wäre alles trivial! 😉

            Eine Sache ist aber etwas nervig. Die Verarbeitung des phpmailers dauert ganz schön.

            Da wäre jetzt interessant, was der eigentlich alles tut. Ruft er am Ende nur die PHP-Funktion mail() auf? Das sollte dann zwar sehr schnell gehen, aber du hast dann auch keine Aussage darüber, ob der Versand wirkllich geklappt hat. Der Rückgabewert von mail() ist nur gleichbedeutend mit "Ich habe den Brief in den gelben Kasten eingeworfen". Oder Oder verhandelt dein phpmailer gleich mit dem zuständigen Mailserver? Das kann dann tatsächlich mal ein paar Sekunden dauern.

            So lange bleibt das Formular noch im Browser stehen, bevor es dann neu geladen wird. Kann ich die mail.php auch irgendwie im Hintergrund "requiren" ?

            Nein. Aber du könntest mit Javascript beim Absenden des Formulars einen Hinweis einblenden, sowas wie "Ihre Nachricht wird übermittelt, bitte haben Sie einen Moment Geduld". Und gleichzeitig den Absende-Button auf disabled setzen, damit Ungeduldige nicht gleich ein zweites Mal draufklicken.

            Live long and pros healthy,
             Martin

            --
            Paradox: Wieso heißen die Dinger Kühlkörper, obwohl sie höllisch heiß werden?
            1. Hallo Martin,

              erstmal Danke für dein Bemühen :)

              Also... das verwirrt mich nur noch mehr, wenn wir jetzt von klein auf Aufdröseln was ich alles nicht kapiere, und was erst recht nicht :-D

              Belassen wir's erstmal dabei. Irgendwann bin ich weit genug, schaue nochmal drüber und denke dann, "Ach, du Vogel du blöder ..."

              Zum versenden der Mail benutze ich die Klasse "PHPMailer\PHPMailer\PHPMailer;"

              https://github.com/PHPMailer/PHPMailer

              Und nicht die php mail() Funktion.

              Die Idee mit dem Javascript finde ich aber gut. Hinweis oder kleines Gif anzeigen, würde auch ausreichen. Jetzt komm ich natürlich wieder als Profi und frage, wie macht man das? :)

              1. okay habe mir mit einem jquery script beholfen :)

                https://www.jqueryscript.net/loading/Minimal-AJAX-Loading-Spinner-Plugin-with-jQuery-pleaseWait.html

                nun kringelt das schön nach abschicken des Formulars :)

                Das reicht so, dankeschön 😀