TYPO3 Website Development: Fluid ViewHelpers

GET and POST Parameters in Fluid Templates

A while ago I wanted to hide the header/title in my overwrite of the fluid_styled_content/List.html template on the condition of the embedded plugin showing a detail view. fluid_styled_content/List.html is called as the wrapping content element for plugin contents. I wanted the list view of the plugin to display the partial tt_content/header – but not the detail view.

I had found a working Typoscript way at teamgeist-medien.de. But firstly I try to avoid Typoscript as much as I can and secondly I had to list every single potential plugin in the Fluid condition that was calling the Typoscript.

So I wrote a ViewHelper that returns an array with all the GET (url query parameters) or POST (submitted web form) parameters or false in case there are no parameters at all. Another use case for it is to hide sections of your layout as soon as a plugin shows the detail view:

Use case scenario

Hide a page’s content elements if plugin shows detail view

I wanted to create a page that contained an introduction (content element of type text) and a list of records of an extension. But when calling the detail view of one of these extension records, I wanted the introduction not to be displayed. I hadn’t found a core way to do this.

So I added a section ContentTop to the BackendLayout (identified by its colPos). Then I moved the Introduction content element to ContentTop and left the Plugin content element in MainContent. In my page template I will call both section in a row – but ContentTop was only to be displayed as long as no plugin detail view is called. For this I created the following ViewHelper (for the Fluid template of this use case, see below):

ViewHelper Returning GET or POST Parameters (and if there are any at all)

(add it to your Site Package Extension folder Classes/ViewHelpers for example)

By default this ViewHelper returns all GET parameters (URL query parameters) or FALSE in case there are no additional query parameters, thus when a regular TYPO3 page is called.

Optionally you can use it to

<?php
namespace YourVendor\YourExtension\ViewHelpers;

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;

/**
 * returns either $_GET (default) or $_POST global arrays or specific query parameter strings
 * returns false if getOrPost argument asks for something else
 * returns false if $_GET or $_POST are empty except when returnEmptyArray argument is set to true (default is false)
 *     example: {namespace:queryParameters(getOrPost: 'POST', returnEmptyArray: 'true')}
 *         returnEmptyArray must either be a lower-case string or 0/1
 * returns false if queryParam argument can not be found in the array
 * @return mixed
 */

class QueryParametersViewHelper extends AbstractViewHelper
{
    public function initializeArguments()
    {
        parent::initializeArguments();
        $this->registerArgument('getOrPost', 'string', 'Can be GET (default) or POST (case sensitive)', false, 'GET');
        $this->registerArgument('returnEmptyArray', 'boolean', 'true if you want an empty array to be returned instead of boolean false', false, false);
        $this->registerArgument('queryParam', 'string', 'add if you need to ask for a specific queryParam (for extension POST queryParams use “tx_yourext_yourplugin|queryParam”)', false, false);
    }

    /**
     * @return string
     */

    public function render()
    {
        $getOrPost = $this->arguments['getOrPost'];
        $returnEmptyArray = $this->arguments['returnEmptyArray'];
        $queryParam = $this->arguments['queryParam'];

        if ($getOrPost === 'GET') {
            $return = GeneralUtility::_GET();
        } elseif ($getOrPost === 'POST') {
            $return = GeneralUtility::_POST();
        } else {
            $return = false;
        }

        if (is_array($return) && (!empty($return) || $returnEmptyArray)) {
            /**
             * sanitise every value recursively
             */

            array_walk_recursive($return, function(&$val) {
                $val = htmlentities($val, ENT_QUOTES, 'UTF-8');
            });

            /**
             * return specific query parameter
             */

            if ($queryParam) {
                if (strstr($queryParam, '|')) {
                    $paramParts = explode('|', $queryParam);
                    $returnParam = $this->recursiveIsset($return, $paramParts);
                } else {
                    $returnParam = (isset($return[$queryParam])) ? $return[$queryParam] : false;
                }
                return $returnParam;
            }

            /**
             * return array
             */

            return $return;
        } else {
            return false;
        }
    }

