Kohana - rules method throwing error - php

I have created the following method in order to do the validation on the submitted data.
public function validate_create($array) {
$array = Validation::factory($array)
-> rules('username', $this - > _rules['username']);
return $array;
}
The rules is defined as
protected $_rules = array(
'username' = > array(
'not_empty' = > NULL,
'min_length' = > array(6),
'max_length' = > array(32),
)
);
The code is throwing the following exception when trying to execute the check() method.
ErrorException [ Warning ]: call_user_func_array() expects parameter 1
to be a valid callback, no array or string given
Can any one advice how to solve this issue?
In signup.php the input field for username is defined as
< ?php echo Form::label('user_name','Username')? > < ?php echo
Form::input('username'); ? >

The format for building the Validation object directly is different from that of your $_rules array.
You can see the correct method signature and definition documented here, and it'd probably be a good idea to also read the signature for Validation::rule.
In short, the rules() method wants an list of arrays, where for each inner array the first element is the validation function and the second an array of parameters to pass to it.
e.g.
$rules = array(
array('not_empty', NULL),
array('min_length', array(':value', 6))
);
$v = Validation::factory($values)
->rules('fieldname', $rules);
Note that this is different than the $_rules array (map) format that you are attempting to use where the key is the validation function and the parameters are the values.
Aslo, is there any reason you're building your own validation function instead of using the ORM::rules() method of validation?

Related

PHP Attempting to invoke a serialized / encoded anonymous function within a class method returns 'Method name must be a string'

I have a class within which I want to
Set an array
Loop through the array
Invoke anonymous functions set in #1 as I loop through the array in #2
EDIT: The code below is working when tested out of my application, but in my CodeIgniter 3 Controller, I keep getting an error: Method name must be a string
My simplified code for the purposes of example (CodeIgniter 3):
<?php
class MyClass {
// Setting my array
public function my_arr ($options = array())
{
$arr = array(
'1' => array(
'a' => 'z',
'b' => function($i) {
return 'c' . $i;
},
),
'2' => array(
'a' => 'y',
'b' => function($i) {
return 'd' . $i;
},
),
);
return $arr;
/**
*
* EDIT: Later in my code I found that there was some kind
* of serialization/encoding attempt like:
* json_decode(json_encode($arr));
*/
}
// Doing My Loop
public function do_loop()
{
$my_arr = $this->my_arr();
$i = 0;
foreach($my_arr as $key => $value){
$anonymous_function = $value['b'];
echo $anonymous_function($i) . '<br>'; // Keep getting `Method name must be a string`
$i++;
}
}
}
(new MyClass())->do_loop();
Posting back here in hopes that it helps.
My issue:
Later on in my codebase, I was trying to encode/serialize my method's
output.
After a bit of reading, I found out that json_encode() &
serialize() don't support serialization of closures / anonymous functions.
So, the Method name must be a string error happend because my attempts to encode/serialize - effectively - stripped the anonymous functions out & thus referenced a null function.
How I resolved the issue:
First I started with Opis Closure library to seralize the closure (I didn't end up going with this, but including it because I think it's a pretty cool library)
After the above, I just realized that I should refactor so that I wasn't trying serialize closures at all.

Laravel: Using validator's sometimes method when input is an array

