Exercise with extbase and Ajax

25 July 2014 Comments Off

Introduction

This example is a dummy problem, which aims to create a basis for explaining the use of 3 concepts: format parameter, StandaloneView and PHP based views. The solution works with TYPO3 version >= 6.0

The problem

Let’s suppose that we need to implement the following task: we have a page that looks, at load, like in the below model:

Box C

When clicking on the link “Show Items” from box A, 2 new boxes (D & E) should be loaded, without page refresh (ajax call)

Box E

 

We will assume that Box D and Box E display the same records, let’s call them Items. Let’s also consider that Box D and Box E need to be templatable. As we can’t include the both boxes in the same Fluid template, due to the page layout, we need 2 different Fluid templates, one for each box. As they display the same records, for performance reasons, we can load them using a single ajax call, which will return a JSON object with 2 attributes, having as values the HTML for Box D and the HTML for BOX E. On clicking the title of the Item, the user will be redirected to a page displaying the details of the item.

Solution

Let’s call the extension test_extension, the controller Test.We will need:
an action to return the JSON, let’s call it ajax,
an action to display the detail (single) page of the item, let’s call it show
let’s consider we would also need an action to display the “Show items” link (although in the current example this is not necessary), let’s call it tease

So we need to resolve 2 issues: 1. the ajax call should return a JSON and 2. the ajax action should parse 2 different templates.

1. The ajax call should return a JSON

Let’s suppose we registered the plugin in ext_localconf.php with the name Test:

\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
Arxia.’ . $_EXTKEY,
‘Test’,
array(
Test=> ajax,show,tease,
),
);

The most convenable solution to handle this ajax request is using typenum (typenum allows to create different versions of the same page, accessible by adding the type GET/POST parameter in the url of the page). So we need to configure a new version of the site (let’s choose typenum 121) in the test_extension/Configuration/Typoscript/setup.txt file.

My favorite way of including a plugin through Typoscript is to actually select a content (tt_content). A container page may be used to add the content. This is my favorite way because we will have access to all the configurations of the plugin’s flexform – that will allow to an advanced editor to easily change some parameters, like the page which contains the records.

testPage = PAGE
testPage.typeNum = 121
testPage.config.disableAllHeaderCode = 1
testPage.config.additionalHeaders = Content-type:application/json
testPage.config.xhtml_cleaning = 0
testPage.10 = CONTENT
testPage.10{
table = tt_content

#
# a content of type plugin, will have in the list_type field a value which will respect the following format
# <extensionname>_<plugin_name>, in lowercase; if the extension name has “_” they will be ignored;
# as we registered the plugin Test in ext_localconf.php, the list_type value will be “testextension_test”;
# adding the below condition, we ensure that no undesired content is selected
#
select.andWhere = list_type = “testextension_test”
select.pidInList = <the container page id>

renderObj < tt_content

# below we remove tt_content default wrapping
# and preserve only the plugin output
renderObj.stdWrap >
renderObj.list.10 >
renderObj.list.20.stdWrap >

}

Although we can simply return the JSON from the action, let’s return it from a view, as, in real life, some output related decisions may be managed from the view. But as we may need the PHP power inside the view, we are going to use a PHP based view. This type of view use pure PHP to generate the output.

We need to add the file corresponding the view in the folder <extension>/Classes/View/<ControllerName>. The file name should follow the naming convention <Action><Format>.php. We need the format to be JSON, so the path for our file should be “test_extension/Classes/View/Test/AjaxJson.php”.

The view should extend TYPO3\CMS\Extbase\Mvc\View\AbstractView (which implements the interface TYPO3\CMS\Extbase\Mvc\View\ViewInterface) and it should implement the method render. Let’s consider that we will assign to the view, from the ajax action, 2 variables: boxAContent and boxBContent. We will retrieve them as keys of the variables attribute of the view. The content of our view file will be:

<?php
namespace Arxia\TestExtension\View\Test;

class AjaxJson extends \TYPO3\CMS\Extbase\Mvc\View\AbstractView {

public function render() {
$jsonArray = array(
‘boxAContent’ => $this->variables['boxAContent'],
‘boxBContent’ => $this->variables['boxBContent']
);
return json_encode($jsonArray);
}
}
?>

However, we need to tell extbase the request is in JSON format, in order to correctly recognize the view. We can do this using the less known parameter: format. Also let’s suppose we want to cache the JSON response (as any other page would be cached). We will create the link to “Show items” in Box A. As this is outputted by the tease action, we also have a corresponding view, which should contain the following code:

<f:link.action action=”ajax” controller=”Test” format=”json” pageType=”121″>
<f:translate key=”LLL:EXT:test_extension/Resources/Private/Language/locallang_db.xlf:show_items” />
</f:link.action>

The parameter format will add tx_testextension_test[format]=json, which will tell extbase that the request is using JSON format. After we have this link, we can use Javascript to change the normal navigation when clicking the link with an ajax request.

2. The ajax action should parse 2 different templates (Fluid files)

In order to parse from an action 2 different Fluid files, we will need to use a special view provided by extbase: TYPO3\CMS\Fluid\View\StandaloneView. We will create in Test controller a protected function which also assign to the specific template some variables. Let’s consider we added the Fluid files in Partial folder, as they may be used in other FLUID views.

protected function renderFileTemplate($templateFile, $variables) {
$view = $this->objectManager->get(‘TYPO3\\CMS\\Fluid\\View\\StandaloneView’);

$view->setControllerContext($this->controllerContext);

$extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);

// generate the template path
$relativeTemplateFilePath = $extbaseFrameworkConfiguration['view']['partialRootPath'] . $this->request->getControllerName() . ‘/’ . $templateFile;
$absoluteTemplateFilePath = TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($relativeTemplateFilePath);
$view->setTemplatePathAndFilename($absoluteTemplateFilePath);

// asign variables to the template
$view->assignMultiple($variables);

return $view->render();
}

The ajax action will call this function 2 times and it will assign the result of the 2 parsed templates to the PHP based view

public function ajaxAction() {
$items = $this->itemRepository->findAll();

// generate Box A
$boxATemplate = ‘BoxA.html’;
$variables = array(‘items’ => $items);
$boxAContent = $this->renderFileTemplate($boxATemplate, $variables));
$this->view->assign(‘boxAContent’, $boxAContent);

// generate Box B
$boxBTemplate = ‘BoxB.html’;
$variables = array(‘items’ => $items);
$boxAContent = $this->renderFileTemplate($boxATemplate, $variables));
$this->view->assign(‘boxBContent’, $boxBContent);
}

Final thoughts
Although the above example is just a dummy problem, all of the 3 concepts may be used in various real problems. An example of a real-life problem solved by StandaloneView can be found here: forge.typo3.org: How to use the Fluid Standalone view to render template based emails

Other useful links are:

Notes:
Most of the comments code were removed in order to save space.

 

Author: Alina Fleşer