TYPO3 Flow – Applikation mit Dynamischen Attributen 2/3

Locations & DataFields

Locations & DataFields

Wie wir in Teil 1 gesehen haben, ist es nicht schwierig, auf Model-Ebene dynamische Attribute hinzuzufügen. Die Ausgabe der Dynamischen Attribute im Frontend gestaltet sich – sobald man sich mit dem Konzept vertraut gemacht hat – auch nicht als grosse Hürde.

Anzeige im Frontend

Wo immer die dynamischen Attribute angezeigt werden sollen, wird ein Fluid-Partial eingebunden. Dieses nennen wir eine DataLocation. Als Parameter muss dabei jeweils ein Location-Objekt, sowie das Datenobjekt übergeben werden, wie folgendes Diagramm illustriert:

Location-DataField

Ebenfalls daraus ersichtlich ist die n:n-Verknüfpung zwischen Locations und DataFields. Im Template wird das Partial wie folgt eingebunden:

<f:render partial="DataLocation" arguments="{location: myLocation, data: product}"/>

Dabei wird dem Parameter data das von AbstractDataModel abgeleitete Objekt und dem Parameter Location eine Location-Instanz übergeben. Mit der Location können mehrere DataFields verknüpft sein, welche dann vom data-Objekt die dynamischen Attribute abfragen.

Ein DataField kann einen beliebigen Typ haben, welcher die Art des Feldes definiert. Möglich sind zum Beispiel:

  • Checkbox
  • Radio-Button
  • Inputfeld
  • Textfeld
  • Datumsfeld

Location-Partial

Die vorhin definierten Typen müssen im Location-Partial abgebildet sein. In unserem Beispiel sieht das so aus:

{namespace m=MyCompany\MyApplication\ViewHelpers}
<f:for each="{location.dataFields}" as="dataField">
	<f:if condition="{dataField.type} == 0">

<div class="checkbox">
            <label>
                <f:form.checkbox name="data[{dataField.identifier}]" checked="{m:data.checked(data:'{data}', identifier:'{dataField.identifier}', value:'1')}" value="1"/>
                {dataField.name}
            </label>
        </div>

	</f:if>
	<f:if condition="{dataField.type} == 1">

<div class="radio">
			<label>
				<f:form.radio name="data[{dataField.identifier}]" value="{dataField.name}" checked="{m:data.checked(data:'{data}', identifier:'{dataField.identifier}', value:'{dataField.name}')}"/>
				{dataField.name}
			</label>
		</div>

	</f:if>
	<f:if condition="{dataField.type} == 2">

<div class="form-group">
			<label>{dataField.name}</label>
			<f:form.textfield name="data[{dataField.identifier}]" value="{m:data.value(data:'{data}', identifier:'{dataField.identifier}')}"/>
		</div>

	</f:if>
	<f:if condition="{dataField.type} == 3">

<div class="form-group">
			<label>{dataField.name}</label>
			<f:form.textarea name="data[{dataField.identifier}]" id="{identifier}{m:format.ucfirst(value:dataField.identifier)}" class="form-control" value="{m:data.value(data:'{data}', identifier:'{dataField.identifier}')}"/>
		</div>

	</f:if>
	<f:if condition="{dataField.type} == 4">

<div class="form-group">

<div class="date datepicker" data-date="{m:data.value(data:'{data}', identifier:'{dataField.identifier}')}">
				<label>{dataField.name}</label>

<div class="input-group">
					<f:form.textfield name="data[{dataField.identifier}]" value="{m:data.value(data:'{data}', identifier:'{dataField.identifier}')}"/>
					<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
				</div>

			</div>

		</div>

	</f:if>
</f:for>

Dem geschulten Auge fallen dabei drei Dinge auf:

  1. Es werden custom ViewHelper verwendet
  2. Für den POST-Request werden die Daten in einem separaten Daten-Array gespeichert; das muss beim Speichern berücksichtigt werden
  3. Das Partial muss immer innerhalb eines <form>-Tags liegen

ViewHelper

Format\UcfirstViewHelper

Ist soweit selbsterklärend. Es braucht nur einen Linien Code in der render-Methode:

return ucfirst($value);

Data\CheckedViewHelper

Bestimmt, ob eine Checkbox oder ein Radio-Button gechecked ist oder nicht. Die render-Methode beinhaltet folgende Zeilen:

if(!$data instanceof \MyCompany\MyApplication\Domain\Model\AbstractDataModel) {
	return 'false';
}
$method = 'get'.ucfirst($identifier);
return ($data->$method() === $value) ? 'checked' : 'false';

Data\ValueViewHelper

Liest die von den in der Location definierten DataFields angeforderten Daten aus einem AbstractDataModel-Objekt.

$method = 'get'.ucfirst($identifier);
$value = $data->$method();

Actions

Die edit- und update-Actions für Views, welche das DataLocation-Partial laden, müssen angepasst werden.

editAction

...
$this->view->assign('myLocation', $this->locationRepository->findOneByIdentifier('myLocationIdentifier'));
...

updateAction

/**
 * @param \MyCompany\MyApplication\Domain\Model\Product $product
 * @param array $data Additional data to be set to product
 * @return void
 */
public function updateAction(Product $product, array $data = array()) {
	...
	foreach($data as $key => $value) {
		$method = 'set'.ucfirst($key);
		$product->$method($value);
	}
	...
}

Hier ist $product natürlich das zu speichernde AbstractDataModel-Objekt.

Abschluss

Nun muss noch die Verwaltung der Location- und DataField-Objekte eingebaut werden. Diese CRUD-Views und -Actions können ganz bequem per Flow-Kommandozeilentool erstellt werden.

Flow-Kommandozeilentool

Damit ist auch der zweite Teil abgeschlossen. Das Feature kann nun in der Typo3-Flow-Applikation verwendet werden.

TYPO3 Flow

Bei uns wurde das Feature natürlich für die spezifischen Kundenwünsche noch weiter angepasst, was auch problemlos möglich ist. Dies würde aber den Scope dieses Beitrages sprengen. Etwas tiefer in die Materie geht’s dann beim dritten Teil: DQL-Generator!

Individuallösungen

Kommentare (0)

Kommentar verfassen