I have a form that posts a structure field as an array. The structure array contains definitions of database table columns.
$validator = Validator::make($request->all(), [
'structure' => 'required|array|min:1',
'structure.*.name' => 'required|regex:/^[a-z]+[a-z0-9_]+$/',
'structure.*.type' => 'required|in:integer,decimal,string,text,date,datetime',
'structure.*.length' => 'nullable|numeric|required_if:structure.*.type,decimal',
'structure.*.default' => '',
'structure.*.index' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_nullable' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_primary' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_auto_increment' => 'required_if:structure.type,integer|boolean',
'structure.*.is_unique' => 'required_if:is_auto_increment,false|boolean',
'structure.*.decimal' => 'nullable|numeric|required_if:structure.*.type,decimal|lt:structure.*.length',
]);
Without going into explanation of all the rules, one thing should be made sure that the length field is always null when the type is not string or decimal as you cannot assign a length to columns other than these types. So, I am trying to use the sometimes method on the $validator instance.
$validator->sometimes('structure.*.length', 'in:null', function ($input) {
// how to access the structure type here?
});
My question is inside the closure, how do I make sure that the length is null only for the array element that has the type set to other than string or decimal.
I have tried the dd function and it seems the whole input array is passed to the closure.
$validator->sometimes('structure.*.length', 'in:null', function ($input) {
dd($input);
});
Here is the output of the dd method.
I can use a foreach construct but wouldn't that be inefficient? Checking all the elements for a single element?
How do I check the type only for the array element under consideration?
Is there a Laravel way to do this?
How about thinking opposite? if the Type is String or Decimal, the Length field will become Required.
$validator->sometimes('structure.*.length', 'required', function ($input) {
return $input->type == 'string' or $input->type == 'decimal';
});
This is a great question. I took a look at the API for sometimes(). It seems, what you want to do, is currently not possible with it.
A possible alternative could be to use an After Validation Hook. For example:
$validator->after(function ($validator) {
$attributes = $validator->getData()['structure'];
foreach($attributes as $key => $value) {
if(! in_array($value['type'], ['string', 'decimal']) && ! is_null($value['length'])) {
$validator->errors()->add("structure.{$key}.length", 'Should be null');
}
}
});

Laravel 5.1. Indexed array's validation

I have input form, where user choose the quantity of item's details for his preorder. Ex: Julia's preorder : drink's - water 1 bottle, milk 1 glass, bulletproof coffee 1 cup.
#foreach ($item->detail()->orderBy('sequence')->get() as $detail)
<td><input name="estimate_quantity[]"></td>
#endforeach
I want to validate my quantity, only integers greater than zero or equal to it.
So I made rule
public function rules()
{
$rules = [
'estimate_quantity' => 'required|array',
];
$estimate_quantity = $this->request->get('estimate_quantity');
foreach ($estimate_quantity as $index => $value){
$rules["estimate_quantity.$index"] = 'integer|min:0';
}
return $rules;
}
It doesn't work. If I entered alpha character, it will be an error
ErrorException in helpers.php line 468:
htmlentities() expects parameter 1 to be string, array given
1.What is the right way to do this validation? Making it in controller doesn't look's nice.
2.If i will make my custom rule in separate file, where better to store it? Create app/Validations folder?
3.What magic happening after rules execution and before controller method's execution?
I'm pretty new in Laravel and programming as well, sorry for this easy questions.
i think you can give the array element instead of . notation use array index notation below should work
public function rules()
{
$rules = [
'estimate_quantity' => 'required|array',
];
$estimate_quantity = $this->input('estimate_quantity');
foreach ($estimate_quantity as $index => $value){
$rules["estimate_quantity[".$index."]"] = 'integer|min:0';
}
return $rules;
}

PHP: Sending a list of options as an argument (alternative to named parameters/ argument bag)

