PHP
Article
By Reza Lavaryan

Quick Tip: Convenience Hacks for Passing Data to Views

By Reza Lavaryan

In MVC based architectures, working with template engines is an inevitable part of the development routine. It usually goes like this: we prepare and pass the data to the view. In the view, we print them based on our layout design.

Here is a basic example of how it works using Twig.

<?php

// Twig initialization

// Preparing data

$user     = 'user data';
$posts    = 'posts';
$comments = 'comments';

$twig->render('author.page', [

    'user'     => $user,
    'posts'    => $posts,
    'comments' => $comments,

]);

// ...

There are times, however, when the number of variables might be much higher than this: ten or more. In that case, we’ll have a tall list of variables (as an associative array), being passed to the respective template. It gets messy and unreadable quickly. If only there was a way to just list what we need by name, and have PHP take care of the rest for us. Well… there is!

In this post, we’re going to talk about a trick for passing the defined variables to the view in a somewhat more convenient way: we can use the PHP native compact() function to handpick the data we need, by referring to the variables by name:

<?php

// Twig initialization

// Preparing data

$user     = 'user data';
$posts    = 'posts';
$comments = 'comments';

$twig->render('author.page', compact('user', 'posts'));

// ...

compact() accepts a list of names, looks for variables with those names (within the current scope) and returns them all as one associative array.

Sometimes it’s even more convenient to specify the variables we don’t need instead of those we do. Since there’s no built-in solution in PHP to compact all the variables (within the current scope) with a few of them excluded, we have to do this as a three-step process.

First we need to get all the variables within the controller’s scope with get_defined_vars():

<?php
// ...
$variables = get_defined_vars();
// ...

Then, we filter out the unwanted variable names from wanted ones, by using PHP’s array_diff function.

array_diff compares an array against one or more arrays, and returns the entries in the first array, which are not present in any of the other arrays.

<?php
// ...
$variables = get_defined_vars();
$including = array_diff(array_keys($variables), ['user', 'posts', 'comments']);
// ...

Finally, we can extract our desired variables (listed in $including) out of $variables by using array_intersect_key().

array_intersect_key() accepts a number of arrays and returns the entries of the first array the keys of which exist in all the other arrays.

Please note since array_intersect_key() compares keys to get the intersection of the arrays, we need to switch the keys with their associated values in $including by using array_flip():

<?php
// ...
$variables = get_defined_vars();
$including = array_diff(array_keys($variables), ['user', 'posts', 'comments']);

$vars = array_intersect_key($variables, array_flip($including));
// ...

Now, to make the procedure reusable, we can abstract the complexity into a helper function:

<?php

//Helpers.php

// ...

if (!function_exists('only_compact')) {

    function only_compact($values, $keys) {
        $keys = array_diff(array_keys($values), $keys);
        return array_intersect_key($values, array_flip($keys));

}

And this is how it will make things easier:

<?php

// Twig initialization...

// Preparing data

$user          = 'user data';
$posts         = 'posts';
$comments      = 'comments';
$anotherOne    = 'some value';
$yetAnotherOne = 0;
$andAnotherOne = 0;
$counter       = 0;
$test_1        = 1;
$test_2        = 2;
$test_3        = 3;
$test_4        = 4;
$test_5        = 5;
$test_6        = 6;

// Even more variables ...

$twig->render('author.page', only_compact(get_defined_vars(), ['counter', 'twig']));
);

// ...

As a result, we get an array of variables with all the unwanted variables excluded.

To use the helper file, we can add it under files (under autoload) in composer.json:

// ...

    "autoload":{
        "files": [
            "helpers.php"
        ]
    }

// ...

Since we’re using PHP’s built-in functions, the performance impact isn’t noticeable. Below is the result of a quick profile test done with Blackfire, before and after using the above techniques:

Without using the functions:

Without the functions

With using the functions:

with the functions

As you can see, the results of both profile runs are almost the same.

Hopefully, this trick will save you some typing!

