pl: Platzhalter in MySQL Funktionen

Soll:

function mysql_julianday($d,$m,$y, PDO $pdo){
    $sth = $pdo->prepare("select 1721060 + to_days('?-?-?') as jd");
    $sth->execute(array($y,$m,$d));
    $r = $sth->fetch();
    return $r['jd'];
}

Kommt aber nix dabei raus. Mit to_days('$y-$m-$d') funktioniert es aber ich hätt's halt lieber mit Platzhaltern.

Idee?

  1. Lieber pl,

    $sth = $pdo->prepare(
      "select 1721060 + to_days(':year-:month-:day') as jd"
    );
    
    $sth->execute(array(
      ':day' => $d,
      ':month' => $m,
      ':year' => $y
    ));
    

    Liebe Grüße

    Felix Riesterer

    1. Ne das wars nicht aber Du hat mich auf den richtigen Weg gebracht:

      function mysql_julianday($d,$m,$y, PDO $pdo){
          $sth = $pdo->prepare("select 1721060 + to_days(concat(?,'-',?,'-',?)) as jd");
          $sth->execute(array($y,$m,$d));
          $r = $sth->fetch();
          return $r['jd'];
      }
      
      

      Tada 😉

      1. Lieber pl,

        function mysql_julianday($d,$m,$y, PDO $pdo){
            $sth = $pdo->prepare("select 1721060 + to_days(concat(?,'-',?,'-',?)) as jd");
            $sth->execute(array($y,$m,$d));
        

        Du übergibst ein Datum also scheibchenweise in der Form Tag, Monat, Jahr? Dann solltest Du es aber in eine geeignete Schreibweise übersetzen, bevor Du das MySQL machen lässt. Wenn man den Code nach Monaten anschaut, wird man sich wieder erst zusammensuchen müssen, was denn da an Parametern warum an den MySQL-Server geht und was der daraus machen soll. Warum also nicht so, dass es auf einen Blick klar ist?

        $date = sprintf('%3$04d-%2$02d-%1$02d', $d, $m, $y)
        $sth = $pdo->prepare("select 1721060 + to_days(?)) as jd");
        $sth->execute(array($date));
        

        Liebe Grüße

        Felix Riesterer

        1. hi

          function mysql_julianday($d,$m,$y, PDO $pdo){
              $sth = $pdo->prepare("select 1721060 + to_days(concat(?,'-',?,'-',?)) as jd");
              $sth->execute(array($y,$m,$d));
          

          Du übergibst ein Datum also scheibchenweise in der Form Tag, Monat, Jahr? Dann solltest Du es aber in eine geeignete Schreibweise übersetzen, bevor Du das MySQL machen lässt. Wenn man den Code nach Monaten anschaut, wird man sich wieder erst zusammensuchen müssen, was denn da an Parametern warum an den MySQL-Server geht und was der daraus machen soll. Warum also nicht so, dass es auf einen Blick klar ist?

          $date = sprintf('%3$04d-%2$02d-%1$02d', $d, $m, $y)
          

          Was bitte soll denn daran klarer sein? Und warum bleibst Du nicht bei Deinem ersten Vorschlag mit den namentlichen Platzhaltern, guckmal:

          function mysql_julianday($d,$m,$y, PDO $pdo){
              $sth = $pdo->prepare("select 1721060 + to_days(concat(:year,'-',:month,'-',:day)) as jd");
              $sth->execute(array(
                  ':year' => $y,
                  ':month' => $m,
                  ':day' => $d
              ));
              $r = $sth->fetch();
              return $r['jd'];
          }
          

          Und das funktioniert bestens. MFG

          1. Hallo pl,

            okay - funktioniert. Ich wundere mich allerdings, dass man den Umweg über den SQL Server drehen muss. Hast Du schon mal nach einer PHP Funktion recherchiert, die das direkt kann? Ich finde auf einen schnellen Blick hin allerdings keinen Kandidaten...

            Rolf

            --
            sumpsi - posui - clusi
          2. Lieber pl,

            $date = sprintf('%3$04d-%2$02d-%1$02d', $d, $m, $y)
            

            Was bitte soll denn daran klarer sein?

            der Variablenname. Der Rest sieht zugegebenermaßen recht verschwurbelt aus.

            Und warum bleibst Du nicht bei Deinem ersten Vorschlag mit den namentlichen Platzhaltern, guckmal:

            Also gut:

            $date = date('Y-m-d', mktime(0,0,0, $m, $d, $y));
            

            $sth = $pdo->prepare("select 1721060 + to_days(concat(:year,'-',:month,'-',:day)) as jd"); [...]

            Und das funktioniert bestens.

            Funktioniert ist schön. Flexibel und unabhängig funktionieren, sowie später auf Anhieb wieder verstehen können, ist noch schöner.

            Ein jeder ist seines Glückes Schmied - oder seiner Hölle Architekt.

            Liebe Grüße

            Felix Riesterer

            1. Hallo Felix,

              Ein jeder ist seines Glückes Schmied - oder seiner Hölle Architekt

              PL Hell... kommt mir irgendwoher bekannt vor

              Rolf

              --
              sumpsi - posui - clusi
            2. Moin,

              die Lesbarkeit einfacher Platzhalter kann man recht einfach dadurch verbessern daß man alles schön untereinander schreibt:

              ?,    ?,    ?,    ?,    ?,        ?
              $foo, $bar, $baz, $boo, $vanille, $zitrone 
              

              MFG

              1. Lieber pl,

                die Lesbarkeit einfacher Platzhalter kann man recht einfach dadurch verbessern daß man alles schön untereinander schreibt:

                das ist ein Layout-Aspekt. Der ist auch wichtig. Mir ging es aber darum, die Funktionsweise des Codes so zu beschreiben, dass man das auch nach Monaten Abstand auf Anhieb noch versteht. Da ist das Layout (oder von mir aus auch coding style) sicherlich nicht unerheblich, aber noch viel wichtiger finde ich, dass klar wird, was welcher Abschnitt wie und warum tut.

                Du willst ein Tagesdatum in MySQL verwenden, das aus Tag, Monat und Jahr zusammengesetzt ist. Warum nur sollte man dafür eine concat-Funktion in SQL verwenden, anstatt das schon mundfertig in der Scriptsprache bereitzustellen? Deine Variablennamen $d, $m, $y kann man sicher schön untereinander layouten, aber wofür sollen sie stehen und warum? Eine Variable $date ist da viel aussagekräftiger - auch im richtigen Layout! Und wenn Du Deinen SQL-Code nachvollziehen willst, ist ein to_days(:date) sicherlich aussagekräftiger, als ein to_days(concat(:y,'-',:m,'-',:d))!

                Ich finde Deine Methode Moon::load in dieser Form ohnehin nicht clever gedacht:

                function load($d,$m,$y, PDO $pdo)

                Warum erwartest Du den Datumswert in seinen Einzelheiten (Tag, Monat und Jahr - und auch noch in dieser Reihenfolge?!?), anstatt als Datumswert (egal, ob ein DateTime-Objekt, UNIX-Timestamp oder Y-m-d-String)?

                Liebe Grüße

                Felix Riesterer

                1. hi Felix,

                  Ich finde Deine Methode Moon::load in dieser Form ohnehin nicht clever gedacht:

                  function load($d,$m,$y, PDO $pdo)

                  Warum erwartest Du den Datumswert in seinen Einzelheiten (Tag, Monat und Jahr - und auch noch in dieser Reihenfolge?!?),

                  Die Antwort ist ganz einfach und steht auch so in meinem Artikel: Weil die Klasse Moon von der Klasse Scaliger erbt und der Konstruktor dieser Elternklasse das Datum als Tag, Monat und Jahr als Einzelwerte erwartet.

                  anstatt als Datumswert (egal, ob ein DateTime-Objekt, UNIX-Timestamp oder Y-m-d-String)?

                  Mein Artikel zeigt die Anwendung der Methode fetchObject():

                  fetchObject('Moon', array($d,$m,$y));
                  #                   ^ Argumente für den Konstruktor
                  #            ^ Name der Klasse desen Instanz zu erstellen ist 
                  

                  Wir haben also 2 Dinge zu beachten, einmal die Argumentenliste des Konstruktors und zum Anderen die Parameter eines SQL-Statements innerhalb der Klassenerweiterung. Nach außen hin jedoch ist diese Klassenerweiterung transparent und bekommt somit Tag, Monat, Jahr als Einzelwerte übergeben.

                  Meine API orientiert sich also an der Elternklasse. In Perl übrigens auch:

                  my $moon = Moon->new( date => '9.2.2020' ) or die $@;
                  #                    gemäß Elternklasse 
                  

                  MFG

                  1. Lieber pl,

                    Weil die Klasse Moon von der Klasse Scaliger erbt und der Konstruktor dieser Elternklasse das Datum als Tag, Monat und Jahr als Einzelwerte erwartet.

                    finde ich (aus bereits angfeührten Gründen) nicht gut!

                    Nach außen hin jedoch ist diese Klassenerweiterung transparent und bekommt somit Tag, Monat, Jahr als Einzelwerte übergeben.

                    Gefällt mir nicht. Es gibt wesentlich effizientere Möglichkeiten Datumswerte zu übergeben.

                    Meine API orientiert sich also an der Elternklasse. In Perl übrigens auch:

                    my $moon = Moon->new( date => '9.2.2020' ) or die $@;
                    #                    gemäß Elternklasse 
                    

                    Aha! Hier verwendest Du einen vollständigen Datumsstring! Keine Einzelteile. Warum aber nur in aller Welt verwendest Du keine generische Formatierung, sondern einen Datumsstring gemäß einer locale wie z.B. in Deutschland?! Kennt Perl denn keine Datumsobjekte? Oder wenigstens Timestamps?

                    Liebe Grüße

                    Felix Riesterer

                    1. Moin,

                      in meinem PHP Artikel geht es um fetchObject() , Vererbung und um Transparenz. Der Artikel beschreibt, wie man infolge Vererbung ein Datumsobjekt direkt aus der Datenbak erstellt und dabei um die von der Elternklasse geerbten Eigenschaften erweitert.

                      Desweiteren zeige ich in meinem Artikel, wie man dassselbe in Perl realisiert und daß in Perl wie auch in PHP bei der Erstellung eines Datumobjekts der Konstruktor der Elternklasse aufgerufen wird damit die Eigenschaften der Elternklasse geerbt werden bevor neue Eigenschaften hinzugefügt werden. Das ist genau das was in PHP fetchObject() spontan tut.

                      Das Thema ist also in PHP sehr komplex, auch wenn es auf den ersten Blick einfach aussieht und hat einige Stolperfallen. Von daher beschreibe ich in meinem Artikel das Wesentliche worauf es bei der Verwendung von fetchObject() ankommt wenn Vererbung im Spiel ist.

                      Schönen Tag noch!

                      1. Tach!

                        Der Artikel beschreibt, wie man infolge Vererbung ein Datumsobjekt direkt aus der Datenbak erstellt und dabei um die von der Elternklasse geerbten Eigenschaften erweitert.

                        Wie kann ein Objekt Eigenschaften von der Elternklasse erben und gleichzeitig um selbige erweitert werden?

                        Desweiteren zeige ich in meinem Artikel, wie man dassselbe in Perl realisiert und daß in Perl wie auch in PHP bei der Erstellung eines Datumobjekts der Konstruktor der Elternklasse aufgerufen wird damit die Eigenschaften der Elternklasse geerbt werden bevor neue Eigenschaften hinzugefügt werden.

                        Auch ohne Konstruktor-Methode hat man in PHP die geerbten Eigenschaften zur Verfügung.

                        Das ist genau das was in PHP fetchObject() spontan tut.

                        Das ist nicht etwa programmiert, sondern passiert spontan aus einer Laune heraus ... ahja.

                        Das Thema ist also in PHP sehr komplex, auch wenn es auf den ersten Blick einfach aussieht und hat einige Stolperfallen. Von daher beschreibe ich in meinem Artikel das Wesentliche worauf es bei der Verwendung von fetchObject() ankommt wenn Vererbung im Spiel ist.

                        Die einzige Stolperstelle, die ich bei der Verwendung von PDOStatement::fetchObject sehe, ist die in der Dokumentation erwähnte und im ersten Kommentar hervorgehobene Eigenart dieser Methode, zuerst die Eigenschaften zu befüllen und danach den Konstruktor aufzurufen.

                        Vielleicht scheint dir die Sachlage komplex, weil du einige Dinge in deinem Artikel anders beschreibst, als sie sind.

                        dedlfix.

                        1. Auch ohne Konstruktor-Methode hat man in PHP die geerbten Eigenschaften zur Verfügung.

                          Sagen wirs mal so: Die geerbten Eigenschaften sind zwar vorhanden aber nicht mit den übergebenen Werten initialisiert. Demzufolge sind sie nutzlos. Also nicht verfügbar. Von daher muss man den Konstruktor der Elternklasse aufrufen, also einen eigenen Konstruktor definieren (siehe Artikel) wenn man auf die Spontanität von fetchObject() verzichtet.

                          die in der Dokumentation erwähnte und im ersten Kommentar hervorgehobene Eigenart dieser Methode, zuerst die Eigenschaften zu befüllen und danach den Konstruktor aufzurufen.

                          Die Reihenfolge ist egal wenn man's diskret macht, dasda:

                            $this->phase = $r['phase'];
                            $this->julianday = $r['julianday'];
                            parent::__construct($d,$m,$y);
                          # oder
                            parent::__construct($d,$m,$y);
                            $this->phase = $r['phase'];
                            $this->julianday = $r['julianday'];
                          

                          kommt auf dasselbe raus.

                          Man muss sich einfach mal richtig mit dem ganzen Thema befassen. MFG

                          1. Tach!

                            Auch ohne Konstruktor-Methode hat man in PHP die geerbten Eigenschaften zur Verfügung.

                            Sagen wirs mal so: Die geerbten Eigenschaften sind zwar vorhanden aber nicht mit den übergebenen Werten initialisiert. Demzufolge sind sie nutzlos. Also nicht verfügbar.

                            Es kommt darauf an, was man erreichen möchte. Von einem generellen "nutzlos" und "nicht verfügbar" kann keine Rede sein.

                            Von daher muss man den Konstruktor der Elternklasse aufrufen, also einen eigenen Konstruktor definieren (siehe Artikel) wenn man auf die Spontanität von fetchObject() verzichtet.

                            Das wird durch Wiederholung nicht besser, da läuft nichts spontan. Oder deine Definition von spontan ist eine andere.

                            Die Reihenfolge ist egal wenn man's diskret macht, dasda:

                              $this->phase = $r['phase'];
                              $this->julianday = $r['julianday'];
                              parent::__construct($d,$m,$y);
                            # oder
                              parent::__construct($d,$m,$y);
                              $this->phase = $r['phase'];
                              $this->julianday = $r['julianday'];
                            

                            kommt auf dasselbe raus.

                            Oder auch nicht, je nachdem, was der Konstruktor macht.

                            Man muss sich einfach mal richtig mit dem ganzen Thema befassen.

                            Danke für diese Binsenweisheit.

                            dedlfix.

                        2. Hallo dedlfix,

                          Die einzige Stolperstelle, die ich bei der Verwendung von PDOStatement::fetchObject sehe, ist die in der Dokumentation erwähnte und im ersten Kommentar hervorgehobene Eigenart dieser Methode, zuerst die Eigenschaften zu befüllen und danach den Konstruktor aufzurufen.

                          Da steht:

                          Clearly somebody thought they were being clever here - allowing you to access hydrated property-values from the constructor. Unfortunately, this is just not how OOP works - the constructor, by definition, is the first method called upon construction.

                          Der Terminus „hydrated“ für ein initialisiertes Feld ist mir auch noch nicht begegnet 😂. Aber der Autor übersieht, dass seine Regel nicht verbietet, dass vor Methodenaufrufen Werte an Felder zugewiesen werden können und dürfen.

                          // C#
                          public class Foo 
                          {
                             public int Bar = Program.GetBar();
                          
                             public Foo() 
                             {
                                Console.WriteLine($"Hier spricht Der Erbauer: {this.Bar}");
                             }
                          }
                          
                          class Program
                          {
                             public static int GetBar()
                             {
                                Console.WriteLine("Hier ist GetBar()");
                                return 47;
                             }
                             static int Main(string[] args)
                             {
                                var x = new Foo();
                             }
                          }
                          

                          Ausgabe ist:

                          Hier ist GetBar()
                          Hier spricht Der Erbauer: 47
                          

                          Und in PHP

                          class Thingy {
                             public $baz = 17;
                             public function __construct() {
                                echo "Your thingy is {$this->baz}\n";
                             }
                          }
                          new Thingy();
                          

                          Auch hier ist baz initialisiert bevor der Konstruktor anläuft.

                          Es ist üblich und erwartbar, dass Feldinitialisierer vor dem Konstruktor laufen. In einer Vererbungshierarchie folgt der C# Ablauf einem up-down Modell: Die Initialisierer werden bottom-up ausgeführt (Subklasse zuerst), die Konstruktoren top-down (Superklasse zuerst). Und sie dürfen sogar mehr tun als nur eine Konstante zuzuweisen. Ein solcher Feldinitialisierer darf nur keine Instanzmethode des erzeugten Objekts nutzen (oder um es in C# Speak zu sagen: CS0236: A field initializer cannot reference the non-static field, method, or property ...)

                          PHP lässt nur konstante Werte zu, von daher kann man nicht monitoren in welcher Reihenfolge die Initialisierung abläuft - es ist wegen der Beschränkung auf Konstanten aber auch wurscht.

                          Für den PDO Einsatz ist die Idee tatsächlich clever und mbMn auch sinvoll. Der Konstruktor kann genutzt werden, falls die geladenen Daten noch ergänzt werden sollen.

                          Diesen Ablauf mit PHP-Sprachmitteln nachzuahmen (d.h. erst Objekt erzeugen, dann ein paar Werte zuweisen und danach erst den Konstruktor laufen lassen) dürfte aber nicht gehen.

                          Rolf

                          --
                          sumpsi - posui - clusi
                          1. Tach!

                            Aber der Autor übersieht, dass seine Regel nicht verbietet, dass vor Methodenaufrufen Werte an Felder zugewiesen werden können und dürfen.

                            Konkret geht es hier um die Konstruktror-Methode. Und ja, man kann Werte vordefinieren. Und, wie der Autor schrieb, die Aufgabe des Konstruktors ist ebenfalls Initialisierung.

                            Für den PDO Einsatz ist die Idee tatsächlich clever und mbMn auch sinvoll. Der Konstruktor kann genutzt werden, falls die geladenen Daten noch ergänzt werden sollen.

                            Das ist dann aber keine Initialisierung mehr, sondern schon Datenverarbeitung. Also eigentlich keine Aufgabe für den Konstruktor.

                            Zudem ist das ein verdecktes Verhalten, wenn die Daten aus dem DBMS nachträglich verändert werden. An der Stelle des fetchObject-Aufrufs sieht man die Manipulation nicht, wenn man nicht weiß, was der Konstruktor macht.

                            Diesen Ablauf mit PHP-Sprachmitteln nachzuahmen (d.h. erst Objekt erzeugen, dann ein paar Werte zuweisen und danach erst den Konstruktor laufen lassen) dürfte aber nicht gehen.

                            Eben. Das ist ein abweichendes Verhalten vom Üblichen. Die Werte der Eigenschaften werden nicht von der Klasse initialisiert, sondern von außen gesetzt.

                            dedlfix.

                      2. Lieber pl,

                        in meinem PHP Artikel geht es um

                        das ist mir egal, worum es Dir in Deinem Artikel geht. Ich finde ihn reichlich wirr. Die Sache mit dem kaputten Datumswert bei der Parameterübergabe reicht mir schon, um ihn als nicht empfehlenswert oder gar don't do this at home, kids zu bewerten.

                        Liebe Grüße

                        Felix Riesterer

      2. Hallo pl,

        heißt also, Fragezeichenmarker in Stringliteralen werden von PDO nicht als Hostvariablen erkannt.

        Hätte man drauf kommen können… (man ≠ ich)

        Rolf

        --
        sumpsi - posui - clusi
      3. Und es funktioniert auch ohne die concat()Funktion. MFG