remove required from field in child model yii2 - php

I have a main model and it has a field rules:
class User extends \yii\db\ActiveRecord
{
/**
* {#inheritdoc}
*/
public static function tableName()
{
return 'users';
}
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['username', 'email'], 'required'],
];
}
}
I must say right away that I cannot make changes to this model, I can make all changes only in the next model that will be inherited from this:
class UserForm extends User
{
public function rules(): array
{
return array_merge(
parent::rules(),
[
['username', 'safe', 'skipOnEmpty' => true],
]
);
}
}
The bottom line is that in the first model the username field is required, but now I need to make it so that when filling out the form it is empty, and when saving the value of the email field is copied to the username field.
For copying I use the function:
public function beforeSave($insert)
{
if (empty($this->username)) {
$this->username = $this->email;
}
return parent::beforeSave($insert);
}
But due to the fact that the username field is required, nothing is saved for me, what I did in the child model does not work. How can I make a field optional without editing the first model?

You can't replace the rule by adding another. If you define rules like that, the result is that both, required and safe rule are applied for username field.
You have to find the required rule in rules definition array and remove username from attributes the rule applies to.
It can be easy if you know that the rule definition will always be in specific position. For example if it's always the first rule in parent model:
public function rules()
{
$rules = parent::rules();
// the array diff is used to remove only 'username' field from list of attributes
$rules[0][0] = array_diff($rules[0][0], ['username']);
return $rules;
}
If you don't know the position of required rule, you will need to use some cycle to find it. For example like this:
foreach ($rules as $rule) {
if (
$rule[1] == 'required'
&& (
(is_array($rule[0]) && in_array('username', $rule[0]))
|| (is_string($rule[0]) && $rule[0] == 'username')
)
) {
// rule found
}
}
But there is another possible solution to your problem. Instead of copying the value of email field in beforeSave() callback you can copy it in beforeValidate() callback. That way when the username field is validated it will already have the value of email field and it will pass validation.

You could bypass the required validation by adding a where clause in your rule:
[['username', 'email'], 'required', 'when' => function() { return false; }],
Since the function always returns false, the validator is never aplied.

Related

How to pass the entity id to the unique validation in a Form Request on Laravel 5.5?

I have the following route defined:
Route::put('/{organisationId}', 'OrganisationController#update');
And I have the following FormRequest for the update request:
<?php
namespace App\Http\Requests\Organisation;
use Illuminate\Foundation\Http\FormRequest;
class UpdateOrganisationRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'required|min:3|max:60|unique:organisations,name,' . $this->get('organisationId')
];
}
}
And I am trying to use it like this in my controller:
public function update($organisationId, UpdateOrganisationRequest $request)
{
$organisation = $this->organisationRepository->byId($organisationId);
if (!$organisation) {
return $this->error('Organisation not found.', $this::Bad_Request);
}
$this->organisationRepository->update($organisation, $request->validated());
return $this->success(
fractal($organisation, new OrganisationTransformer())
);
}
This appears to trigger the unique validation error, because it doesn't appear to exclude the id I am trying to update.
Any ideas why this isn't working?
Before using FormRequest, this is how I implemented the same functionality above and it was working fine:
https://pastebin.com/raw/CDEg6qLt
I was able to update the same organisation with the same name and the unique validation rule didn't trigger an Validation exception.
All rules defined in a FormRequest's rules method should be in form of a key / value pair that corresponds to an input name and it's validation rule respectively. You missed key here so chances are validator looks for a field named 0 that doesn't exist.
Add name and test result:
return [ 'name' => '....' ];

How to update only a group of fields present in FormRequest rules and ignore rest in laravel 5?

Same table with 2 request files which fills some group of fields respectively
public function update_f1(F1Request $request){
$projecttable->fill($request->all());
$projecttable->save();
}
Another one
public function update_f2(F2Request $request){
$projecttable->fill($request->all());
$projecttable->save();
}
Say f1 fills field_abc1 which is validated in F1Request.
f2 fills field_abc2 which is validated in F2Request.
Now if f2 is called it should fill only field_abc2
and not field_abc1 because its not validated in F2Request even if its fillable. Anyone can add field_abc1 from developers tools.
Yes I can do individually like
$projecttable->field_abc2 = $request->field_abc2
But its better to use fill for large number of fields
$project->fill($request->all())
In short fill only those fields which are present in Request rules and ignore all others even if fillable
You can filter these fields in Your Requests with replace() method.
Like this:
class F1Request extends Request
{
private $rules = [
/* rules goes here */
]
public function authorize()
{
return true;
}
public function rules()
{
$this->filter();
return $this->rules;
}
public function filter()
{
$input = array_intersect_key($this->all(), $this->rules);
$this->replace($input);
}
}
If it's common case, than even move filter() method to Request class.
Works Perfectly :)
public function update_f1(F1Request $request){
$rules = $request->rules();
$validfieldsgroup = array_keys($rules);
$projecttable->fill($request->only($validfieldsgroup));
$projecttable->save();
}
I think what you want is to use
$request->only($request->rules);
where $rules is the array with fields, instead of $request->all();
Source: http://laravel.com/docs/5.0/requests#retrieving-input

