Kontextbasierter Inhalt mit Flag-Modul und eigenem Views-Default-Argument-Handler

Das Views-Modul ist von Haus aus ein sehr mächtiger Partner für das Erstellen von Drupal-Webseiten. Es ist das am häufigsten installierte Modul und wurde in Drupal 8 aufgrund seiner vielfältigen Möglichkeiten in den Core integriert.

Besonders hervorzuheben ist die Möglichkeit, die Funktionalität von Views durch das komplexe Pluginsystem zu erweitern. Mit Hilfe von Handlern, Filtern und anderen Plugins können eigene Use-Cases durch Views abgedeckt werden. 

In diesem Blogbeitrag möchte ich anhand eines ausgedachten Beispiels erläutern, wie man einen "Default Argument Handler" schreibt und ein einfaches Kontext-System mit Hilfe des Flag-Moduls erstellt.

Hinweis: Der Code des fertigen Moduls befindet sich zum Testen der Funktionalität auch auf Github.

Der Use-Case

Der Kunde produziert im Familienbetrieb biologisches Tierfutter und möchte seinen Absatz nun über den Onlinehandel erweitern. Desweiteren vertreibt die Tochter des Kunden für Katzen und Hunde jeweils ein monatlich erscheinendes Magazin mit Tipps und Tricks. Die Magazine sollen auf der Seite des Kunden mit beworben werden. Ältere Artikel aus dem Magazin werden später online zur Verfügung gestellt.

Ein Hauptziel soll es sein, dass der User nur für seine Lieblingstierart ausgewählte Produkte und Artikel angezeigt bekommen soll. Über das Flag-Modul markieren wir pro Benutzer, welche Tierarten bevorzugt werden.

Hinweis: Der Einfachheit halber werden wir die Tierarten als Benutzer manuell wählen. 

Vorbereitung

Anlegen der Flags

Zunächst legen wir ein Vokabular "Flag Context" mit beliebigen Begriffen (Terms) an, die später dann geflaggt werden.

blog flag

Anschließend erstellen wir unter admin/structure/flags eine neue Flag mit Flagtype "Taxonomy Term", der als Bundle "Flag Context" auswählt.

flag bundle

Inhaltstyp erweitern

Damit wir später einer Flag Inhalte zuweisen können, fügen wir ein "Entity Reference"-Feld zu einem Inhaltstyp (hier "Article" aus der Standardinstallation) hinzu, damit wir Inhalte mit den Terms verlinken können. Testweise legen wir je Term dann mindestens einen Node an. Für den Use-Case würden wir dieses Field ebenso an Produkte anfügen, uns reicht nun aber erst mal das Beispiel mit dem Inhaltstypen.

node type

Anschließend flaggen wir einen Term, damit wir später die Funktionalität testen können. Dazu gehen wir auf eine Termseite und markieren den Term.

flag content

Views erstellen

Anschließend erstellen wir zur Visualisierung einen View, um dem Benutzer später seine Präferenzen anzeigen zu können.

Der View filtert "Taxonomy Terms" auf das Vokabular "Flag Context" und hat als Relationship "Flags: Taxonomy Term flag" auf den "Flag Context" und den "Current User". Als Display nutzen wir "Block".

views relationship
flag relationship 2
views flag detail

Zusätzlich zum Titel blenden wir einen Link ein, damit die Flag auch unflagged werden kann. 

flag flag link

Der Block wird testweise in die "Sidebar First" geschoben. Zum Schluss der Vorbereitung erstellen wir die Basis für den späteren Funktionstest. Dazu erstellen wir einen View mit Page Display, der Inhalte vom Typ "Article" filtert. Als "Contextual Filter" nutzen wir "Content: has taxonomy term ID". Diesen "Contextual Filter" erweitern wir später mit der gewünschten Kontextfunktion.

Programmieren des Handlers

