When to use laravel filled() Vs PHP empty() - laravel

The filled function Vs using the empty function. What is the reason for choosing filled Vs !empty()?

I think they basically do the same work. The only difference that I noticed is that the filled() method is a Laravel's helper function and only available on a $request instance or in the Illuminate\Http\Request class. While the empty() method is available globally because its a PHP's helper function. You can use empty() on any variable in any class or controller. While on the other hand filled() can only be used wherever you're receiving a request or you have to manually create an instance of Request class.
Personally, I've never used the filled() method, so I can't tell you exactly what is the technical difference between them (if there is any).

The big difference between filled or its inverse blank and empty is what is considered empty.
For example:
filled(0); // true
blank(0); // false
!empty(0); // false
filled(' '); // false;
blank(' '); // true;
!empty(' '); // true;
filled(''); // false;
blank(''); // true;
!empty(''); // false;
The functions work in a very different way, so it's not a matter of just picking one of the two. It depends on what you are trying to do in your code.

empty example
$test = array(
1 => '',
2 => "",
3 => null,
4 => array(),
5 => FALSE,
6 => NULL,
7=>'0',
8=>0,
);
foreach ($test as $k => $v) {
if (empty($v)) {
echo "<br> $k=>$v is empty";
}
}
/**
Output
1=> is empty
2=> is empty
3=> is empty
4=>Array is empty
5=> is empty
6=> is empty
7=>0 is empty
8=>0 is empty
**/
if(isset($test)) // return true because $test is defined
if(is_null($test)) // return false because $test is not null

Related

Return Laravel options() as array when no optional parameter has been provided

Laravel has the super handy optional() helper.
I would like to combine it with a custom Model attribute like this:
// this method is on the User model
public function getDataAttribute()
{
// this data comes from another service
$data = [
'one' => 1,
'two' => 2,
];
return optional($data);
}
So I can use it like this:
$user->data->one // 1
$user->data->two // 2
$user->data->three // null
However, I am also trying to return the entire array by doing:
dump($user->data); // this should dump the internal $data array
But this will return an instance of Illuminate\Support\Optional with a value property.
Illuminate\Support\Optional {#1416 ▼
#value: {#2410 ▼
+"one": 1
+"two": 2
}
}
Is it possible to return the original $data array if no "sub"parameter (= a child attribute of $user->data) is given? Or is there a possibility to detect a child parameter in the getDataAttribute()?
I hope it's clear what I am trying to achieve.
What you're asking for cannot be achieved.
My suggestion would be to keep things simple and define a getter method and pass the key of the array you want and from there return your data respectively, e.g.:
public function getData($key = null) {
$data = [
'one' => 1,
'two' => 2,
];
if (!$key) {
return $data;
}
return $data[$key] ?? null;
}
Notice also how this method is no longer an attribute, this is because AFAIR, you cannot pass args to attribute methods.
Reading Material
Null coalescing operator
Thanks to lagbox for pushing me in the right direction. I have solved this by using the following macro:
Illuminate\Support\Optional::macro('toArray', function()
{
return (array) $this->value;
});
This way I can access all data by using:
$user->data->toArray();

Array that was sent as parameter turns into string with only first value of original array

