Zweimal dasselbe Modul auf einer Seite

Das Forum soll der Ablage von Lösungen für immer wieder auftauchende Problemstellungen dienen. // This forum contains solutions to problems that frequently occur.
wal
Beiträge: 10
Registriert: 04.08.2008, 08:26:29

Zweimal dasselbe Modul auf einer Seite

Beitrag von wal » 11.08.2008, 10:47:26

Hallo Dr.E.

ich habe das einfache Formularmodul calc mir angeschaut, und dieses zweimal auf einem template aufgerufen


hier der Ausschnitt aus der entsprechenden website.html:
<core:addtaglib namespace="core::pagecontroller" prefix="core" class="setattribute" />
<table>
<tr>
<td>
<h2>erster einschub</h2>
<core:importdesign namespace="modules::calc::pres::templates" template="calc" />
<core:setattribute name="numero" value="1" />
</td>
</tr>
<tr>
<td>
<h2>zweiter einschub</h2>
<core:importdesign namespace="modules::calc::pres::templates" template="calc" />
<core:setattribute name="numero" value="2" />
</td>
</tr>
</table>
//Ende des Ausschnittes

Man kann nun die insgesamt 4 Felder mit 4 verschiedenen Zahlen füllen, und auch zwei verschiedene Operatoren wählen. Aber je nachdem ob man den oberen oder unteren "Berechnen"-Button drückt, führt er auf beiden diesselbe Berechnung aus.
Kannst du mir einen Tip geben, auf welcher Ebene ich den Modulen eine Art eigene ID mitgebe, so daß sie nur auf ihren template-Ausschnitt zugreifen ?

Vielen Dank im voraus, und danke für dieses Modulbeispiel

viele liebe Grüße

wal

Benutzeravatar
dr.e.
Administrator
Beiträge: 4525
Registriert: 04.11.2007, 16:13:53

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von dr.e. » 11.08.2008, 17:21:13

Hallo wal,

an diesem Punkt kommen wir zu der Schwiergigkeit, dass das Modul, aus ein und derselben Code-Basis gespeist wird. Im Klartext: das im FAQ-Post gezeigte Modul ist dafür zu statisch. Grundsätzlich sind die Formular-Taglibs sind so implementiert, dass sie Namen als Referenz für die Zuordnung der Inhalte und die Validierung verwenden, was in diesem Fall hinderlich ist, da nicht übergeben wird, welches Formular von beiden abgeschickt wird.

Um die Einbindung auf einer Seite unabhängig von einander möglich vornehmen zu können, müssen die Namen der Felder und/oder die Bezeichnungen der Buttons eindeutig gewählt oder generiert werden. Hierzu bietet sich die Möglichkeit an, die Tag-Attribute per Controller zu manipulieren oder eigene Tags zu erstellen, die diese Funktion bereits beinhalten. Auf den ersten Blick vermute ich jedoch, dass die bisherige Funktionalität der Formular-Taglibs nicht ausreicht, da die Definition eines Formulars per Design dafür zu statisch ist.

Out-of-the-box fällt mir jedoch keine Lösung ein, man könnte jedoch folgendes in Betracht ziehen:
  • Update der Formular-Taglibs um ein generiertes Hidden-Feld, das eine eindeutige ID des Formulars mitgibt. Eine Validierung/Verarbeitung erfolgt dann nur, wenn das Formular auch "aktiv" ist.
  • Implementierung eines eigenen Form-Tags, dass diese Funktion übernimmt und die entsprechenden Elemente des nicht aktiven Formulars "deaktiviert".

Die Frage ist jedoch, ob dieses künstlich generierte Beispiel in real-life-Applikationen auch auftauchen wird. Mir fällt im Moment kein sinnvoller Anwendungsfall ein, vielleicht kennst du einen...
Viele Grüße,
Christian

Benutzeravatar
fliegermichl
Beiträge: 114
Registriert: 29.01.2008, 11:51:45
Wohnort: Echzell

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von fliegermichl » 12.08.2008, 10:28:49

Moin Doc,

