Yii2 whenClient validation issue - conditional-statements

For some reason I cannot get conditional rule 'required' to work. Even if I reduce the condition to "always return false", required-validation seems to check this unnecessary field:
public function rules() {
return [
[['order_id', 'product_id', 'quantity'], 'required'],
['product_date', 'required',
'whenClient' => "function(attribute, value) {
return false;
}"
],
// more rules here
[['date_create', 'date_update', 'product_date'], 'safe'],
// more rules here
];
}
On form submit save() fails and $model->getErrors() points to product_date as a necessary field. What have I missed? Thank you in advance.

You should add the server-side condition to the rule as well (documentation: when):
['product_date', 'required',
'when' => function ($model) {
return false;
},
'whenClient' => "function(attribute, value) {
return false;
}"
],
whenClient is only for JS on the client side. When the form gets submitted the validation has to be done (or skipped) at the server as well. Usually you should have a when definition if you have whenClient definition. The when definition is much more important, whenClient is just to improve the user experience.
Find more infos here.

Replace
['product_date', 'required',
'whenClient' => "function(attribute, value) {
return false;
}"
],
With
['product_date', function(attribute, value) {
return false;
}],

Related

Cypress conditional statement depending on whether the input field is disabled or not

I have some input fields on a page. They can be disabled or not. So I need to type the text in this field just in case when it's not disabled. I try to do this way:
fillPickUpTown(pickUpTown: string) {
cy.get(`[data-testid="pick-up-town"]`)
.should('not.be.disabled')
.then(() => {
cy.get(`[data-testid="pick-up-town"]`).type(pickUpTown);
});
}
But I have failed test with error "Timed out retrying after 10000ms: expected '' not to be 'disabled'".
How can I type to this field just when it's not disabled and do nothing when it is?
Drop the should, this will only succeed for the one condition. Instead, test inside then()
fillPickUpTown(pickUpTown: string) {
cy.get(`[data-testid="pick-up-town"]`)
.then($el => {
if (!$el.is(':disabled')) {
cy.wrap($el).type(pickUpTown);
}
});
}
You can use JQuery :enabled to check and implement If-Else.
fillPickUpTown(pickUpTown: string) {
cy.get(`[data-testid="pick-up-town"]`).then(($ele) => {
if ($ele.is(":enabled")) {
cy.get(`[data-testid="pick-up-town"]`).type(pickUpTown)
}
})
}

How to add captcha required only for a particular condition yii2

I am trying make the captcha field required only when the number of failed login attempts exceed 3 times. For which I have written below code till now.
In LoginForm model I have added the below rules
public function rules()
{
return [
[['username', 'password'], 'required'],
['password', 'validatePassword'],
['verifyCode', 'captcha', 'when' => function($model) {
return $this->checkattempts();
}],
];
}
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addLoginAttempt($user->id);
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
public function checkattempts()
{
$user = $this->getUser();
$ip = $this->get_client_ip();
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $user->id])
->one();
if($data["attempts"] >=3){
return false;
}else{
return true;
}
}
public function addLoginAttempt($uid) {
$ip = $this->get_client_ip();
$data = (new Query())->select('*')
->from('login_attempts')
->where(['ip' => $ip])->andWhere(['user_ref_id' => $uid])
->one();
if($data)
{
$attempts = $data["attempts"]+1;
#Yii::$app->db->createCommand("UPDATE login_attempts SET attempts=".$attempts." where ip = '$ip' AND user_ref_id=$uid")->execute();
}
else {
Yii::$app->db->createCommand("INSERT into login_attempts (attempts, user_ref_id, ip) VALUES(1,'$uid', '$ip')")->execute();
}
}
Here I am validating the password first. If the password is incorrect then I am incrementing the count by 1. This part is working fine. The count is incrementing successfully.
After this I am trying to get the count of failed attempts while validating captcha using the function checkattempts(), but it is not working.
Can anyone please tell me where I have made mistake.
Thanks in advance.
In your model:
if (!$model->checkattempts())
//show the captcha
Then, in your model rules you'll need something like:
['captcha', 'captcha'],
In your case, what you can do is use different scenarios depending on the user attempts, and in one scenario (more than X attempts) make the captcha required.
More documentation about the captcha and about scenarios.

How to call a function within the same controller?

