Bernd: Einträge löschen

Hallo,

ist dieses der richtige Weg um zu prüfen ob dazugehörige Einträge vorhanden sind, die sich auf den zu löschenden Eintrag beziehen?

// Eine Rechnungsadresse löschen
if(isset($_GET["deleteRechnungsadresse"])){

	$stmt_kunde = $mysqli->prepare("SELECT count(*) AS Anzahl FROM ansprechpartner WHERE ap_rid=?");

	$stmt_kunde->bind_param("s", $_GET["deleteRechnungsadresse"]);
	$stmt_kunde->execute();
	$stmt_kunde->bind_result($Anzahl);
	$stmt_kunde->fetch();
	$stmt_kunde->close();

	if ($Anzahl == 0) {
		$delete = $_GET["deleteRechnungsadresse"];
		$stmt = $mysqli->prepare("DELETE FROM rechnungsadressen WHERE rs_id=?");
		$stmt->bind_param("s", $delete);
		$stmt->execute();
		$stmt->close();
		
		header("Location: kundendatenblatt.php?kunde=$kd_code");
	} else {
		$hinweis = false;
	}
}

if ($hinweis == false):
  <p>Rechnungsadresse kann nicht gelöscht werden, da dieser Ansprechpartner zugeordnet sind </p>
endif 
  1. Tach!

    ist dieses der richtige Weg um zu prüfen ob dazugehörige Einträge vorhanden sind, die sich auf den zu löschenden Eintrag beziehen?

    Man kann das mit einer vorherigen Abfrage tun. Man kann aber auch mittels korrekt gesetzter Fremdschlüssel die referenzielle Integrität in der Datenbank selbst sicherstellen. Dazu gibt es die Möglichkeit (ON DELETE ...), zu definieren was bei einem Löschen passieren soll: Mitlöschen der referenzierenden Datensätze oder Verweise auf NULL setzen oder verbieten. Letzteres gibt dann eine Fehlermeldung, auf die man reagieren kann.

    dedlfix.

  2. Hallo,

    du solltest kein GET verwenden, um Einträge zu löschen. Dafür ist POST gedacht (im Falle von Formular-Submits) oder DELETE o.ä., wenn du eine ReSTful-API schreibst.

    Außerdem ist deine id doch höchstwahrscheinlich ein integer und kein String. Daher kannst du bei bind_param auch "i" angeben.

    Zuguterletzt sehen wir nicht, wo die Variable $kdcode herkommt. Ggf. musst du hier noch URL-encoden.

    Viele Grüße Matti

    1. Tach!

      du solltest kein GET verwenden, um Einträge zu löschen. Dafür ist POST gedacht (im Falle von Formular-Submits) oder DELETE o.ä., wenn du eine ReSTful-API schreibst.

      Richtig, Bots folgen Links, die dann zu GET-Requests werden, und dann war es das mit den Datensätzen.

      Außerdem ist deine id doch höchstwahrscheinlich ein integer und kein String. Daher kannst du bei bind_param auch "i" angeben.

      In $_GET stehen nur Strings und gegebenenfalls Arrays. Die Bindung mit i vorzunehmen bringt keine Vorteile gegenüber s. Korrekterweise müsste man einen Typecast von String zu Integer durchführen, aber den wird PHP auch selbst vornehmen. Es gibt jedenfalls kein Problem, einer i-Bindung einen String zu übergeben, selbst wenn der keine gültige Zahl darstellt. Lässt man das bei s braucht es seitens PHP keine Anpassung, aber MySQL nimmt dann den Typecast vor. Bleibt sich also gleich, was man nimmt, und es ist kein Problem, statt i und d auch s beim Binden zu nehmen. Dass die Variablentypen überhaupt eine Rolle spielen, kommt von der C-Funktion, die dieser PHP-Funktion zugrunde liegt.

      dedlfix.

    2. Hallo,

      danke für den Hinweis, die Gefahr ist bei einem internen System zwar relativ gering dass ein Google und Co einem Link folgen der zum löschen führt. Aber ich bin ja immer für neues offen.

      Das würde ja bedeuten ich müsste für jeden Eintrag ein eigenes Formular basteln mit einem verstecktem Feld?

      Derzeit sieht das ganze bei mir so aus

      <?php 
      $FirmenRechnungsadressen = FirmenRechnungsadressen($mysqli, $_GET['kunde']);
      
      if($FirmenRechnungsadressen > 0) {?>
      
      <?php foreach($FirmenRechnungsadressen as $array){?>
      
      <a href="rechnungsadresse.php?code=<?php echo htmlspecialchars($array['rs_firmenid']); ?>"><img src="icons/edit42.png" width="16" height="16" alt="Rechnungsadresse bearbeiten" title="Rechnungsadresse bearbeiten" class="right_05">
       </a> 
      
      <a href="kundendatenblatt.php?kunde=<?php echo htmlspecialchars($array['rs_firmenid']); ?>&deleteRechnungsadresse=<?php echo htmlspecialchars($array['rs_id']); ?>" onclick="return confirm('Wollen Sie diese Rechnungsadresse wirklich löschen?')"><img src="icons/delete84.png" width="16" height="16" alt="Löschen" title="Rechnungsadresse löschen">
      </a>
      
      <?php echo htmlspecialchars($array['rs_firma']); ?>
      <?php if ($array['rs_abteilung'] != "") {?>
      	- <?php echo htmlspecialchars($array['rs_abteilung']); ?>
      <?php }?>
      <br>
      <?php } ?>
      <?php
      	}
      else { echo "Keine Ansprechpartner vorhanden"; }
      ?>
      
      1. danke für den Hinweis, die Gefahr ist bei einem internen System zwar relativ gering dass ein Google und Co einem Link folgen der zum löschen führt.

        Aus dem Email von 15:55 Uhr:

        <h1>Sicherheitsproblem</h1>
        
        <p>Sie sollten sich unverzüglich bei Ihrer FooBox <a href="https://foo.box/cgi/showConfig?item=Door">anmelden und nachschauen ob die Tür offen ist</a>.<p>
        

        Aus dem Email von 16:00 Uhr:

        <h1>Du denkst, Deine Frau geht zum Sport?</h1>
        <p>Bild 1: Deine Frau beim Fremdgehen.</p>
        <img src="https://foo.box/cgi/setConfig?item=OpenTheDoor" alt="Deine Frau beim Fremdgehen" />
        <p>Ich habe mehr davon.</p>
        

        Externe Inhalte anzeigen?

        Genau dieses Problem hatten zahlreiche DSL-Boxen. Man war angemeldet (erstes Mail) und sorgte unwissentlich dafür, dass auf der Box Aktionen angestoßen wurden (zweites Mail).

        In Deinem Fall könnte ein geschasster Mitarbeiter womöglich kostenlos einkaufem, weil er nach dem Versand den Verkaufsvorgang infolge dilettantischer Ausführung der Software deletieren kann...

        1. Für diese -1 will ich jetzt wirklich mal eine Begründung.

          Das Beispiel zeigt nämlich - wenngleich in einem Punkt etwas verkürzt - sehr gut, wie ganz ohne "Google und Co" Einbrüche in die Netzwerke mit bestimmten DSL-Boxen oder via IoT-Dingern erfolgten. Das nennt sich "Phishing".

      2. Hallo,

        Das würde ja bedeuten ich müsste für jeden Eintrag ein eigenes Formular basteln mit einem verstecktem Feld?

        nicht notwendigerweise. Wenn du als Parameter nur die ID der Rechnungsadresse brauchst und ansonsten kein Formular benötigt wird, kannst du stattdessen auch button-Elemente nehmen, und deren Values auf die ID der Rechnungsadresse setzen. Über den name kannst du die Operation bestimmen, z.B. "deleteRechnungsadresse".

        Wenn sich aber, wie unten gezeigt, auch die Kunden-ID ändern kann, ist es wohl sinnvoller, auf eher kleinteilige Formulare zu setzen.

        if($FirmenRechnungsadressen > 0) {?> <?php foreach($FirmenRechnungsadressen as $array){?>

        Auch wenn PHP das zulässt, würde ich dir empfehlen, den Vergleich per count zu machen (count($FirmenRechnungsadressen) > 0) - liest sich besser, und man muss sich über implizite Konvertierung keine Gedanken machen.

        <a href="rechnungsadresse.php?code=<?php echo htmlspecialchars($array['rs_firmenid']); ?>"><img src="icons/edit42.png" width="16" height="16" alt="Rechnungsadresse bearbeiten" title="Rechnungsadresse bearbeiten" class="right_05"> </a>

        right_05 ist kein ordentlicher Klassenname - was bedeutet 05, und was machst du, wenn es irgendwann nicht mehr rechts, sondern links sein soll? Nutze semantische Klassennamen. width/height gehört aus dem HTML raus und ins CSS rein.

        <a href="kundendatenblatt.php?kunde=<?php echo htmlspecialchars($array['rs_firmenid']); ?>&deleteRechnungsadresse=<?php echo htmlspecialchars($array['rs_id']); ?>" onclick="return confirm('Wollen Sie diese Rechnungsadresse wirklich löschen?')"><img src="icons/delete84.png" width="16" height="16" alt="Löschen" title="Rechnungsadresse löschen"> </a>

        Das &-Zeichen (hier: in der URL als Begrenzer vor deleteRechnungsadresse) gehört maskiert als &amp;

        Ansonsten koppelst du (für meinen Geschmack) Logik und Ausgabe zu sehr. Versuche das ein wenig zu trennen, indem du deine Business-Logik in eine separate Schicht verschiebst. Als Anfang hilft dir vielleicht Symfony versus Flat PHP im Abschnitt "Isolating the Presentation".

        Viele Grüße Matti

  3. Hallo Bernd,

    DELETE 
    FROM rechnungsadressen rs
    WHERE rs_id=?
      AND EXISTS(SELECT * FROM ansprechpartner ap WHERE ap_rid=rs_id)
    

    sollte das Problem mit einer Query lösen. Bitte in einer Test-DB ausprobieren!!!

    Dein Code hat aber ein schweres Problem. Mit einer fabrizierten URL kann man alles mögliche löschen, nicht nur die Rechnungsadressen, die gerade auf der angezeigten Seite verlinkt sind. Man findet sowas leider in allen möglichen Systemen, und es lädt zu bösartigem Schabernack ein. Wenn der User, der vor dem Bildschirm sitzt, Löschrecht für alle Adressen hat, ist es nicht so schlimm, aber wenn es ein Rechtekonzept gibt, dann musst Du vor dem Löschen prüfen, ob der aktive User das darf.

    Rolf

    --
    sumpsi - posui - clusi
    1. Hallo,

      wenn folgendes direkt in phpMyAdmin ausführe

      DELETE 
      FROM rechnungsadressen rs
      WHERE rs_id='17'
        AND EXISTS(SELECT * FROM ansprechpartner ap WHERE ap_rid=rs_id)
      

      erhalte ich folgenden Meldung

      #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'rs WHERE rs_id='16' AND EXISTS(SELECT * FROM ansprechpartner ap WHERE ap_rid' at line 2

      Wenn ich es leicht abändere

      DELETE 
      FROM rechnungsadressen
      WHERE rs_id='17'
        AND EXISTS(SELECT * FROM ansprechpartner ap WHERE ap_rid=rs_id)
      

      erhalte ich folgende Meldung

      0 Datensätze gelöscht. ( Die Abfrage dauerte 0.0002 Sekunden )

      Das würde passen, denn in diesem Fall gibt es in der anderen Tabelle zwei Einträge die mit diesem verknüpft sind.

      Wenn dieses stimmt, kann ich diese Meldung bei einem DELETE irgendwie abfangen um eine Meldung dem User anzeigen zu lassen?

      1. Hallo Bernd,

        nu bin ich platt - ich bin fest davon ausgegangen dass der Tablename im DELETE FROM einen Alias bekommen kann.

        Aber MS SQL kann's auch nicht. MySQL kann's ab Version 8.

        Wenn es zu einem Namenskonflikt käme, müsste man also ausfühlich den Tablename schreiben:

        DELETE 
        FROM rechnungsadressen
        WHERE rs_id='17'
          AND EXISTS(SELECT * FROM ansprechpartner ap WHERE ap_rid=rechnungsadressen.rs_id)
        

        Sowas blödes.

        Wenn Du ein Non-Query Statement (INSERT/DELETE/UPDATE) aus PHP absetzt, dann tust Du das mit $mysqli->query(), oder mit $mysqli->prepare und $stmt->execute().

        Im ersten Fall solltest Du mit $mysqli->affected_rows, im zweiten Fall mit $stmt->affected_rows die Anzahl der eingefügten/gelöschten/geänderten Sätze bekommen.

        Rolf

        --
        sumpsi - posui - clusi
  4. Rechnungsadressen löscht man nicht. MFG

    1. Tach!

      Rechnungsadressen löscht man nicht.

      Alle oder nur solche, die auch in Verwendung waren? Letzteres hat er ja vor zu verhindern.

      dedlfix.

      1. Rechnungsadressen werden überhaupt nicht gelöscht sondern archiviert und Punkt. Das hat mit Finanzamt, Haftung und Revision zu tun. MFG

        1. Tach!

          Rechnungsadressen werden überhaupt nicht gelöscht sondern archiviert und Punkt. Das hat mit Finanzamt, Haftung und Revision zu tun.

          Gut, in dem Fall sind Rechnungsadressen also Adressen, die für Rechnungen verwendet worden sind. Alle anderen sind nur normale Adressen und können gelöscht werden, auch wenn sie in der Tabelle für Rechnungsadressen abgelegt sind. Ich sehe also kein Problem in dem, was Bernd vorhat.

          dedlfix.

          1. Hallo dedlfix,

            Gut, in dem Fall sind Rechnungsadressen also Adressen, die für Rechnungen verwendet worden sind. Alle anderen sind nur normale Adressen und können gelöscht werden,

            Sollten. Im Sinne der Datensparsamkeit (DSGVO).

            Bis demnächst
            Matthias

            --
            Pantoffeltierchen haben keine Hobbys.
            ¯\_(ツ)_/¯
            1. Im Sinne der Datensparsamkeit (DSGVO).

              Genau! Man muss diese Sache nämlich genau andersherum anfassen! Solange es um die Datenerhebung (Stammdatenerfassung) geht, darf ein Kunde auch seine Daten bearbeiten oder löschen.

              Aber ab dem Zeitpunkt, ab dem es zu einem Kunden einen Geschäftsvorgang gibt, darf an den Stammdaten nichts mehr geändert werden, da wird ein Attribut gesetzt und fertig. MFG

              1. Tach!

                Genau! Man muss diese Sache nämlich genau andersherum anfassen! Solange es um die Datenerhebung (Stammdatenerfassung) geht, darf ein Kunde auch seine Daten bearbeiten oder löschen.

                Gut, dann darf man also entgegen deiner ersten Antwort doch Adressen in der Tabelle für Rechnungsadressen löschen.

                Aber ab dem Zeitpunkt, ab dem es zu einem Kunden einen Geschäftsvorgang gibt, darf an den Stammdaten nichts mehr geändert werden, da wird ein Attribut gesetzt und fertig.

                Und da gibt es ein Gesetz dafür, dass man da ein Flag setzen muss und es nicht ausreicht, zu prüfen ob es Datensätze gibt, die sich darauf beziehen?

                dedlfix.

                1. Ein "Flag" könnte dem Finanzamt womöglich nicht reichen.

                  Auf der sicheren Seite ist man mit dem Storage-Typ "archives".

                  Grund:

                  Characteristics

                  Supports INSERT and SELECT, but not DELETE, UPDATE or REPLACE.

                  Einfach eine Archiv-Tabelle für jedes Jahr oder meinetwegen jeden Monat anlegen und mit den "steuerlich relevanten Vorgängen" füttern. Nach Ablauf der Aufbewahrungsfrist kann man die ganzen Tabellen löschen.

          2. Wenn es zu einer Adresse noch keinen geschäftlichen Vorgang gibt, ist die Frage nach einer Integritätsprüfung völlig überflüssig! MFG

        2. Pl, sorry, du redest eine geballte scheiße da platzt mir echt der Kragen! Du weißt nicht was ich vorhabe und ob die Adressen überhaupt jemals für eine Rechnung gebraucht wurden daher ist deine Aussage einfach nur sinnlos und unnötig.

          Schau dir lexoffice, fastbill oder ‎SevDesk an, die alle samt zertifiziert sind und man überall Rechnungsadressen löschen kann, selbst in offline Software wie Wiso mein Büro kann ich Rechnungsadressen löschen.

          Hauptsache du hast mal wieder ein Kommentar abgegeben der einfach nur Müll ist. Vielleicht solltest du bei diesem schönen Wetter einfach mal raus gehen und eine Runde laufen um den Kopf frei zu bekommen.

          Ich werde auf deine Kommentare hier nicht weiter eingehen und werde mich auch nicht weiter dazu äußern.

          1. Ach komm wir wissen doch was Zertifikate wert sind und wie sie entstehen. Das ist doch wohl ein Witz in diesem Zusammenhang auf Zertifikate hinzuweisen. Im Übrigen ist es Dein Shop und wenn Du Dir unbedingt eine Abmahnung wegen einer unzulänglichen Buchhaltung einfangen willst, ich bin der Letzte der Dich daran hindert. Das ist alles nicht mein Problem. GGA