Beiträge getaggt mit extbase

So was wie MenuItemProcFunc in Content Element „Menü“ mit Fluid Styled Content

Früher, in den Zeiten von css_styled_content konnte man in das Inhaltselement „Menü“ mittels itemArrayProcFunc eigene Menü-Items einhängen. Das funktioniert nach wie vor mit HMENU und sieht so aus:

1 = TMENU
1 [...]
2 < .1
2 {
	wrap = <ul>|</ul>
	itemArrayProcFunc = My\Extension\Hooks\MenuItemArrayProcessor->process
}

In der Funktion kann man dann z.B. Items aus der eigenen Extension ins Menü hängen, damit es so aussieht als wären es Seiten.

Das hat mit css_styled_content gut funktioniert, da dort das Rendering des Inhaltselemente (unter anderem Menü) ebenfalls mit TypoScript gemacht wurde. Also wenn man den TypoScriptObjectBrowser richtig bedienen kann, dann findet man auch schnell raus, wo man den Code einsetzen muss.

Nun stand ich vor einem Problem (nein, falsch – Herausforderung). In fluid_styled_content ist das Rendering der Content Element komplett über Fluid. So gibt es z.B. einen ViewHelper, der für „Menü ausgewählter Seiten“ die Seiten holt. Die Ausgabe geschieht dann mit Fluid. Nix TypoScript!

Ich habe folgendes implementiert: Falls eine bestimmte Seiten-ID in der Liste vorkommt (es ist meine Ziel-Detailseite), dann schmeiss ich einen weiteren ViewHelper an, der mit die Items mit Titel und Parameter ermittelt. Das Rendering dann wieder mit Fluid.

(mehr …)

Einträge in Liste in Zweiergruppen ausgeben

Folgende Aufgabenstellung: Mit Fluid sollen für eine Slideshow jeweils zwei Elemente einer Liste gruppiert (also in 1 Div eingeschlossen) ausgegeben werden. Als erstes muss am Anfang ein Slide geöffnet werden, wenn die Liste nicht leer ist. Nach dem letzten Element muss man den Slide schließen, egal wieviele Elemente in der Liste sind. Dazwischen muss man nach zwei Elementen den Slide schließen und wieder öffnen. Und sieht dann der Code aus:

<div class="video-slideshow">
	<div class="slick-slideshow" data-slick='{
			"adaptiveHeight": true
		}'>
		<div class="slide"><!-- Slide BEGIN -->
			<f:for each="{videos}" as="video" iteration="i">
				<f:render section="Video" arguments="{video: video, settings: settings}" />
				<f:if condition="{i.isLast}">
					<f:then>
						</div><!-- Slide END -->
					</f:then>
					<f:else>
						<f:if condition="{i.index} % 2">
							</div><div><!-- Slide -->
						</f:if>
					</f:else>
				</f:if>
			</f:for>
	</div><!-- slick-slideshow -->
</div><!-- video-slideshow -->

Ok, übersichtlich ist was anderes…

Fluid Select mit Objekten in Hierarchie darstellen

Folgendes Datenmodell: Objekte vom Typ ‚Items‘ und Kategorien. Eine Kategorie hat eigentlich nur einen Titel, kann aber über das Feld ‚parent‘ einer anderen Kategorie als Kind zugewiesen werden, so dass eine Art Baum entsteht. So ist z.B. in Kategorie A.1 ist Kategorie A als Parent gesetzt.

- A
-- A.1
-- A.2
--- A.2.I
--- A.2.II
-- A.3
- B

Nun will ich in einem Select-Feld die Kategorien ausgeben, dabei würde ich gerne den vollen Pfad zur Kategorie im Select ausgeben und nicht nur den Titel der Kategorie. So würde dann die Auswahl im Select-Feld so aussehen:

A
A > A.1
A > A.2
A > A.2.I
[...]

Und das ist meine (wie ich finde elegante) Lösung: das Select-Feld in Fluid wird dann so definitert:

<f:form.select property="category" options="{categories}" prependOptionLabel="Bitte wählen" prependOptionValue="" optionLabelField="titlePath"/>

Die Eigenschaft titlePath exisitert in der Kategorie natürlich nicht, aber Fluid bedient sich da des Getters, also muss nur der entsprechende Getter definiert werden. In dem Getter ruft man dann die gleiche Funktion vom Parent auf. That’s it!

/**
 * @return string
 */
public function getTitlePath() {
    if($this->parent == null) {
        return $this->title;
    }
    return $this->parent->getTitlePath().' - '.$this->getTitle();
}

