mit Sicherheit habe ich noch nicht alle Geheimnisse entdeckt und verstanden und vielleicht fehlt doch ein Stück Doku... (also nicht hauen ):
Ich habe eine Bitte: "Keep it simple" in der GenericDB... Ich habe danach gesucht, aber (noch) keinen Weg gefunden, mit der GenericDB "freie" Abfragen zu gestalten.
Vorschlag:
Ich würde mir entweder eine "FreeSelect"-Methode in der genericdb wünschen (na ja, oder so etwas wie die selectfrom-Methode aus meinem Thread zum Newsletter) oder eine FreeQuery-Methode, bei der man mit AddTable, AddWhere usw. die Elemente zusammenfügt, jedoch keine "Intelligenz" integriert ist (analog select-Methode); man muss die Tabellen und die PrimaryKeys der "fremden" Klassen halt selbst angeben. Bei AddWhere müsste die "Filz"-Funktion deaktivierbar sein.
Als Erläuterung ein (langes) Beispiel:
Tabelle M: Members, PrimaryKey: idM
Tabelle G: Groups, PrimaryKey: idG
Tabelle MG: Members in groups, PrimaryKey: idMG
Folgendes SQL-Statement soll erzeugt werden:
Code: Alles auswählen
"SELECT DISTINCT M.idM FROM M, G, MG WHERE
M.idclient = '".$client ."' AND M.idlang = '" . $lang . "' AND M.deactivated = '0' AND M.confirmed = '1' AND
M.idM = MG.idM AND MG.idG = G.idG AND G.defaultgroup = '1' AND G.idclient = '" . $client . "' AND G.idlang = '".$lang."'"
IMHO stehen zwei Abfrage-Methoden zur Verfügung: select und query.
SELECT:
Mit <CollectionClass>.Select kann ein fast beliebiges SQL-Statement übergeben werden. Automatisch (und darauf hat man IMHO keinen Einfluss) wird die ID der Klasse als Feld ausgewählt.
Aus <CollectionClass>.Select ("A = B","A") wird "SELECT <PrimaryKey> FROM <ClassTable> WHERE A = B ORDER BY A".
Mit Select ist jedoch kein DISTINCT oder eine Verknüpfung/Join von Tabellen möglich, da keine Möglichkeit besteht, Tabellen im FROM-Bereich anzugeben.
Für die Ausführung der Abfrage muss die <CollectionClass> geladen werden.
QUERY:
Die Funktion ist atemberaubend...Theoretisch ist damit eine flexible Abfrage möglich, aber...
So würde es funktionieren:
Am obigen Beispiel sind drei Tabellen beteiligt. Für jede Tabelle muss eine Collection-Class definiert werden (siehe z.B. class.frontend.groups.php und class.frontend.users.php).
Der Konstruktor jeder Klasse muss einen Verweis auf die ItemClass enthalten:
Code: Alles auswählen
class GCollection extends ItemCollection {
function GCollection()
{
global $cfg;
parent::ItemCollection($cfg["tab"]["some_groups"], "idg");
$this->_setItemClass("G");
Code: Alles auswählen
class MGCollection extends ItemCollection {
function MGCollection()
{
global $cfg;
parent::ItemCollection($cfg["tab"]["some_groupmembers"], "idmg");
$this->_setJoinPartner ('GCollection');
$this->_setJoinPartner ('MCollection');
$this->_setItemClass("MG");
Code: Alles auswählen
$groupmembers = new MGCollection;
$groupmembers->link('MCollection');
$groupmembers->link('GCollection');
$groupmembers->setWhere('MCollection.idclient',$client);
$groupmembers->setWhere('MCollection.idlang',$lang);
$groupmembers->setWhere('MCollection.deactivated','0');
$groupmembers->setWhere('MCollection.confirmed','1');
$groupmembers->setWhere('GCollection.defaultgroup','1');
$groupmembers->setWhere('GCollection.idclient',$client);
$groupmembers->setWhere('GCollection.idlang',$lang);
$groupmembers->query();
Code: Alles auswählen
"SELECT MCollection.idM, GCollection.idG, MGCollection.idMG FROM M AS MCollection, G AS GCollection, MG AS MGCollection WHERE
MCollection.idclient = '<$client>' AND MCollection.idlang = '<$lang>' AND
MCollection.deactivated = '0' AND MCollection.confirmed = '1' AND
MCollection.idM = MGCollection.idM AND MGCollection.idG = GCollection.idG AND
GCollection.defaultgroup = '1' AND GCollection.idclient = '<$client>' AND GCollection.idlang = '<$lang>'"
Man erhält also die PrimaryKeys von allen drei Tabellen. Außerdem handelt es sich um das MembersInGroup-Objekt und eigentlich sollte es ein Members-Objekt werden, damit man via $member->get("name"); darauf zugreifen könnte.
An sich kein Problem: Mit $member = $groupmembers->fetchObject("MCollection"); wird aus MCollection der PrimaryKey ermittelt und damit das Member-Objekt mit dem gewünschten Key zurückgegeben. Aber das kostet natürlich zusätzlich Zeit.
Damit die Abfrage ausgeführt werden kann, muss jede beteiligte Klasse geladen werden. Ein Distinct ist (bisher) nicht möglich. Eine Verwendung (ja, und das habe ich gerade gebraucht... ) des IN-Statements dürfte nicht möglich sein (mit $restriction = "'" . $this->_itemClassInstance->_inFilter($item["restriction"]) . "'"; werden in der genericdb die Einträge "gefilzt". Bei einem Konstrukt á la setWhere ("A","['a','b','c']","IN") für "WHERE A IN ['a','b','c']" würden IMHO die Hochkommata verloren gehen).
Meine Meinung:
Toller Code, gute Idee, aber das Ganze ist recht kompliziert und erleichtert wenig die Arbeit: Das SQL-Statement muss ich praktisch schon im Geiste konstruiert haben, sonst kann ich die Bedingungen und Verknüpfungen nicht richtig setzen.
Zusätzlich muss ich nun wissen, wie das System intern funktioniert, damit ich z.B. die Felder richtig benenne ($groupmembers->setWhere('GCollection.idlang',$lang); ). Jede beteiligte Klasse wird geladen. Die Flexibilität, die SQL bietet, wird u.U. begrenzt.
Ich komme an das gewünschte Element nur über eine weitere Zeile, die wieder kreuz und quer Informationen ermittelt, die der Programmierer schon hat (hier: Name des PrimaryKeys der Klasse).
Abfragen á la "SELECT M.idM FROM M, F WHERE M.email = F.email" sind IMHO nicht möglich, da es keine Klasse gibt, die M und F via "email" verbindet/verbinden kann (hey, da gibt es sicherlich einen Trick, den ich noch nicht gefunden habe...)
Mein Vorschlag? Siehe oben.
Jetzt hoffe ich, dass ich nicht zu viel Murks erzählt habe ...
Gruß
HerrB