Have any other tips to share in regards to templating and view efficiency? Let us know!

  • xwero

    The only_compact function has too many steps.
    1. get the keys form the haystack array
    2. get the keys array based on the haystack keys and excluded keys
    3. flip the keys array
    4. reduce the haystack array

    With the array_filter function the steps are reduced to one.
    1. check if the haystack key is not in the excluded array
    https://gist.github.com/xwero/d3fabe18744ca122b6e1d6d71c942546

    • Reza Lavaryan

      Thanks for the comment! However, this approach is not backward compatible as the optional flag in array_filter() has been introduced in PHP 5.6.

      Furthermore, array_intersect_key() has been specially designed for computing the intersection of arrays, so performance-wise, it is quite faster than array_filter() for calculating the intersection.

      • xwero

        Who uses versions below 5.6? They are end of life.

        If you need performance tweaks for your twig file data, you are doing something wrong.

        The point I am making is write code that does the job with the least amount of effort. The more steps that are in a function the harder it is to debug.

        • Reza Lavaryan

          Still many people using versions below 5.6, although it’s no longer supported.

          Although there are ways to improve the performance, but still micro optimizations like this should be considered.

          As you can see the complexity has been abstracted into a helper function. That’s what we do once, then we call it when needed. No performance sacrificed, no older platforms would be bothered.
          The maximum amount of effort here is just calling a function name just like the function you provided.

          Please explain what is being done wrong here, so we’ll be able to improve the article.

          Thanks.

          • xwero

            In a time with virtual machines all over the place, it’s bad that people don’t upgrade php.

            If you show this code to someone with little php experience it’s going to confuse them.
            You are doing something simple, and it looks complex.

            When i see code like that in a project i have to maintain, it’s thrown out the first chance I get. It’s just how I work. You have a different view and it will help with the way you work.
            I hope others learn from this article that is why i want to show another solution.

          • Reza Lavaryan

            I’m mainly talking about production environments.

            Of course It’s good to have more than a solution to a problem. Which one to use? It depends on your perspective :)

  • Waste of time. There are better places to improve your code.

    • Reza Lavaryan

      Well, this article is not about code improvement, but it’s just a quick convenient tip for anyone who might find this helpful.

      If you believe this is a waste of time, please share your ideas in this comment, so we’ll be able to improve the article.

      Thanks.

      • By your code I meant overall project code. Don’t get me wrong, thank you for your input, but I find this hacks you say useless. Imvho they don’t bring any benefit and just obscure what’s going on in the controller. And if you pass tons of variables to the view… you surely doing it wrong.

        • Reza Lavaryan

          Passing tons of variables to a view is a different problem and this trick is not meant to obscure that.
          This is just a way to save some lines of code by passing the defined variables within the current scope – excluding those we don’t need in the view.
          Tens of lines can be saved this way; However, I agree this trick might not be suitable for some people.

  • Thank you for this article.
    However for me you are creating complexity in your code for… not having a long list of variable? What the benefit to do so? Who will understand easily your helper?

    Interesting but I wouldn’t use that in my code.

    • Reza Lavaryan

      Thanks for reading through the article and thanks for the comment,

      As an answer to the question, the benefit is that it would give the user a little bit of convenience.. The complexity has been abstracted into a helper function, so the user will just have to remember the name.

      If we were to avoid all the complexities, PHP would have no built-in functions at all, or we would have no framework because the core was quite complex.

      • I still don’t see the complexity regarding the method render() with a long list of variable.
        However I see the complexity in your helper when you use four different function for manipulating arrays in two lines.

        The difference with PHP built-in functions: it is part of the language itself, which means that is it highly tested by a lot of people and well documented.

        • Reza Lavaryan

          There’s no complexity in render method and it there’s no section in the article mentioning that.
          By complexity, I meant the complexity of this technique.

          I think you got it wrong, this isn’t meant to be well-tested, well-documented module, which every’s must use, It is just a technique we’ve shared with others to do something in a more convenient way without sacrificing the performance.

  • bago zonde

    Interesting idea but what I’m doing in this case is that I’m creating kind of Data Transfer Objects or just entities, which contains properties related to the subject so the structure isn’t flat but segregated into separate objects/models. This way it’s testable without checking view layer itself, and in tests you can define expected/optional properties.

  • Jens von der Heydt

    Thanks for the article and your input but I wouldn’t use this method. The no 1 reason for me is this:
    You are using some “magic” helper functions here to shorten your code but using this method will make life of IDEs like PHPStorm very hard since you cannot use the refactoring methods like “rename variable” anymore. I’m pretty sure PHPStorm and other IDEs will even mark your variables as unused and that is a big problem. Somebody might just rename or delete those variables and only reading through the whole code will prevent somebody from doing so.

Recommended
Sponsors
Get the latest in PHP, once a week, for free.