I have to call a soap service using laravel and done so correctly. This soap service requires me to send a login request prior to sending any other request.
The code I'm using works, but I want to improve by removing the login from all the functions and creating one function.
I tried changing the following for one function:
public function getcard($cardid)
{
SoapWrapper::add(function ($service) {
$service
->name('IS')
->wsdl(app_path().'\giftcard.wsdl')
->trace(true);
});
$data = [
'UserName' => 'xxxx',
'Password' => 'xxxx',
];
$card = [
'CardId' => $cardid,
];
SoapWrapper::service('IS', function ($service) use ($data,$card) {
$service->call('Login', [$data]);
$cardinfo=$service->call('GetCard', [$card]);
dd($cardinfo->Card);
});
}
Into:
public function login()
{
SoapWrapper::add(function ($service) {
$service
->name('IS')
->wsdl(app_path().'\giftcard.wsdl')
->trace(true);
});
$data = [
'UserName' => 'xxxx',
'Password' => 'xxxx',
];
SoapWrapper::service('IS', function ($service) use ($data) {
return $service->call('Login', [$data]);
//$service->call('Login', [$data]);
//return $service;
});
}
public function getcard($cardid)
{
$this->login();
$card = [
'CardId' => $cardid,
];
$cardinfo=$service->call('GetCard', [$card]);
dd($card);
}
But this doesn't work. I also tried it with the commented out part, but that doesn't work. Both options result in an error that it didn't find 'service'.
I know it has something to do with oop, but don't know any other option.
I took this as an example, but I probably implemented it wrong?
So my question is: How do I reuse the login part for all other functions?
Your return statement in the login() method is within the scope of that closure. You need to return the result of the closure as well.
return SoapWrapper::service('IS', function ($service) use ($data) {
return $service->call('Login', [$data]);
});
EDIT:
To explain a little bit. You have a function:
SoapWrapper::service('IS' ,function() {}
Inside of a function : public function login()
If you need to return data from your login() method, and that data is contained within your SoapWrapper::service() method, then both methods need a return statement

Yii2 autologin doesn't work

I try to realize the autologin feature in yii2.
So I've enabled autologin in configuration:
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
'loginUrl' => ['account/login', 'account', 'account/index'],
],
Also I've added rememberMe field in form configuration
public function scenarios() {
return [
'login' => ['username','password','rememberMe'],
'activate' => ['password','passwordrepeat'],
'register' => ['username', 'mail'],
'setup' => ['username', 'password', 'passwordrepeat', 'mail', 'secretkey'],
];
}
// ...
[
['rememberMe'],
'boolean',
'on' => 'login',
],
I'm using this now at login:
public function login() {
//var_dump((bool) ($this->rememberMe)); exit();
if (!$this->validate()) {
return false;
}
return Yii::$app->user->login($this->getUser(), (bool) ($this->rememberMe) ? 3600*24*30 : 0);
}
If I log in, users function getAuthKey function is called and a new auth_key is generated.
public function generateAuthKey() {
$this->auth_key = Yii::$app->getSecurity()->generateRandomString();
Helper::save($this);
// Helper is a database helper which will update some rows like last_modified_at and similar in database
}
/**
* #inheritdoc
*/
public function getAuthKey()
{
$this->generateAuthKey();
return $this->auth_key;
}
But always, I log in, it doesn't set some cookie variables.
My cookies are always
console.write_line(document.cookie)
# => "_lcp=a; _lcp2=a; _lcp3=a"
And if I restart my browser I'm not logged in.
What am I doing wrong?
It seems that Yii doesn't work with cookies correctly:
var_dump(Yii::$app->getRequest()->getCookies()); exit();
Results in:
object(yii\web\CookieCollection)#67 (2) { ["readOnly"]=> bool(true) ["_cookies":"yii\web\CookieCollection":private]=> array(0) { } }
If I access via $_COOKIE I have the same values as in JS.
Thanks in advance
I guess you don't have to generate auth key every time in your getAuthKey method. Your app tries to compare database value to the auth key stored in your cookie. Just generate it once before user insert:
/**
* #inheritdoc
*/
public function getAuthKey()
{
return $this->auth_key;
}
/**
* #inheritdoc
*/
public function beforeSave($insert)
{
if (!parent::beforeSave($insert)) {
return false;
}
if ($insert) {
$this->generateAuthKey();
}
return true;
}
Could be your timeout for autologin is not set
Check if you have a proper assignment to the value assigned to the variable:
$authTimeout;
$absoluteAuthTimeout;
See for more

Conditional sign-in validation using Fluent Validation

I am trying to use Fluent Validation and it seems easy to use at the beginning but now there is some problem. I need to validate a SignIn view model, shown below:
public SignInViewModelValidator(IMembershipService membershipService)
{
_membershipService = membershipService;
RuleFor(x => x.EMail).NotEmpty().EmailAddress();
RuleFor(x => x.Password).NotEmpty().Length(6, 20);
Custom(x =>
{
var user = _membershipService.ValidateUser(x.EMail, x.Password);
if (user == null)
return new ValidationFailure("EMail", "Your E-Mail Address or password was invalid.");
return null;
});
}
But I'm getting all the errors at once, like this:
'E Mail' should not be empty.
Your E-Mail Address or password was invalid.
'Password' should not be empty.
How can I change this behavior to not check the Custom validation rule when the other rules are invalid? In other words, it should only check the Custom validation rule when 'EMail' and 'Password' fields are valid.
I managed this in this way:
public SignInViewModelValidator(IMembershipService membershipService){
_membershipService = membershipService;
bool firstPhasePassed = true;
RuleFor(x => x.EMail)
.NotEmpty().WithMessage("")
.EmailAddress().WithMessage("")
.OnAnyFailure(x => { firstPhasePassed = false; });
RuleFor(x => x.Password)
.NotEmpty().WithMessage("")
.Length(6, 16).WithMessage("")
.OnAnyFailure(x => { firstPhasePassed = false; });
When(x => firstPhasePassed, () =>
{
Custom(x =>
{
if (_membershipService.ValidateUser(x.EMail, x.Password) == null)
return new ValidationFailure("EMail", "");
return null;
});
});
}
You can use the When method to check your custom rule only when your email/password rules are valid.
To make this easier, I suggest moving your Custom rule logic into a separate method (something like IsValidEmailAndPassword) and use the Must method to validate both email and password. Since you're passing in multiple parameters (email and password) into that method, read the documentation on the overload of Must that "accepts an instance of the parent object being validated" in order to implement this rule.
Hopefully those links point you to the right direction.