I am sending an array from method 'filterResults' to method 'home' via redirect. The array does go through because the URI changes to www.site.com/country/area/city/category.
But when I print the array inside method 'home' it is not an array anymore but instead it is a string with only the first value (country) of the original array. Why is this the case? Where are the other values? and why is it now a string instead of an array?
Thanks for your help!
Route:
Route::any('home/{s1?}/{s2?}/{s3?}/{s4?}', array(
'as' => 'home',
'uses' => 'HomeController#home'
));
Controller Method 'filterResults':
public function filterResults() {
$filter = array('country' => $country, 'area' => $area, 'city' => $city, 'category' => $category);
return Redirect::route('home', $filter);
}
Controller Method 'Home':
public function home($filter = array()) {
print_r($filter); //result is a string with the value for country
}
That's not really how Laravel routing works. When you pass it an array, it is expecting an array of arguments.
So your home method will actually receive 4 arguments (the elements of the array).
It makes more sense to make the method something like:
public function home($country = null, $area = null, $city = null, $category = null) {
print_r($country);
print_r($area);
print_r($city);
print_r($category);
}
Keep in mind that when you're at /country/area/city/category, Laravel has absolutely no idea that you want those in an array. An alternative if you really have to have it in an array would be to remove the arguments from the home method altogether and use PHPs func_get_args which would give you the arguments in array form. They'd be 0-based indexed though. You could even then pass that through array_map to map 0 to country, 1 to area, 2 to city, and 3 to category, but that is probably a lot of excess work when you can just get at everything via parameters.
You need to define them to get the parameters for example
public function home($param1 = null, $param2 = null, $param3 = null, $param4 = null) {
}
It creates a url because Laravel is smart enough to do that according to the documentation. See array section in route parameters.

One-shot laravel validator

I have a form where someone searches for something. Based on this form, I validate if the input is correct:
$validator = Validator::make(Input::all() , array(
'address' =>'required',
));
if($validator->fails()) {
return Redirect::to('/')->withErrors($validator);
}
After this, I want to validate something else (that a result object isn't empty), which is completely unrelated to the search. In other words, it's NOT input from a form.
1) Do I create another validator to validate this? Or
2) Is there a better way to simply check this value and spawn an object that can be returned with "withErrors"?
UPDATE
This isn't working for me:
$validator = Validator::make(
array(
'searches' => sizeof($search)
) ,
array(
'searches' => 'required|min:1'
)
);
if($validator->fails()) {
return Redirect::to('/')->withErrors($validator);
}
It's not working because for some reason it's picking up that the "searches" item should only be validated "sometimes"
you have two ways. one is custom validator
or there is a simpler way,
suppose,
private function foo()
{
$data = ''; //retrieved the data error here with whatever call you want to make
return !empty($data) ? true : false;
}
in the controller,
public function bar()
{
if(!$this->foo())
{
$messages = new \Illuminate\Support\MessageBag;
// you should use interface here. i directly made the object call for the sake of simplicity.
$messages->add('custom', 'custom error');
return Redirect::back()->withErrors($messages)->withInput();
}
}
in the view:
#if($errors->has('custom'))
<p>custom error output.</p>
#endif
it is just the outline to give you the idea.

Validation of array form fields in laravel 4 error

