【问题标题】:How to handle Undefined Offset in laravel?如何在 laravel 中处理未定义的偏移量?
【发布时间】:2023-04-03 01:21:01
【问题描述】:

登录后进入页面时出现此错误:

compiled.php 第 11573 行中的 ErrorException:未定义的偏移量:0(查看: C:\xampp\htdocs\campusguru\resources\views\home.blade.php)

我知道这个错误的原因是我传递给视图的空变量。

我已经试过了:

if(isset($blog)) { do something }

在刀片视图中为:

{{ $blogs[0]->title or '' }}

无论如何我可以处理这个错误。还是有更好的方法?

【问题讨论】:

  • 所以这个修复有效吗?如果是的话,我觉得没问题,除了你应该使用三方括号({{{ 和 }}})来防止解释 HTML(XSS 缺陷)。
  • @christophetd 仅在 Laravel 4.x 中,Laravel 5 默认转义 HTML(使用双括号时)
  • 你解决了吗?
  • @davejal 我刚刚将获取方法从对象更改为数组并且它起作用了。
  • 当你在集合数组上循环时,只有 isset 是不够的,你还必须检查它的长度,所以,像这样改变条件 if( isset($blogs) 和 size($blog) > 0)

标签: php laravel undefined offset


【解决方案1】:

尝试以下方法:

{{ isset($blogs[0]) ? $blogs[0]->title : '' }}

如果您使用 foreach 来获取每个 $blog->title 使用

@foreach ($blogs as $blog)
  {{ $blog->title }}
@endforeach

【讨论】:

  • 根据你的说法可能是问题所在,因为我知道数组是空的,我也在正确处理它,可能会错过一些东西。
  • 好的,我整理好了。我只是将获取方法从对象更改为数组并且它起作用了。我仍然不知道为什么它不适用于对象。谢谢大家
【解决方案2】:

问题在于 $blogs 实际已定义,其值为 [](即空的 array),因此这意味着 isset($blogs) 语句将评估为 true。同样的事情对集合有效。如果集合为空(即没有元素但已定义)isset($blogs) 仍将评估为true,但访问$blogs[0] 将导致Undefined offset: 0 错误。

您可以尝试以下解决方案:

使用count

if(count($blogs)) { /* do something */ }

如果$blogs = []$blogs = null 函数count 将返回零,这意味着$blogs 为空。

使用empty

if(!empty($blogs)) { /* do something */ }

这相当于!isset($var) || $var == false 中描述的PHP Manual - empty

如果 var 存在并且具有非空、非零值,则返回 FALSE 价值。否则返回 TRUE

