Hallo T-Rex,
das Problem ist, dass Klassenkonstanten statische Elemente sind, d.h. nur einmal pro Klasse existieren. Und sie werden festgelegt, wenn die Klasse deklariert wird.
Du möchtest aber eine ID, die einerseits in der Superklasse deklariert ist, andererseits aber pro abgeleiteter Klasse einen unterschiedlichen Wert hat. Das geht mit Klassenkonstanten nicht. In C++ könnte man möglicherweise mit Templates etwas erreichen - das hilft Dir in PHP natürlich nichts.
Ich sehe nur die Möglichkeit, zur Ausführungszeit etwas zu machen. Zur Deklarationszeit nicht. Um Schreibarbeit zu sparen, kann man einen Trait verwenden (das ist ein einbindbarer Baustein für Klassen, der statischen Eigenschaften, statische Methoden und Instanzmethoden enthalten darf). Da PHP auch keinen statischen Initializer kennt, muss das der Konstruktor der Objekte anstoßen. Man kann das aber an einer Stelle, in der Superklasse, erledigen.
trait ColumnNames
{
public static $ID;
public static $COLX;
public static $COLY;
public static function initColumnNames()
{
echo "Initialisiere " . get_class() . "\n";
$t = self::TABLENAME;
self::$ID = $t . "_ID";
self::$COLX = $t . "_COLX";
self::$COLY = $t . "_COLY";
}
}
class AbstractTable
{
public function __construct()
{
echo "Erzeuge " . get_called_class() . "\n";
if (method_exists($this, "initColumnNames") && !($this::$ID))
$this::initColumnNames();
// ODER - um bei vergessenem Trait hart zu crashen, als Einzeiler:
if (!($this::$ID)) $this::initColumnNames();
}
}
class FooTable extends AbstractTable
{
use ColumnNames; // Gimme the Trait!
const TABLENAME = "Foo";
}
class BarTable extends AbstractTable
{
use ColumnNames; // Gimme the Trait!
const TABLENAME = "Bar";
}
$f1 = new FooTable();
$f2 = new FooTable();
$b = new BarTable();
echo "ColumnNames: " . ColumnNames::$ID . "\n"; // Leer
echo "Foo: " . FooTable::$ID . "\n"; // Foo_ID
echo "Bar: " . BarTable::$ID . "\n"; // Bar_ID
Die eigentlichen Implementierungsklassen müssen lediglich den Trait einbinden und TABLENAME deklarieren. Die statischen Properties im Trait werden durch den use in der einbindenden Klasse angelegt, das ist Voraussetzung, damit diese Lösung funktioniert.
Den Rest übernehmen Trait und Superklasse. Ohne die Echos ist es eigentlich recht kompakt. Die Abfrage, ob die Initialisierung bereits ausgeführt wurde, mache ich im abstrakten Konstruktor, um bei vorhandener Initialisierung den Methodenaufruf in den Trait zu sparen.
Fragen? Anregungen? Vorschläge?
Rolf
sumpsi - posui - obstruxi