Einfache Vererbung und Objekterstellung mit Daten aus MySQL
Diese Sache hat es in sich, für mich der Anlaß für diesen Artikel. Worum es geht: In PHP ist es möglich, Objekte beim Initialisieren der Eigenschaften direkt beim Abholen der Daten aus der Datenbank zu erstellen. Für das Beispiel habe ich eine Tabelle mit Mondphasen, da gibt es die Felder julianday
und phase
. Ziel der Übung ist es, mit den beiden Werten ein Objekt zu initialisieren, das beispielsweise so aussieht:
Moon Object ( [julianday] => 2458889 [phase] => Full [JD] => 2458889 [DAY] => 9 [MONTH] => 2 [YEAR] => 2020 [EPOCH] => G [WDAY] => 7 [LEAP] => 1 [ISA] => userobject [KW] => 6 [DAYS_INMONTH] => 29 )
Wie zu sehen, sind da außer julianday
und phase
noch weitere Eigenschaften erwünscht, die sich aus dem Wert für julianday
ergeben. Von daher erbt unsere Klasse Moon
von der Klasse Scaliger
wo diese Eigenschaften für ein Datumobjekt berechnet werden. Werfen wir nun einen Blick auf den Code:
class Moon extends Scaliger{ # Zusätzliche Eigenschaften # für die Instanz public $julianday, $phase; # Klassenmethode zur Instanzerstellung function load($d,$m,$y, PDO $pdo){ $sth = $pdo->prepare(" SELECT julianday, phase FROM moon WHERE julianday = 1721060 + to_days(?'-'?'-'?) "); $sth->execute(array($y,$m,$d)); if( $m = $sth->fetchObject(__CLASS__, array($d,$m,$y) ) ) { return $m; } else{ throw new Exception("Can't create Moon-Object!"); } } } $pdo = pdo4base(); $m = Moon::load(9,2,2020, $pdo); # Kontrollausgabe print_r($m);
Beachte: Die Instanz wird hier nicht mit new
erstellt wird sondern durch den Aufruf einer Klassenmethode: $m = Moon::load();
Damit wird der Konstruktor umgangen, denn wir wollen ja die Instanz aus der Datenbank heraus erstellen. Die Klasse Moon
braucht keinen Konstruktor, um die Instanzerstellung einschließlich Aufruf des Konstruktors der Elternklasse kümmert sich das PDO-Objekt. Die der Instanz hinzuzufügenden Eigenschaften julianday
und phase
sind in der Subklasse also außerhalb des Konstruktors zu deklarieren, ansonsten werden sie nicht hinzugefügt.
Mit $sth->fetchObject(..)
nun, wird das Objekt als eine Instanz der Klasse Moon
erstellt, dazu werden der Name der Klasse in __CLASS__
und das Datum (Tag, Monat, Jahr) übergeben. Spontan wird der Konstruktor der Elternklasse aufgerufen womit die dort definierten und geerbten Eigenschaften der frischgebackenen Instanz hinzugefügt werden. Der Aufruf dieser Methode ist also äquivalent zu:
new Scaliger($day, $month, $year); # Elternklasse
Beachte auch: Wenn die Datenbankabfrage keine Daten liefert, wird kein Objekt erstellt! Das heißt, daß auf einen solchen Fall unbedingt geprüft werden muss.
Schauen wir nun was man tun muss, um das Objekt ohne $sth->fetchObject(..)
zu erstellen. Grundsätzlich muss der Konstruktor der Elternklasse aufgerufen werden damit die Eigenschaften initialisiert und geerbt werden. Vorher jedoch wird geprüft, ob die Abfrage auf die Mond-Tabelle für den betreffenden Tag einen Eintrag liefert. Ist das nicht der Fall, wird eine Exception geworfen. Sofern jedoch Daten vorliegen, werden julianday
und phase
als weitere Eigenschaften der Instanz hinzugefügt.
class Moon extends Scaliger{ public $julianday, $phase; function __construct($d,$m,$y, PDO $pdo){ $sth = $pdo->prepare(" SELECT julianday, phase FROM moon WHERE julianday = 1721060 + to_days(?'-'?'-'?) "); $sth->execute(array($y,$m,$d)); if( $r = $sth->fetch() ){ # rufe den Konstruktor der Elternklasse parent::__construct($d,$m,$y); # füge neue Eingenschaften hinzu $this->phase = $r['phase']; $this->julianday = $r['julianday']; } else{ throw new Exception("Can't create Moon-Object!"); } } } $pdo = pdo4base(); $m = new Moon(9,2,2020,$pdo);
Beachte: In diesem Fall also, sieht die API vor, das Datumsobjekt mit new
zu erzeugen. Hierzu muss ein Konstruktor definiert sein über den der Konstruktor der Elternklasse aufgerufen wird damit die Eigenschaften der Elternklasse geerbt und auch initialisiert werden. Beachte auch: Es ist unerheblich, ob der Vaterkonstruktor vor oder nach dem Hinzufügen der neuen Eigenschaften aufgerufen wird.
Der Vollständigkeit halber wollen wir das Ganze nun in Perl machen, also ein Objekt erstellen was in etwa so aussieht:
$VAR1 = bless( { 'age' => 'G', 'day' => '9', 'gregdate' => '9.2.2020', 'jd' => 2458889, 'julidate' => '27.1.2020', 'leap' => 'Y', 'month' => '2', 'phase' => 'Full', 'ultimo' => 29, 'wd' => 7, 'wochentag' => 'Sonntag', 'year' => '2020' }, 'Moon' );
Untenstehend die Package. Zunächst wird eine Instanz der Elternklasse erstellt, hierzu wird also der Konstruktor der Elternklasse aufgerufen. Als Nächstes werden für den betreffenden Tag gehörigen Daten aus der DB abgerufen wobei auch geprüft wird, ob es für den betreffenden Tag überhaupt Daten gibt. Sofern das der Fall ist, wird die Phase als eine neue Eigenschaft hinzugefügt und die Instanz zrückgegeben. Andernfalls wird mit die(..)
eine Exception geworfen.
package Moon{ use base qw(Scaliger); use dbh; sub new{ my $class = shift; # rufe den Konstruktor der Elternklasse my $self = $class->SUPER::new(@_); return eval{ my $dbh = $self->dbh(); my $m = dbh->selectrow_hashref(q( select julianday, phase from moon where julianday=? ), {}, $self->{jd}) || die "Das Objekt kann nicht erstellt werden!\n"; $self->{phase} = $m->{phase}; # Neue Eingenschaft hinzufügen $self; # Instanz zurückgeben } } }; my $moon = Moon->new( date => '9.2.2020' ) or die $@; # gemäß Elternklasse
Wenn ohnehin eine MySQL-Verbindung besteht, kann man die JulianDays auch so berechnen:
select 1721060 + to_days('2020-2-1');
Die Korrektur zu 1721060
Tagen ergibt sich dadurch, daß MySQL durchweg den Gregorianischen Kalender sowie das Jahr 0 annimmt.
Datenschutzerklärung: Diese Seite dient rein privaten Zwecken. Auf den für diese Domäne installierten Seiten werden grundsätzlich keine personenbezogenen Daten erhoben. Das Loggen der Zugriffe mit Ihrer Remote Adresse erfolgt beim Provider soweit das technisch erforderlich ist. sos@rolfrost.de.