Views-Handler müssen durch ein Modul eingebunden werden, damit es durch das Plugin-System registriert wird. Dazu ist nicht viel nötig, bietet hier aber die meisten Schwierigkeiten wenn es darum geht, herauszufinden, warum der eben erstellte Handler nicht zur Verfügung steht.

1. Die .info-Datei

Es bietet sich an, Views-Handler und -Plugins in jeweils eigene Unterverzeichnisse zu legen. Damit Drupal die Dateien findet, müssen wir in der ".info"-Datei den Ort des Handlers angeben! 

[gist:5263964]

2. Die .module-Datei

Damit sich Views für unser Modul interessiert, müssen wir zunächst hook_views_api() einbinden:

[gist:5263997]

Views sucht dann nach einer Datei, die für das Modul relevanten Code zur Verfügung stellt. Übliche Praxis ist es, das Ganze in eine modulename.views.inc Datei auszulagern.

3. Die .views.inc-Datei

In einem Array sagen wir Views, welche Art von Plugins wir benutzen möchten und wo der Code für das jeweilige Plugin zu finden ist. Dazu verwenden wir den hook_views_plugins()

[gist:5264010]

Mit diesen 3 Schritten haben wir unseren Handler vorbereitet. Nun geht es daran, den Handler mit Funktionalität zu füllen.

4. Die views_argument_default_flag.inc-Datei

In dieser Datei definieren wir den Handler als Klasse, indem wir von views_plugin_argument_default die Methoden erben und mit unserer Logik überschreiben. 

Der Baum:

[gist:5264038]

Zunächst definieren wir eine Form für die Einstellungen des Handlers. Wir wollen frei konfigurieren können, welche Flags und welche Vokabulare wir nutzen, um später einfacher weitere Flags und Vokabulare hinzufügen zu können. Desweiteren sollte man mehrere Terms übergeben und diese unterschiedliche kombinieren können. Außerdem sollte es eine Möglichkeit geben, einen Fallback zu definieren, falls es keinen Inhalt gibt. Dazu holen wir uns zunächst alle verfügbaren Flags aus dem System und bilden ein Checkboxes-Element. 

Wichtig ist hier, dass wir den "Default Value" korrekt implementieren. Über das Views-Objekt sind die entsprechenden Optionen verfügbar. 

[gist:5264049]

Die fertige Funktion:

[gist:5264059]

[gist:5264081]

Wie jede Form können wir auch eine Validate- und eine Submit-Funktion definieren. Wir beschränken uns hier auf den Submit:

[gist:5264086]

Anschließend müssen wir die Funktion get_argument() überschreiben. Diese Funktion kümmert sich später um die Rückgabe der Werte an den Filter.

In der Funktion vergleichen wir die vom Nutzer geflaggten Inhalte mit den Werten aus den Optionen. Dazu filtern wir alle Term-IDs mit den jeweiligen Vokabularen in einer Hilfsfunktion. Wichtig dabei ist, dass das Ergebnis als String zurückgegeben wird, also eben genauso, als ob man das Argument per Hand nutzt. 

[gist:5264112]

Die Hilfsfunktion für die .module-Datei:

[gist:5264117]

Damit wäre der Handler fertig. 

Nun fügen wir den Handler noch zum Contextual-Filter im zuletzt erstellten View hinzu.

default handler

Anschließend sollte in der Vorschau bereits der Artikel zu dem Tier sein, welches wir vorhin geflaggt haben.

galileo cat content

Zusammenfassung

Mit Hilfe von Flags lassen sich Nutzerpräferenzen einfach verwalten. Mit den richtigen Mitteln können Seitenelemente auf diese Flags reagieren und relevanten Inhalt je Nutzer anzeigen. Mit Hilfe des hier vorgestellten Handlers können dynamische Blöcke einfach erstellt und erweitert werden. Durch ein paar zusätzliche Tricks lässt sich noch mehr aus der Funktionalität herausholen, z. B. lässt sich das Flaggen beim Betrachten von Artikeln mit Rules automatisieren.

Github: https://github.com/Cyberschorsch/views_flag_context