【问题标题】:Pass multiple parameters to a blade directive将多个参数传递给刀片指令
【发布时间】:2017-04-21 14:04:06
【问题描述】:

我正在尝试创建一个刀片指令来突出显示将从我的搜索查询中返回的一些单词。

这是我的刀片指令:

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

如何解决?

【问题讨论】:

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


    【解决方案1】:
    Blade::directive('custom', function ($expression) {
        eval("\$params = [$expression];");
        list($param1, $param2, $param3) = $params;
    
        // Great coding stuff here
    });
    

    在刀片模板中:

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

    【讨论】:

      【解决方案2】:

      对于关联数组,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" => "i should be sting", "" => "", ...]。

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

      但是我们不能传递变量。例如:@directive(["s" =&gt; $var]) 如果我们使用 eval$var 将在指令函数范围内未定义。 (不要忘记指令只是一种漂亮地生成模板的方法,并将丑陋(不是真的丑陋)的 php 代码变成这样的指令。事实上它是相反的,我们正在将美丽的指令变成将是的 php 代码最后执行。而您在这里所做的只是生成、构建、编写将形成最终 php 页面或文件的表达式。)

      您可以做的是以这种方式传递变量 ["s" => "$var"] ,所以它将通过 eval。然后在你的 return 语句中,使用它的例子:

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

      当模板生成时,这将是&lt;?php echo $var;?&gt;

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

      另一种解决方案

      easy)与建议的解析解决方案一样,是使用 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() 是最好的选择。请记住,这是在模板生成时完成的,这些模板稍后会被缓存,并且不会更新,直到我们再次进行更改。记住不要在 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 删除 () ...等内容 [简而言之,我们锻炼自己的解析。正则表达式可能会有所帮助。并且取决于我们想要实现的目标。

      以上所有方法都是解析条目并将它们分成用于生成模板的变量的方法。因此,您的退货声明也是如此。

      如果您只想让您的指令从视图范围中获取一个包含变量的数组:

      点赞@section('', ["var" =&gt; $varValue])

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

      关键是当您想要传递要在代码中使用的数组时(视图范围)。您只需按原样使用它。 (这可能会令人困惑)。

      例如:

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

      这将评估为

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

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

      【讨论】:

      • 我喜欢 json 解决方案!
      • 很高兴听到这个消息!
      【解决方案3】:

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

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

      用法(示例):

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

      理解:

      这相当于写出:

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

      【讨论】:

        【解决方案4】:

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

        @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

        【讨论】:

        • 错误:Illegal string offset 'expression'
        【解决方案5】:
        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}\"; ?>";
            });
        

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              【解决方案8】:

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

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

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

              【讨论】:

                【解决方案9】:

                完成这项任务的最佳方法正是按照@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:
                

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-08-15
                  • 2015-11-16
                  • 1970-01-01
                  • 2019-05-05
                  相关资源
                  最近更新 更多