Using filters from other classes within rules method in Yii 2?

Consider this code inside a model:
public function rules() {
return [
[['company_name', 'first_name', 'last_name'], 'sanitize'],
//........
];
}
sanitize is a custom method inside the current class, which is:
public function sanitize($attribute) {
$this->{$attribute} = general::stripTagsConvert($this->{$attribute}, null, true);
}
Now this method obviously will come in handy in many models so I don't want to keep repeating the same code in every model. Is there a way I can reference another class in the rules in place of the current sanitize method name which is binded to the current class?
Yes, it's definitely possible.
Create separate validator. Let assume it's called SanitizeValidator and placed in common/components folder.
Your custom validator must extend from framework base validator and override validateAttribute() method. Put your logic inside this method:
use yii\validators\Validator;
class SanitizeValidator extends Validator
{
/**
* #inheritdoc
*/
public function validateAttribute($model, $attribute)
{
$model->$attribute = general::stripTagsConvert($model->$attribute, null, true);
}
}
Then in model you can attach this validator like this:
use common/components/SanitizeValidator;
/**
* #inheritdoc
*/
public function rules()
{
return [
[['company_name', 'first_name', 'last_name'], SanitizeValidator::className()],
];
}
Check the official documentation about custom validators here and there.

Yii Validate an un-bound Variable (non-stored)

Classic problem:
verify that a user accepted the contract terms but the value of the acceptance is not stored (bound) in the database...
Extend CFormModel rather than CActiveForm (because CActiveForm binds
values to DB)
Post a CFormModel to a controller action
Validate a CFormModel
I'm asking this question to answer it because the existing questions end in see the documentation...
extend CFormModle, define the rules and got to validate. With bound variables you validated as part of save. Now you validate() by itself but Validate requires a list of attributes which is not defined in CFormModel. So, what do you do? You do this:
$contract->validate($contract->attributeNames())
Here's the full example:
class Contract extends CFormModel
{
...
public $agree = false;
...
public function rules()
{
return array(
array('agree', 'required', 'requiredValue' => 1, 'message' => 'You must accept term to use our service'),
);
}
public function attributeLabels()
{
return array(
'agree'=>' I accept the contract terms'
);
}
}
Then in the controller you do this:
public function actionAgree(){
$contract = new Contract;
if(isset($_POST['Contract'])){
//$contract->attributes=$_POST['Contract']; //contract attributes not defined in CFormModel
...
$contract->agree = $_POST['Contract']['agree'];
...
}
if(!$contract->validate($contract->attributeNames())){
//re-render the form here and it will show up with validation errors marked!
}
The results:

different rules for the same AR model in Yii

I have one model extending AR class with specific rules. But now i need to insert row into this table, but with other rules. Is i need to create other model with new rules, or it is possible to define orther rules?
You can set validation scenario. For example:
$model = new Post();
$model->scenario = 'new_line';
$model->attributes = $_GET['data'];
if ($model->validate()){
$model->save(false);
}
in your model:
public function rules()
{
return array(
array('username, text', 'required','on' => 'new_line')
);
}
In model rules all array lines must have key "on", else this rules will not apply.
Read more here.
If you are extending your class (active records) then you can actually just override your rules() function i.e.:
class User extends ActiveRecord(){
function rules(){
return array(array(
// Nomrally a rule
))
}
}
And then make your next class:
class User_extended extends ActiveRecord(){
function rules(){
return array(array(
// Nomrally a rule
))
}
}
And that should be it. You can then call the User_extended class and your rules will apply to the parent User class since Yii grabs the rules in a $this context and $this will be your child class.
But you can also use scenarios here, but it might get dirty especially if you need to override other methods.
thx. Now i'm trying to use this
/**
* #param string $attribute fields names wich should be validated
* #param array $params additional params for validation
*/
public function ValidatorName($attribute,$params) { … }

Categories