zweimal dasselbe Modul ist zwar eher unwahrscheinlich, aber zwei unterschiedliche Module auf einer Seite schon.

Ich habe jetzt noch flugs ein neues ganz simples Modul gebastelt um den Zusammenhang zu verdeutlichen

die bootstrap Datei hast jetzt folgenden Inhalt:

Code: Alles auswählen

<?
 ini_set('html_errors', 'off');
 require_once('../apps/core/pagecontroller/pagecontroller.php');
 $Reg = &Singleton::getInstance('Registry');
 $Reg->register('apf::core', 'LogDir', '/www/planer/logs');
 // Lasse ich obige Zeile weg == Fehlermeldung da LogDir mit "/" initialisiert ist!
 $Page = new Page();
 $Page->loadDesign('sites::planer', 'pres/templates/site');
 echo $Page->transform();
?>


Die aufgerufene Seite "apps/sites/planer/pres/templates/site.html" hat folgenden Inhalt

Code: Alles auswählen

<core:importdesign namespace="modules::calc::pres::templates" template="calc" />
<h2>This is some Text from my Mainpage</h2>
<core:importdesign namespace="modules::color::pres::templates" template="color" />


bindet also den kleinen Rechner aus der FAQ ein, gibt etwas Text aus und dann mein Color Modul
Das Color Modul hat folgenden simplen Controller

Code: Alles auswählen

<?
class color_controller extends baseController {

 function color_controller() {
 }

 function transformContent() {
   $Form = &$this->__getForm('ColorForm');
   if ($Form->get('isSent'))
   {
    $Form->setPlaceHolder('PickedColor', 'You choosed color '.
         $Form->
         getFormElementByName('color')->
         getSelectedOption()->
         getAttribute('value'));
   }
   $Form->transformOnPlace();
 }
}
?>


und das dazugehörige Template

Code: Alles auswählen

<@controller namespace="modules::color::pres::documentcontroller" file="color_controller" class="color_controller" @>
<core:addtaglib namespace="tools::form::taglib" prefix="html" class="form" />
<html:form name="ColorForm" method="post" action="">
  Please select a Color
  <form:select name="color">
   <select:option value="red">red</select:option>
   <select:option value="green">green</select:option>
   <select:option value="blue">blue</select:option>
  </form:select>
  <form:button name="send" value="Farbe ändern" />
  <form:placeholder name="PickedColor" />
</html:form>


Rufe ich nun meine Seite auf, stellen sich beide Module korrekt vor.
Trage ich eine Rechenoperation im calc Modul ein und drücke den Absenden Button, erscheint das richtige Ergebnis, der ColorPicker zeigt seinen Initialwert "red". Wähle ich nun eine andere Farbe im Color Modul und drücke auf absenden, sind die Werte im calc Modul leer. Trage ich nun im calc Modul neue Werte ein, ist meine gewählte Farbe wieder weg usw.

Das ist natürlich ein gekünsteltes Beispiel. zeigt aber, was ich meine.

Viele Grüße
Michl

Benutzeravatar
dr.e.
Administrator
Beiträge: 4525
Registriert: 04.11.2007, 16:13:53

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von dr.e. » 12.08.2008, 13:11:33

Hallo fliegermichl,

die Problemstellung habe ich denke ich verstanden. IMHO liegt die Ursache bei dir darin, dass du hier zwei unterschiedliche Formulare verwendest. Selbes Verhalten würde sich auch ohne Verwendung der Form-TagLibs ergeben, wenn du einfachen PHP-Code für das Ausfüllen der Felder verwendest.

Möchtest du die beiden Module in Abhängigkeit setzen, so benötigen diese einen Raum für gemeinsame Informationen. Diesen Raum kann dir der Frontcontroller bieten. Hierzu würde ich wie folgt vorgehen:

  • Gemeinsame Action erstellen, die die Informationen des Formulars verarbeiten und aufbereiten kann.
  • Die Controller der beiden Module greifen auf die Informationen der FrontController-Action zu um ihre Darstellung zu generieren (Model!).
  • Das action-Attribut der Formulare wird mit einer FrontController-URL gefüllt.

