2

I am working on a special template for the news extension tx_news in Typo3. I am completely new to Typo3 and especially Fluid.

What I want is an output of exactly 4 news items, but each of these items must have an image.

What I need is programming logic, something like: If the newsItem has an image, and less than 4 items have been rendered so far, then render. Else don't do anything.

I read this question and answer: TYPO3 Fluid complex if conditions so I suspect I need something like a viewhelper.

So far my templates has this code to output items:

    <f:for each="{news}" as="newsItem" iteration="iterator">
        <f:if condition="{newsItem.falMedia}">
            <f:if condition="{iterator.cycle}<=4">
                <f:render partial="List/TeaserItem" arguments="{newsItem: newsItem,settings:settings,iterator:iterator, contentObjectData:contentObjectData}" />
            </f:if>
        </f:if>
    </f:for>

But this will of course stop after iterating through news 4 times. So if one entry without image didn't get rendered, I will only have three items output.

I'd need an if condition kind of like this:

if ({newsItem.falMedia} && {iterator.cycle}<=4){
      render image }
else {iterator.cycle--}

but I can't figure out how to pass the iterator variable of my for-loop to the new viewhelper, and especially to pass it back to the for-loop.

Community
  • 1
  • 1
deadfishli
  • 729
  • 5
  • 17
  • I now tried writing an extendedForViewHelper, but while the regular if conditions respect condition={newsItem.falMedia} and return true/false, I can't seem to put that into my forViewHelper. I added param: `public function render($each, $as, $key = '', $reverse = FALSE, $iteration = NULL, $condition)` and inside `$iterationData = array( 'index' => 0, 'cycle' => 1, 'imageCount' => 0, 'total' => count($arguments['each']) );` and `if($arguments['condition']===TRUE){ $iterationData['imageCount']++; }` – deadfishli Jan 28 '15 at 09:50

1 Answers1

1

In short words this kind of logic isn't possible in Fluid - reason is simple -it's template engine.

You need to create your own extension and create a ViewHelper in it, which will take the collection of News will check if it has required settings (falMedia existing in this case) and will return limited array which you can iterate. Indeed, reusing f:for will be fastest solution.

I'm afraid, that's only way.

Here's the sample (compare it to original f:for viewhelper):

<?php
namespace TYPO3\CMS\Fluid\ViewHelpers;

class ForNewsWithMediaViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {

    /**
     * Iterates through elements of $each and renders child nodes
     *
     * @param array $each The array or \TYPO3\CMS\Extbase\Persistence\ObjectStorage to iterated over
     * @param string $as The name of the iteration variable
     * @param string $key The name of the variable to store the current array key
     * @param boolean $reverse If enabled, the iterator will start with the last element and proceed reversely
     * @param string $iteration The name of the variable to store iteration information (index, cycle, isFirst, isLast, isEven, isOdd)
     * @param int $limit Limit of the news items to show
     * @return string Rendered string
     * @api
     */
    public function render($each, $as, $key = '', $reverse = FALSE, $iteration = NULL, $limit = NULL) {
        return self::renderStatic($this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext, $limit);
    }

    /**
     * @param array $arguments
     * @param \Closure $renderChildrenClosure
     * @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
     * @param int $limit Limit of the news items to show
     * @return string
     * @throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception
     */
    static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext, $limit = NULL) {
        $templateVariableContainer = $renderingContext->getTemplateVariableContainer();
        if ($arguments['each'] === NULL) {
            return '';
        }
        if (is_object($arguments['each']) && !$arguments['each'] instanceof \Traversable) {
            throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('ForViewHelper only supports arrays and objects implementing \Traversable interface', 1248728393);
        }

        if ($arguments['reverse'] === TRUE) {
            // array_reverse only supports arrays
            if (is_object($arguments['each'])) {
                $arguments['each'] = iterator_to_array($arguments['each']);
            }
            $arguments['each'] = array_reverse($arguments['each']);
        }
        $iterationData = array(
            'index' => 0,
            'cycle' => 1,
            'total' => count($arguments['each'])
        );

        $limitCycle = 1;
        $output = '';
        /**
         * @type  $singleElement Tx_News_Domain_Model_News
         */
        foreach ($arguments['each'] as $keyValue => $singleElement) {

            if (is_null($singleElement->getFalMedia())
                || !is_null($limit) && $limitCycle > $limit
            ) {
                continue;
            }

            $limitCycle++;

            $templateVariableContainer->add($arguments['as'], $singleElement);
            if ($arguments['key'] !== '') {
                $templateVariableContainer->add($arguments['key'], $keyValue);
            }
            if ($arguments['iteration'] !== NULL) {
                $iterationData['isFirst'] = $iterationData['cycle'] === 1;
                $iterationData['isLast'] = $iterationData['cycle'] === $iterationData['total'];
                $iterationData['isEven'] = $iterationData['cycle'] % 2 === 0;
                $iterationData['isOdd'] = !$iterationData['isEven'];
                $templateVariableContainer->add($arguments['iteration'], $iterationData);
                $iterationData['index']++;
                $iterationData['cycle']++;
            }
            $output .= $renderChildrenClosure();
            $templateVariableContainer->remove($arguments['as']);
            if ($arguments['key'] !== '') {
                $templateVariableContainer->remove($arguments['key']);
            }
            if ($arguments['iteration'] !== NULL) {
                $templateVariableContainer->remove($arguments['iteration']);
            }
        }
        return $output;
    }
}

So you can use it in your view as:

<f:forNewsWithMedia each="{news}" as="newsItem" iteration="iterator" limit="4">
    <f:render partial="List/TeaserItem" arguments="{newsItem: newsItem,settings:settings,iterator:iterator, contentObjectData:contentObjectData}" />
</f:forNewsWithMedia>
biesior
  • 55,576
  • 10
  • 125
  • 182
  • Thanks a lot! I am a little bit confused, though. So I have to set up a new extension to put this viewhelper in? I can't just put it into the news-extension as a new viewhelper? – deadfishli Jan 28 '15 at 13:54
  • This sample you can put into `typo3/sysext/fluid/Classes/ViewHelpers/ForNewsWithMediaViewHelper.php` at least for testing, anyway, yes you shouldn't make any changes to the source of TYPO3 or extensions from repository, so ideal solution is creating own ext with custom namespace and then pasting it there. Otherwise you risk, that you'll lost it after next source update. – biesior Jan 28 '15 at 14:16
  • So, it does output only four elements, but it still outputs elements without pictures... – deadfishli Jan 28 '15 at 15:31
  • hey, I figured it out! I changed the first part of your conditions to this: `if (!$singleElement->getFalMedia() || !count($singleElement->getFalMedia()))` because getFalMedia always returns an array, so your condition happened to be true always. By counting the resulting array I can make it work! Thanks a lot! I'll still accept your answer as right, because it helped me figure out the gist of the whole thing! – deadfishli Jan 28 '15 at 16:07