fesja's avatar

How to do send two parameters to a blade directive?

Hi,

I want to write a directive that takes an array and a field name @join($job->locations, 'name') and produces New York / San Francisco.

I have tried to do this first approach but it's not working because it's receiving just one parameter, not two:

Missing argument 2 for App\Providers\AppServiceProvider::App\Providers{closure}() in AppServiceProvider.php line 28 at HandleExceptions->handleError('2', 'Missing argument 2 for App\Providers\AppServiceProvider::App\Providers{closure}()', '/home/vagrant/Sites/ajobs/app/Providers/AppServiceProvider.php', '28', array('array' => '($job->locations, 'name')')) in AppServiceProvider.php line 28

Blade::directive('join', function($array, $field) {

             return '<?php
                $values = [];
                foreach('.$array.' as $l) {
                    $values[] = $l->'.$field.';
                }
                echo join(" / ", $values);
            ?>';
        });

How can I fix this?

Thanks!

0 likes
11 replies
robgeorgeuk's avatar

I think you can only pass one parameter. It's not pretty but you could pass your parameters as an array like so:

@join([$job->locations, 'name'])

So your directive could be

Blade::directive('join', function($array) {

             return '<?php
                $values = [];
                foreach('.$array[0].' as $l) {
                    $values[] = $l->'.$array[1].';
                }
                echo join(" / ", $values);
            ?>';
        });
pmall's avatar

If it is a collection : { { $job->locations->implode('/') } }

usman's avatar

@fesja The Closure receives the arguments as a string for instance in your case the passed string will be ($job->locations, 'name') <-- note that the braces are included. You will need to split this and then it will work:

        \Blade::directive('join',function($arguments) {
            list($locations, $field) = explode(',',str_replace(['(',')',' '], '', $arguments));
            
            return '<?php '
                .'$values = [];'
                .' foreach(' . $locations . ' as $value) {'
                        .'$values[] = $value->'.$field.';'
                    .'}'
                .'echo join("/", $values)'
                . '?>';

        });

Also make sure the view cache is cleared. On L5.1 you can use the artisan view:clear command to clear the view cache. This is necessary for the recompilation to take place.

Usman.

4 likes
mikemand's avatar

Here's what I did for a rough datetime Directive which can accept up to two parameters:

\Blade::directive('datetime', function ($expression) {
    $segments = explode(',', preg_replace("/[\(\)]/", '', $expression), 2);

    $date = with(trim($segments[0]));

    $output = "<?php ";

    $output .= "echo (! empty({$date}) and {$date}->timestamp > 0) ? ";

    if (count($segments) > 1) {
        $format = trim($segments[1]);
        $output .= "{$date}->format({$format})";
    } else {
        $output .= "{$date}->format('m/d/Y')";
    }

    $output .= " : '';";

    $output .= " ?>";

    return $output;
});

I can then call it with any of the following (assuming the date is not NULL or 0000-00-00 00:00:00):

@datetime($entity->my_date_col) -> m/d/Y

@datetime($entity->my_date_col, 'Y-m-d') -> Y-m-d

@datetime($entity->my_date_col, 'c') -> ISO 8601 date, ex: 2004-02-12T15:19:21+00:00

@datetime($entity->my_date_col, 'l, F jS, Y \a\t h:i a') -> Friday, October 23rd, 2015 at 01:05 pm

If the date is NULL or 0000-00-00 00:00:00, it will return an empty string.

Edit: I should probably check if $date is an instance of Carbon or DateTime instead of just empty, but I was just sketching out a rough directive so I could stop running these checks in my views and save myself some headache.

fesja's avatar
fesja
OP
Best Answer
Level 1

@usman thanks a lot for the answer! I've used your answer, although we need to remove also the ' chars. This is the final code.

Blade::directive('join', function($arguments) {

            list($locations, $field) = explode(',',str_replace(['(',')',' ', "'"], '', $arguments));

            return '<?php '
                .'$values = [];'
                .'foreach(' . $locations . ' as $value) {'
                    .'$values[] = $value->'.$field.';'
                .'}'
                .'echo join(" / ", $values)'
                .'?>';
        });

@robgeorgeuk thanks for your time, however your answer didn't work. @mikemand thanks for your time, I ended up using other answer.

1 like
usman's avatar

You just copied my code and marked it as your own correct answer :( anyways happy programming laravel!

12 likes
kyslik's avatar

I use following one, works on L5.*

if ($expression[0] === '(')
{
    //Laravel 5.2 and less
    $expression = substr($expression, 1, -1);
}
$expression = str_getcsv($expression, ',', '\''); //expression is now array

In my case I call str_getcsv() in static function that does all the processing and note I have to enclose $expression like this SortableLink::render(\"{$expression}\")

1 like
reinier@OpenSourceAcademy.nl's avatar

Another option is to JSON encode the arguments: @join(["$job->locations", "name"]) and then in the serviceprovider: list($locations, $field) = json_decode($arguments);

1 like
sineld's avatar

This is my url directive helper:

    Blade::directive('url', function ($arguments) {
        $url = $arguments;
        if (strpos($arguments, ',')) {
            list($url, $extra) = explode(',', $arguments);
        }

        if (isset($extra)) {
            return "<?php echo url($url, $extra); ?>";
        }
        return "<?php echo url($url); ?>";
    });

// usage:

@url('my/sample/link', 123)
Daniel-Pablo's avatar

I want to add a solution to this conversation:

@role('distributor,provider,client,master,admin')
// html tags
@endrole

this tag call the directive the on the serviceProvider I end up with this

    Blade::if('role', function ($parameters) {
       $roles = explode("," , $parameters);
       foreach ($roles as $role) {
        $eval = //evaluate here your $role if statement,  like $user->role->isADMIN();
        if ($eval) {
           return true;
           break;
       }
   }
   return false;
});

this makes the magic, hope this helps some one !

Ken-vdE's avatar

TL;DR:
From my Stackoverflow post (stackoverflow.com/a/79894634/3017716):

\Blade::directive('someDirective', function($expression) {
    return <<<PHPSTRING
        <?php (function(\$param1, \$param2) {
            // Do something with the params, e.g.:
            \$param1 = 'prefix' . \$param1;
            echo '<div>' . \$param1 . \$param2 . '</div>';
        })($expression); ?>
        PHPSTRING;
});

Please or to participate in this conversation.