Damit kannst du sicherstellen, dass die beiden Module eine von dir definierte Abhängigkeit haben und die Informationen des anderen für sich nutzen können. Grundsätzlich ziehe ich jedoch persönlich nur dann einen Rahmen um eine Funktionalität, wenn sie keine direkte Abhängigkeit zu einem anderen Softwareteil hat. Wenn Verquickungen - wie von dir beschrieben - notwenig sind, fasse ich das in ein Modul und versuche innerhalb des Moduls zu "modularisieren".

Wenn du & wal wünschen, dann können wir uns das Thema "Abhängigkeit von Modulen über FrontController-Actions" nochmal genauer anschauen und gemeinsam ein Beispiel erstellen.
Viele Grüße,
Christian

Benutzeravatar
fliegermichl
Beiträge: 114
Registriert: 29.01.2008, 11:51:45
Wohnort: Echzell

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von fliegermichl » 12.08.2008, 13:29:41

Hallo Doc,

dr.e. hat geschrieben:die Problemstellung habe ich denke ich verstanden. IMHO liegt die Ursache bei dir darin, dass du hier zwei unterschiedliche Formulare verwendest. Selbes Verhalten würde sich auch ohne Verwendung der Form-TagLibs ergeben, wenn du einfachen PHP-Code für das Ausfüllen der Felder verwendest.

Möchtest du die beiden Module in Abhängigkeit setzen, so benötigen diese einen Raum für gemeinsame Informationen. Diesen Raum kann dir der Frontcontroller bieten. Hierzu würde ich wie folgt vorgehen:


Die Module haben doch gar keine Abhängigkeiten. Das ist ja genau das Problem. Das eine Modul stellt Berechnungen an und zeigt das Ergebnis, das andere bietet hier eben nur eine Auswahl.

Worum es mir geht ist doch, daß ich nach einer Möglichkeit suche ein Modul in eine bestehende Seite einbinden zu können, egal was da bereits drin ist

Eventuell ist die Registry eine Lösung für das Problem.
Wenn das Modul initialisiert wird, speichert es was auch immer es sich merken muß und wenn ein "Absenden" Button irgendeines anderen Moduls/Formulares gedrückt wird, kann es seinen Zustand durch auslesen der Registry wiederherstellen.

Wenn du & wal wünschen, dann können wir uns das Thema "Abhängigkeit von Modulen über FrontController-Actions" nochmal genauer anschauen und gemeinsam ein Beispiel erstellen.


Ich hab in dem Forum "Entwicklung" mal ein paar Infos zu dem Projekt hinterlassen, mit dem wal und ich uns demnächst befassen wollen.

Ich denke, es kommen spannende Zeiten auf uns zu :-)

Viele Grüße
Michl

phpdummi
Beiträge: 18
Registriert: 23.11.2007, 16:15:15

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von phpdummi » 12.08.2008, 14:35:15

Hallo Michl!

Geh doch erstmal - ohne das Framework zu involvieren - von ganz simplen PHP- bzw. HTML-Code aus!

Wenn du zwei <form> Bereiche hast, und in einem davon eine Aktion getätigt wird, weiß der andere Bereich nichts davon.
Wenn du nun aber beim aktualisieren einer Form den Wert/Inhalt der anderen wieder anzeigen möchtest sind die beiden
von einander abhängig.

Um nun den Zustand eines Moduls - im Kontext des Frameworks - zu speichern, sobald der Absenden-Button des konkreten Moduls geklickt wurde,
bietet sich eher die Session an.

Benutzeravatar
fliegermichl
Beiträge: 114
Registriert: 29.01.2008, 11:51:45
Wohnort: Echzell

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von fliegermichl » 12.08.2008, 14:54:51

Hallo phpdummy,

das ist ja gerade die Krux bei Webanwendungen, daß ich bei der kleinsten Änderung/Aktion des Benutzers den Zustand der gesamten Applikation restaurieren muß. In meinem Planer Script eben dadurch, daß ich hundert "hidden" Elemente mitschleife, welche dann beim erneuten Aufbau der Seite dazu verwendet werden den Zustand der "Applikation" wiederherzustellen.

