将多个参数传递给刀片指令

时间:2016-12-06 19:54:53

标签: php laravel blade laravel-5.3 laravel-blade

我正在尝试创建一个裁剪指令,以突出显示将从我的搜索查询中返回的一些字词。

这是我的刀片指令:

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}()

如何解决?

9 个答案:

答案 0 :(得分:5)

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

    // Great coding stuff here
});

并在刀片模板中:

@custom('param1', 'param2', 'param3')

答案 1 :(得分:3)

对于关联数组, eval()可能是最简单的。但是它的使用被认为是危险的,因为它就像您开了一个洞,是执行代码的针。同时 eval()在运行时执行,然后将它存储要执行的代码存储在数据库中(缓存[这意味着它缓存了已编译的字节码])。这是额外的开销,因此性能会受到打击。这是一篇很好的论文,主题为[未读或未进入细节])https://link.springer.com/chapter/10.1007%2F978-981-10-3935-5_12

好吧,我可能已经了解您了!,服务器服务性能没有任何性能差异,因为视图是缓存的,只有在更改视图时才生成。指令被翻译成php代码,并在另一个过程中被缓存。 (您可以在存储/框架/视图中找到生成的视图)

enter image description here 所以

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语句中,使用它作为示例:

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

模板生成时将为<?php echo $var;?>

请记住,如果您决定使用eval,请不要在返回的字符串中使用它!或者在某些情况下可能需要。

另一种解决方案

(这很容易实现),是使用 json格式将数据传递到指令,而只需使用 json_decode < / strong>。 (它刚来找我)

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()是最佳选择。请记住,这是在模板生成时完成的,该模板稍后会缓存,并且不会更新,直到我们再次进行更改。并记住不要在return中使用eval();指令说明。 (仅在您的应用程序需要时)

仅用于多个参数,而不用于数组: 像这样的函数就可以了:

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

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删除()... etc之类的内容[总之,我们使用自己的解析方法。正则表达式可能会有所帮助。并取决于我们要实现的目标。

以上所有方法都是解析条目并将其分离为用于生成模板的变量的方法。因此,用于制作您的退货声明。

如果您想让自己的指挥权与视野范围内的变量作斗争,则

喜欢@section('', ["var" => $varValue])

特别是在这里,我们使用多参数解析,然后分别恢复[“” => ..]表达式(这里不是重点)。

关键是当您想要传递要在代码中使用的数组(查看范围)时。您可以按原样使用它。 (这可能会造成混淆)。

例如:

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

这将得出

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

因此一切都会正常。我举了一个例子,我们使用一个函数,可以放所有逻辑。指令只是一种以更优美的方式编写视图的方式。它还允许预览处理和生成。安静点。

答案 2 :(得分:1)

我认为你只能传递一个参数。它不漂亮,但您可以将参数作为数组传递:

@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

答案 3 :(得分:0)

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

        list($arg1, $arg2) = explode(',',str_replace(['(',')',' ', "'"], '', $arguments));

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

        $output = "";

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

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

答案 4 :(得分:0)

blade指令函数上收到的值是一个字符串,因此,您必须进行解析才能获取这些值:

刀片

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

AppServiceProvider

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) ?>";
});

答案 5 :(得分:0)

我一直在寻找这个确切的解决方案,然后在阅读完所有内容后决定尝试不同的方法,最后提出了您和我都在寻找的解决方案。

除非需要以后用于更复杂的功能,否则不需要JSON解决方法,爆炸,关联数组等。

由于Blade只是写出PHP代码以供以后解释,因此您将@highlight指令中放置的内容完全是字符串格式的PHP代码,将在以后进行解释。

做什么:

制作并注册一个可以在整个应用程序中调用的辅助函数。然后在您的Blade指令中使用helper函数。

助手定义:

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::directive('highlight', function ($passedDirectiveString){
        return "<?php echo highlight($passedDirectiveString);?>";
    });

用法(示例):

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

理解:

这等同于写出:

<div>
    {! highlight('ho', 'house') !}
</div>

答案 6 :(得分:0)

如果要在自定义的Blade指令中引用变量,则可能不需要将其直接传递给指令。我通过从刀片组件内部调用blade指令解决了这个问题。刀片组件具有局部变量作用域,因此您可以简单地将调用中所需的所有变量传递给刀片组件(不会污染视图范围)。只要您实际上不需要修改变量或将它们用作指令中的控制逻辑,就足够了。

//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};?>
});

答案 7 :(得分:0)

我找到了一种在Blade指令中访问View变量的替代方法。

我想检查给定的字符串是否在视图范围内可访问的变量中作为数组键出现。

当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指令将其视为弹簧。当连接的字符串返回到刀片时,它将被评估为变量。

答案 8 :(得分:0)

完成此任务的最佳方法正是@Everrett建议的。

我检查了刀片服务器的代码,这正是laravel团队必须要做的。

如果您在vendor/laravel/framework/src/Illuminate/View/Compilers/Concerns/CompilesHelpers.php函数中浏览compileDd,您会发现它们像其他所有编译一样使用$arguments而不是$expressionvendor/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 splat表示法处理变量参数。

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:

这样做的最好理由是,如果您的函数进行了演化或更改,则只需修改下划线函数。每次更改代码都不必清除视图。