23

I am getting this error when I land on the page after logging in:

ErrorException in compiled.php line 11573: Undefined offset: 0 (View: C:\xampp\htdocs\campusguru\resources\views\home.blade.php)

I know that the cause of this error is the empty variable that I passed to the view.

I have already tried:

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

and in blade view as:

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

Is there anyway I could handle this error. Or is there a better way of doing it?

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
vs_lala
  • 715
  • 2
  • 8
  • 18
  • So this fix does work? If yes it looks OK to me, except that you should use triple brackets ({{{ and }}}) to prevent from interpreting HTML (XSS flaw). – christophetd Apr 12 '15 at 11:28
  • 2
    @christophetd Only in Laravel 4.x, Laravel 5 escapes HTML by default (when double brackets are used) – lukasgeiter Apr 12 '15 at 12:46
  • did you sort this out? – davejal Jan 10 '17 at 01:59
  • @davejal I just changed the fetching method from objects to array and it worked. – vs_lala Feb 20 '17 at 18:50
  • only isset is not enough when you are looping over the collection array, you also have to check for its length so , change condition like this if( isset($blogs) and size($blog) > 0) – Manish Champaneri Aug 31 '17 at 07:35
  • `if(isset($blog)) { do something }` Is there a typographical error within your if it should be `$blogs` instead if `$blog` and if its not an error then you can go for `!empty($blogs) && count($blogs)` – Narendrasingh Sisodia Sep 04 '17 at 13:43
  • all you can do is add @ before variable so it won't through error if that variable is not defined like this `@$blogs[0]->title` – Bhaumik Pandhi Sep 04 '17 at 15:52
  • @PandhiBhaumik Suppressing errors like that is really bad practice. – Robert Sep 05 '17 at 09:49

7 Answers7

16

Try the following:

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

If you are using a foreach to get every $blog->title use

@foreach ($blogs as $blog)
  {{ $blog->title }}
@endforeach
AlexioVay
  • 4,338
  • 2
  • 31
  • 49
iisurge
  • 321
  • 1
  • 5
  • what according to u could be the problem, because I know the array is empty and I am handling it properly too, may be m missing out on something. – vs_lala Apr 13 '15 at 06:23
  • Okay iI sorted it out. I just changed the fetching method from objects to array and it worked. I still dont know why it does not work on objects. Thanks guys – vs_lala Apr 13 '15 at 06:37
14