Es wäre doch toll, wenn ich mich nur um einen kleinen abgeschlossenen Bereich (Ich nenn das eben Modul) kümmern bräuchte und dieses nur die Parameter verwalten muß, die es selbst betrifft und sich eben nicht um die Belange anderer Komponenten/Module usw. kümmern bräuchte.

Viele Grüße
Michl

Benutzeravatar
dr.e.
Administrator
Beiträge: 4525
Registriert: 04.11.2007, 16:13:53

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von dr.e. » 12.08.2008, 15:02:51

Hallo fliegermichl,

In meinem Planer Script eben dadurch, daß ich hundert "hidden" Elemente mitschleife, welche dann beim erneuten Aufbau der Seite dazu verwendet werden den Zustand der "Applikation" wiederherzustellen.

Nein, ich würde hier nur diejenigen Informationen in einem Formular abbilden, die ich auch gerade brauche. Der Rest sollte in der Session gespeichert und per FrontController-Action in ein Model-Objekt übertragen werden, so dass diese im Controller eines konkreten Formulars verfügbar sind.

Es wäre doch toll, wenn ich mich nur um einen kleinen abgeschlossenen Bereich (Ich nenn das eben Modul) kümmern bräuchte und dieses nur die Parameter verwalten muß, die es selbst betrifft und sich eben nicht um die Belange anderer Komponenten/Module usw. kümmern bräuchte.

Wie oben bereits angesprochen, muss man hier bei Web-Anwendungen sauber modularisieren bzw. strukturieren. Gerade für deinen Planer sollte man deshalb genau analysieren, welche Model-Informationen im Ganzen und im speziellen View benötigt werden. Hier hilft dir, mal die Software zu zerlegen und aufzuschreiben, welche Views mit welchen Informationen benötigt werden.
Viele Grüße,
Christian

phpdummi
Beiträge: 18
Registriert: 23.11.2007, 16:15:15

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von phpdummi » 12.08.2008, 15:08:23

Genau! Von der Session ab ins Model.

Benutzeravatar
fliegermichl
Beiträge: 114
Registriert: 29.01.2008, 11:51:45
Wohnort: Echzell

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von fliegermichl » 13.08.2008, 10:12:41

Hallo zusammen,

was spricht dagegen, eine Art persistente Registry zu machen, welche meinetwegen eine Session dazu nutzt die Informationen über mehrere Seitenaufrufe hinweg zu erhalten.
So daß ein Documentcontroller seine Werte wiederherstellen kann.

Code: Alles auswählen

class myController extends Documentcontroller {
 function transformContent() {
  $Reg = &Singleton->getInstance('PersistentRegistry');
  $wert1 = $Reg->recieve('calc_module', $this->InstanceID, 'wert1');
  $wert2 = $Reg->recieve('calc_module', $this->InstanceID, 'wert2');
  $operation = $Reg->recieve('calc_module', $this->InstanceID, 'operation');

 // Evtl. hat der Anwender genau die Werte meiner Instanz geändert, dann übernehmen wir diese ansonsten die letzten
 // bekannten Werte
 $Form = &$this->__getForm('Calc_Form');
 if ($Form->get('isSent')) {
  $wert1 = $Form->getFormElementByName('wert1')->getAttribute('value');
  $wert2 = $Form->getFormElementByName('wert2')->getAttribute('value');
  $operation = $Form->getFormElementByName('operation')->getSelectedOption()->getAttribute(' value');
 }
 $Reg->register('calc_module', $this->InstanceID, 'wert1', $wert1);
 $Reg->register('calc_module', $this->InstanceID, 'wert2', $wert2);
 $Reg->register('calc_module', $this->InstanceID, 'operation', $operation);
 
 // Hier muß natürlich die Fehlerprüfung noch dazu

 $this->setPlaceHolder('wert1', $wert1);
 $this->setPlaceHolder('wert2', $wert2);
 $this->setPlaceHolder('operation', $operation);
 } // end function
} // end class