    /**
     * checks for an array element in an array of unspecified dimension depth
     * and returns the value of that array element if found or false if not
     * @param array $getOrPost The array to look in
     * @param array $arrayDimensions An array of dimensional keys
     *     (e.g. ['dimA','dimB'] will check for $getOrPost['dimA'][dimB‘])
     * @return mixed
     *
     * taken from this stackoverflow answer: https://stackoverflow.com/a/11041473/4274248
     */

    private function recursiveIsset($getOrPost, $arrayDimensions, $i = 0)
    {
        $newGetOrPost = null;

        if (array_key_exists($arrayDimensions[$i], $getOrPost)) {
            $newGetOrPost = $getOrPost[$arrayDimensions[$i]];
        }

        if (!isset($newGetOrPost)) { // isset() returns false on an element that exists but is null
            return false;
        } elseif (count($arrayDimensions) > $i + 1) {
            return $this->recursiveIsset($newGetOrPost, $arrayDimensions, $i + 1);
        } else {
            return $newGetOrPost;
        }
    }
}
}

Fluid Usage Examples

Page template that shows certain sections only for basic plugin views

The basic plugin view is that which you embed in the content element. It will be displayed on the page without additional query parameters in the URL. This is often the list view of a collection of records. The above mentioned ViewHelper will by default return FALSE in this case.

{namespace someName = YourVendor\YourExtension\ViewHelpers}

<f:layout name="base" ></f:layout>
<f:section name="Content">
        <f:if condition="{someName:queryParameters()} == FALSE">
                <f:format.raw>{content-top}</f:format.raw>
        </f:if>
        <f:format.raw>{main-content}</f:format.raw>
</f:section>
        <f:format.raw>{main-content}</f:format.raw>
</f:section>

fluid_styled_content/List.html template hiding the tt_content/header partial for any GET parameter

{namespace someName = YourVendor\YourExtension\ViewHelpers}

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <div id="c{data.uid}" class="content-box insert-plugin">
        <f:if condition="{someName:queryParameters()} == FALSE">
            <f:render partial="Header" arguments="{_all}" ></f:render>
        </f:if>
        <f:cObject typoscriptObjectPath="tt_content.list.20.{data.list_type}" data="{data}" table="tt_content"></f:cObject>
    </div>
</html>
    </div>
</html>

[UNTESTED] fluid_styled_content/List.html template hiding the tt_content/header partial for any GET parameter except the News pagination

this isn’t tested yet but it should work

{namespace someName = YourVendor\YourExtension\ViewHelpers}

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <div id="c{data.uid}" class="content-box insert-plugin">
        <f:if condition="{someName:queryParameters()} == FALSE || {someName:queryParameters(queryParam: 'tx_news_pi1|@widget_0|currentPage')}">
            <f:render partial="Header" arguments="{_all}" ></f:render>
        </f:if>
        <f:cObject typoscriptObjectPath="tt_content.list.20.{data.list_type}" data="{data}" table="tt_content"></f:cObject>
    </div>
</html>
    </div>
</html>

fluid_styled_content/List.html template showing the tt_content/header partial for any plugin action and showing an additional header for the detail view (show action)

  • the partial Header will only be displayed if a specific controller action is being called
  • an additional H3 will be displayed in the detail view (show action)
{namespace someName = YourVendor\YourExtension\ViewHelpers}

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <div id="c{data.uid}" class="content-box insert-plugin">
        <f:if condition="{someName:queryParameters(queryParam: 'tx_yourext_yourplugin|action')}">
            <f:render partial="Header" arguments="{_all}" ></f:render>
        </f:if>
        <f:if condition="{someName:queryParameters(queryParam: 'tx_yourext_yourplugin|action')} == 'show'">
            <h3>additional header for detail view</h3>
        </f:if>
        <f:cObject typoscriptObjectPath="tt_content.list.20.{data.list_type}" data="{data}" table="tt_content"></f:cObject>
    </div>
</html>
    </div>
</html>

Just as in the previous example you can ask for FALSE in this one as well if you need it to avoid the f:then/f:else tags:

<f:if condition="{someName:queryParameters(queryParam: 'tx_yourext_yourplugin|action')} == FALSE">