How can we validate form fields that are arrays? Take a look at the following code
UserPhone Model:
public static $rules= array(
'phonenumber'=>'required|numeric',
'isPrimary'=>'in:0,1'
)
...........
UserController:
$validation = UserPhone::validate(Input::only('phonenumber')));
if($validation->passes())
{
$allInputs = Input::only('phonenumber','tid');
$loopSize = sizeOf($allInputs);
for($i=0;$i<$loopSize;$i++)
{
$phone = UserPhone::find($allInputs['tid'][$i]);
$phone->phonenumber = $allInputs['phonenumber'][$i];
$phone->save();
}
return Redirect::to('myprofile')->with('message','Update OK');
}
else
{
return Redirect::to('editPhone')->withErrors($validation);
}
}
the $validation comes from a BaseModel which extends Eloquent.
In my view:
<?php $counter=1; ?>
#foreach($phones as $thephone)
<section class="col col-12">
<label class="label">Phone Number {{$counter++}}</label>
<label class="input">
<i class="icon-append icon-phone"></i>
{{Form::text('phonenumber[]',$thephone->phonenumber)}}
{{Form::hidden('tid[]',$thephone->id)}}
</label>
</section>
#endforeach
Everything is working fine and I get all the phone numbers I want in the Update Form, but I cannot update the model because the validation fails with the message "Phonenumber must be a number".
I know that there is not a simple solution for validating array form fields and I tried to extend the validator class but with no success.
How can I validate this kind of fields?
Here's the solution I use:
Usage
Simply transform your usual rules by prefixing each. For example:
'names' => 'required|array|each:exists,users,name'
Note that the each rule assumes your field is an array, so don't forget to use the array rule before as shown here.
Error Messages
Error messages will be automatically calculated by the singular form (using Laravel's str_singular() helper) of your field. In the previous example, the attribute is name.
Nested Arrays
This method works out of the box with nested arrays of any depth in dot notation. For example, this works:
'members.names' => 'required|array|each:exists,users,name'
Again, the attribute used for error messages here will be name.
Custom Rules
This method supports any of your custom rules out of the box.
Implementation
1. Extend the validator class
class ExtendedValidator extends Illuminate\Validation\Validator {
public function validateEach($attribute, $value, $parameters)
{
// Transform the each rule
// For example, `each:exists,users,name` becomes `exists:users,name`
$ruleName = array_shift($parameters);
$rule = $ruleName.(count($parameters) > 0 ? ':'.implode(',', $parameters) : '');
foreach ($value as $arrayKey => $arrayValue)
{
$this->validate($attribute.'.'.$arrayKey, $rule);
}
// Always return true, since the errors occur for individual elements.
return true;
}
protected function getAttribute($attribute)
{
// Get the second to last segment in singular form for arrays.
// For example, `group.names.0` becomes `name`.
if (str_contains($attribute, '.'))
{
$segments = explode('.', $attribute);
$attribute = str_singular($segments[count($segments) - 2]);
}
return parent::getAttribute($attribute);
}
}
2. Register your validator extension
Anywhere in your usual bootstrap locations, add the following code:
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new ExtendedValidator($translator, $data, $rules, $messages);
});
And that's it! Enjoy!
Bonus: Size rules with arrays
As a comment pointed out, there's seems to be no easy way to validate array sizes. However, the Laravel documentation is lacking for size rules: it doesn't mention that it can count array elements. This means you're actually allowed to use size, min, max and between rules to count array elements.
It works best to extend the Validator class and re-use the existing Validator functions:
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new Validation($translator, $data, $rules, $messages);
});
class Validation extends Illuminate\Validation\Validator {
/**
* Magically adds validation methods. Normally the Laravel Validation methods
* only support single values to be validated like 'numeric', 'alpha', etc.
* Here we copy those methods to work also for arrays, so we can validate
* if a value is OR an array contains only 'numeric', 'alpha', etc. values.
*
* $rules = array(
* 'row_id' => 'required|integerOrArray', // "row_id" must be an integer OR an array containing only integer values
* 'type' => 'inOrArray:foo,bar' // "type" must be 'foo' or 'bar' OR an array containing nothing but those values
* );
*
* #param string $method Name of the validation to perform e.g. 'numeric', 'alpha', etc.
* #param array $parameters Contains the value to be validated, as well as additional validation information e.g. min:?, max:?, etc.
*/
public function __call($method, $parameters)
{
// Convert method name to its non-array counterpart (e.g. validateNumericArray converts to validateNumeric)
if (substr($method, -7) === 'OrArray')
$method = substr($method, 0, -7);
// Call original method when we are dealing with a single value only, instead of an array
if (! is_array($parameters[1]))
return call_user_func_array(array($this, $method), $parameters);
$success = true;
foreach ($parameters[1] as $value) {
$parameters[1] = $value;
$success &= call_user_func_array(array($this, $method), $parameters);
}
return $success;
}
/**
* All ...OrArray validation functions can use their non-array error message counterparts
*
* #param mixed $attribute The value under validation
* #param string $rule Validation rule
*/
protected function getMessage($attribute, $rule)
{
if (substr($rule, -7) === 'OrArray')
$rule = substr($rule, 0, -7);
return parent::getMessage($attribute, $rule);
}
}
each()
It's not in the docs, but the 4.2 branch may have a simple solution around line 220.
Just like the sometimes($attribute, $rules, callable $callback) function, there is now an each($attribute, $rules) function.
To use it, the code would be something simpler than a sometimes() call:
$v->each('array_attribute',array('rule','anotherRule')); //$v is your validator
Caveats
sometimes() and each() don't seem to be easily chainable with each other so if you want to do specifically conditioned rules on array values, you're better off with the magic solutions in other answers for now.
each() only goes one level deep which isn't that different from other solutions. The nice thing about the magic solutions is that they will go 0 or 1 level deep as needed by calling the base rules as appropriate so I suppose if you wanted to go 1 to 2 levels deep, you could simply merge the two approaches by calling each() and passing it a magic rule from the other answers.
each() only takes one attribute, not an array of attributes as sometimes() does, but adding this feature to each() wouldn't be a massive change to the each() function - just loop through the $attribute and array_merge() $data and the array_get() result. Someone can make it a pull request on master if they see it as desirable and it hasn't already been done and we can see if it makes it into a future build.
Here's an update to the code of Ronald, because my custom rules wouldn't work with the array extension. Tested with Laravel 4.1, default rules, extended rules, …
public function __call($method, $parameters) {
$isArrayRule = FALSE;
if(substr($method, -5) === 'Array') {
$method = substr($method, 0, -5);
$isArrayRule = TRUE;
}
//
$rule = snake_case(substr($method, 8));
// Default or custom rule
if(!$isArrayRule) {
// And we have a default value (not an array)
if(!is_array($parameters[1])) {
// Try getting the custom validation rule
if(isset($this->extensions[$rule])) {
return $this->callExtension($rule, $parameters);
}
// None found
throw new \BadMethodCallException("Method [$method] does not exist.");
} // Array given for default rule; cannot be!
else return FALSE;
}
// Array rules
$success = TRUE;
foreach($parameters[1] as $value) {
$parameters[1] = $value;
// Default rule exists, use it
if(is_callable("parent::$method")) {
$success &= call_user_func_array(array($this, $method), $parameters);
} else {
// Try a custom rule
if(isset($this->extensions[$rule])) {
$success &= $this->callExtension($rule, $parameters);
}
// No custom rule found
throw new \BadMethodCallException("Method [$method] does not exist.");
}
}
// Did any of them (array rules) fail?
return $success;
}
There are now array validation rules in case this helps anybody. It doesn't appear that these have been written up in the docs yet.
https://github.com/laravel/laravel/commit/6a2ad475cfb21d12936cbbb544d8a136fc73be97

