I'm trying to make yii2 to validate my ActiveForm field that should be numeric and of 8 characters long.
Following is what I tried in the default LoginForm model of yii2/advanced/backend, but unfortunately the isNumeric validator simply doesn't kick in:
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// username should be numeric
['username', 'isNumeric'],
// username should be numeric
['username', 'string', 'length'=>8],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
/**
* Validates if the username is numeric.
* This method serves as the inline validation for username.
*
* #param string $attribute the attribute currently being validated
* #param array $params the additional name-value pairs given in the rule
*/
public function isNumeric($attribute, $params)
{
if (!is_numeric($this->username))
$this->addError($attribute, Yii::t('app', '{attribute} must be numeric', ['{attribute}'=>$attribute]));
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*
* #param string $attribute the attribute currently being validated
* #param array $params the additional name-value pairs given in the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
I also tried adding a scenario as suggested in a related post (https://stackoverflow.com/a/27817221/2037924) but that only worked (as in displayed the error) if I did not include the password field in the scenario.
Is this a good way to achieve this at all, or can you think of a better way of doing it?
Note: the reason I define username as string is because the numbers may contain leading 0's.
Try with integer data type:
[['username'], 'integer'],
[['username'], 'string', 'min' => 8],
It will validate both numeric and length. This will do the trick.
Read more about the validations here: http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html#number
This works fine for me using the contact form from yii2-basic
/**
* #return array the validation rules.
*/
public function rules()
{
return [
// name, email, subject and body are required
[['name', 'email', 'subject', 'body'], 'required'],
// email has to be a valid email address
['email', 'email'],
['subject', 'is8NumbersOnly'],
// verifyCode needs to be entered correctly
['verifyCode', 'captcha'],
];
}
public function is8NumbersOnly($attribute)
{
if (!preg_match('/^[0-9]{8}$/', $this->$attribute)) {
$this->addError($attribute, 'must contain exactly 8 digits.');
}
}
public function rules()
{
return [
// username should be numeric
['username', 'match', 'pattern' => '/^\d{8}$/', 'message' => 'Field must contain exactly 8 digits.],
// ...
];
}
try this:
public function rules()
{
return [
// name, email, subject and body are required
[['name', 'email', 'subject', 'body'], 'required'],
// email has to be a valid email address
['email', 'email'],
[['subject'], 'number'],
[['subject'], 'string', 'max' => 8, 'min' => 8],
// verifyCode needs to be entered correctly
['verifyCode', 'captcha'],
];
}
Related
I have merged all login, create user, and forgot password on one and the same page, and for error handling the "email" input on the 3 forms should differ. I have sat the name on the email-input for the login form to: "email" as default. I have sat the name on create user to: "register-email" and altered the "validator" function in RegisterController so it looks like this:
protected function validator(array $data)
{
$validator = Validator::make($data, [
'register-email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
'register-password' => ['required', 'string', 'min:8', 'max:255', 'confirmed'],
]);
$validator->setAttributeNames([
'register-email' => 'email',
'register-password' => 'password',
]);
return $validator;
}
And it works like a charm!
Now i set the email-input name to "forgot-email" on the forgot-form but I don't know how to correct the ForgotPasswordController to handle this.
I have tried to overide the following functions, with the updated name:
protected function validateEmail(Request $request)
{
$request->validate(['forgot-email' => 'required|email']);
}
protected function credentials(Request $request)
{
return $request->only('forgot-email');
}
protected function sendResetLinkFailedResponse(Request $request, $response)
{
if ($request->wantsJson()) {
throw ValidationException::withMessages([
'forgot-email' => [trans($response)],
]);
}
return back()
->withInput($request->only('forgot-email'))
->withErrors(['forgot-email' => trans($response)]);
}
But i just get an error saying:
SQLSTATE[42703]: Undefined column: 7 ERROR: column "forgot-email" does
not exist LINE 1: select * from "users" where "forgot-email" = $1
limit 1 ^ (SQL: select * from "users" where "forgot-email" =
test#domain.com limit 1)
The problem is obviously, it thinks that the input-name is the same as the column name, and that's not the case here. So how do I tell the controller to use "forgot-email" as email?
I found a solution. I just merge the forgot-email request to email request in the validateEmail function like this:
protected function validateEmail(Request $request)
{
$request->merge(['email' => $request->get('forgot-email')]);
$request->validate(['email' => 'required|email']);
}
protected function credentials(Request $request)
{
return $request->only('email');
}
protected function sendResetLinkFailedResponse(Request $request, $response)
{
if ($request->wantsJson()) {
throw ValidationException::withMessages([
'forgot-email' => [trans($response)],
]);
}
return back()
->withInput($request->only('email'))
->withErrors(['forgot-email' => trans($response)]);
}
UPDATE: This is only a part of a solution. The error is still send out as "email" when i do the #error in blade
UPDATE 2: Okay if the validation failed, the error was binded to "email", but for all other errors it was: "forgot-email" so I just changed the validate method to validate on "forgot-email" instead of email and now it seems to work properly. The validateEmail function now looks like this:
protected function validateEmail(Request $request)
{
$request->merge(['email' => $request->get('forgot-email')]);
$request->validate(['forgot-email' => 'required|email']);
}
Although there are quite a lot of questions concerning Yii2 captchas as well as problems with saving, I couldn't find and answer that solved my problem. Hope someone can help:
In my website I included a contact form along with a captcha. Therefor I added code to the model, to the IndexController and the form as described here: https://yii2-cookbook-test.readthedocs.io/forms-captcha/#how-add-captcha-to-a-form
The captcha is displayed and prevents from submitting the form if the code wasn't entered correctly. However, if I include the captcha validation rules, the message is not saved to the database. So there seems to be something wrong with the validation rule.
['verifyCode', 'captcha', 'captchaAction' => '/contact/index/captcha']
I just don't see what is wrong here. When I comment it out, it the model is saved but the captcha isn't validated. Leaving the part with "captchaAction" out leads to an Invalid Captcha ID error, the solution is to include the captchaAction as described here: Yii2 Invalid CAPTCHA action ID in module
Does anyone have an idea of what might be wrong here? Running in circles...
Form:
...
<div class="form-group">
<div><?= Yii::t('ContactModule.form', 'Please enter the letters from the image.'); ?></div>
<?= $form->field($model, 'verifyCode')->widget(Captcha::class, [
'captchaAction' => '/contact/index/captcha',
])->label(false); ?>
</div>
...
Model:
class Contact extends ActiveRecord{
public $verifyCode;
/**
* #inheritdoc
*/
public static function tableName()
{
return 'contact';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['name','email','message'], 'required'], //Checking that all the fields are required
['email', 'email'], // Validating that email is a valid email
[['name'],'string', 'max' => 50], //Verifying that name is not greater than its max length
[['email'], 'string', 'max' => 50], //Verifying that email is not greater than its max length
[['message'], 'string', 'max' => 255],//Verifying that message is not greater than its max length
['state', 'boolean'],
[['date'], 'date', 'format' => 'php:Y-m-d'],
['verifyCode', 'captcha', 'captchaAction' => '/contact/index/captcha'],
];
}
...
Controller
class IndexController extends Controller
{
public $subLayout = "#contact/views/layouts/default";
/**
* #inheritdoc
*/
public function actions()
{
return [
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
]
];
}
/**
* Renders the index view for the module
*
* #return string
*/
public function actionIndex()
{
$model = new Contact();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->save();
...
Trying to validate if there are only spaces in input box.
This is my validations:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use App\Rules\allAreSpaces;
class StoreUser extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'email' => 'required|email|max:64',
'phone' => 'bail|required|numeric|phone_min|phone_max|unique:users|not_in:0',
'password' => [new allAreSpaces, 'required', 'min:8', 'max:16', 'confirmed'],
'password_confirmation' => 'required',
];
}
public function messages()
{
return [
'email.required' => 'Email address cannot be empty',
'email.email' => 'Enter a valid Email address',
'email.max' => 'Email address cannot exceed 64 characters',
'email.unique' => 'Email already exists',
'phone.required' => 'Mobile number cannot be empty',
'phone.numeric' => 'Mobile number has to be numeric',
'phone.phone_min' => 'Mobile number should contain a minimum 5 characters',
'phone.phone_max' => 'Mobile number cannot exceed 11 characters',
'phone.unique' => 'Mobile number already exists',
'phone.not_in' => 'Enter a valid mobile number ',
'password.required' => 'Password cannot be empty',
'password.min' => 'Password should contain minimum 8 characters',
'password.max' => 'Password cannot exceed 16 characters',
'password.confirmed' => 'Password mismatch.Retry',
'password_confirmation.required' => 'Confirm Password cannot be empty',
];
}
public function withValidator(Validator $validator)
{
$email = $this->request->get( 'email' ); // Start with
$user = \App\User::Where('email', $email)->first();
if($user && $user->activated == 0){
$row = \App\User::find($user->id);
$row->delete();
}else{
$validator->sometimes('email', 'unique:users', function ($input) {
return true;
});
}
}
}
The following is the custom rule object that i was created:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class allAreSpaces implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
if(strlen($value) > 0){
if (strlen(trim($value)) == 0){
return false;
}
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'Password cannot contain space character alone';
}
}
Initially its returning, 'Password cannot contain space character alone'. but after i type some spaces then submit it shows 'Password cannot be empty'. But i want 'Password cannot contain space character alone' if the typed characters are only spaces.
Eventhough i did use the following code, its not showing if the input has some spaces, instead it shows, 'Password cannot be empty'.
public function passes($attribute, $value)
{
return false;
}
i was wondering why is that?
Have you checked the global middleware, that is ran for all routes in your application which is set in your HTTP Kernel? These might come into play:
"By default, Laravel includes the TrimStrings and ConvertEmptyStringsToNull middleware in your application's global middleware stack. These middleware are listed in the stack by the App\Http\Kernel class. These middleware will automatically trim all incoming string fields on the request, as well as convert any empty string fields to null. This allows you to not have to worry about these normalization concerns in your routes and controllers.
If you would like to disable this behavior, you may remove the two middleware from your application's middleware stack by removing them from the $middleware property of your App\Http\Kernel class."
Laravel 5.5 Docs - Requests - Input trimming and normalization
This might be relevant as well, goes into some complications that can happen because of these middleware:
"By default, Laravel includes the TrimStrings and ConvertEmptyStringsToNull middleware in your application's global middleware stack. These middleware are listed in the stack by the App\Http\Kernel class. Because of this, you will often need to mark your "optional" request fields as nullable if you do not want the validator to consider null values as invalid. "
Laravel 5.5 Docs - Validation - A note on optional fields
Is there a possibility for a field to be required in one form, and in the other not? I have two different forms, different models, that have one field in common, and I want it to be required in the first one, and optional in the other.
http://www.yiiframework.com/doc-2.0/guide-structure-models.html#scenarios
public function rules()
{
return [
// username, email and password are all required in "register" scenario
[['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER],
// username and password are required in "login" scenario
[['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN],
];
}
It's better to add scenarios as constants to model instead of hardcoded strings, for example:
const SCENARIO_CREATE = 'create';
Then you can use it like this:
[['email','password'], 'required', 'on' => self::SCENARIO_CREATE],
Another way is to describe it in scenarios() method:
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_CREATE] = ['email', 'password'];
return $scenarios;
}
That way you need to specify all safe attributes for each scenario for different actions.
Set scenario in the model
$model = new User;
$model->scenario = User::SCENARIO_CREATE;
I have created a custom validation function. Now I want to validate email inside that function, how can I do that?
Below is my code:
public function rules()
{
return [
['email', 'filter', 'filter' => 'trim'],
['email', 'required'],
['email', 'validateEmail'],
];
}
public function validateEmail($attribute, $params) {
if(ctype_digit($this->email)){
if(strlen($this->email)!=10)
$this->addError($attribute,'Phone number should be of 10 digits');
else{// Email validation using emailvalidator.}
}
}
You can call the email validator directly. The message is the error message that will be added to the model when validateAttribute is used. If you want to use the default error message you can leave it out:
public function validateEmail($attribute, $params) {
...
else {
$emailValidator = new \yii\validators\EmailValidator();
$emailValidator->message = "Invalid Email";
$emailValidator->validateAttribute($this, $attribute));
}
}