[gelöst] formular on the fly

am 02.07.2010 - 23:44 Uhr in
Hallo an alle Drupal-Profis!
Ich arbeite gerade an meinem 1. Drupal-Projekt und bin echt von der Genialität dieses CMS/Frameworks begeistert!
Jetzt stehe ich leider vor einem Problem und hoffe, dass mir hier jemand (nach lange Suche und ausprobieren) helfen kann:
Ich benötige für einen Reiseveranstalter, der über 50 verschiedene Arrangements anbietet, Buchungsformulare.
Problem: fast jedes Arrangement hat andere Buchungsoptionen und buchbare Einzel-Leistungen.
Um nicht 50 einzelne Formulare erstellen zu müssen (das wäre der blanke Wahnsinn), möchte ich die Felder dynamisch, je nach Auswahl des Kunden (= klick auf einen Link "dieses Arrangement buchen"), generieren.
Wie gehe ich das an?
Ich bin euch für eine schnelle Antwort und jeden Lösungsansatz dankbar!!!
Viele Grüße!
Kelly
- Anmelden oder Registrieren um Kommentare zu schreiben
Hast du evtl. ein Beispiel
am 03.07.2010 - 00:45 Uhr
Hast du evtl. ein Beispiel (am Besten in Form einer Website oder eines Screenshots), wie so ein Formular für ein Arrangement aussehen soll und wie die Oberfläche aussehen soll, auf der die User auswählen können?
hier 2 screenshots
am 03.07.2010 - 01:25 Uhr
Hallo!
Beim Klick auf den Button "... zur Anfrage/Buchung" soll der User zu diesem speziellen Formular gelangen.
Vielen Dank für die schnelle Antwort!
Ausgewähltes Arrangement:

form:

Die Frage ist allerdings,
am 03.07.2010 - 01:44 Uhr
Die Frage ist allerdings, woher Drupal wissen soll, wie das Formular auszusehen hat. Zwar könnte man bei einem Klick auf den Link ein spezielles Formular ausgeben aber woher nimmt Drupal die Daten dafür?
Denn auf irgendeiner Basis muss das Formular schließlich erzeugt werden. Also muss wahrscheinlich trotzdem für jedes Formular irgendwo die Info gespeichert sein, wie es aufgebaut sein soll.
Beispiel:
Mal angenommen Arrangement 1 hat bei "Optionen: Allgemeine Leistungen" die 6 Checkboxen aus dem Screenshot oben und Arrangement 2 hat nur die oberen beiden und 4 andere Checkboxen an dieser Stelle. Aufgrund welcher Information soll Drupal jetzt entscheiden, welche Optionen ausgegeben werden müssen? Ja klar, anhand der Information, dass es Arrangement 1 oder 2 ist. Aber Drupal benötigt ja dennoch die Informationen, was gemacht werden soll, wenn es sich um Arrangement 1, 2 oder 45 handelt, diese Infos kann es sich ja nicht denken, die müssen zur Verfügung gestellt werden.
Welche Inhalte aus dem Screenshot oben können denn in einem anderen Arrangement anders ausfallen?
Meine 1. Idee
am 03.07.2010 - 02:07 Uhr
... war:
1. Ein Standard-Formular zu erstellen, das alle Felder beinhaltet:
2. Beim Klick auf "Anfrage..." ein Modul aufrufen, das die einzelnen Felder über irgendwelche ID's ein- bzw. ausblendet
und das Formular anzeigt.
Mein Problem war:
- wie übergebe ich - über den Link "Anfrage.." die Parameter mit den einzelnen Feldern (ID's) an das Modul???
Welche Inhalte aus dem Screenshot oben können denn in einem anderen Arrangement anders ausfallen?
Das sind die Bereiche: "Bitte wählen Sie" und "Optionen"
Die Form geht noch ewig weiter nach unten.
Noch eine andere Idee:
gibt es die Möglichkeit die einzelnen Formularfelder in der Datenbank zu speichern (eine neue Tabelle)?
Wenn ja, dann könnte man die Form mit einer SQL-Abfrage generieren.
Wäre das möglich?
Zitat:gibt es die
am 03.07.2010 - 03:10 Uhr
gibt es die Möglichkeit die einzelnen Formularfelder in der Datenbank zu speichern (eine neue Tabelle)?
Wenn ja, dann könnte man die Form mit einer SQL-Abfrage generieren.
Geht bestimmt, die Frage ist nur, ob das sinnvoll wäre, denn dann müsstest du evtl. auch für jedes Formular eine eigene Abfrage erstellen müssen, da kann man auch gleich das Formular selbst für jedes Arrangement erstellen.
wie übergebe ich - über den Link "Anfrage.." die Parameter mit den einzelnen Feldern (ID's) an das Modul???
Das könnte man z.B. über Get machen. Da gibt man dann z.B. als Link-Pfad sowas an:
www,example.com/?feld1=Wert1&Feld2=Wert2 usw.
Daraus könnte man sich dann mittels Get die Werte rausnehmen usw. Man könnte dann allen Formular-Elementen eine ID zuweisen und diese IDs in der URL angeben.
Das ganze hat aber einen ganz entscheidenden Nachteil:
Das klappt zwar, wenn man einfach auf den Link klickt, aber wenn jemand manuell in die URL noch andere Werte einträgt, bekommt er evtl. Formular-Felder ausgegeben, die gar nicht dazu gehören.
Beispiel:
Ein Arrangement soll nur Feld1 und Feld 2 ausgeben, dann könnte das ganze z.B. so aussehen:
www,example.com/?arrangement=A1&feld1=Wert1&Feld2=Wert2
Wenn jetzt aber jemand die URL so ändert:
www,example.com/?arrangement=A1&feld1=Wert1&Feld2=Wert2&Feld3=Wert3
--> dann bekommt er das Formular für Arrangement 1 mit einem zusätzlichen Feld --> nicht gut.
Meine Idee, die mir zu dieser nachtschlafenden Zeit gerade eben gekommen ist:
Du weißt jedem Formular-Element eine eindeutige ID zu. Und zwar wirklich jedem, ganz egal, wo es verwendet wird.
Außerdem bekommt jedes Arrangement eine feste ID zugewiesen.
Nun benötigen wir am besten ein eigenes Modul und eine neue Tabelle in der Drupal-Datenbank.
In der Tabelle gibt es folgende Spalten:
- ID --> (notwendig) ID des Arrangements
- Bezeichnung --> (nicht zwingend notwendig) die Bezeichnung oder kurze erklärung des Arrangements --> macht die Ansicht in der Datenbank übersichtlicher
- Elemente --> (notwendig) eine Aufzählung aller zu verwendenden Form-Elemente in der Reihenfolge, in der sie angezeigt werden sollen (hier würde sich z.B. eine mit Komma getrennte Liste anbieten)
So könnte das dann aussehen:
(es soll erst das Element 3, dann Element 1 und dann Element 2 im Formular erscheinen)
- 1
- Arrangement für Wellness-Wochenende
- 3,1,2
In diese Tabelle müssen dann also alle Arrangements mit einer Liste aller ihrer Form-Elemente eingetragen werden.
Nun schreibt man ein Modul, welches einen bestimmten Pfad mit Argument bedient, z.B. diesen Pfad:
arrangement/bestellung/*
--> man gibt an, dass arg(2) (der Stern) als Argument übergeben wird. Ruft man also arrangement/bestellung/1 auf, dann wird das Formular für Arrangement 1 ausgegeben usw.
So, dann kommt jetzt der Teil mit der Schreibarbeit -,-'
Wir benötigen eine Funktion, der man die Element-ID als Parameter übergibt und die dann als Rückgabewert den Code für das entsprechende Element ausspuckt.
Hinweis: Formular in Drupal werden über die Form API erstellt (Liste aller Form-Elemente)
In dieser Funktion benötigt man eine schöne große Fallunterscheidung, die alle möglichen Formular-IDs abdeckt und so aufgebaut wäre:
<?php
function _mymodule_get_form_element($id){
$code = new stdClass();
switch($id){
//Code für Element 1 ausgeben
case 1: $code->titel = 'Titel 1';
$code->inhalt = array(
'#type' => 'textfield',
'#title' => t('Subject'),
'#default_value' => $node->title,
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);
break;
//Code für Element 2 ausgeben
case 2: $code->titel = 'Titel 2';
$code->inhalt = array(
//Code
);
break;
//usw.
}
return $code;
}
?>
Wenn nun der Pfad arrangement/bestellung/1 aufgerufen wird, nimmt man sich dort mit arg(2) die ID des Arrangements heraus. Damit führt man eine SQL-Abfrage auf der neuen Tabelle aus und nimmt sich daraus die Element-Liste. Die kann man dann mittels explode in ein Array aufteilen. Dann hat man ein Array und in jedem Feld steht eine Element-ID.
Dann behandelt man dieses Array mit foreach(). Dann wird das Array Feld für Feld, ID für ID durchlaufen und die aktuelle ID in einer Variable zwischengespeichert. Und dann kann man im foreach-Ablauf die Funktion _mymodule_get_form_element($id) aufrufen. Die Funktion wertet die momentan behandelte Element-ID aus und gibt den entsprechenden Code des Elements zurück. Daraus kann man dann Schritt für Schritt das Formular erstellen, z.B. so:
<?php
function mymodule_formular($id){
//Die ID wird im Modul im hook_menu aus der URL übergeben
//Formular-Variable mit Fieldset beginnen
$form['arrangement'] = array(
'#type' => 'fieldset',
'#title' => t('Arrangement'),
'#description' => 'Beschreibung',
'#tree' => TRUE,
);
$sql = 'SELECT * FROM {meine_tabelle} WHERE ID LIKE "%s";';
$result = db_query($sql, $id);
$row = db_fetch_object($result);
$elementliste = $row->elementliste;
$elemente = explode(',', $elementliste);
foreach($elemente as $key => $element_id)
{
$element = _mymodule_get_form_element($element_id);
$titel = $element->titel;
$inhalt = $element->inhalt;
$form['arrangement'][$titel] = $inhalt;
}
return $form;
}
?>
Man muss vermutlich noch ein paar Feinheiten ausarbeiten etc. es ist eben schon relativ spät bzw. früh und mein Hirn läuft nur noch auf Mindestleistung zum wach bleiben :-P aber alles in allem dürfte das ganze so funktionieren.
Ist zwar ein wenig Aufwand, für jedes Arrangement die Elementliste anzulegen und in der Tabelle zu speichern und auch die Funktion mit der großen switch-Anweisung ist ein wenig schreibkram, aber letztendlich ist es immer noch weniger, als für jedes Arrangement das ganze Formular zu erstellen und man ist relativ flexibel (wenn man die Elemente in Ihrer Anordnung ändern will etc. muss man einfach in der Tabelle die Elementliste anpassen). Außerdem ist es wesentlich sicherer, als die Element-IDs per GET zu übergeben, weil die User so nur vorgefertigte Formulare anfordern können.
Die Idee gefällt mir
am 03.07.2010 - 10:26 Uhr
Vielen herzlichen Dank für deine Mühe!
Deine Vorgehensweise finde ich sehr gut! Und das mitten in der Nacht :-)
Ich werde heute mit der Umsetzung anfangen und dann berichten, was daraus geworden ist.
Kann sein, dass ich dich dann noch mit weiten Fragen belästige.
1000 Dank noch mal!
Ok, dann bin ich ja mal
am 03.07.2010 - 12:09 Uhr
Ok, dann bin ich ja mal gespannt, ob dieser nächtliche Einfall ordentlich klappt, so wie gedacht ;-)
Wenn du noch Fragen hast, einfach hier posten.
Implementierung von hook_menu()
am 09.07.2010 - 21:37 Uhr
Hallo Exterior,
beim Klick auf meinen Link:
<a href="<?php print base_path()?>arrangement/buchung/33">Buchungsformular</a>
bekomme ich "Die angeforderte Seite konnte nicht gefunden werden. " zurück.
Was mache ich hier (buchung_forms.module) falsch?:
function buchung_forms_menu() {
$items['arrangement/buchung/%']= array(
'page callback' => 'buchung_forms_formular',
'access callback' => TRUE,
'page arguments'=> array(2),
'type' => MENU_CALLBACK,
);
return $items;
}
Zur Info:
Die Tabelle mit den entsprechenden Feldern ist angelegt und mit Werten für das Arrangement mit der ID 33 gefüllt.
Die Funktion "buchung_forms_formular" gibt es auch.
Danke im Voraus!
LG
Implementierung von hook_menu() - Problem behoben
am 09.07.2010 - 21:49 Uhr
Nach dem ich den Cache gelöscht habe funktionert der Aufruf.
Bekomme zwar erstmal leider nur "Array" als Inhalt und kein Formular, aber das krieg ich schon raus.
foreach ($elemente as $key => $element_id): Aufruf der Funktion
am 10.07.2010 - 22:53 Uhr
... buchung_forms_get_form_element($element_id) funktioniert nicht.
Ich bekomme mit dem folgenden Quelltext und den Tabellenwerten nur das Fieldset 'Arrangement' angzeigt.
Nach dem ich einige Tests durchgeführt habe, habe ich festgestellt, dass es an der Stelle:
foreach($elemente as $key => $element_id)
{
$element = buchung_forms_get_form_element($element_id);
...
}
nicht funktioniert.
Aber ich habe leider nicht herausgefunden, warum. Alle Werte werden richtig übergeben, das habe ich getestet.
Habe testweise folgenden Code eingesetzt und er spuckt immer nur den 1. Fall aus ('anrede'), egal welche Werte in der Tabelle stehen.
function test($id){
$sql = 'SELECT * FROM {arrangement_forms} WHERE ID LIKE "%s";';
$result = db_query($sql, $id);
$row = db_fetch_object($result);
$elementliste = $row->Elemente;
$elemente = explode(',', $elementliste);
foreach($elemente as $key => $value)
{
echo switch_test($value);
}
}
function switch_test($id){
switch($id){
//Code für Elemente ausgeben
case 1: $code = 'anrede';
break;
case 2: $code = 'name';
break;
case 3: $code = 'E-Mail-Adresse';
break;
}
return $code;
}
Hier die Testwerte in der Tabelle "arrangement_forms":
ID Bezeichnung Elemente
390 Arrangement 1 1,2,4
33 Arrangement 2 1,3,4,5
hier der Quelltext:
/**
* Implementierung von hook_menu().
*/
function buchung_forms_menu() {
$items['arrangement/buchung/%']= array(
'title' => 'Buchung',
'page callback' => 'get_form_page',
'access callback' => TRUE,
'page arguments'=> array(2),
'type' => MENU_CALLBACK,
);
return $items;
}
// für die Anzeige der Form
function get_form_page($id) {
return drupal_get_form("buchung_forms_formular", $id);
}
function buchung_forms_formular($id){
//Formular-Variable mit Fieldset beginnen
$form['arrangement'] = array(
'#type' => 'fieldset',
'#title' => t('Arrangement'),
'#description' => 'Ihre Buchungsanfrage',
'#tree' => TRUE,
);
// hier wird die neue Tabelle "arrangement_forms" angesprochen
// Spalten: ID, Bezeichnung, Elemente
$sql = 'SELECT * FROM {arrangement_forms} WHERE ID LIKE "%s";';
$result = db_query($sql, $id);
$row = db_fetch_object($result);
$elementliste = $row->Elemente;
$elemente = explode(',', $elementliste);
foreach($elemente as $key => $element_id)
{
$element = buchung_forms_get_form_element($element_id); // wenn ich an dieser Stelle einen Wert z. B. 4 (anstatt der Variablen) übergebe, wird das Element (case 4) auch angezeigt.
$titel = $element->titel;
$inhalt = $element->inhalt;
$form['arrangement'][$titel] = $inhalt;
}
return $form;
}
function buchung_forms_get_form_element($id){ // die ID kommt hier immer richtig an
$code = new stdClass();
switch($id){
//hier stehen erstmal nur einige Elemente zum testen
case 1: $code->titel = 'anrede';
$code->inhalt = array(
'#type' => 'textfield',
'#title' => t('Anrede'),
'#default_value' => $node->title,
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);
break;
case 2: $code->titel = 'name';
$code->inhalt = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#default_value' => $node->title,
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);
break;
case 3: $code->titel = 'Strasse';
$code->inhalt = array(
'#type' => 'textfield',
'#title' => t('Strasse, Nr.'),
'#default_value' => $node->title,
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);
break;
case 4: $code->titel = 'PLZ';
$code->inhalt = array(
'#type' => 'textfield',
'#title' => t('PLZ'),
'#default_value' => $node->title,
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);
break;
case 5: $code->titel = 'Ort';
$code->inhalt = array(
'#type' => 'textfield',
'#title' => t('Ort'),
'#default_value' => $node->title,
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);
break;
}
return $code;
}
Ich finden den Fehler nicht
am 11.07.2010 - 14:24 Uhr
Kann mir bitte jemand sagen, warum in dem Formualar nur das eine fieldset angezeigt wird?
Quelltext s. o.
Danke im Voraus!
Ich vermute, dass es daran
am 11.07.2010 - 19:17 Uhr
Ich vermute, dass es daran liegt, dass du die Funktion für die Form-Ausgabe falsch gemacht hast, um genau zu sein, hast du beim Funktions-Namen einen Parameter vergessen:
<?php
//So beginnst du deine Funktion:
function buchung_forms_formular($id){
...
}
//Aber so müsste sie beginnen:
function buchung_forms_formular($form_state, $id){
...
}
?>
Zu so einer Form-Funktion gehört immer $form_state dazu. Füge das mal ein und teste das ganze nochmal (den Rest kannst du eigentlich so lassen, der müsste passen.)
Sorry, bin Drupal-Anfänger :-)
am 11.07.2010 - 19:41 Uhr
100000 Dank!!! Du hast mir sehr geholfen, jetzt läuft alles, wie geschmiert!
Dein Vorschlag für "Form on the fly" hat mir viel Arbeit erspart!
LG!
Formular unter Inhaltsverwaltung->Webformulare anzeigen...
am 11.07.2010 - 20:05 Uhr
Wie erreiche ich, dass das Formular unter Formular unter Inhaltsverwaltung->Webformulare angezeigt wird?
bzw. wie kann ich jetzt die ganzen Einstellungen zu diesem Formular vornehmen, wie "Ziel-E-Mail-Adresse" usw?
Zitat: Wie erreiche ich, dass
am 11.07.2010 - 23:36 Uhr
Wie erreiche ich, dass das Formular unter Formular unter Inhaltsverwaltung->Webformulare angezeigt wird?
Weiß gar nicht, ob das einfach so geht, weil dieser Menüpunkt vom Modul Webforms kommt, da werden Inhalte vom Inhaltstyp Webform angezeigt, nicht irgendwelche x-beliebigen Formulare, die man mit der FAPI erstellt hat. Zumal du nichtmal ein festes Formular hast.
wie kann ich jetzt die ganzen Einstellungen zu diesem Formular vornehmen, wie "Ziel-E-Mail-Adresse" usw?
Ich glaube, du verstehst das Prinzip ein wenig falsch. Es gibt für Formulare keine Einstellung "Ziel-Adresse" usw. So ein Formular macht erstmal überhaupt gar nichts. Du darfst die FAPI nicht mit der Funktion des Webforms-Moduls verwechseln, das sind zwei völlig unterschiedliche Dinge.
Wenn du willst, dass die Daten des Formulars per Mail gesendet werden, musst du dir eine eigene Submit-Funktion für dieses Formular schreiben, darin die Mail konfigurieren und per mail() oder drupal_mail versenden.
Die Daten aus so einem eigenen Formular werden auch nirgendwo gespeichert. Wenn sie gespeichert werden sollen, musst du in einer Submit-Funktion selbst die SQL-Abfrage ausführen, um das alles in eine Datenbank-Tabelle einzutragen.
Wenn du die Form API verwendest, musst du alles selbst erstellen, Formular, Submit-Funktion, Validierungs-Funktion wenn nötig usw. Von selbst passiert da überhaupt nichts, weil es ja kein Modul oder ähnliches ist, sondern ein völlig neu erschaffenes Konstrukt.