I am using spectacular Laravel Framework but i have a validation issue that i cannot solve in any way trying to validate a single email field.
My form input is:
{{ Form::email('email', Input::old('email'), array('id' => 'email', 'class' => 'span4', 'placeholder' => Lang::line('newsletter.email')->get($lang), 'novalidate' => 'novalidate')) }}
My controller
public function post_newsletter() {
$email = Input::get('email');
$v = Newsletter::validator($email, Newsletter::$rules);
if($v !== true)
{ ... }
else
{ ... }
}
and my model
class Newsletter extends Eloquent {
/**
* Newsletter validation rules
*/
public static $rules = array(
'email' => 'required|email|unique:newsletters'
);
/**
* Input validation
*/
public static function validator($attributes, $rules) {
$v = Validator::make($attributes, $rules);
return $v->fails() ? $v : true;
}
}
I ve done this so many times with success with much complicated forms but now even if i enter a valid email i get an error message about required fied etc Am I missing something? Maybe is the fact that i try to validate just one single field? I really don't get it why this happens.
I believe this might be because you're not passing in an array of attributes (or an array of data, basically).
Try using the compact function to generate an array like this.
array('email' => $email);
Here is how you should do it.
$v = Newsletter::validator(compact('email'), Newsletter::$rules);
Related
In my CI4 learning, I have started by trying to simulate user sign in functionality. I have a Controller, two Views (not shown here, but really simply pages- one a pretty much just single form, and the other one a “blank” success HTML page), a set of custom rules in the Validation.php file, and a CustomRule.php file with the first of the methods that will implement all my custom rules (which, ultimately, I’d like to have all set in the Validation.php file). For lack of a better idea, I’ve stuck the CustomRules.php file in the app\Config\ folder.
Here is my problem:
For the life of me, I can’t figure out how to get the Validation service to pass additional parameters (from the form) to my custom rules function called ‘user_validated’. The CI4 documentation describes what the custom function needs to cater for when accepting additional parameters, but not how to trigger the Validation service to pass these additional parameters to one’s custom function… so although ‘user_validated’ is called, only ‘user_email_offered’ is ever passed as in as a string- nothing else goes in, from what I can tell. How do I get around this?
I have tried inserting < $validation->setRuleGroup('user_signin'); > before the call to validate, but found that I could move the setting of the rule group into the call to validate, using: $validationResult = $this->validate('user_signin'), which seemed to do the same, and which doesn't seem to work without the rule-group as a parameter (?). This still doesn't seem to be what triggers the additional data to be passed to the custom rule's method.
Extracts from my hack are appended below.
I’d be very grateful one of you knowledgeable folk could please point me in the right direction.
In app\Controllers\SignupTest.php:
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
class SignupTest extends BaseController
{
public function index() { // redirection from the default to signup(), signin(), ...
return $this->signup();
}
public function signup() {
helper(['form']);
$validation = \Config\Services::validation();
if ($this->request->getPost()) { // still TBD: any different to using $this->request->getGetPost() ?
$validationResult = $this->validate('user_signin'); // set the rules to use: 'user_signin', 'user_signup'
if (!$validationResult) {
$validationErrors = $validation->getErrors();
return view('SignupTestView', $validationErrors); // redisplay simple html form view with list of validation errors
} else {
return view('SignupTestViewSuccess'); // display view to show success
}
} else {
return view('SignupTestView'); // initial display, in the event of there being no POST data
}
}
}
In \app\Config\CustomRules.php:
<?php
namespace Config;
use App\Models\UserModel;
//--------------------------------------------------------------------
// Custom Rule Functions
//--------------------------------------------------------------------
class CustomRules
{
public function user_validated(string $str, string $fields = NULL, array $data = NULL, string &$error = NULL) : bool{
$user_email_offered = $str;
$user_password_offered = ''; // to be extracted using $fields = explode(',', $fields), but $fields is never provided in the call to this user_validated method
if (($user_email_offered !== NULL) && ($user_password_offered !== NULL)) {
$usermodel = new UserModel(); // intended to create a UserEntity to permit connectivity to the database
$user_found = $usermodel->find($user_email_offered); // we're going to assume that user_email is unique (which is a rule configured in the database table)
if ($user_found === NULL) { // check if user exists before doing the more involved checks in the else-if section below, which may throw exceptions if there's nothing to compare (?)
...
}
}
In \app\Config\Validation.php:
?php
namespace Config;
class Validation
{
//--------------------------------------------------------------------
// Setup
//--------------------------------------------------------------------
/**
* Stores the classes that contain the
* rules that are available.
*
* #var array
*/
public $ruleSets = [
\CodeIgniter\Validation\Rules::class,
\CodeIgniter\Validation\FormatRules::class,
\CodeIgniter\Validation\FileRules::class,
\CodeIgniter\Validation\CreditCardRules::class,
\Config\CustomRules::class,
];
/**
* Specifies the views that are used to display the
* errors.
*
* #var array
*/
public $templates = [
'list' => 'CodeIgniter\Validation\Views\list',
'single' => 'CodeIgniter\Validation\Views\single',
];
//--------------------------------------------------------------------
// Custom Rules
//--------------------------------------------------------------------
/* configurable limits for validation rules array below*/
const user_email_min_lenth = 9;
const user_email_max_lenth = 50;
const user_password_min_lenth = 6;
const user_password_max_lenth = 25;
public $user_signin = [
'user_email' => [
'label' => 'e-mail address',
'rules' => 'trim|required|valid_email|user_validated', // user_validated is custom rule, that will have a custom error message
'errors' => [
'required' => 'You must provide an {field}',
'valid_email' => 'Please enter a valid {field}',
]
],
'user_password' => [
'label' => 'password',
'rules' => 'trim|required',
'errors' => [
'required' => 'Enter a {field} to sign in',
'user_password_check' => 'No such user/{field} combination found',
]
Calling custom rule with parameters should be exactly the same as calling CI4's regular rules. Let's get for example "required_without". You use it like in this example:
$validation->setRule('username', 'Username', 'required_without[id,email]');
And the function is declared as so:
public function required_without($str = null, string $fields, array $data): bool
{
$fields = explode(',', $fields);
//...
}
where $str - this is your main field, $fields - string, packing a comma-separated array.
As for Grouping rules, you do not need to group rules to be able to use custom rules with parameters.
If you have only 2 fields to test against you can go a bit cheaper, which will not be perfect but still works:
Function:
public function myrule(string $mainfield, string $fieldtotestwith): bool
{
//doing stuff
}
Validating rule:
$validation->setRule('somemainfield', 'Something', 'myrule[somesecondfield]');
I am attempting to add validation to my request validator for a group of fields to make sure that the group of fields does not exceed a certain amount. Once a user submits their form, they can view it in pdf form and my objective is to make sure that the collection of f_name, m_name and l_name does not run into another group of text on the pdf (thus making it illegible).
Essentially, I want something like this:
public function rules()
{
return [
'f_name' + 'm_name' + 'l_name' => 'max:50',
...
];
}
I have searched all over but the only questions I can find related are simply how to do validation. If anyone knows how to group values in validation or links to previously asked questions, please let me know. Thanks mates.
Laravel does not support multifields validation with one rules stack. Try write custom validator or reinvent the wheel:
public function rules()
{
$fields = [
"max: 50" => ["f_name", "m_name", "l_name"]
];
$rules = [];
foreach($fields as $rule => $fieldArray){
if(is_array($fieldArray)){
foreach($fieldArray as $field){
$rules[$field] = $rule;
}
}
}
return $rules;
}
You need a custom validator so you can add several fields.
In your AppServiceProvider, something like this:
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('groupedFieldsLongerThan50', function($attribute, $value, $parameters, $validator) {
$f_name = array_get($this->data, $parameters[0]);
$m_name = array_get($this->data, $parameters[1]);
$l_name = array_get($this->data, $parameters[2]);
if((strlen($f_name)+strlen($m_name)+strlen($l_name))<=50)
return true;
return false;
});
}
...
Where you are declaring your rules add:
'f_name' => 'groupedFieldsLongerThan50:f_name,m_name,l_name'
I'm searching for a cleaner way to validate tags when storing a Post.
All of the input validation takes place within my custom request StorePostRequest. The problem is that I need to check whether the given tags exist in the database, only existing tags are allowed. The function $request->input('tags') returns a string with comma seperated values, for example: Tag1,Tag2,Tag3.
Here is the code:
/**
* Store a newly created resource in storage.
*
* #param StorePostRequest $request
* #return Response
*/
public function store(StorePostRequest $request)
{
//THIS PIECE OF CODE
$tags = explode(',', $request->input('tags'));
$tags = Tag::whereIn('title', $tags)->lists('id');
if(count($tags) < 1)
{
return redirect()->back()->withInput()->withErrors([ trans('tags.min') ]);
}
else if(count($tags) > 5)
{
return redirect()->back()->withInput()->withErrors([ trans('tags.max') ]);
}
//TILL HERE
$post = $request->user()->posts()->create([
'slug' => unique_slug('Post', $request->input('title')),
'title' => $request->input('title'),
'description' => $request->input('description'),
'summary' => $request->input('summary'),
]);
$post->tags()->attach($tags);
return redirect(route('theme.post.show', [$theme->slug, $post->slug]))->with(['success', trans('messages.post.store')]);
}
The code is a little sloppy and redundant when using it in multiple controllers.
In order to solve this, I've created a ValidationServiceProvider to extend the core validator rules. Something like this:
$this->app['validator']->extend('tags', function ($attribute, $value, $parameters)
{
$tags = explode(',', $value);
$tags = Tag::whereIn('title', $tags)->lists('id');
if(count($tags) < 1 || count($tags) > 5))
{
return false;
}
});
Pretty neat. The thing is I still need to be able to access the $tags variable within the controller (because of ->attach($tags)).
Is there a better way of tackling this problem? Or should I stop thinking and just use (and repeat) the code I have?
Thanks in advance, hope it makes some sence.
I am assuming that you understand the use of this class because I have seen that you have defined StorePostRequest class. So, just for clarify, the rules method could looks like:
public function rules()
{
return [
'tags' => ['required', 'tags'] //kb
];
}
Finally, with all the tools in correct place, you only make manipulate the data in your controllers like this:
public function store(StorePostRequest $request)
{
// at this point, the tags are already validated, so we, proceed get them:
$tags = explode(',', $$request->get('tags'));
$post = $request->user()->posts()->create([
'slug' => unique_slug('Post', $request->input('title')),
'title' => $request->input('title'),
'description' => $request->input('description'),
'summary' => $request->input('summary'),
]);
$post->tags()->attach($tags);
return redirect(route('theme.post.show', [$theme->slug, $post->slug]))->with(['success', trans('messages.post.store')]);
}
Keep in mind that inyecting StorePostRequeststore in the controller's function it is already validating and running the rules.
That is enough if you really has defined the StorePostRequest's rules correctly.
foreach($request->tags as $k=>$tags){
$this->validate($request, [
'tags.'.$k => 'required|string|max:20'
]);
}
I am trying to validate an update user profile form, whereby the validation should check that the email doesn't exist already, but disregard if the users existing email remains.
However, this continues to return validation error message 'This email has already been taken'.
I'm really unsure where I'm going wrong. Otherwise, the update form works and updates perfectly.
HTML
{{ Form::text('email', Input::old('email', $user->email), array('id' => 'email', 'placeholder' => 'email', 'class' => 'form-control')) }}
Route
Route::post('users/edit/{user}', array('before' => 'admin', 'uses' => 'UserController#update'));
User Model
'email' => 'unique:users,email,{{{ $id }}}'
Your rule is written correctly in order to ignore a specific id, however, you'll need to update the value of {{{ $id }}} in your unique rule before attempting the validation.
I'm not necessarily a big fan of this method, but assuming your rules are a static attribute on the User object, you can create a static method that will hydrate and return the rules with the correct values.
class User extends Eloquent {
public static $rules = array(
'email' => 'unique:users,email,%1$s'
);
public static function getRules($id = 'NULL') {
$rules = self::$rules;
$rules['email'] = sprintf($rules['email'], $id);
return $rules;
}
}
You can accomplish this with the sometimes function of the validator
Something like:
$validator->sometimes('email', 'unique:users,email', function ($input) {
return $input->email == Input::get('email');
});
See http://laravel.com/docs/4.2/validation#conditionally-adding-rules for more info
I'm using a validation service to validate user submitted form input (something along the lines of: http://laravel.io/bin/vrk).
Using this approach (validation service classes) to validate user submitted form data against a set of rules, how can I validate user submitted data when rules have a unique rule. For example, if a user has the username of John then when I try to update the model validation fails (because John exists as a username, even though it belongs to the current model).
To solve this in Laravel I can do something like 'username' => 'required|alpha_dash|unique:users,username'.$id. How should I modify my current code, in the link, to best accommodate this? Should I have separate validator classes depending on the scenario (for example, UserCreateValidator, UserUpdateValidator, etc). Or should I do something like create separate validation rules in UserValidator class and pass which rule I want as an argument to either the constructor or the passes() method when calling UserValidator?
I think you could do something like this
First update UserValidator rules like this.
class UserValidator extends Validator {
// Override parent class $rules
protected $rules = [
'default' => [
'username' => 'required|alpha_dash|unique:users',
'password' => 'required|between:6,16|confirmed',
'password_confirmation' => 'required|between:6,16'
],
'update' => [
'username' => null,
]
];
}
Then modify Validator's passes method like this
public function passes($rule = null) {
$rules = $this->rules['default'];
if ($rule && isset($this->rules[$rule])) {
$rules = array_merge($rules, $this->rules[$rule]);
}
$validator = \Validator::make($input, $rules);
if ($validator->fails()) {
$this->validator = $validator;
return false;
}
return true;
}
Then in your controller's PUT method, this will merge update rules to default rules
$rule = 'update';
// user has changed his username
if ($input['username'] !== $old_username) {
$rule = 'create'; // validate uniqueness
}
else {
unset($input['username']); // remove it, we don't validate it anymore since it's the same
}
$validator->passes($rule); // override 'default' rules with 'update' rules
You don't have to change your controller's POST method, it'll stay the same
$validator->passes(); // use 'default' rules
If I'm understanding right, you have issues updateng data because of primary key constraints on your model. What you need to do is to create 2 sets of rules, one for insert, and one for update.
Asuming you have a set of rules like this:
protected $rules = [
'id' => 'required|unique:users'
]
You should implement something like this:
protected $rules = [
'id' => 'required|unique|unique:users,id,' . $this->id
];
This should tell laravel to ignore the duplicate id in the table users for the specified id, in this case, the id for the current object.
You can read more about this on laravel's documentation at http://laravel.com/docs/validation
unique:table,column,except,idColumn
The field under validation must be unique on a given database table.
If the column option is not specified, the field name will be used.
Well, what are you doing on post?
Because this is what you should be doing:
$user = User::find($userId);
$user->username = $input['username'];
$user->email = $input['email'];
$user->save();
To update a record.
Or
$input = array('username' => 'w0rldart', 'email' => 'hahafu#dumbledore.com');
// Retrieve the user by the attributes, or create it if it doesn't exist,
// based on the data above, which can come from an Input::all();
$user = User::firstOrCreate($input);
... many possibilities. But you could also do:
$input = array_forget($input, 'username');
To comply with your case, by removing the username index from the input array.
This is all I call tell you, based on the information you gave us. If you want more, post the controller's put method.
Update:
Here's my version of your PUT method: http://laravel.io/bin/OaX
I really think that try catch syntax is useless, since it's obvious that a User model will always be there. But I still don't know what you're trying to update. Even though I can't test it right now, I don't think that updating should be giving that problem, and if it does, retrieve user by username/id then unset the username index in your input array, and update it according to your specifications.
A little modification in UserValidator class
class UserValidator extends Validator {
// Override parent class $rules
protected $rules = [
'username' => 'required|alpha_dash|unique:users',
'password' => 'required|between:6,16|confirmed',
'password_confirmation' => 'required|between:6,16'
];
// ADD THIS
public function __construct(Array $rules = array())
{
parent::__construct();
if(count($rules)){
foreach($rules as $k => $v) $this->rules[$k] = $v;
}
}
}
In your controller putUpdate method
$user = User::whereUsername($username)->firstOrFail();
$rules = ['username' => 'required|alpha_dash|unique:users,username,'. $user->id];
// Pass the rule to update the rule for username in this method
$validator = \Services\Validators\UserValidator(Input::all(), $rules);
Check the manual here.