The problem is that $blogs is actually defined and its value is [] (i.e. empty array) so it means that isset($blogs) statement will evaluate to true. Same thing is valid for collections. If a collection is empty (i.e. has no elements but it's defined) isset($blogs) will still evaluate to true but accessing $blogs[0] will cause an Undefined offset: 0 error.

You could try the following solutions:

Using count

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

if $blogs = [] or $blogs = null the function count will return zero so that means that $blogs is empty.

Using empty

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

This is the equivalent of writing !isset($var) || $var == false as described in the PHP Manual - empty:

Returns FALSE if var exists and has a non-empty, non-zero value. Otherwise returns TRUE.

The following things are considered to be empty:

  • "" (an empty string)
  • 0 (0 as an integer)
  • 0.0 (0 as a float)
  • "0" (0 as a string)
  • NULL
  • FALSE
  • array() (an empty array)
  • $var; (a variable declared, but without a value)
  • Checking if a collection is empty

    If $blogs is a Collection is sufficient to check if it is not empty using `isNotEmpty() method:

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

    EDIT

    I forgot to add the blade syntax:

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

    or

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

    EDIT 2

    I'm adding more content to this answer in order to address some of the issues presented in the comments. I think that your problem is the following:

    $blogs is an empty collection, so it's defined but it has no elements. For this reason the if(isset($blogs)) statement will evaluate to true passing the first if condition. In your blade template you are making the check {{ $blogs[0]->title or '' }} that is absolutely not equal to <?php isset($blogs[0]->title) ? $blogs[0]->title : '' ?> as pointed out in the comments, but it is an expression that will return true or false, so it will never print out title parameter even if $blogs[0] exists. The problem here is that when checking the condition $blogs[0]->title you are actually accessing the element 0 of the $blogs collection that will trigger the exception Undefined offset: 0 because the collection is actually empty. What i was saying is that in addition to the

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

    (that checks that $blogs is set and that it's length is greater than 0) in your template you should do

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

    or more concisely

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

    assuming that the control flow will arrive there only if the $blogs passed the first if. If the issue still persists the problem is elsewhere in your code IMHO.

    Desh901
    • 2,633
    • 17
    • 24
    • For `{{ $blogs[0]->title or '' }}`, the blade actually do `title)?$blogs[0]->title:'';?>`. So the OP actually also do the `isset()` checking on `$blogs[0]->title`, not only on `$blogs`. – cytsunny Aug 31 '17 at 02:06
    • Personally i don't understand your comment. The question clearly asks to detect the `Undefined offset: 0` error, and if the `isset($blogs`)` retruns `true` what i've explained in the answer above addresses all the possible causes of the error. Moreover i don't get the downvote, it should be used when an answer is totally out of the question scope or totally wrong. Even if my answer does not resolve the issue i think that it is detailed enough and it is explaining in details good practices to avoid the issue. So i just don't get it. – Desh901 Aug 31 '17 at 07:33
    • Moreover i think that `$blogs` is an empty collection and so the `if(isset($blogs))` check will be evaluated to `true`. When the value is passed to the view, checking `{{ $blogs[0]->tile or "" }}` will throw an error, because actually the `$blogs[0]->title` expression is evaluated from the interpreter like that `access element 0 of $blogs and get the title parameter`. Moreover `{{ $blogs[0]->title or '' }} is a boolean expression, so it will never print the `title` parameter, it should be `{{ $blogs[0]->title ?: '' }}`. – Desh901 Aug 31 '17 at 07:43
    • From the second comment, I guess you don't know laravel? `{{ $blogs[0]->tile or "" }}` is a laravel blade template comment that translate to `title)?$blogs[0]->title:'';?>` as I mentioned. See the 'Echoing Data If It Exists' section of the document: https://laravel.com/docs/5.2/blade#displaying-data – cytsunny Sep 01 '17 at 02:07
    • That's why I said OP has already do the `isset()` checking on `$blogs[0]->title` but still get the error, and the OP asking for a reason and a solution. – cytsunny Sep 01 '17 at 02:10
    • And I've widely discussed it – Desh901 Sep 01 '17 at 06:33
    6

    You can simply solve this with the data_get() helper.

    For example:

    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"
    >>> 
    

    So in this case:

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

    data_get() will work both on arrays and objects, returning the key or attribute defined in the second param (this can be laravel.dot.notation.style, or just an array), the 3rd param will be the default return value if the object/array or the key/attribute does not exist, the default is null.


    Edit:

    Just saw the request for the extra explanation on why the original code wasn't working.

    Index 0 simply does not exist on the array/collection that is passed to the view.

    >>> $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"
    

    It doesn't matter if OP used the blade or default, it doesn't even make it to the ternary statement because of the missing 0 index on $blogs.


    Edit 2 as requested:

    So the reason you get the Undefined offset: x error is because of the order in which PHP evaluates the code.

    Blade's or default is behind the scenes nothing more than a ternary statement:

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

    So this will make:

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

    isset() will check if title on the object is set, but to do so, it will require $blogs[0] to be a valid object. In order to do that, it will try and get the object from the $blogs array at index 0. But since this index does not exist, it will trigger the Exception with an Undefined offset: 0.

    In order to make this work with Blade's or default, you would first have to ensure that $blogs[0] is defined (and preferably also check that it's an object, otherwise you'll get the trying to get property of non-object error, please note that this should not be the responsibility of the view), after that you would be able to use the or default as you would any other time.

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

    Basically you will get the same offset error when using data_get(), because index 0 still does not exist.

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

    You could play dirty and do this (this would not pass any code review anywhere and I should not have typed this at all, this is just to illustrate)

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

    Anyway, with data_get() you would still end up doing something like this, as you would need to make sure $blogs[0] is something you can work with:

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

    Bottomline, the best option would be not to rely on indexes like this in your view, this is simply not the responsibility of your view.

    Blade's or default works perfectly on single variables, but when dealing with object attributes, you would just have to make sure the (parent) object exists when doing so.

    Robert
    • 5,703
    • 2
    • 31
    • 32
    • You mean the OP is passing `$blog[0]` explicitly to the blade view, instead of passing the whole array `$blog`? This could be the reason for OP, but I am starting this bounty because I have this problem too, and I am sure in my case, I am passing the whole array `$blog` to my view. (Well, not exactly the same variable name, I mean the whole array) – cytsunny Sep 05 '17 at 02:33
    • Well not explicitly passing `$blogs[0]`, but OP is using `$blogs[0]` hardcoded in his view. It basically depends on how you fetch your array/collection. Not all keys might exist. You can easily debug this with a `dd($array)` and check the keys. I'm not sure what your usecase is, but I would not suggest using hardcoded keys like this on the array. If you want the first item, use a collection and use `$items->first()` or `$items->shift()`, or use the `head()` helper to fetch the first item of an array. Maybe if you can describe your exact usecase and current code, it can be solved another way? – Robert Sep 05 '17 at 09:45
    • Although OP is using `$blogs[0]` in a hardcoded way, the ternary statement should have checked that and should therefore output the default value, instead of throwing exception? According to the official document, ( https://laravel.com/docs/5.2/blade#displaying-data ), `{{ $blogs[0]->title or '' }}` will be translated to `{{ isset($blogs[0]->title) ? $blogs[0]->title : '' }}`. With the `isset()` checking, why would that throw the exception? – cytsunny Sep 05 '17 at 10:45
    • See the second part of my initial answer. `$blogs[0]` will trigger the Exception because it's evaluated prior to the isset check. The or default does a simple ternary statement, in your case this will only work if you do an isset on $blogs[0] itself prior to the blade or default. But since that's long and ugly, try data_get() instead :) – Robert Sep 05 '17 at 11:07
    • I don't understand... Why would `$blogs[0]` trigger the exception before the isset check in blade? And if that's the case, any variable that is not set will break the `or` statement in blade. What's the point of having the `or` shorthand in blade then? – cytsunny Sep 05 '17 at 11:19
    • Because it is evaluated before the isset, the isset is performed on the title of the object in $blogs[0], so it needs the value of title. So it will always fail before the isset is performed. – Robert Sep 05 '17 at 11:39
    • 1
      The point of having the or default in blade is that it can be done on values that are null/undefined or set, like a normal ternary statement. It is a ternary statement and it will also fail because of the undefined index, see my original answer. You are responsible for making sure the value to check is valid, undefined indexes is something you should handle as developer, even more so if you request an index in your view like this. – Robert Sep 05 '17 at 11:45
    • Mind explain a bit more on this in the answer? This is the best explanation I have ever seen under this post. – cytsunny Sep 05 '17 at 12:45
    • Um.... seems this is a blade template specific problem? `echo isset($testing_array[10]->testing)?$testing_array[10]->testing:'Not Exist';` does not throw exception even without declaring `$testing_array` before. – cytsunny Sep 06 '17 at 05:38
    2

    I do this way in controller:

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

    OR in blade:

       @if (!empty($allFares) || count($allFares)>0)
                   @foreach(allFares as $key=>$value)
    
                    @endforeach
       @endif
    
    Nur Uddin
    • 1,798
    • 1
    • 28
    • 38
    1

    If you have an object that's passed to the view and let's say your data is "posts" which is being held inside an object like this: $obj->posts.

    If you then go and do a foreach loop which would iterate trough every post and print out its parameters like in the example below it works perfectly well when you actually have posts.

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

    Before doing the loop you'd want to check if attribute has been set with values. You can use isset() for this, and since it's a special form it can be used as isset($obj->posts) or isset($obj->posts[0]). The difference is that the latter will only check if the array key has any value so if your index key is anything but 0, it'll return false. For instance you have:

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

    The way I'd make the check is the following:

    @if(isset($obj->posts))
       @foreach($obj->posts as $post)
          ...
       @endoforeach
    @endif
    
    Nino Korent
    • 31
    • 1
    • 7
    • Mind if adding an example showing how `isset($obj->posts[0])` is different from `isset($posts[0])`? It seems you want to state the difference in the middle paragraph but I am not sure if that's why you want to say. – cytsunny Aug 29 '17 at 10:08
    • The difference is that the first one is accessing array inside an object while the second is accessing the variable which is an array. The main difference is that the OP is using object and thus he has to first check if that said object has any value for the parameter he's trying to access in order to avoid raising errors for undefined indexes. For the latter to work you already had to access the attribute and store it in variable $posts, which would raise a problem if the said attribute has no values. – Nino Korent Aug 29 '17 at 11:14
    • So you mean `isset()` cannot handle array inside object? – cytsunny Aug 31 '17 at 02:07
    • It can, but that's not the problem the OP had. As I understand it, the OP has problem that he checked an index on an array but didn't actually access it trough the object the array was in. – Nino Korent Aug 31 '17 at 16:02
    0

    As of PHP7 you can use null coalescing operator ?? for checking ternary conditions:

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

    And if you want to print any variable directly then check first that the variable exists or not in condition, so you can do as below:

    {{ $blogs && $blogs[0]->title ? $blogs[0]->title : '' }}
    
    Haritsinh Gohil
    • 5,818
    • 48
    • 50
    -1

    For simply solving the issue use the @ operator (error control operator)

    {{ @$blogs[0]->title }}
    

    The @ error control operator (also called STFU operator with mixed feelings), that suppresses errors just for the expression that immediately follows.

    XxJames07-
    • 1,833
    • 1
    • 4
    • 17
    Thamnees
    • 11
    • 1