Fluid Formular für neues Objekt mit Select und leerer Option

Ich implementiere gerade eine Extension mit (vereinfacht) folgendem Aufbau: Objekte vom Typ ‚Items‘ können jeweils einer Kategorie zugeordnet werden. Die Kategorien sind wiederum in der Datenbank als Datensätze hinterlegt. So etwas kommt häufig vor, das beliebteste Beispiel ist wahrscheinlich ein Blog-Eintrag und die dazugehörige Kategorie.

Erstellt man diese Extension z.B. über den Extension Builder und lässt sich eine new/create-Action anlegen, kann man im Frontend nun auch Objekte vom Typ ‚Item‘ anlegen. Nun hatte ich folgende Hürde zu meistern: das Feld Kategorie ist ein Pflichtfeld, trotzdem soll in der Kategorieauswahl im Frontend eine zusätzliche leere Option angezeigt werden. Da höre ich schon den Schrei der Empörung: „Warum brauchst du eine leere Option? Das Feld ist doch ein Pflichtfeld!“. Weil dann der Benutzer gezwungen ist, sich für eine Option bewusst zu entscheiden und nicht einfach die erste Option angenommen wird.

Eine leere Option im Formular dranzuhängen ist einfach:

<f:form.select property="category" options="{categories}" prependOptionLabel="Bitte wählen" prependOptionValue="" optionLabelField="title" />

Im Model das Feld als Pflichtfeld deklarieren:

/**
 * @var \MY\MyExtension\Domain\Model\Category
 * @validate NotEmpty
 */
protected $category = null;

Wenn man nun das Formular im Frontend testet, passiert folgendes: beim Absenden des Formulars ohne etwas auszuwählen gibt es eine Exception: Exception while property mapping at property path "": PHP Catchable Fatal Error: Argument 1 passed to MY\MyExtension\Domain\Model\Item::setCategory() must be an instance of MY\MyExtension\Domain\Model\Category, null given. Das erwartete Verhalten wäre aber, dass der Validator greift und eine Fehlermeldung ausgibt.

Die Lösung ist das Setzen eines Default-Wertes im Setter:

/**
 * Sets the category
 *
 * @param \MY\MyExtension\Domain\Model\Category $category
 * @return void
 */
public function setCategory(\MY\MyExtension\Domain\Model\Category $category = null)
{
    $this->category = $category;
}

Fehler im allgemeinen Validator an ein Feld hängen

(attach object errors to a specific field)

Es gibt die Möglichkeit in Extbase Validatoren für Objekte zu definieren. Wenn man also ein Model mit dem Namen „Domain_Model_ObjectModel“ hat, wird falls vorhanden der Validator mit dem Namen „Domain_Validator_ObjectValidator“. Dabei wird nachdem die einzelnen Felder geprüft wurden mit dem angegebenen Validator das komplette Objekt überprüft. So kann man wie in meinem Beispiel feststellen, ob der Titel bereits vergeben worden ist (Unique). Normalweise wird die Fehlermeldung bei einer nicht erfolgreichen Validierung an das Objekt drangehängt. Da ich die Fehlermeldungen neben dem Feld ausgebe (siehe dazu Fehlermeldung mit Fluid direkt neben dem Feld ausgeben), wollte ich den Fehler an das Attribut title dranhängen.

class Tx_MyExtension_Domain_Validator_ObjectValidator 
	extends Tx_Extbase_Validation_Validator_AbstractValidator
{
 
    /**
     * Check if there is a object with the same name
     * @param  Tx_MyExtension_Domain_Model_Object $object
     * @return bool
     */
	public function isValid($object) {
		$objectRepository = t3lib_div::makeInstance('Tx_MyExtension_Domain_Repository_ObjectRepository');
		$sameNameCount = $objectRepository->countByTitleExcludeUid($object->getTitle(), $object->getUid());
		if($sameNameCount > 0) {
			if (!isset($this->errors['title'])) {
				$this->errors['title'] = new Tx_Extbase_Validation_PropertyError('title');
			}
			$error = new Tx_Extbase_Validation_Error(Tx_Extbase_Utility_Localization::translate('error.object_title_exists', 'MyExtension'), '1312448731');
			$this->errors['title']->addErrors(array($error));
			return FALSE;
		}
		return TRUE;
    }
 
}

Tags: , ,

Geschrieben in TYPO3 | Kommentare deaktiviert für Fehler im allgemeinen Validator an ein Feld hängen