Sophie: PHP Funktion mit unbekannten Werten

Hallo,

vielleicht könnt ihr mir weiterhelfen. Ich habe eine kleine PHP Funktion

 function zeiterfassung($mysqli, $name=false, $projektnummer=false, 
                                 $datum_von=false, $datum_bis=false) {
        
       $select = "SELECT id, code, projektnummer, name, datum, betreff, anzStunden, jahr, monat, tag
                  FROM zeiterfassung ";
        
        if ($projektnummer) {
                
                $stmt = $mysqli->prepare($select . " WHERE projektnummer =?" );
                $stmt->bind_param("s", $projektnummer);
                
        } else {
                $stmt = $mysqli->prepare($select);
        }

        if ($name) {
                
                $stmt = $mysqli->prepare($select . " WHERE name =?" );
                $stmt->bind_param("s", $name);
                
        } else {
            $stmt = $mysqli->prepare($select);
        }

        $stmt->execute();
        $stmt->bind_result($id, $code, $projektnummer, $name, $datum, $betreff, $anzStunden,
                           $jahr, $monat, $tag);
        $stmt->store_result();

Ich möchte gerne nach allem suchen können. Also entweder nach Namen, Projektnummer oder dem Datum, oder auch mal nach zwei Kombinationen gleichzeig also z.B. Name = Sophie Projektnummer A0101

Ich habe gehört, dass bind_param() immer alle Werte benötigt, die rein kommen und man die Funktion call_user_func_array nehmen muss. Allerdings verstehe ich nicht, wie ich diese auf meine Funktion anwenden soll.

Der Aufruf meiner Funktion

$name			      = NULL;
$projektnummer	= NULL;
$datum_von 		  = NULL;
$datum_bis 		  = NULL;

if(isset($_POST['abschicken'])){
	
	if(!empty($_POST['name'])) { $name = $_POST['_name']; }
	if(!empty($_POST['projektnummer'])) { $projektnummer = $_POST['projektnummer']; }
	if(!empty($_POST['datum_von'])) { $datum_von = $_POST['datum_von']; }
	if(!empty($_POST['datum_bis'])) { $datum_bis = $_POST['datum_bis']; }

}

$zeiterfassung_ausgabe = zeiterfassung($mysqli, $name, $projektnummer, $datum_von, $datum_bis);

akzeptierte Antworten

  1. Hello,

    eine Idee wäre es, deine SQL-Abfrage in der API so aufzubereiten, dass sie neutrale Werte erhält, wenn kein Wunschwert übergeben wurde.

    Neutraler Wert könnte z. B. NULL sein. Das hängt aber vom jeweiligen Spaltentyp ab. GGf müsstest Du das auch in der SQL-Abfrage selber durch ein if abfangen.

    Liebe Grüße
    Tom S.

    --
    Es gibt nichts Gutes, außer man tut es
    Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
  2. Hey,

    Nur eine kleine Änderung um etwas sauberer zu sein.

    (1 Minute später) Und dabei fällt mir dann auch grad das auf was du erreichen möchtest, so wie ich glaube.

    (noch 2 Minuten später) Wieso setzt du die Variablen $name, $projektnummer,... =false, da kommt doch in der Funktion nur False an Du brauchst dort die Variablen nicht auf False setzen oder tu das dierkt bei der initialisierung.

    Mit der geänderten Kontrollstruktur, ist es dann auch möglich, in einem Datumsbereich zu suchen. Könnte dann aber unübersichtlich werden.

     function zeiterfassung($mysqli, $name=false, $projektnummer=false, 
                                     $datum_von=false, $datum_bis=false) {
            
           $select = "SELECT id, code, projektnummer, name, datum, betreff, anzStunden, jahr, monat, tag
                      FROM zeiterfassung ";
            
            if ($projektnummer != false && $name != false) {
                    
                    $stmt = $mysqli->prepare($select . " WHERE projektnummer =? AND name=?" );
                    $stmt->bind_param("ss", $projektnummer, $name);
                    
            } elseif ($projektnummer != false) {
                    
                    $stmt = $mysqli->prepare($select . " WHERE projektnummer =?" );
                    $stmt->bind_param("s", $projektnummer);
                    
            } elseif ($name != false) {
                    
                    $stmt = $mysqli->prepare($select . " WHERE name =?" );
                    $stmt->bind_param("s", $name);
                    
            } else {
                $stmt = $mysqli->prepare($select);
            }
    
            $stmt->execute();
            $stmt->bind_result($id, $code, $projektnummer, $name, $datum, $betreff, $anzStunden,
                               $jahr, $monat, $tag);
            $stmt->store_result();
    

    Gruß
    Jo

    1. Hall und guten Abend @j4nk3y

      vielen vielen vielen lieben Dank für deine Hilfe. Du hast mir wirklich den Abend gerettet.
      Also haben mich andere angelogen die meinten, es geht nur mit der PHP Funktion call_user_func_array?

      Deine Änderungen funktionieren wunderbar. Jetzt habe ich noch eine Frage. Wenn ich nach dem Datum noch filtern möchte, dann muss ich diesen Blog kopieren und eben auf das Datum abändern?

      elseif ($usz_name != false) {
                      
        $stmt = $mysqli->prepare($select . " WHERE usz_name =?" );
        $stmt->bind_param("s", $usz_name);
                      
      }
      

      Das gleiche gilt dann für den Blog, bis richtig? Wenn ich von bis filtern möchte, dann muss ich einen weiteren Blog einfügen, richtig?

      Und jetzt treibe ich es noch ein Schritt weiter, wenn ich sage, ich möchte nach folgenden filtern:

      • Name
      • Von
      • Bis

      Oder

      • projektnummer
      • Bis

      Oder

      • projektnummer
      • von
      • bis

      Oder

      • Name
      • bis

      Das heißt also, ich muss für jede Option ein Blog einbauen, richtig?

      1. Hallo Sophie,

        Also haben mich andere angelogen die meinten, es geht nur mit der PHP Funktion call_user_func_array?

        Lügen bedeutet wissentlich die Unwahrheit sagen. Wenn jemand aus Unwissenheit etwas behauptet, das sich als unwahr erweist, lügt er nicht. Möglich auch, dass sein Kenntnisstand einfach veraltet ist. Etwa, wenn jemand sagt, dass es in unserem Sonnensystem 9 Planeten gibt.

        Bis demnächst
        Matthias

        --
        Rosen sind rot.
      2. Hey,

        vielen vielen vielen lieben Dank für deine Hilfe. Du hast mir wirklich den Abend gerettet.

        Gerne :)

        Also haben mich andere angelogen die meinten, es geht nur mit der PHP Funktion call_user_func_array?

        Keine Ahnung, ich hab die Funktion noch nie gebraucht/gesehen. Dementsprechend weiß ich nicht was diese macht oder wofür man diese brauchen könnte.

        Deine Änderungen funktionieren wunderbar. Jetzt habe ich noch eine Frage. Wenn ich nach dem Datum noch filtern möchte, dann muss ich diesen Blog kopieren und eben auf das Datum abändern?

        elseif ($usz_name != false) {
                        
          $stmt = $mysqli->prepare($select . " WHERE usz_name =?" );
          $stmt->bind_param("s", $usz_name);
                        
        }
        

        Das gleiche gilt dann für den Blog, bis richtig? Wenn ich von bis filtern möchte, dann muss ich einen weiteren Blog einfügen, richtig?

        Und jetzt treibe ich es noch ein Schritt weiter, wenn ich sage, ich möchte nach folgenden filtern:

        • Name
        • Von
        • Bis

        Oder

        • projektnummer
        • Bis

        Oder

        • projektnummer
        • von
        • bis

        Oder

        • Name
        • bis

        Das heißt also, ich muss für jede Option ein Blog einbauen, richtig?

        Richtig aber wie du merkst wird das recht komplex. Wie ich es auch gerade drehe und wende, finde ich keine elegante Lösung. Die Schwierigkeit liegt zum einen darin, dass du 4 Variablen hast, welche dann (wenn ich mich grad nicht irre) in 4! = 16 Möglichkeiten endet und darin, dass das Statement ein AND in der Bedingung braucht wenn es denn mehrere gibt. Sprich bei jeder Möglichkeit, außer bei keiner false und alle false, musst du prüfen ob davor schon eine wahr war um das entsprechende AND zu setzen.

        Eine Möglichkeit gäbe es vielleicht um starke Kopfschmerzen zu vermeiden. Du baust dir das Statement so wie du es brauchst und wenn die Variable false ist lässt du dir alles ausgeben mit Where xyz Like=%. Das kann man dann noch in eine abfrage bauen mit ((xyz == false) ? "Where xyz Like=%" : "Where xyz = ".$xyz ).

        FYI ((Abfrage) ? Ergebnis true : Ergebnis false)

        Und wo ich es gerad fertig geschrieben hab kommt mir wieder das AND in die Quere.

        Ich lasse mir das heute Nacht nochmal durch den Kopf gehen, vielleicht komme ich ja auf eine schicke Lösung.

        Gruß
        Jo

        1. Hallo j4nk3y,

          Die Schwierigkeit liegt zum einen darin, dass du 4 Variablen hast, welche dann (wenn ich mich grad nicht irre) in 4! = 16 Möglichkeiten endet

          Hier irrst du teilweise.
          4! = 1 × 2 × 3 × 4 = 24.
          Da es für 4 Variablen aber jeweils 2 Möglichkeiten gibt, sind es insgesamt
          2 × 2 × 2 × 2 = 2⁴ = 16 Möglichkeiten.

          Bis demnächst
          Matthias

          --
          Rosen sind rot.
          1. Hey,

            Die Schwierigkeit liegt zum einen darin, dass du 4 Variablen hast, welche dann (wenn ich mich grad nicht irre) in 4! = 16 Möglichkeiten endet

            Hier irrst du teilweise.
            4! = 1 × 2 × 3 × 4 = 24.
            Da es für 4 Variablen aber jeweils 2 Möglichkeiten gibt, sind es insgesamt
            2 × 2 × 2 × 2 = 2⁴ = 16 Möglichkeiten.

            Danke für die Korrektur, bin schon etwas müde und hab morgen eine Klausur und bin nicht ganz bei der Sache.

            Gruß
            Jo

            1. Hallo j4nk3y,

              bin schon etwas müde und hab morgen eine Klausur

              Viel Erfolg!

              Bis demnächst
              Matthias

              --
              Rosen sind rot.
    2. Tach!

      (noch 2 Minuten später) Wieso setzt du die Variablen $name, $projektnummer,... =false, da kommt doch in der Funktion nur False an Du brauchst dort die Variablen nicht auf False setzen oder tu das dierkt bei der initialisierung.

      Das sind nur Default-Werte, die genommen werden, wenn man weniger Wert an die Funktion übergibt, als sie eigentlich Parameter haben möchte. Wenn Werte übergeben werden, werden diese statt dem false in der Variable landen.

      Mit der geänderten Kontrollstruktur, ist es dann auch möglich, in einem Datumsbereich zu suchen. Könnte dann aber unübersichtlich werden.

      Eben, und deshalb war die Empfehlung mit dem call_user_func_array() schon nicht schlecht. Damit muss man nicht für jeden neuen Parameter die Struktur erweitern. Mit den steigenden Kombinationsmöglichkeiten wachsen dann auch die Verzweigungen, und das nicht linear.

      Allerdings ist auch eine Lösung damit nicht trivial. mysqli_stmt::bind_param() möchte Variablen per Referenz übergeben haben. Und das passt nicht so ganz zur Arbeitsweise von call_user_func_array(). Einfacher kommt man in einem solchen Fall mit PDO. Dessen PDOStatement::bindParam() lässt sich einzeln und damit auch problemlos in einer Schleife aufrufen. Alternativ kann man auch dem PDOStatement::execute() einfach ein Array übergeben.

      dedlfix.

      1. Hey,

        (noch 2 Minuten später) Wieso setzt du die Variablen $name, $projektnummer,... =false, da kommt doch in der Funktion nur False an Du brauchst dort die Variablen nicht auf False setzen oder tu das dierkt bei der initialisierung.

        Das sind nur Default-Werte, die genommen werden, wenn man weniger Wert an die Funktion übergibt, als sie eigentlich Parameter haben möchte. Wenn Werte übergeben werden, werden diese statt dem false in der Variable landen.

        Das ist mir bewusst aber sie werden ja in jedem Fall mit dem Wert NULL an die Funktion gegeben außer, sie werden durch den in $POST[] stehenden Wert überschrieben.

        Gruß
        Jo

  3. Ich würde die Filter-Logik von PHP komplett nach MySQL verfrachten.

    Du kannst in der WHERE-Klausel ausdrücken, dass du nur nach einem Namen filtern willst, wenn auch wirklich ein Name angebgen wurde. Das sähe dann so aus WHERE (? IS NULL OR name = ?). Bei bind_param müsstest du dann beide ? durch $name ersetzen lassen. Wenn $name === null ist, dann wird die Klausel zu WHERE (NULL IS NULL OR name = NULL), das heißt die Bedingung ist immer wahr und es wird kein Datensatz gefiltert. Wenn $name === 'Biff' ist, dann wird die Klausel zu WHERE ('Biff' IS NULL OR name = 'Biff') und der Filter ist aktiv. Der Filter lässt sich dann auch einfach kombinieren:

    function zeiterfassung($mysqli, $name = null, $projektnummer = null, $datum_von = null, $datum_bis = null) {
        $select = "
            SELECT id, code, projektnummer, name, datum, betreff, anzStunden, jahr, monat, tag
            FROM zeiterfassung
            WHERE (? IS NULL OR name = ?)
            AND   (? IS NULL OR projektnummer = ?)
            AND   (? IS NULL OR datum_von >= ?)
            AND   (? IS NULL OR datum_bis <= ?)";
        
        $query = $mysqli->prepare($select);
        $query->bind_param(
            'ssssiiii',
            $name, $name,
            $projektnummer, $projektnummer,
            $datum_von, $datum_von,
            $datum_bis, $datum_bis
        );
        // …
    }
    
    1. Hello,

      danke für dieses ausführliche Beispiel. (+1)

      Kommt gleich in meine Sammlung.

      Ich würde die Abfragewerte allerdings als Array übergeben. Damit lassen sich dann die Daten bequemer (als "Record") aus dem allgemeinen Programmfluss entnehmen.

      Liebe Grüße
      Tom S.

      --
      Es gibt nichts Gutes, außer man tut es
      Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
      1. Tach!

        Ich würde die Abfragewerte allerdings als Array übergeben. Damit lassen sich dann die Daten bequemer (als "Record") aus dem allgemeinen Programmfluss entnehmen.

        Dann musst du dir aber was ausdenken, wie du das Array an bind_param() übergibst, und das auch noch funktionierend, weil Referenzen beachtet werden müssen. Entweder war das die ziemlich gravierende Herausforderung oder beim Ergebnis-Binden. Ich hab das Wissen darüber schon wieder verdrängt. Lieber PDO nehmen, das ist deutlich verwenderfreundlicher bei Prepared Statements.

        dedlfix.