So würde das Modul calc die für ihn relevanten Werte behalten oder aber falls der Anwender diese geändert hat eben übernehmen. InstanceID währe ein Attribut, welches der Templatedesigner angeben muß innerhalb des <core:importdesign>
Tags.

Auf diese Weise könnte ich auch sehr umfangreiche Applikationen mit vielen Modulen schreiben ohne die Belange aller Module an jeder Stelle berücksichtigen zu müssen.

Viele Grüße
Michl

Benutzeravatar
fliegermichl
Beiträge: 114
Registriert: 29.01.2008, 11:51:45
Wohnort: Echzell

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von fliegermichl » 13.08.2008, 10:39:23

Ich hatte versucht die Klasse SessionSingleton dafür zu verwenden, klappt aber nicht.

Benutzeravatar
fliegermichl
Beiträge: 114
Registriert: 29.01.2008, 11:51:45
Wohnort: Echzell

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von fliegermichl » 13.08.2008, 12:00:56

Klappt ja doch!!

Wenn ich in meinem calc_controller->transformDocument
folgenden Code verwende, klappt es so wie ich mir das wünschte.

Code: Alles auswählen

<?

import ('core::singleton', 'SessionSingleton');

class calc_controller extends baseController {

 function calc_controller() {
 }

 function transformContent() {
  $hasErrors = false;
  $Ergebnis = 0;
  $Form__CalcForm = &$this->__getForm('CalcForm');
  $operations = &$Form__CalcForm->getFormElementByName('operations');

  // Hinzufügen der möglichen Operationen
  $operations->addOption('+', 'plus');
  $operations->addOption('-', 'minus');
  $operations->addOption('*', 'mal');
  $operations->addOption('/', 'durch');
  $operations->addOption('hoch', 'hoch');

  $Reg = &SessionSingleton::getInstance('Registry');
  //$Reg = &Singleton::getInstance('Registry');
  $wert1 = $Reg->retrieve('calc_module_' . $this->getAttribute('InstanceID'), 'wert1');
  $wert2 = $Reg->retrieve('calc_module_' . $this->getAttribute('InstanceID'), 'wert2');
  $operation = $Reg->retrieve('calc_module_' . $this->getAttribute('InstanceID'), 'operation');
  $operation = isset($operation) ? $operation : 'plus';

  if ($Form__CalcForm->get('isSent'))
  {
    // Das Formular wurde durch den Benutzer abgeschickt, also validieren wir die Eingabewerte
    $wert1 = $Form__CalcForm->getFormElementByName('operand1')->getAttribute('value');
    if (!is_numeric($wert1)) {
     $Form__CalcForm->setPlaceHolder('operand1error', 'Bitte eine Zahl eingeben!');
     $hasErrors = true;
    }

    $wert2 = $Form__CalcForm->getFormElementByName('operand2')->getAttribute('value');
    if (!is_numeric($wert2)) {
     $Form__CalcForm->setPlaceHolder('operand2error', 'Bitte eine Zahl eingeben!');
     $hasErrors = true;
    }

    $operation = $Form__CalcForm->getFormElementByName('operations')->getSelectedOption()->getAttribute('value');
    if (($operation == 'durch') && ($wert2 == 0)) {
     $Form__CalcForm->setPlaceHolder('operand2error', 'Bitte nicht durch Null dividieren!');
     $hasErrors = true;
    }

    if (!$hasErrors) {
     if     ($operation == 'plus')  { $Ergebnis = $wert1 + $wert2; }
     elseif ($operation == 'minus') { $Ergebnis = $wert1 - $wert2; }
     elseif ($operation == 'mal')   { $Ergebnis = $wert1 * $wert2; }
     elseif ($operation == 'durch') { $Ergebnis = $wert1 / $wert2; }
     elseif ($operation == 'hoch')  {
      $Ergebnis = $wert1;
      for ($i = 1; $i < $wert2; $i++) {
       $Ergebnis = $Ergebnis * $wert1;
      }
     }
     $Form__CalcForm->setPlaceHolder('Ergebnis', $Ergebnis);
    }
  }
  error_log('calc_module_' . $this->getAttribute('InstanceID'));
  $Reg->register('calc_module_' . $this->getAttribute('InstanceID'), 'wert1', $wert1);
  $Reg->register('calc_module_' . $this->getAttribute('InstanceID'), 'wert2', $wert2);
  $Reg->register('calc_module_' . $this->getAttribute('InstanceID'), 'operation', $operation);
  $Form__CalcForm->getFormElementByName('operand1')->setAttribute('value', $wert1);
  $Form__CalcForm->getFormElementByName('operand2')->setAttribute('value', $wert2);
  $Form__CalcForm->getFormElementByName('operations')->setOption2Selected($operation);
  $Form__CalcForm->transformOnPlace();
 }

}
?>


