Thomas Williams Thomas Williams - 17 days ago 44
Twig Question

Get Twig to only show authorised parts of a page or menu

I am planning on converting a website to using MVC and php.
I am planning on using the Twig template engine so that I can remove all code from the page templates, but I have hit on a problem, and that is with my menu. I only want a certain level of user to access each menu item.

I have a function which I call which returns true if a level is allowed.
So for instance if you have a level of purchase you will be allowed to see Menu Item 1, but not allowed to see Menu Item 2. For instance Sales would not be able to see the first two menu items.

I know that you cannot call functions or classes in Twig.

eg (my function is in a class called logon, but that shouldn't matter)

<?php if ($logon->user_access('purchase','accountant','poweruser')):?>
Menu Item 1
<?php endif;?>

<?php if ($logon->user_access('accountant','poweruser')):?>
Menu Item 2
<?php endif;?>

<?php if ($logon->user_access('sales')):?>
Menu Item 2
<?php endif;?>


The question is how do I make a template in twig with this functionality without using php?
I am totally new to using a template engine.

edit...
@Yoshi

Ok I include twig in my index.php with

require_once dirname(__DIR__).'/vendor/Twig/vendor/autoload.php';


Then whenever I want to render the page I call a function from my core/view

public static function renderTwig($template,$args =[])
{
static $twig = null;

if($twig === null){
$loader = new \Twig_Loader_Filesystem('../App/Views');
$twig = new \Twig_Environment($loader);
echo $twig->render($template, $args);
}
}


Then in my controller I call

View::renderTwig('Home/index.html',[
'css'=> $css,
'name' => 'Dave',
'colours' => ['red','green','blue']
]);


I set this all up from a tutorial I have been following called "Create php mvc project from scratch".

PS. I suppose in my renderTwig function I could send in variables which are used on every page and add it to the arg array for the page. That way I wouldn't need to add authentication to every page.

Answer Source

Here are some ways to add the function (or the object) to your views.

Disclaimer: I think it's really bad practice to setup the twig environment inside a static method of View. I'm just going along with it, so to not over complicate things here. But if you're interested in how to do it better. Read up on dependency injection.

<?php

require_once dirname(__DIR__).'/vendor/Twig/vendor/autoload.php';

// only as an example example
class AccessChecker
{
    public function user_access(...$levels)
    {
        return true;
    }
}

class YourTwigExtension extends \Twig_Extension
{
    /**
     * @var AccessChecker
     */
    private $logon;

    /**
     * @param AccessChecker $logon
     */
    public function __construct(AccessChecker $logon)
    {
        $this->logon = $logon;
    }

    /**
     * @inheritdoc
     */
    public function getFunctions()
    {
        return [
            new Twig_Function('user_access', [$this, 'checkUserAccess']),
        ];
    }

    /**
     * @param array ...$roles
     *
     * @return mixed
     */
    public function checkUserAccess(...$roles)
    {
        return $this->logon->user_access(...$roles);
    }
}

class View
{
    public static function renderTwig($template, $args =[])
    {
        static $twig = null;

        if($twig === null) {
            $loader = new \Twig_Loader_Filesystem('../App/Views');
            $twig = new \Twig_Environment($loader);

            // the extras:
            $logon = new AccessChecker();

            // with globals:
            $twig->addGlobal('logon', $logon);

            /*
             * {% if logon.user_access('purchase','accountant','poweruser') %}
             *   ...
             * {% endif %}
             */

            // with functions:
            $twig->addFunction(new Twig_Function('user_access', [$logon, 'user_access']));

            /*
             * {% if user_access('purchase','accountant','poweruser') %}
             *   ...
             * {% endif %}
             */

            // with an extension
            $twig->addExtension(new YourTwigExtension($logon));

            /*
             * {% if checkUserAccess('purchase','accountant','poweruser') %}
             *   ...
             * {% endif %}
             */
        }

        echo $twig->render($template, $args);
    }
}

Note: You obviously only need to choose one method of adding the functionality. I'd suggest to go for the extension. This way, later additions can happen in the same place, without a need to change the wiring up code.