I'm trying to create blade directive which echo variable (if variable defined) or echo "no data" if variable undefined.
This is my code in AppServiceProvider.php:
<?php
namespace App\Providers;
use Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Blade::directive('p', function($ex) {
error_log(print_r($ex,true));
return '<?php $defined_vars = get_defined_vars(); if(array_key_exists(\''. $ex .'\', $defined_vars) ): echo ' . $ex . ' ; else: echo \'no data\'; endif;?>';
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
Here is my index.blade.php:
<p class="lead">#p($myvar)</p>
But my directive "p" gives "no data" if variable defined.
If I use isset error occurres: Cannot use isset() on the result of an expression (you can use "null !== expression" instead)
How could I check inside directives if variable defined?
Blade has a directive to check if a variable is set:
#isset($var)
#endisset
Try checking if the variable is empty:
#if(empty($myvar))
<p>Data does not exist</p>
#else
<p>Your data is here!</p>
#endif
Can also check this thread
For Laravel 5.7 onwards use.
{{ $checkvariable ?? 'not-exist' }}
You can use in Blade functionality for checking isset i.e
{{ $checkvariable or 'not-exist' }}
https://laravel.com/docs/5.2/blade#displaying-data
For Laravel version >=5.7
{{ $value ?? '' }}
For Laravel version <5.7
{{ $value or '' }}
The best and cleanest way check if a variable exists in blade:
{!! !empty($myvariable) ? $myvariable : 'variable does not exist' !!}
You can use the #isset blade directive to check whether the variable is set or not. Alternatively, if you have the default value for that variable you can directly use it as {{ $vatiable ?? 'default_value' }}. The ?? way is available in Laravel v5.7 onwards.
If you want to check for multiple variables at once, you can do it by applying AND operation to expression as #if(isset($var_one) && isset($var_two)).
There are also other ways (lengthy) using #if directive as #if(isset($variable)) but it's not recommended.
Some developer uses # symbol for error control in a similar situation. The at-sign (#) is used as error control operator in PHP. When an expression is prepended with the # sign, error messages that might be generated by that expression will be ignored. If the track_errors feature is enabled, an error message generated by the expression and it will be saved in the variable $php_errormsg. This variable will be overwritten on each error. The use of # is very bad programming practice as it does not make the error disappear, it just hides them, and it makes debugging a lot worse since we can’t see what’s actually wrong with our code.
You can do it in few different ways.
Sample-1:
#if( !empty($data['var']))
{{ $data['var'] }}
#endif
Sample-2:
{{ $data['var'] or 'no data found' }}
Sample-3: Using ternary operator
<a href="" class="{{ ( ! empty($data['var'] ? $data['var'] : 'no data found') }}">
For the last version of Larvael make the variable optional in the blade template.
Replace $myvar with
{{ $myvar }} with {{ $myvar?? '' }}
What are you trying to pass to your custom directive? If it's just a string/int the following should work.
Blade::directive('p', function($expression){
$output = $expression ? $expression : 'nodata';
return "<?php echo {$output}; ?>";
});
In Blade Template
#p('Foo')
The #empty directive might be useful:
#empty($var)
$var is unset or false-y
#endempty
To check if variable exist in Laravel blade directive, do this:
Blade::directive('datetime', function ($value) {
return "<?php echo isset($value) ? ($value)->format('d/m/Y H:i:s') : null; ?>";
});
If you trying check a bool variable you can use #unless
<input type="text" class="#unless ($variable) d-none #endunless" >
Related
Migrating from php 7.1 to 7.4. We have like 500 functional tests for an API, and some of them started to fail with an error after the migration was complete. These tests were passing before everywhere, and now fail everywhere - not all, just 39.
Environment information:
php 7.4
codeception
yii2
Stack trace:
...\api\vendor\codeception\codeception\src\Codeception\Subscriber\ErrorHandler.php:83
...\api\tests\functional\SomeFileHereCest.php:72
...\api\vendor\codeception\codeception\src\Codeception\Lib\Di.php:127
...\api\vendor\codeception\codeception\src\Codeception\Test\Cest.php:138
...\api\vendor\codeception\codeception\src\Codeception\Test\Cest.php:97
...\api\vendor\codeception\codeception\src\Codeception\Test\Cest.php:80
...\api\vendor\codeception\codeception\src\Codeception\Test\Test.php:88
... more stuff here, not important
Since ErrorHandler.php:83 this is just catching the error, let's look at the SomeFileHereCest.php:72:
// declaration of the apiPrefix variable in the class.
protected $apiPrefix;
//...
public function _before(FunctionalTester $I)
{
$this->apiPrefix = $this->config['backend']['api_prefix']; // this is the line 72
//... more similar stuff later
So the $this->config['backend']['api_prefix'] this is a string("v1")
And I dont see where is the issue with this and how to dig into it deeper. Any ideas?
Sounds like your variable isn't set.
Check with the following isset calls:
isset($this->config);
isset($this->config['backend']);
isset($this->config['backend']['api_prefix']);
You can actually check multiple vars in one isset call (isset($x, $y, $z)), but this will let you see which var specifically is missing
use (??) (double question mark operator) ("null coalescing operator") to avoid unset
arrays.
this unit test is giving me "success"
class PhpTest extends TestCase
{
public function test_php_74()
{
//Trying to access array offset on value of type null
$this->assertSame('7.4.9', phpversion());
$a = null;
$this->assertTrue($a ?? true);
$this->assertTrue($a['a'] ?? true);
$this->assertTrue($a['a']['a'] ?? true);
$a = [];
$this->assertSame([], $a);
$this->assertTrue($a['a'] ?? true);
$this->assertTrue($a['a']['a'] ?? true);
}
}
It is related to PHP 7.4 Issue.
The solution is we can put isset in PHP or Laravel Blade
Old Code
#foreach ($widgets->get('dashboard') as $widget)
{!! $widget->render() !!}
#endforeach
New code update with isset
#if(isset($Widget))
#foreach ($widgets->get('dashboard') as $widget)
{!! $widget->render() !!}
#endforeach
#endif
I have a laravel crud with relations. but when i want to load the page some table rows dont have a value. and this gives the error: 'Attempt to read property "name" on null'
But how do i ignore this message and keep loading in the page?
index.blade:
#foreach($files as $file)
<tr>
<td>{{$file->id}}</td>
<td>{{$file->title}} </td>
<td>{{$file->description_short}} </td>
<td>{{$file->description_long}} </td>
<td>{{$file->file}}</td>
<td>{{$file->language->name}} </td>
<td>{{$file->subfolder->name}} </td>
<td>{{$file->role->name}} </td>
<td>
Edit
</td>
<td>
<form action="{{ route('admin.file.destroy', $file->id)}}" method="post">
#csrf
#method('DELETE')
<button class="btn btn-danger" type="submit">Delete</button>
</form>
</td>
</tr>
#endforeach
</tbody>
</table>
controller:
public function index()
{
$files = File::with('subfolder', 'language', 'tag')->get();
$languages = Language::all();
$tags = Tag::all();
$subfolders = Subfolder::all();
$users = User::all();
return view('admin.file.index', compact('files', 'languages', 'tags', 'users', 'subfolders'));
}
i want the index to ignore all the NULL properties
Using the # before your php Variable will work for you like below.
{{#$file->language->name}}
There are various ways:
Error control operator:
#$file->language->name the # operator suppress the errors messages (imho not very clean :) , but same result)
From documentation:
// Note: The #-operator works only on expressions. A simple rule of thumb is: if one can take the value of something, then one can prepend the # operator to it.
// For instance, it can be prepended to variables, functions calls, certain language construct calls (e.g. include), and so forth.
// It cannot be prepended to function or class definitions, or conditional structures such as if and foreach, and so forth.
Classical ternary operator way
$file->language ? $file->language->name : null;
From PHP7+ : Null Coalescing Operator
$file->language->name ?? null;
From PHP8+ : null safe Operator
$file->language?->name; (amazing! :-))
And also, by using
{{ $file->language->name ?? '' }} # typical PHP way
{{ $file->language?->name }} # if object/relation "language" exist then access "name" propery
Note:
When you use # to the variable {{#$file->language->name}}, we're skipping the error message. This means we're telling Laravel to "Leave this Variable alone". And It's not the best way to check if empty
Laravel has a built-in helper for this, called optional().
https://laravel.com/docs/9.x/helpers#method-optional
<td>{{ optional($file->language)->name }} </td>
The optional function accepts any argument and allows you to access properties or call methods on that object. If the given object is null, properties and methods will return null instead of causing an error.
I am using Laravel 7. I am thinking of using Blade directive to format numbers in my view. But I'm having problem with the variable showing up in the final output.
Here is a simplified version of my original code that works. (The actual code has a number of conditional #if to format the number differently depending on the value.)
<div>{{ number_format( $stat->number1,0,'.',',' ) }}</div>
<div>{{ number_format( $stat->number2,0,'.',',' ) }}</div>
<div>{{ number_format( $stat->number3,0,'.',',' ) }}</div>
Here is the custom directive I created.
Blade::directive('format_number_thousand_sep', function ($expression) {
return "{{ number_format( $expression,0,'.',',' ) }}";
});
Here is the blade template that I've changed to call this directive. When I run this, it gives me an error "number_format() expects parameter 1 to be float, string given". I figured the string "$stat->number1" is being used instead of the value of the variable.
<div>#format_number_thousand_sep( '$stat->number1' )</div>
<div>#format_number_thousand_sep( '$stat->number2' )</div>
<div>#format_number_thousand_sep( '$stat->number3' )</div>
To troubleshoot, I modified the directive to just return the variable to make sure it displays on the output. I've tried variations like the ones below and the output is an error (e.g. unexpected '{'), the literal string "$stat->number1", or a partial string because the "
return "$expression";
return "{{ $expression }}";
return "<?php echo '{{' . $expression . '}}'; ?>";
I've tried researching this online. Most questions seems to be trying to use the value of the variable within the directive, which I know wouldn't work. I just want the output to include the proper syntax so the variable value will be inserted in the output.
What is the proper syntax for passing a variable name to a blade directive and have it evaluate properly in the final output?
I'm not sure but can you try like this:
Blade::directive('format_number_thousand_sep', function ($expression) {
return number_format( $expression,0,'.',',' );
});
Here is my urlto in laravel
{{ URL::to('forgot'); }}
It will point as mysite.com/forgot
How can i insert the value here
i.e.,
$value = '1';
{{ URL::to('forgot'); }}
And it should point mysite.com/1/forgot
How can i insert the value of $value before the forgot
You should concatenate your $value before your url
i.e.,
$value = '1';
{{ URL::to($value.'/forgot/'); }}
You can use named routes with parameters.
Example route:
Route::get('{value}/forgot', array('as' => 'forgot', function($value = null)
{
return "My value: " . $value;
}));
Now building your URL:
{{ route('forgot', 1); }}
You need to use like this check the documentation Documentation Here
$value = '1';
{{ URL::to($value.'/forgot'); }}
one way is to add a variable to your route
an example here my route is
Route::get('/user/{id}'
then at the destination if i call $id, i get the value that was entered in id.
so if i put /user/1 , $id =1.
this can be a issue however if there is nothing. you need to include code to handle if the $id has no value. another way to do it is
return View::make('searchresults', compact('challenges', 'ideas','users'));
here i have sent arrays with search results, and on the page searchresults if i call $ideas i get that array, this will also work with strings i believe.
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?
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
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.
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.
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
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
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 : '' }}
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.