Zwar wird in dem error_log Aufruf einmal "calc_module_calc1" und einmal "calc_module_calc2" gespeichert. Aber
$Form->get('isSent') liefert für beide Instanzen true oder false wenn das calc Modul zweimal auf einer Seite
mittels

Code: Alles auswählen

Rechner 1 <br />
<core:importdesign namespace="modules::calc::pres:templates" InstanceID="calc1" template="calc" /> <br />
Rechner 2 <br />
<core:importdesign namespace="modules::calc::pres::templates" InstanceID="calc2" template="calc" /> <br />


aufgerufen wird.

Viele Grüße
Michl

Benutzeravatar
dr.e.
Administrator
Beiträge: 4525
Registriert: 04.11.2007, 16:13:53

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von dr.e. » 13.08.2008, 14:43:51

Hallo fliegermichl,

was spricht dagegen, eine Art persistente Registry zu machen, welche meinetwegen eine Session dazu nutzt die Informationen über mehrere Seitenaufrufe hinweg zu erhalten.

Nein, das kannst du gerne machen - und hast du im letzten Post auch getan. Die SessionSingleton-Klasse ist genau für diese Aufgaben gedacht und bietet dir einen komfortablen Wert ein Objekt transparent über eine Session zu persistieren.

Klappt ja doch!!

Guter Ansatz, danke! :geek:

Dabei ist jedoch zu beachten, dass die Instanz der Registry, die für die globale Konfiguration (siehe http://adventure-php-framework.org/Seite/013-Grundlagen, Kapitel 3.3) keine SessionSingleton-Instanz ist. Deshalb muss der Entwickler eine "eigene" Instanz der Registry mit Hilfe der SessionSingleton erstellen und diese nutzen. Hintergrund ist, dass die Klassen Singleton und SessionSingleton gemäß Pattern den Namen der Klasse als eindeutigen Identifer verwenden. Keine Angst, deine Lösung beachtet das bereits! :)

Noch eine Frage: wenn es dich nicht stört, würde ich deine Lösung gerne als Tutorial übernehmen.
Viele Grüße,
Christian

Benutzeravatar
fliegermichl
Beiträge: 114
Registriert: 29.01.2008, 11:51:45
Wohnort: Echzell

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von fliegermichl » 13.08.2008, 15:07:41

Hallo Doc,

Noch eine Frage: wenn es dich nicht stört, würde ich deine Lösung gerne als Tutorial übernehmen.


Natürlich.
Was hältst Du von der Idee, wenn man dem baseController eine Liste $persistentValues spendiert, welche vom PageController
vor dem transform() automatisch mittels $Reg->retrieve() lädt und danach mit $Reg->register in der Session speichert.
Dann muß man dies nicht jedesmal für jede Variable manuell erledigen.

Viele Grüße
Michl

phpdummi
Beiträge: 18
Registriert: 23.11.2007, 16:15:15

Re: Zweimal dasselbe Modul auf einer Seite

Beitrag von phpdummi » 13.08.2008, 16:59:22

Bläht den baseController aber unnötig auf. Die Session ist doch Singleton, kannst du dir also überall holen.

Antworten

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast