laravel 向blade指令传递多个参数

hrysbysz  于 2023-02-25  发布在  其他
关注(0)|答案(9)|浏览(151)

我正在尝试创建一个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}()

如何解决呢?

jogvjijk

jogvjijk1#

对于关联数组,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 ..";
});

还可以,没什么好谈的()和性能(它已经完成并缓存了,生成的php代码将被反复运行(只要确保该指令返回的php代码不包含eval(),除非有原因)。使用eval()(它将被反复用于不同的请求)将影响性能。(我想谈谈eval(),我认为这些是有用的信息)
我们可以解析数组形式["sometin"=〉"i should be sting",""=〉"",...]。

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。(我刚刚想到的)

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();指令。(仅当应用程序需要时)

    • 只用于多个参数,而不是数组:**这样的函数就可以完成这项工作:
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来删除像()......等这样的内容[简而言之,我们自己进行解析。RegEx可能会有帮助。这取决于我们想要实现什么。
以上这些都是解析条目并将它们分离成变量的方法,这些变量用于生成模板,也用于编写return语句。

如果您只想让您的指令从视图范围中获取一个变量数组,该怎么办:

例如@section('', ["var" => $varValue])
这里我们特别使用多参数解析,然后分别恢复[""=〉..]表达式(这不是重点)。
关键是当你想传递一个数组在你的代码中使用时(视图范围)。你只要照原样使用它就行了。(这可能会让人困惑)。

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

这将计算为

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

所以一切都会好的,我举了一个例子,我们使用一个函数,你可以把所有的逻辑,指令只是一种方法,用一种更漂亮的方式写视图,它也允许预览处理和生成,很不错。

qcuzuvrc

qcuzuvrc2#

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

    // Great coding stuff here
});

在叶片模板中:

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

twh00eeo3#

我一直在寻找这个确切的解决方案,然后决定尝试一些不同的东西后,阅读一切,并最终想出了解决方案,你和我都在寻找。
不需要JSON解决方案、爆炸、关联数组等......除非您以后需要更复杂的功能。
因为Blade只是写出PHP代码供以后解释,所以无论您在@highlight指令中放置了什么,都是字符串格式的PHP代码,这些代码将在以后解释。

操作方法:

创建并注册一个可以在整个应用程序中调用的helper函数,然后在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>
8ulbf1ek

8ulbf1ek4#

我认为你只能传递一个参数,虽然不太美观,但你可以像这样把参数作为数组传递:

@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

kuhbmx9i

kuhbmx9i5#

完成这项任务的最好方法正是@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函数中用... 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:

这样做的最好理由是,如果你的函数进化或改变,你只需要修改下划线函数,你不必每次改变代码时都清除视图。

vlf7wbxs

vlf7wbxs6#

我发现了另一种访问Blade指令中的View变量的方法。
我想检查给定的字符串是否作为数组键出现在视图范围内可访问的变量中。
由于刀片指令返回的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指令将其视为字符串。当连接后的字符串返回到Blade时,它将被视为变量。

avwztpqn

avwztpqn7#

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

2o7dmzc58#

blade指令函数接收到的值是字符串,因此,必须进行解析以获取值:

刀片

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

6ju8rftf9#

如果你想在一个自定义的blade指令中引用变量,你可能不需要把它们直接传递给这个指令。我通过从一个blade组件中调用blade指令解决了这个问题。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};?>
});

相关问题