以下的东西被认为是空的:

  • ""(一个 空字符串)
  • 0(0 为整数)
  • 0.0(0 作为 浮动)
  • "0"(0 作为字符串)
  • NULL
  • FALSE
  • array()(一个空数组)
  • $var;(一个 声明了变量,但没有值)
  • 检查集合是否为空

    如果$blogsCollection 就足以使用`isNotEmpty() 方法检查它是否为空:

    @if($blogs->isNotEmpty()) <!-- Do your stuff --> @endif
    

    编辑

    我忘了添加刀片语法:

    @if(count($blogs)) <!-- Do whatever you like --> @endif
    

    @if(!empty($blogs)) <!-- Do whatever you like --> @endif
    

    编辑 2

    我正在为这个答案添加更多内容,以解决 cmets 中出现的一些问题。我认为您的问题如下:

    $blogs 是一个空集合,因此它已定义但没有元素。出于这个原因,if(isset($blogs)) 语句将评估为 true 通过第一个 if 条件。在您的刀片模板中,您正在检查 {{ $blogs[0]-&gt;title or '' }},它 绝对不等于 &lt;?php isset($blogs[0]-&gt;title) ? $blogs[0]-&gt;title : '' ?&gt;,正如 cmets 中所指出的那样,但它是一个将返回 truefalse 的表达式,因此即使$blogs[0] 存在,它也永远不会打印出title 参数。这里的问题是,当检查条件$blogs[0]-&gt;title 时,您实际上正在访问$blogs 集合的元素0,这将触发异常Undefined offset: 0,因为该集合实际上是空的。我说的是除了

    if(count($blogs)) { /* do something */ }
    

    (检查是否设置了$blogs 并且它的长度大于0)在您的模板中您应该这样做

    {{ isset($blogs[0]->title) ? $blogs[0]->title : '' }}
    

    或者更简洁

    {{ $blogs[0]->title ?: '' }}
    

    假设只有当$blogs 通过第一个if 时,控制流才会到达那里。如果问题仍然存在,恕我直言,问题出在代码的其他地方。

    【讨论】:

    • 对于{{ $blogs[0]-&gt;title or '' }},刀片实际上是&lt;?php isset($blogs[0]-&gt;title)?$blogs[0]-&gt;title:'';?&gt;。所以 OP 实际上也对$blogs[0]-&gt;title 进行isset() 检查,而不仅仅是$blogs
    • 我个人不明白你的评论。该问题清楚地要求检测Undefined offset: 0 错误,如果isset($blogs)` 重新运行true,我在上面的答案中解释了所有可能的错误原因。此外,我没有得到反对票,当答案完全超出问题范围或完全错误时,应该使用它。即使我的回答没有解决问题,我认为它已经足够详细,并且正在详细解释避免该问题的良好做法。所以我就是不明白。
    • 此外,我认为$blogs 是一个空集合,因此if(isset($blogs)) 检查将被评估为true。当值被传递给视图时,检查{{ $blogs[0]-&gt;tile or "" }} 将引发错误,因为实际上$blogs[0]-&gt;title 表达式是从解释器中评估的,就像access element 0 of $blogs and get the title parameter 一样。还有{{ $blogs[0]-&gt;title or '' }} is a boolean expression, so it will never print the title`参数,应该是{{ $blogs[0]-&gt;title ?: '' }}
    • 从第二条评论,我猜你不知道 laravel? {{ $blogs[0]-&gt;tile or "" }} 是一个 laravel 刀片模板注释,正如我所提到的,它转换为 &lt;?php isset($blogs[0]-&gt;title)?$blogs[0]-&gt;title:'';?&gt;。请参阅文档的“如果存在则回显数据”部分:laravel.com/docs/5.2/blade#displaying-data
    • 这就是为什么我说 OP 已经对 $blogs[0]-&gt;title 进行了 isset() 检查,但仍然出现错误,并且 OP 询问原因和解决方案。
    【解决方案3】:

    您可以使用 data_get() 帮助程序轻松解决此问题。

    例如:

    php artisan tink
    Psy Shell v0.8.11 (PHP 7.0.22-0ubuntu0.16.04.1 — cli) by Justin Hileman
    >>> 
    >>> $a = collect([[], null, App\Models\User::find(1)]);
    => Illuminate\Support\Collection {#887
         all: [
           [],
           null,
           App\Models\User {#896
             id: 1,
             name: "user1",
             email: "user1@thisisdevelopment.nl",
             last_name: "Gabrielle",
             first_name: "Rempel",
             deleted_at: null,
             created_at: "2017-08-12 15:32:01",
             updated_at: "2017-09-05 12:23:54",
           },
         ],
       }
    >>> data_get($a[0], 'name', 'nope');
    => "nope"
    >>> data_get($a[1], 'name', 'nope');
    => "nope"
    >>> data_get($a[2], 'name', 'nope');
    => "user1"
    >>> 
    

    所以在这种情况下:

    {{ data_get($blogs[0], 'title', '') }}
    

    data_get() 将适用于数组和对象,返回在第二个参数中定义的键或属性(这可以是laravel.dot.notation.style,或只是一个数组),如果对象,第三个参数将是默认返回值/array或key/attribute不存在,默认为null。


    编辑:

    刚刚看到要求额外解释原始代码为何不起作用的请求。

    索引 0 根本不存在于传递给视图的数组/集合中。

    >>> $a = [1 => App\Models\User::find(1)];
    => [
         1 => App\Models\User {#890
           id: 1,
           name: "user1",
           // ... etc
         },
       ]
    >>> $a[0]->name ?: 'nope';
    PHP error:  Undefined offset: 0 on line 1
    >>> $a[1]->name ?: 'nope';
    => "user1"
    

    如果 OP 使用刀片 or default 并不重要,它甚至不会进入三元语句,因为 $blogs 上缺少 0 索引。


    按要求编辑 2:

    所以你得到Undefined offset: x 错误的原因是因为 PHP 评估代码的顺序。

    Blade 的or default 在幕后不过是三元声明:

    return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/si', 'isset($1) ? $1 : $2', $value);
    

    所以这将使:

    isset($blogs[0]->title) ? $blogs[0]->title : ''
    

    isset() 将检查对象上的标题是否已设置,但要这样做,它需要 $blogs[0] 是一个有效对象。为了做到这一点,它将尝试从索引 0 处的 $blogs 数组中获取对象。但由于该索引不存在,它将触发带有 Undefined offset: 0 的异常。

    为了使用 Blade 的 or default 进行这项工作,您首先必须确保已定义 $blogs[0](最好还检查它是否是一个对象,否则您将尝试获取非对象错误,请注意,这不应该是视图的责任),之后您将能够像其他任何时候一样使用or default

    @if (isset($blogs[0]) && is_object($blogs[0]))
        {{ $blogs[0]->title or '' }}
    @else
        // some other default placeholder
    @endif
    

    在使用data_get() 时,基本上你会得到同样的偏移量错误,因为索引 0 仍然不存在。

    {{ data_get($blogs[0], 'title', '') }} // Undefined offset: 0
    

    你可以玩弄脏了然后这样做(这不会在任何地方通过任何代码审查,我根本不应该输入这个,这只是为了说明

    {{ data_get($blogs, '0.title', '') }} // Will display '' as it will check if key 0 exists
    

    无论如何,使用data_get() 你最终还是会做这样的事情,因为你需要确保$blogs[0] 是你可以使用的东西:

    @if (isset($blogs[0]))
        {{ data_get($blogs[0], 'title', '') }}
    @else
        // some other default placeholder
    @endif
    

    底线,最好的选择是在你的视图中依赖这样的索引,这根本不是你的视图的责任。

    Blade 的or default 可以完美地处理单个变量,但是在处理对象属性时,您只需要确保(父)对象存在即可。

    【讨论】:

    • 您的意思是 OP 将 $blog[0] 显式传递给刀片视图,而不是传递整个数组 $blog?这可能是 OP 的原因,但我开始这个赏金是因为我也有这个问题,而且我确信在我的情况下,我将整个数组 $blog 传递给我的视图。 (嗯,不完全一样的变量名,我的意思是整个数组)
    • 没有明确传递$blogs[0],但 OP 在他的视图中使用硬编码的$blogs[0]。它基本上取决于你如何获取你的数组/集合。并非所有键都可能存在。您可以使用dd($array) 轻松调试它并检查密钥。我不确定你的用例是什么,但我不建议在数组上使用这样的硬编码键。如果您想要第一项,请使用集合并使用$items-&gt;first()$items-&gt;shift(),或使用head() 帮助器获取数组的第一项。也许如果你能描述你的确切用例和当前代码,它可以用另一种方式解决?
    • 虽然 OP 以硬编码的方式使用$blogs[0],但三元语句应该已经检查过了,因此应该输出默认值,而不是抛出异常?根据官方文档,(laravel.com/docs/5.2/blade#displaying-data),{{ $blogs[0]-&gt;title or '' }}会被翻译成{{ isset($blogs[0]-&gt;title) ? $blogs[0]-&gt;title : '' }}。使用isset() 检查,为什么会抛出异常?
    • 见我最初回答的第二部分。 $blogs[0] 将触发异常,因为它是在 isset 检查之前评估的。 or default 执行一个简单的三元语句,在您的情况下,这仅在您在刀片或默认值之前对 $blogs[0] 本身执行 isset 时才有效。但是由于那又长又丑,请尝试使用 data_get() :)
    • 在刀片中使用或默认值的意义在于,它可以对空/未定义或设置的值执行,就像普通的三元语句一样。这是一个三元语句,它也会因为未定义的索引而失败,请参阅我的原始答案。您有责任确保要检查的值是有效的,未定义的索引是您作为开发人员应该处理的事情,如果您像这样在视图中请求索引,则更是如此。
    【解决方案4】:

    如果您有一个传递给视图的对象,假设您的数据是“帖子”,它被保存在这样的对象中: $obj->帖子。

    如果你然后去执行一个 foreach 循环,它将遍历每个帖子并打印出它的参数,如下例所示,当你实际有帖子时它可以很好地工作。

    @foreach($obj->posts as $post)
       <h1>$post->title</h1>
       <p>$post->content</p>
    @endforeach
    

    在执行循环之前,您需要检查属性是否已设置值。您可以为此使用 isset(),由于它是一种特殊形式,因此可以用作 isset($obj->posts) 或 isset($obj->posts[0])。不同之处在于后者只会检查数组键是否有任何值,因此如果您的索引键不是 0,它将返回 false。例如你有:

    $foo = ['first' => somevalue1, 'second' => somevalue2];
    isset($foo[0]); //returns false
    isset($foo['first']); //returns true
    isset($foo); //returns true
    

    我进行检查的方式如下:

    @if(isset($obj->posts))
       @foreach($obj->posts as $post)
          ...
       @endoforeach
    @endif
    

    【讨论】:

    • 介意添加一个示例来说明isset($obj-&gt;posts[0])isset($posts[0]) 的不同之处吗?您似乎想在中间段落中说明差异,但我不确定这是否是您要说的原因。
    • 不同之处在于第一个是访问对象内部的数组,而第二个是访问一个数组变量。主要区别在于 OP 正在使用对象,因此他必须首先检查该对象是否具有他试图访问的参数的任何值,以避免引发未定义索引的错误。要使后者工作,您已经必须访问该属性并将其存储在变量 $posts 中,如果所述属性没有值,这将引发问题。
    • 所以你的意思是isset() 不能处理对象内部的数组?
    • 可以,但这不是 OP 的问题。据我了解,OP 有问题,他检查了数组上的索引,但实际上并没有通过数组所在的对象访问它。
    【解决方案5】:

    我在控制器中这样做:

     if (empty($allFares) || count($allFares)==0){
                return back()->withError('No Fare Found For The City!');
            }
    

    刀片中的或:

       @if (!empty($allFares) || count($allFares)>0)
                   @foreach(allFares as $key=>$value)
    
                    @endforeach
       @endif
    

    【讨论】:

      【解决方案6】:

      从 PHP7 开始,您可以使用空合并运算符 ?? 来检查三元条件:

      @if($posts?? '')
       @foreach($posts as $post)
         <h1>$post->title</h1>
         <p>$post->content</p>
       @endforeach
      @endif
      

      如果你想直接打印任何变量,那么首先检查变量是否存在,所以你可以这样做:

      {{ $blogs && $blogs[0]->title ? $blogs[0]->title : '' }}
      

      【讨论】:

        猜你喜欢
        • 2020-01-02
        • 2021-07-12
        • 2014-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-20
        • 2021-07-06
        • 1970-01-01
        相关资源
        最近更新 更多