Is there a shorter way than this to check values when using same Add form for Edit

I have the same code for an Add and an Edit form. Therefore in the controller I need a check to check if a) POST vars submitted (for saving), and if not then b) the original values (for editing) and if not then no value (blank for Adding). I put them in a $data array to pass to the view. Then in the form I can put:
value="<?php echo $member_id;?>"
So my question is, in Codeigniter is there a shorter way than the following to check if POST, then if not check if the original data exists, and if not then its nothing.
$data = array(
'member_id' => ( isset($_POST['member_id']) ? $_POST['member_id'] : (isset($member->member_id ) ? $member->member_id : '') )
);
I know about set_value() but looks like that wont add in the current data when editing a form, so have not used that.
You can allways make function for it.
function get_value_or_default($array, $key, $default) {
return isset($array[$key] ? $array[$key] :
isset($default) ? $default : '';
}
Or even better:
function update_from_post($object) {
$data = array();
foreach ($object as $prop_name => value) {
$value = get_value_or_default($_POST, $prop_name, $object->{$prop_name});
$data[$prop_name] = $value;
}
Assuming you have different methods in the controller for create vs edit: (you can use the same view in different methods by specifying it in $this->load->view()):
Your create method would assume it was new, and always read the $_POST variables (if $_POST)
Your edit method would first load the object from the database, and then overwrite with $_POST variables if present.
Finally, CodeIgniter has the input helper:
$this->input->post('field_name');
returns false if that field is not in $_POST.
To use your code above:
create
$data = array(
'member_id' => $this->input->post('member_id') ? $this->input->post('member_id') : '')
);
edit
$data = array(
'member_id' => $this->input->post('member_id') ? $this->input->post('member_id') : $member->member_id )
);

Resources