Передайте несколько параметров директиве блейда

Я пытаюсь создать директиву блейда, чтобы выделить некоторые слова, которые будут возвращаться из моего поискового запроса.

Это моя директива лезвия:

class AppServiceProvider extends ServiceProvider

{
    public function boot()
    {
        Blade::directive('highlight', function($expression, $string){

            $expressionValues = preg_split('/\s+/', $expression);

            foreach ($expressionValues as $value) {
                $string = str_replace($value, "<b>".$value."</b>", $string);
            }

            return "<?php echo {$string}; ?>";
        });
    }

    public function register()
    {
    }
}

И я вызываю лезвие так:

@highlight('ho', 'house')

Но эта ошибка преследует меня:

Missing argument 2 for App\Providers\AppServiceProvider::App\Providers\{closure}()

Как это решить?


person Caio Kawasaki    schedule 06.12.2016    source источник


Ответы (8)


Blade::directive('custom', function ($expression) {
    eval("\$params = [$expression];");
    list($param1, $param2, $param3) = $params;

    // Great coding stuff here
});

и в шаблоне лезвия:

@custom('param1', 'param2', 'param3')
person Agung Darmanto    schedule 17.04.2018

Для ассоциативных массивов проще всего использовать eval(). Но его использование рекламируется как опасное, потому что это как открытие дыры, иглы для выполнения кода. В то же время eval() выполняется во время выполнения, он сохраняет код для выполнения в базе данных (кэширование [ну, это означает, что он кэширует скомпилированный байт-код]). Это дополнительные накладные расходы, поэтому производительность пострадает. Вот хорошая статья по теме [не читал и не вникал в детали]) https://link.springer.com/chapter/10.1007%2F978-981-10-3935-5_12.

Возможно, я вас понял! нет никакой разницы в производительности сервера, обслуживающего производительность, потому что представления кэшируются и генерируются только тогда, когда вы их меняете. Директивы транслируются в php-код и в другом процессе кэшируются. (вы можете найти сгенерированное представление в storage/framework/views)

введите описание изображения здесь Итак, для

Blade::directive('custom', function ($expression) {
    eval("\$myarray = [$expression];");

    // do something with $myarray
    return "<?php echo ..";
});

Все в порядке. Не о чем говорить про eval() и производительность (это делается и кешируется, а сгенерированный php-код — это тот, который будет выполняться снова и снова (просто убедитесь, что возвращенный php-код директивой не содержит eval() , если нет причины. Непосредственное использование eval() (которое будет использоваться для разных запросов снова и снова) повлияет на производительность. (Я хотел поговорить о eval(), я думаю, что это полезная информация)

так как мы можем анализировать форму массива ["sometin" => "я должен быть жалом", "" => "", ...].

eval("\$array = $expression;");
// then we can do what we want with $array 

Однако мы не можем передавать переменные. пример: @directive(["s" => $var]) если мы используем eval, $var не будет определено в области действия функции директивы. (не забывайте, что директива — это всего лишь способ красиво сгенерировать tempalte и превратить уродливый (не совсем уродливый) php-код в такую ​​​​директиву. На самом деле это наоборот, мы превращаем красивую директиву в php-код, который будет выполняется в конце. И все, что вы здесь делаете, это генерируете, строите, пишете выражение, которое сформирует окончательные php-страницы или файлы.)

Вместо этого вы можете передать переменную таким образом ["s" => "$var"] , чтобы она прошла через eval. А затем в своем операторе возврата используйте его пример:

return "<?php echo ".$array['s'].";?>";

когда шаблон будет сгенерирован, это будет <?php echo $var;?>;

Помните, если вы решите использовать eval, никогда не используйте его в возвращаемой строке! или, возможно, вы захотите в некоторых случаях.

Другое решение

(что просто) наряду с предлагаемыми решениями синтаксического анализа заключается в использовании формата json для передачи данных в вашу директиву и просто использовании json_decode. . (до меня только что дошло)

class AppServiceProvider extends ServiceProvider

{
    public function boot()
    {
        Blade::directive('highlight', function($json_expression){

            $myArray = json_decode($json_expression)

            // do something with the array
        });
    }

    public function register()
    {
    }
}

Вот пример, где мне нужно было это сделать: цель состоит в том, чтобы автоматизировать это

@php
    $logo = !empty($logo) ? $logo : 'logo';
    $width = !empty($width) ? $width : 'logo';
    //...    // wait i will not always keep doing that ! h h
@endphp // imaging we do that for all different number of view components ...

и поэтому я написал эту директиву:

 public function boot()
    {
        Blade::directive('varSet', function ($expr) {
            $array = json_decode($expr, true);

            $p = '<?php ';
            foreach ($array as $key => $val) {
                if (is_string($val)) {
                    $p .= "\$$key = isset(\$$key) && !empty(\$$key) ? \$$key : '$val'; ";
                } else {
                    $p .= "\$$key = isset(\$$key) && !empty(\$$key) ? \$$key : $val; ";
                }
            }
            $p .= '?>';

            return $p;
        });
    }

Мы используем это так:

@varSet({
    "logo": "logo",
    "width": 78,
    "height": 22
})// hi my cool directive. that's slick.

Почему эта форма работает? он передается как строковый шаблон, подобный этому

"""
{\n
    "logo": "logo",\n
    "width": 78,\n
    "height": 22\n
}
"""

Для использования в переменной шаблона передайте их как строку, подобную этой "$var", так же, как мы сделали с eval.

Для разбора из ["" => "", ..] формат может быть eval() - лучший выбор. Помните, что это делается при создании шаблонов, которые позже кэшируются и не обновляются, пока мы снова не внесем изменения. И не забудьте не использовать eval() в return ; директивная инструкция. (только если это нужно вашему приложению)

только для нескольких аргументов, а не для массива: подобная функция выполнит эту работу:

 public static function parseMultipleArgs($expression)
{
    return collect(explode(',', $expression))->map(function ($item) {
        return trim($item);
    });
}

or

public static function parseMultipleArgs($expression)
    {
        $ar = explode(',', $expression);
        $l = len($ar);

        if($l == 1) return $ar[0];

        for($i = 0; $i < $l; $i++){$ar[$i] = trim($ar[$i])}

        return $ar;
    }

и вы можете настроить их по своему усмотрению, используя str_replace для удаления таких вещей, как () ... и т. д. [короче говоря, мы тренируем свой собственный синтаксический анализ. RegEx может быть полезным. И зависит от того, чего мы хотим достичь.

Все вышеперечисленное — это способ анализа записей и разделения их на переменные, которые вы используете для создания шаблона. И так для того, чтобы сделать заявление о возврате.

ЧТО ЕСЛИ ВСЕ, ЧТО ВЫ ХОТИТЕ, ЭТО ПОЛУЧИТЬ, ЧТОБЫ ВАША ДИРЕКТИВА ВЗЯЛА МАССИВ С ПЕРЕМЕННЫМИ ИЗ ОБЛАСТИ ПРЕДСТАВЛЕНИЯ:

как в @section('', ["var" => $varValue])

Ну тут конкретно мы используем многоаргументный разбор, потом восстанавливаем выражение ["" => ..] отдельно (и тут не в этом дело).

Дело в том, что вы хотите передать массив, который будет использоваться в вашем коде (область просмотра). Вы просто используете его как есть. (можно запутать).

пример:

Blade::directive("do", function ($expr) {
    return "<?php someFunctionFromMyGlobalOrViewScopThatTakeArrayAsParameter($expr); ?>
});

Это будет оцениваться как

<?php someFunctionFromMyGlobalOrViewScopThatTakeArrayAsParameter(["name" => $user->name, .......]); ?>

А так все будет работать нормально. Я взял пример, где мы используем функцию, вы можете поставить всю логику. Директивы — это просто способ написать представление более красивым способом. Также это позволяет выполнять предварительную обработку и генерацию. Тихо приятно.

person Mohamed Allal    schedule 09.11.2018
comment
Мне понравилось решение json! - person realtebo; 23.01.2019
comment
Рад это слышать! - person Mohamed Allal; 24.01.2019

Я искал именно это решение, затем решил попробовать что-то другое, прочитав все, и в итоге нашел решение, которое вы и я искали.

Нет необходимости в обходных решениях JSON, взрывах, ассоциативных массивах и т. д., если только вы не захотите использовать эту функциональность для чего-то более сложного позже.

Поскольку Blade просто пишет PHP-код, который будет интерпретирован позже, все, что вы поместили в свою директиву @highlight, является точным PHP-кодом в строковом формате, который будет интерпретирован позже.

Что делать:

Создайте и зарегистрируйте вспомогательную функцию, которую вы можете вызывать во всем приложении. Затем используйте вспомогательную функцию в директиве вашего лезвия.

Определение помощника:

if(!function_exists('highlight')){

    function highlight($expression, $string){
        $expressionValues = preg_split('/\s+/', $expression);

        foreach ($expressionValues as $value) {
            $string = str_replace($value, "<b>".$value."</b>", $string);
        }

        return $string;
    }
}

Директива Blade:

Blade::directive('highlight', function ($passedDirectiveString){
        return "<?php echo highlight($passedDirectiveString);?>";
    });

Использование (пример):

<div>
    @highlight('ho', 'house')
</div>

Понимание:

Это эквивалентно написанию:

<div>
    {! highlight('ho', 'house') !}
</div>
person Everett    schedule 15.06.2019

Я думаю, вы можете передать только один параметр. Это не красиво, но вы можете передать свои параметры в виде массива следующим образом:

@highlight(['expression' => 'ho', 'string' => 'house'])

Таким образом, ваша директива может быть

class AppServiceProvider extends ServiceProvider

{
    public function boot()
    {
        Blade::directive('highlight', function($array){

            $expressionValues = preg_split('/\s+/', $array['expression']);

            foreach ($expressionValues as $value) {
                $array['string'] = str_replace($value, "<b>".$value."</b>", $array['string']);
            }

            return "<?php echo {$array['string']}; ?>";
        });
    }

    public function register()
    {
    }
}

Нашел здесь: https://laracasts.com/discuss/channels/laravel/how-to-do-this-blade-directive

person Santiago Mendoza Ramirez    schedule 06.12.2016
comment
Ошибка: Illegal string offset 'expression' - person Caio Kawasaki; 07.12.2016

Значение, полученное функцией директивы блейда, является жалом, поэтому вы должны выполнить синтаксический анализ, чтобы получить значения:

ЛЕЗВИЕ

@date($date, 'd-M-Y')

Провайдер службы приложений

Blade::directive('date', function ($str) {
  // $str = "$date, 'd-M-Y'";
  $data = explode(',',str_replace(' ', '', $str));
  //$data = ["$date", "'d-M-Y'"]
  $date = $data[0];
  $format = $data[1];
  return "<?= date_format(date_create($date), $format) ?>";
});
person Heddie Franco    schedule 12.06.2019

Если вы хотите сослаться на переменные в пользовательской директиве блейда, вам может не понадобиться передавать их непосредственно в директиву. Я решил эту проблему, вызвав директиву блейда из компонента блейда. Блейд-компоненты имеют локальную область переменных, поэтому вы можете просто передать все необходимые переменные в вызове блейд-компонента (не загрязняя область вашего представления). Этого достаточно, если вам на самом деле не нужно изменять переменные или использовать их для логики управления в вашей директиве.

//view.blade.php
@component('my-component',['myVar1'=> $something, 'myVar2'=>$somethingElse])
@endcomponent

//my-component.blade.php
@myBladeDirective('Two variables accessible')

//Boot method of relevant service provider
Blade::directive('myBladeDirective', function ($someVar) {
    return "<?php echo $someVar : {$myVar1} and {$myVar2};?>
});
person Trent Crawford    schedule 24.08.2019

Я нашел альтернативный подход к доступу к переменным View в Blade Directive.

Я хотел проверить, отображается ли данная строка как ключ массива в переменной, доступной в области представления.

Поскольку директива Blade возвращает PHP, который оценивается позже, можно «обмануть» директиву, разбивая имя переменной, чтобы она не пыталась его анализировать.

Например:

    Blade::directive('getElementProps', function ($elementID) {
        return "<?php
            // Reference the $elementData variable
            // by splitting its name so it's not parsed here
            if (array_key_exists($elementID, $" . "elementData)) {
                echo $" . "elementData[$elementID];
            }
        ?>";
    }); 

В этом примере мы разделили имя переменной $elementData, поэтому Blade Directive рассматривает его как пружину. Когда конкатенированная строка будет возвращена в лезвие, она будет оцениваться как переменная.

person Malcolm Wax    schedule 25.11.2019

Лучший способ выполнить эту задачу - именно так, как предложил @Everrett.

Я проверил код блейда, и именно так должна поступить и команда laravel.

Если вы посмотрите vendor/laravel/framework/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php на функцию compileDd, вы заметите, что они используют $arguments вместо $expression, как и во всех других функциях компиляции, найденных в vendor/laravel/framework/src/Illuminate/View/Compilers/Concerns/.

// CompilesHelpers.php
protected function compileDd($arguments)
{
    return "<?php dd{$arguments}; ?>";
}

//CompilesConditionals.php
protected function compileIf($expression)
{
    return "<?php if{$expression}: ?>";
}

И если вы посмотрите на vendor/symfony/var-dumper/Resources/functions/dump.php, вы увидите, что Laravel обрабатывает переменные аргументы с ... сплат-нотацией в функции dd.

if (!function_exists('dd')) {
    function dd(...$vars)
    {
}}

Таким образом, вы можете сделать такую ​​директиву: (я поместил свою пользовательскую функцию в app\helpers) Если вы сделаете то же самое, вам нужно убедиться, что вы избегаете обратной косой черты.

Blade::directive('customFunc', function ($expression) {
    return "<?php \\App\\Helpers\\customFunc({$arguments}); ?>";
});

и пользовательская функция, например:

/**
 * Custom function to demonstrate usage
 * @param mixed ...$args
 * @return void
 */
function customFunc(...$args): void {
    // Extract variables //
    // Use pad to get expected args, and set unset to null //
    list($arg1, $arg2, $arg3) = array_pad($args, 3, null);

    // Echo out args //
    echo "arg1: ${arg1} | arg2: ${arg2} | arg3: {$arg3}";
}

беги php artisan view:clear

И затем используйте директиву:

<div>
    @customFunc('hello','wonderful', 'world')
</div>

// Returns:
arg1: hello | arg2: wonderful | arg3: world

// Using
<div>
    @customFunc('hello', 'world')
</div>
// Returns:
arg1: hello | arg2: world | arg3:

Лучшая причина сделать это таким образом заключается в том, что если ваша функция развивается или изменяется, вам нужно только изменить функцию подчеркивания. Вам не придется очищать представления каждый раз, когда вы меняете код.

person Casper Wilkes    schedule 19.02.2020