[gelöst] Node Reference/Back Reference über mehrere Content Typen
Eingetragen von Milchbar (39)
am 06.07.2010 - 18:19 Uhr in
am 06.07.2010 - 18:19 Uhr in
Hey zusammen,
ich bin mir nicht sicher ob der Weg den ich zur Zeit einschlage nicht eh schon der falsche ist. Deswegen hier ein kurzer Überblick über das was ich erreichen möchte.
- Ich habe 3 Inhaltstypen; Projekt, Kunde und Diensleistung
- Beim erstellen eines neuen Projektes kann ich aus vorhandenen Kunden und Diensleistungen (via Node Reference) eine Auswahl treffen und möchte folgende Referenzen speichern:
- neues Projekt: speichert Referenz zu ausgewähltem Kunde und ausgewählten Dienstleistungen
- ausgewählte/r Kunde/n: speichert Referenz zum erstellten Projekt und den darin ausgewählten Dienstleistungen
- ausgewählte Diensleistung/-en: speichert Referenz zum erstellten Projekt und den darin ausgewählten Kunden
Nun dachte ich mir das man das Ganze über das Modul "Back Reference"[1] lösen kann. Das funktioniert auch bedingt, allerdings scheinbar immer nur zwischen maximal 2 Nodes. Hat jemand schonmal ein ähnliches Problem gehabt oder zufällig einen Lösungsansatz mit dem man das komfortabel lösen kann?
Beste Grüße,
Milchbar
- Anmelden oder Registrieren um Kommentare zu schreiben
Du möchtest also ein neues
am 06.07.2010 - 22:31 Uhr
Du möchtest also ein neues Projekt "Projekt A" speichern und wählst dort z.B. die Nodes "Kunde 1" und "Dienstleistung 1" als Nodereference. Damit referenziert also "Projekt A" auf "Kunde 1" und "Dienstleistung 1". Gleichzeitig soll aber automatisch "Kunde 1" auf "Projekt A" und "Dienstleistung 1" referenzieren und "Dienstleistung 1" soll auf "Projekt A" und "Kunde 1" referenzieren, habe ich das richtig verstanden?
Dürfte sich eigentlich alles mit Rules machen lassen.
Projekt referenziert Kunde und Dienstleistung --> geschieht ja eigentlich automatisch.
Dann wählt man zweimal die Aktion "referenzierten Node laden" und wählt jeweils die beiden Referenz-Felder und benennt die entstehenden Objekte entsprechend.
Dann kann man doch einfach wählen, dass man ein CCK-Feld füllen will, wählt den ersten referenzierten Node und das Nodereference-Feld und trägt dort die Node-IDs der referenzierten Nodes ein.
Ist nur ein einfall und nicht getestet.
Edit:
Habe mir die Regeln usw. gerade mal angeschaut und das dürfte problemlos klappen, eben mit etwas PHP-Auswertung.
Hi Exterior, richtig, genau
am 07.07.2010 - 09:13 Uhr
Hi Exterior,
richtig, genau das möchte ich erreichen und genau das habe ich probiert. Allerdings habe ich hierbei das Problem das u.U. Mehrfach-Referenzen gesetzt werden. Beispiel:
Projekt A und Projekt B mit sich überschneidenden Dienstleistungen sind beide für Kunde A. Hier wird dann eine doppelte Referenz beim Kunden A gespeichert. Somit kann es passieren dass der Kunde x-mal die Dienstleistung A aufgeführt hat. Auch an PHP habe ich gedacht doch anscheinend den falschen Ansatz gehabt. Folgenden php-code habe ich zum füllen der Referenzen benutzt:
$a = $node_reference->field_services; // schauen ob Kunde bereits services eingetragen hat
$b = $node->field_services; // neue services die hinzugefügt werden
$result = array_merge($a, $b); // array_merge überschreibt doppelte einträge
return $result;
Das funktioniert aber nicht wie gewünscht. Das Hauptproblem sind nach wie vor die Duplikate. Zur Not muss ich wohl das Array Wert für Wert mit dem neuen vergleichen. Werde mir dann das ganze nochmal genauer anschauen.
-- EDIT:
So, habe das ganze nochmal neu aufgebaut. Interessanterweise werden Duplikate nun anscheinend nicht mehr angezeigt, aber das muss ich noch ausführlicher testen. Allerdings folgendes Problem: die Regel scheint nicht alle verknüpften Referenzen anzusprechen. Beispiel:
Wenn ich bei einem Projekt mehrere Diensleistungen auswähle, diese ausgewählten Diensleistungen dann mit Rules ansprechen möchte, speichert er die Änderungen nur in der *ersten* gewählten Diensleistung. Die anderen bleiben unberührt.
Hier meine aktuelle Rule.
ON event After saving new content
IF Created content is Project
DO
Load a referenced node // Kunden
Populate client referenced content's field 'field_clientprojects'
Populate client referenced content's field 'field_clientservices'
Load a referenced node // Dienstleistungen
Populate service referenced content's field 'field_serviceprojects'
Populate service referenced content's field 'field_serviceclients'
Populate service referenced content's field 'field_serviceclients' besitzt folgende PHP Anweisungen zum füllen:
$a = $node->field_projectclients + $service_referenced_node->field_serviceclients;
return $a;
Hat jemand Vorschläge? ^^
Mmmh, da fällt mir auf die
am 07.07.2010 - 23:33 Uhr
Mmmh, da fällt mir auf die Schnelle nur ein, als Rule-Aktion Custom-PHP-Code auszuführen. Habe jetzt bei Nodereference auch keine Funktion gefunden, die man einfach mal aufrufen könnte, um die Referencen einzutragen.
In dem PHP-Code könntest du folgendes machen:
--> den höchsten Delta-Wert aus der Tabelle des Referenz-Feldes für diesen Node suchen und speichern + inkrementieren
--> mit foreach() durch das Array $node->field_reference_feld wandern
Und im foreach-Abschnitt folgendes machen:
-->per SQL-Abfrage die IDs des aktuellen Nodes sowie des aktuellen Reference-Nodes und den Delta-Wert eintragen
--> Delta inkrementieren
Könnte dann so aussehen:
<?php
$reference = $node->nid;
//mit foreach() durch array wandern
foreach ($node->field_reference_feld as $key => $value){
$node_id = $value['nid'];
//höchstes Delta des Nodes ermitteln
$sql = "SELECT MAX(delta) FROM {content_field_reference_feld} WHERE vid = %d;";
$delta = db_result(db_query($sql, $node_id));
//Delta inkrementieren
$delta++;
//Aktuellen Referenz-Node eintragen
$sql = "INSERT INTO content_field_reference_feld (vid, nid, delta, field_reference_feld_nid) VALUES (%d, %d, %d, %d);";
$result = db_query($sql, $reference, $reference, $delta, $node_id);
}
?>
Damit würden alle Einträge im Node-Reference-Feld in die entsprechende Tabelle eingetragen. Man könnte natürlich noch prüfen, ob in der Tabelle schon ein Eintrag mit dieser Reference-ID existiert und nur den Datensatz eintragen, wenn dies nicht zutrifft usw.
Das dürfte zwar im Grunde funktionieren, aber leider gibt's da ein Problem: Ich habe keine Ahnung, was passiert wenn das Script gleichzeitig von zwei Leuten ausgeführt wird. Denn dann könnte sich der Delta-Wert der beiden überschneiden, ich hoffe du weißt, was ich meine.
Hat vielleicht einer von den erfahreneren eine Lösung dafür?
Den PHP-Code müsste man natürlich noch anpassen etc, aber so könnte es schonmal klappen.
VBO?
am 09.07.2010 - 06:43 Uhr
Ich nochmal. Nachdem ich mir gestern das Beispiel von Exterior angeschaut hab und noch das ein oder andere Forum durchwühlt habe bin ich auf "Views Bulk Operations (VBO)" [1] gestoßen. Hat hier schonmal einer mit gearbeitet? Ich versuche über den vormittag mal das Problem damit [2] zu lösen, hab aber mit VBO noch nie gearbeitet. Daher bin ich für weitere Anregungen dankbar :) - nicht das ich hier den heiligen Gral vermute, der sich als Pappbecher entpuppt.
[1] http://drupal.org/project/views_bulk_operations
[2] http://thereisamoduleforthat.com/content/event-driven-bulk-updates-using...
VBO funktioniert im Grunde
am 09.07.2010 - 12:47 Uhr
VBO funktioniert im Grunde ganz gut, soweit ich das beurteilen kann.
Aber ich habe keine Ahnung, ob du damit dieses Problem lösen kannst, musst du mal probieren.
Ich glaube, ich könnte eine
am 09.07.2010 - 19:34 Uhr
Ich glaube, ich könnte eine Möglichkeit gefunden haben:
Zuerst erstellst du dir ein neues RegelSet, nennen wir es mal "Regelset Backreference". Als "maschinenlesbaren Namen" geben wir auch einfach "backreference" ein (daraus ergibt sich, dass das Regelset den Namen rule_backreference erhält).
Außerdem geben wir dem Regelset zwei Argumente mit auf den Weg, beide vom Typ "Inhalt". Das eine bekommt die Bezeichnung "Referenzierter Node" und den Namen "referenced_node". Das andere Argument bezeichnen wir mit "Erstellter Inhalt" und benennen es mit "node_added".
Darin erstellen wir zwei Regeln.
Regel 1:
Bekommt als Bedingung, dass der Inhalt "Referenzierter Node" vom Typ Kunde ist.
Die Regel bekommt 2 Aktionen:
"Ein CCK-Feld füllen" --> als Inhalt wird der Referenzierte Node genommen, als Feld das Node-Reference-Feld für Projekte, das heißt bei mir gerade field_projekt_referenz.
Den Wert legen wir mit PHP fest und tragen folgendes ein:
<?php
return array(
0 => array('nid' => $node_added->nid),
);
?>
Als 2. Aktion nehmen wir nochmal "Ein CCK-Feld" füllen, wieder vom Referenzierten Node (welcher in dieser Regel vom Typ Kunde ist) und nehmen das Node-Reference-Feld für die Dienstleistung (field_dienstleistung_referenz).
Auch dort legen wir den Wert mit PHP fest und tragen da folgendes ein:
<?php
$werte = array(); $i = 0;
foreach ($node_added->field_dienstleistung_referenz as $row) {
$id = $row['nid'];
$werte[$i] = array('nid' => $id);
$i++;
}
return $werte;
?>
Das war die erste Regel.
Die zweite sieht fast genauso aus. Man prüft als Bedingung ob der Referenzierte Node vom Typ Dienstleistung ist.
Die erste Aktion trägt wieder die Projekt-Referenz ein.
Als zweite Aktion in dieser Regel füllen wir wieder ein CCK-Feld des Referenzierten Nodes, allerdings diesmal field_kunde_referenz und tragen folgendes ein:
<?php
$werte = array(); $i = 0;
foreach ($node_added->field_kunde_referenz as $row) {
$id = $row['nid'];
$werte[$i] = array('nid' => $id);
$i++;
}
return $werte;
?>
Damit ist das Regelset fertig. Jetzt legen wir noch eine Reaktive Regel an, als Ereignis nehmen wir "Nach dem Speichern von neuem Inhalt". Als Bedingung prüfen wir, dass der neue Inhalt vom Typ "Projekt" ist.
Als Aktion führen wir Custom PHP aus und tragen folgenden PHP-Code ein:
<?php
//Backreference für alle referenzierten Dienstleistungen ausführen
foreach ($node->field_dienstleistung_referenz as $r) {
$id = $r['nid']; $n = node_load($id);
//Führt das Regelset aus und übergibt hinten die beiden Node-Objekte als Argumente
rules_invoke_rule_set('rules_backreference', array('referenced_node' => $n, 'node_added' => $node));
}
//Backreference für alle referenzierten Kunden ausführen
foreach ($node->field_kunde_referenz as $r) {
$id = $r['nid']; $n = node_load($id);
//Führt das Regelset aus und übergibt hinten die beiden Node-Objekte als Argumente
rules_invoke_rule_set('rules_backreference', array('referenced_node' => $n, 'node_added' => $node));
}
?>
Das war's, wenn man jetzt einen Node A vom Typ "Projekt" erstellt, dann bekommen alle referenzierten Dienstleistungen das Projekt A sowie die darin referenzierten Kunden als Referenz und genauso verhält es sich mit allen referenzierten Kunden. In dem PHP-Code des Regelsets kann man dann noch Anpassungen vornehmen, um bestehende Inhalte beizubehalten und Doppel-Einträge zu vermeiden, sollte keine große Sache sein.
Hab's gerade getestet und es scheint ordentlich zu funktionieren.
Respekt! ;)
am 09.07.2010 - 20:07 Uhr
Hab Deinen Beitrag nur kurz überflogen und werde es später am Abend noch testen. Melde mich dann hier nochmal, aber das sieht wirklich danach aus, als ob das die Lösung ist. Vielen Dank für die Mühe das selbst zu testen und hier noch so einen langen Artikel zu schreiben!
--edit: Konnte doch nicht mehr abwarten und hab es direkt probiert ;) Es funktioniert einwandfrei! Nochmals besten Dank!
Schön dass es klappt :) Dann
am 09.07.2010 - 20:34 Uhr
Schön dass es klappt :)
Dann noch oben [gelöst] hin und alles ist wunderbar ^^
Zu früh gefreut...
am 11.07.2010 - 09:42 Uhr
leider ist das Problem nicht wirklich gelöst. Wenn man nur ein Projekt erstellt dann funktioniert es superb. Sobald ich aber ein zweites Projekt mit exakt denselben Eigenschaften(Diensleistung und Kunde) erstelle werden die Verknüpfungen (sprich: Node-Referenzen) überschrieben. Zum Anhängen von neuen Nodes habe ich die erste Aktion von
<?php
return array(
0 => array('nid' => $node_added->nid),
);
?>
zu
<?php
$old = $referenced_node->field_clientprojects
$new = array(0 => array('nid' => $node_added->nid));
$merged = $old + $new;
return $merged;
?>
Das funktioniert weil hier eben immer nur 1 Projekt (das gerade erstellte) hinzugefügt wird. Für die 2. Aktion (s.o. Beitrag von Exterior) schaut das aber anders aus. Erstelle ich beispielsweise 2 verschieden Projekte mit denselben Kunden und denselben Diensleistungen, dann taucht in den entsprechenden Dienstleistungen und Kunden nur das zuletzt erstellte Projekt auf.
Ich habe probiert erst das ursprüngliche Node-Reference-Array auszulesen und dann das neue anzufügen. Problerm hierbei: es tauchen Duplikate auf und das ganze wirkt irgendwie nicht sonderlich "stabil".
Gelöst
am 11.07.2010 - 12:16 Uhr
Habs jetzt einfach mit ner 2. foreach gelöst und eine funktion zum entfernen von duplikaten in multidimensionalen arrays hinzugefügt.
Die Funktion kommt als "Custom PHP Code" in die erstellte "Triggered Rule". Somit sieht der Code folgendermaßen aus:
<?php
// super_unique: findet und entfernt Duplikate in einem mehrdimenionsalen Array
// via: http://www.php.net/manual/de/function.array-unique.php#97285
function super_unique($array){
$result = array_map("unserialize", array_unique(array_map("serialize", $array)));
foreach ($result as $key => $value) {
if(is_array($value)){
$result[$key] = super_unique($value);
}
}
return $result;
}
//Backreference für alle referenzierten Dienstleistungen ausführen
foreach ($node->field_projectservices as $r) {
$id = $r['nid']; $n = node_load($id);
//[...]
?>
Die Aktion im RegelSet wird folgendermaßen angepasst:
<?php
$new= array(); $i = 0;
// Bereits eingetragene nid des referenzierten Nodes auslesen...
foreach ($referenced_node->field_serviceclients as $row){
$id = $row['nid'];
$new[$i] = array('nid' => $id);
$i++;
}
//... und die neue(/n) nid hinzufügen
foreach ($node_added->field_projectclients as $row) {
$id = $row['nid'];
$new[$i] = array('nid' => $id);
$i++;
}
// Zum Schluss via super_unique alle Duplikate entfernen
return super_unique($new);
?>
Bis jetzt scheint es problemlos zu funktionieren. Ich werde noch ein paar Tests laufen lassen und ggfs. den Thread wieder öffnen, sollte es doch noch zu Problemen kommen.