How to pass values to filter / Filter Loader

If you need to pass a filter (also known as a helper) already defined values or database results, you can do it through anynomous function.

Static list

$genderTypes = [
    'ma' => 'Male',
    'f' => 'Female',
];

$this->template->addFilter('gender', function($gender) use($genderTypes) {
    if (isset($genderTypes[$gender])) {
        return $genderTypes[$gender];
    }
    return 'gender unknown';
});

Items list from database

$categoryList = $this->models->categories->fetchAll();

$this->template->addFilter('category', function($categoryId) use($categoryList) {
    if (isset($categoryList[$categoryId])) {
        return $categoryList[$categoryId];
    }
    return 'unknown';
});

Filter Loader

Another and easily extendable solution is to use your own filter loader. You can extend the class and simply test them and move to other projects.

Following example respects Dependency Injection, so it's passing only required services, you can use to access database, predefined parameters, path to dirs temp, app etc. Our loader contains filter `profilePicture`, which based on file name return route to profile photo, resp. to deafult “no profile photo” file.

namespace App;

class TemplateFilters
{
    /** @var string */
    private $wwwDir;

    /** @var Nette\Http\IRequest */
    private $httpRequest;

    public function __construct($wwwDir, Nette\Http\IRequest $httpRequest)
    {
        $this->wwwDir = $wwwDir;
        $this->httpRequest = $httpRequest;
    }

    /**
     * Method we will register as callback
     * in method $template->addFilter().
     */
    public function loader($helper)
    {
        if (method_exists($this, $helper)) {
            return [$this, $helper];
        }
        return call_user_func_array(callback($this, $helper), array_slice(func_get_args(), 1));
    }

/* === Following particular helpers === */

    /**
     * Display profile photos
     *
     * <code>
     * <img src="{'JohnDoe.jpg'|profilePicture}">
     * </code>
     *
     * @param  string name of file with photo
     * @return string route to profile photo of default picture
     */
    public function profilePicture($fileName)
    {
        $basePath = $this->httpRequest->url->scriptPath;
        if (is_file($this->wwwDir . '/photos/' . $fileName)) { // profile photo exits
            return $basePath . '/photos/' . $fileName;
        } else { // profile photo doesn't exist
            return $basePath . '/photos/noPhoto.jpg';
        }
    }
}

If you need use presenter functions in filter (redirect(), link() etc.), you have to pass service @application through the __construct. Presenter will be accessible through $application->getPresenter() during template rendering.

How to register filter loader?

Set it as a service first in application config.

common:
    services:
        myTemplateFilters:
            factory: App\TemplateFilters( %wwwDir%, @httpRequest )

Than we can register Filter loader in BasePresenter.php by callback to our method loader() into Template::addFilter().

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    /**
    * @var \App\TemplateFilters @inject
    */
    public $myTemplateFilters;

    public function createTemplate($class = null)
    {
        $template = parent::createTemplate($class);
        $template->addFilter(null, [
            $this->myTemplateFilters,
            'loader'
        ]);
        return $template;
    }
}