I wish to give a list of options as an argument to a function.
The Ideal Scenario: Named Parameters
If PHP has named parameters it would be done like so:
function setOptions($title, $url, $public = true, $placeholder = "type here...") {
...
}
setOptions($title = "Hello World", $url = "example.com", $placeholder = "hi");
Unfortunately PHP does not have named parameters (please tell me if PHP7 is planned to have some as a comment).
The solution everyone else is using: Associative Array
Most PHP scripts I have seen use an alternative array approach like so:
function setOptions($options) {
...
}
setOptions(array(
'title' => "Hello World",
'url' => "example.com",
'placeholder' => "hi"
));
Drawbacks of Associative Array Approach
Although this works fine, there are the following drawbacks:
The user does not benefit from autocompletion (taking a long time to write)
The user can easily makes mistakes in spellings
The don't know what options is available, so may frequently revert back to documentation
Is there a better way?
Is there a better way that can address these issues (either in current PHP or PHP7 or maybe even hacklang(?)).
In Hack, you can use Shapes. Shapes define a structure for associative arrays so that things can be autocompleted (depending on IDE support) and spelling mistakes are picked up by the type checker.
For instance, your example could be reworked like:
function setOptions(shape(
'title' => string,
'url' => string,
'public' => ?bool,
'placeholder' => ?string,
) $options) {
$title = $options['title'];
$url = $options['url'];
$public = Shapes::idx($options, 'public', true);
$placeholder = Shapes::idx($options, 'placeholder', 'type here...');
...
}
setOptions(shape(
'title' => 'Hello World',
'url' => 'example.com',
'placeholder' => 'hi',
));
This marks title and url to both be required options and public and placeholder are optional (all nullable types in shapes are considered to be optional). Shapes::idx is then used to get the value provided, or the default value (the third argument) if a value was not passed in.
Solution: Using fluent setters
A potential solution I have found to this problem is to use classes and fluent setters like so:
class PostOptions {
protected
$title,
$url,
$public = TRUE,
$placeholder = "type here..."; //Default Values can be set here
static function getInstance(): PostOptions {
return new self();
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function setUrl($url) {
$this->url = $url;
return $this;
}
public function setPublic($public) {
$this->public = $public;
return $this;
}
public function setPlaceholder($placeholder) {
$this->placeholder = $placeholder;
return $this;
}
}
You can then send the options like so:
function setOptions(PostOptions $postOptions) {
//...
}
setOptions(
PostOptions::getInstance()
->setTitle("Hello World")
->setUrl("example.com")
->setPlaceholder("hi")
);
Doing it quickly! (This looks long)
Although this may look long, it can actually be implemented VERY quickly using IDE tools.
e.g. In InteliJ or PHPStorm, just type ALT+INS > Select setters > Select the fields you want to set and check the checkbox for fluent setters > click OK
Why Fluent Setters? Why Not just make all the fields public?
Using public fields is a LOT slower. This is because fluent setters can make use of chained methods, whilst the public fields way must be written like this:
$options = new PostOptions();
$options->title = "hello";
$options->placeholder = "...";
$options->url "..."
setOptions($options);
Which is a lot more typing compared to the proposed solution
Why is this better?
It's faster in IDE's when using autocomplete than the array approach
Unlikely to make mistakes in spellings (thanks to autocomplete)
Easy to see what options is available (again thanks to autocomplete)
Can give individual documentation for individual fields using PHPDoc
Can use nested options more easily e.g. If you had a list of options, and that option also had more list of options
Other OOP advantages e.g. Inheritance & Abstract Classes
How much faster is this approach?
I implemented a quick class for Wordpress labels array in: https://codex.wordpress.org/Function_Reference/register_post_type
I found that setting a property for each value (with the documentation next to you on a 2nd monitor) that the fluent setters approach is approximately 25% faster than the array approach thanks to autocomplete! However, if the documentation was not next to you, I expect this approach will far exceed 25%, as discovery of options is much quicker!
Alternative approaches are welcome
Declaration from array
This is how I normally declare my class structure. The only drawback is that it takes a while longer to write, but it allows optional parameters, defaults values, etc.
public static $defaults = array(
'user_id' => null,
'username' => null,
'avatar' => null,
'email' => null,
'description' => null,
);
public function __construct(array $args = array()) {
$this->dbc = Database::connection();
$defaults = self::$defaults;
$args = array_merge($defaults, $args);
//Assign the object properites
$this->user_id = (is_numeric($args['user_id'])) ? $args['user_id'] : null;
$this->username = $args['username'];
$this->avatar = AVATAR_DIR . $args['avatar'];
$this->email = $args['email'];
$this->description = $args['description'];
}
This way, you can declare an object like $x = new User(), and it will work perfectly fine. Let's say you've only selected a few columns from your SQL statement. You can make the keys in the public static $defaults into the same name as the columns you've selected, that way to instantiate your object, you can easily do:
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$object = new User($row);
The array_merge takes care of having any extraneous keys that you don't need in the argument they provided. If you need to change options, you can declare them the same way for __construct() with a default array and array_merge to catch arguments and mimic named parameters and defaults values (like in Python)
With Syntactic: https://github.com/topclaudy/php-syntactic
you can just do:
function foo($a = 1, $b = 2, $c = 3, $d = 4){
return $a * $b * $c * $d;
}
And call it with the arguments you want:
//Call with argument b only
echo s('foo')->in('b', 5)->out(); //Outputs 60
//Call with argument a and argument at index/position 1 (b),
echo s('foo')->in('a', 7)->in(1, 5)->out(); //Outputs 420
//Call with argument c only through dynamic method
echo s('foo')->c(9)->out(); //Outputs 72
If U have that much parameters I'd think about creating an object that you'll pass to class instead of n parameters and every parameter is one field there. In constructor you put required parameters and this is then clean solution.

Checking parameters passed into function by array

I'm wrapping a model function in Yii 1.8 that has the signature:
public save($runValidation=true, array $attributes=NULL)
With a function:
public xSave(array $params)
That allows the addition of a flag and optional message that causes the wrapper function to throw an Exception in the case that the delegated save() function returns false.
I toyed with the idea of overwriting save() with:
public save(
$runValidation=true,
array $attributes=NULL,
$exception_on_error=false,
$exception_message=false
)
but would like to allow the specification of the last two parameters independently from the first and like the idea of allowing the extra readability of passing in an array with string keys.
I have so far:
/**
* Enhanced save function.
* Delegates to standard model save to allow exceptions to be thrown
* in the case where the model was not saved.
**/
public function xSave(array $params=array()){
$_params=array(
'run_validation'=>true,
'attributes'=> null,
'exception_on_failure'=>false,
'exception_message'=>false,
);
array_merge($_params, $params);
// Call the save method.
$is_saved=$this->save($_params['run_validation'],$_params['attributes']);
// Throw exception to if flag set and model not saved.
if($_params['exception_on_failure'] && !$is_saved){
// If no exception message was passed in, use the default.
if($_params['exception_message'] === false){
throw new CException('
Could not '.($this->isNewRecord()?'create':'update').' '.get_class($this).';
Errors: '.CJSON::encode($this->errors)
);
}
// Else throw using the one provided.
throw new CException($exception_message);
}
// Return result of standard save method.
return $is_saved;
}
Firstly I'd like to know if this is a sensible choice, as I may well use it for other parts of the system. I'm currently not too worried about the typing of the parameters although I agree this could be an issue in the future.
Secondly I would also like the ability to throw an exception in the case that $params has a key that is not defined in $_params with a message specifying that key, and would like to do this as part of the array_merge if possible.
To 1), yes, passing arrays is the usual lame workaround in languages that don't support named arguments (see jQuery etc). With the new array syntax, this is even almost readable:
$some->save([
$runValidation => true,
$attributes => ['foo', 'bar']
]);
Inside a function, you can use extract to avoid ugly $params[foobar] references.
For better taste though, persuade #NikiC to get this patch ready ))
To 2), if you plan to use argument arrays systematically, consider a helper function like this:
function parseArgs($args, $defaults) {
foreach($args as $k => $v) {
if(!array_key_exists($k, $defaults))
throw new Exception("invalid argument: $k");
// might want to add some type checking, like
// if(gettype($v) != gettype($defaults[$k])) bang!
}
return $args + $defaults;
}
Usage:
public function xSave(array $params=array()){
extract(parseArgs($params, [
'run_validation'=>true,
'attributes'=> null,
'exception_on_failure'=>false,
'exception_message'=>false,
]));
if ($run_validation)
etc....
The decision to either use single parameters or parameter-arrays is opinion based. It depends on the situation. At least I would keep the design consistent across the whole project.
To decide if there had been unknown parameters passed, you can use array_diff():
$a = array(
'test' => 'foo',
'name' => 'bar'
);
$b = array(
'test' => 'foo',
'name' => 'bar',
'abcd' => '123'
);
$d = array_diff(
array_keys($b), array_keys($a)
);
echo "The following keys can't be understood: " . implode(', ', $d) . PHP_EOL;
However, I would skip that check as it will not "harm" if there are unknown parameters.

Categories