inline validator function not working for model in yii2 - php

I am creating offer for products thats why I have implemented several model at create function in controller
public function actionCreate()
{
$model = new Offer();
$wmodel = new Wmoffer();
$pmodel = new Product();
$ummodel = new Unitofmeasurement();
$qvmodel = new OfferingValue();
$blmodel = new OfferLocation();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
// code manipulation
}else{
return $this->render('create', [
'model' => $model,
'wmodel' => $wmodel,
'pmodel' => $pmodel,
'qvmodel' => $qvmodel,
'blmodel' => $blmodel,
'ummodel' => $ummodel
]);
}
all of my model extends by ActiveRecord aspect Wmoffer() this model looks like as below
use Yii;
use yii\base\Model;
use yii\web\UploadedFile;
use yii\helpers\FileHelper;
class Wmoffer extends Model
{
public $bIsProductOrService;
public $iCatalogueID;
public $imageProduct;
public $nHasCurrencyValue;
public $nHasCurrencyValueMRP;
public $BusinesslocationIds;
public function rules()
{
// validation rules
Now i need to implement inline validator for start,end date comparison [Start Date should be greater than End Date ]
i have tried this and this but this is not working i know something is missing which causes this
Any suggestions will be appreciable. Thanks

You should check $model->errors value after calling $model->validate() to look for validation errors.
Your dates validator method can be something like:
public function validateDates($attribute, $params) {
if ($this->hasErrors()) {
return;
}
if ($this->dateStart > $this->dateEnd)) {
$this->addError($attribute, 'Start date can not be greater than end date');
}
}
Add it to rules() in your backend models.

Related

Custom validation rule with request class not working laravel 7

Below is my code;
FruitRequest.php
class FruitRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|alpha',
'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
];
}
public function messages()
{
return ['name.required' => response("Name should be mandatory", 404),
'name.alpha' => response("Name should be contains only letters", 404),
'image.required' => response("Foto should be mandatory", 404),
'image.mimes' => response('Foto should be jpeg,png,jpg,gif,svg', 404),
'image.max' => response('Foto size should be blow 2 MB', 404),
];
}
}
FruitController.php
use App\Http\Controllers\Controller;
use App\Http\Requests\FruitRequest;
class FruitController extends Controller
{
public function store(FruitRequest $request)
{
echo $request->input('name');
//above line gives nothing to me
}
}
If I use extends Request instead of extends FruitRequest then this gives me value which is passed by user in postman. I don't know why this custom Request class not working.I attached screenshot. Please help....
extend your request class with FormRequest
use Illuminate\Foundation\Http\FormRequest;
class FruitRequest extends FormRequest
for more details visit official doc of laravel: https://laravel.com/docs/7.x/validation#creating-form-requests
long time not using postman, i'm testing with my code
I'm using FormRequest like this:
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
class YourRequest extends FormRequest
{
//this function called if Validator::make()->fails();
//here where you can modifying your message
protected function failedValidation(Validator $validator)
{
//note this only for API, for formData use \Illuminate\Validation\ValidationException($validator)
throw new HttpResponseException(response()->json($validator->errors()->all(), 422));
//this will get parameter attribute set from FormRequest
//attributes() along with the error message,
//or $validator->errors()->all() to get messages only like my screenshot
//or modify message with your logic
}
public function authorize() { return true; }
public function rules() { return []; }
public function attributes() { return []; }
public function messages() { return []; }
}
in controller :
use YourRequest;
public function store(YourRequest $req)
{
return response($req->all())->setStatusCode(200);
}
in your FormRequest replace response(), just text:
public function messages()
{
return ['name.required' => "Name should be mandatory"],
}
2nd, validation alpha only accepts alphabet, which your name is numeric,
result from my code(i use default validator message which in array of messages) :

Yii2 handle beforesave event in behaviour

i try to develop application with yii2 framework,i use beforeSave event to handle createdAt and updatedAt with time() function, this method is :
public function beforeSave($insert)
{
if ($insert) {
$this->createdAt = time();
}
$this->updatedAt = time();
return parent::beforeSave($insert);
}
i just wanna move this method to behaviour and attach it to my model, i create the behaviour class and attach the behaviour to model but it does not work abd pass null to database, my behaviour class is :
namespace app\modules\imdb\behaviors;
use yii\base\Behavior;
use yii\db\ActiveRecord;
class saveTimeBehavior extends Behavior
{
public function events()
{
return [
ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
];
}
public function beforeSave($event)
{
if ($event) {
$this->createdAt = time();
}
$this->updatedAt = time();
return parent::beforeSave($event);
}
}
and the attach code in my model is :
public function behaviors()
{
return [
saveTimeBehavior::className(),
];
}
please help me to handle this with correct way, thanks alot :)
You can do this with TimeStampBehavior. If you have declared the columns(created_at, updated_at) as int(11) for being UNIX timestamp.
use yii\behaviors\TimestampBehavior;
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
But as your attribute names are different or you want to use a different way of calculating the timestamp, you may configure the $createdAtAttribute, $updatedAtAttribute and $value properties like the following:
use yii\db\Expression;
use yii\behaviors\TimestampBehavior;
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'createdAt',
'updatedAtAttribute' => 'updatedAt',
'value' => new Expression('NOW()'),
],
];
}
Uou can do that in a simple way , by identify the type of created_at and updated_at as timestamp when you create the table, and it will be automatically filled in the database when you add new record or update existing one.
Example :
'created_at'=> $this->timestamp(),
'updated_at'=> $this->timestamp(),
Note: You can find more information about creating table using the below link : Yii2 Migration

How events work in Yii2

I have been trying to learn how event works in Yii from the Yii Guide. I have found there are most important things: Event Handlers, Attaching Event Handlers and Triggering Events. I have read entire the article properly. But I don't understand how to implement these three things properly. How to see the effect of it's implementation. I have extended component class as:
namespace app\components;
use yii\base\Component;
use yii\base\Event;
use yii\web\View;
class Foo extends Component{
const EVENT_HELLO = 'hello';
public function bar()
{
$this->trigger(self::EVENT_HELLO);
}
}
I do not understand what is the next part to do. Where I should write the Attaching Event Handlers. Can you help me someone, at least I can see a simple output using event.
You may create init() method in model:
public function init()
{
$this->on(Event::ACTION_ADD, ['app\models\Event', 'sendInLog']);
$this->on(Event::ACTION_DELETE, ['app\models\Event', 'sendInLog']);
$this->on(Event::ACTION_UPDATE, ['app\models\Event', 'sendInLog']);
}
In initialize events in second parameter you may use current model or set other model. If you want use current model set like that:
[$this, 'sendInLog']
sendInLog it is method in model. In method sendInLog one parameter it is $event. This is object \yii\base\Event. In property $event->name - it is event name. In property $event->sender - it is model class from trigger event.
In my class app\models\Event like that:
namespace app\models;
class Event extends Component
{
const ACTION_ADD = 1;
const ACTION_DELETE = 2;
const ACTION_UPDATE = 3;
const TYPE_PROJECT = 10;
const TYPE_BIDS = 20;
const TYPE_BIDS_DATA = 30;
/**
* #param $event
*/
public static function sendInLog($event)
{
/** #var \yii\base\Event $event */
/** #var ActiveRecord $event->sender */
$userId = Yii::$app->user->id;
$model = new Logs([
'type' => $event->sender->getType(),
'action' => $event->name,
'id_user' => $userId,
'old_data' => Json::encode($event->sender->attributes),
'new_data' => Json::encode($event->sender->oldAttributes),
]);
$model->save();
}
}
Run trigger like that:
public function afterDelete()
{
$this->trigger(Event::ACTION_DELETE);
parent::afterDelete();
}
Or
public function actionView()
{
$this->trigger(Event::ACTION_VIEW);
$this->render(...);
}
EDIT:
For example. If you want run trigger after delete, insert, update. You may use trigger in afterDelete, afterSave in model. If you want run trigger in controller run trigger like that:
public function actionCreate()
{
$model = new Bids();
$model->id_project = Yii::$app->request->get('projectId');
$fieldsDefaults = BidsFieldsDefaults::find()->orderBy(['order' => SORT_ASC])->all();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
$model->trigger(Event::ACTION_ADD);
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'fieldsDefaults' => $fieldsDefaults
]);
}
}
I show you two different ways to run trigger. Which one to use is up to you :)

How add Custom Validation Rules when using Form Request Validation in Laravel 5

I am using form request validation method for validating request in laravel 5.I would like to add my own validation rule with form request validation method.My request class is given below.I want to add custom validation numeric_array with field items.
protected $rules = [
'shipping_country' => ['max:60'],
'items' => ['array|numericarray']
];
My cusotom function is given below
Validator::extend('numericarray', function($attribute, $value, $parameters) {
foreach ($value as $v) {
if (!is_int($v)) {
return false;
}
}
return true;
});
How can use this validation method with about form request validation in laravel5?
While the above answer is correct, in a lot of cases you might want to create a custom validation only for a certain form request. You can leverage laravel FormRequest and use dependency injection to extend the validation factory. I think this solution is much simpler than creating a service provider.
Here is how it can be done.
use Illuminate\Validation\Factory as ValidationFactory;
class UpdateMyUserRequest extends FormRequest {
public function __construct(ValidationFactory $validationFactory)
{
$validationFactory->extend(
'foo',
function ($attribute, $value, $parameters) {
return 'foo' === $value;
},
'Sorry, it failed foo validation!'
);
}
public function rules()
{
return [
'username' => 'foo',
];
}
}
Using Validator::extend() like you do is actually perfectly fine you just need to put that in a Service Provider like this:
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ValidatorServiceProvider extends ServiceProvider {
public function boot()
{
$this->app['validator']->extend('numericarray', function ($attribute, $value, $parameters)
{
foreach ($value as $v) {
if (!is_int($v)) {
return false;
}
}
return true;
});
}
public function register()
{
//
}
}
Then register the provider by adding it to the list in config/app.php:
'providers' => [
// Other Service Providers
'App\Providers\ValidatorServiceProvider',
],
You now can use the numericarray validation rule everywhere you want
The accepted answer works for global validation rules, but many times you will be validating certain conditions that are very specific to a form. Here's what I recommend in those circumstances (that seems to be somewhat intended from Laravel source code at line 75 of FormRequest.php):
Add a validator method to the parent Request your requests will extend:
<?php namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Validator;
abstract class Request extends FormRequest {
public function validator(){
$v = Validator::make($this->input(), $this->rules(), $this->messages(), $this->attributes());
if(method_exists($this, 'moreValidation')){
$this->moreValidation($v);
}
return $v;
}
}
Now all your specific requests will look like this:
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class ShipRequest extends Request {
public function rules()
{
return [
'shipping_country' => 'max:60',
'items' => 'array'
];
}
// Here we can do more with the validation instance...
public function moreValidation($validator){
// Use an "after validation hook" (see laravel docs)
$validator->after(function($validator)
{
// Check to see if valid numeric array
foreach ($this->input('items') as $item) {
if (!is_int($item)) {
$validator->errors()->add('items', 'Items should all be numeric');
break;
}
}
});
}
// Bonus: I also like to take care of any custom messages here
public function messages(){
return [
'shipping_country.max' => 'Whoa! Easy there on shipping char. count!'
];
}
}
Custom Rule Object
One way to do it is by using Custom Rule Object, this way you can define as many rule as you want without need to make changes in Providers and in controller/service to set new rules.
php artisan make:rule NumericArray
In NumericArray.php
namespace App\Rules;
class NumericArray implements Rule
{
public function passes($attribute, $value)
{
foreach ($value as $v) {
if (!is_int($v)) {
return false;
}
}
return true;
}
public function message()
{
return 'error message...';
}
}
Then in Form request have
use App\Rules\NumericArray;
.
.
protected $rules = [
'shipping_country' => ['max:60'],
'items' => ['array', new NumericArray]
];
Alternatively to Adrian Gunawan's solution this now also can be approached like:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreBlogPost extends FormRequest
{
public function rules()
{
return [
'title' => ['required', 'not_lorem_ipsum'],
];
}
public function withValidator($validator)
{
$validator->addExtension('not_lorem_ipsum', function ($attribute, $value, $parameters, $validator) {
return $value != 'lorem ipsum';
});
$validator->addReplacer('not_lorem_ipsum', function ($message, $attribute, $rule, $parameters, $validator) {
return __("The :attribute can't be lorem ipsum.", compact('attribute'));
});
}
}
You need to override getValidatorInstance method in your Request class, for example this way:
protected function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
$validator->addImplicitExtension('numericarray', function($attribute, $value, $parameters) {
foreach ($value as $v) {
if (!is_int($v)) {
return false;
}
}
return true;
});
return $validator;
}
You don't need to extend the validator to validate array items, you can validate each item of a array with "*" as you can see in
Array Validation
protected $rules = [
'shipping_country' => ['max:60'],
'items' => ['array'],
'items.*' => 'integer'
];
All answers on this page will solve you the problem, but... But the only right way by the Laravel conventions is solution from Ganesh Karki
One example:
Let’s take an example of a form to fill in Summer Olympic Games events – so year and city. First create one form.
<form action="/olimpicyear" method="post">
Year:<br>
<input type="text" name="year" value=""><br>
City:<br>
<input type="text" name="city" value=""><br><br>
<input type="submit" value="Submit">
</form>
Now, let’s create a validation rule that you can enter only the year of Olympic Games. These are the conditions
Games started in 1896
Year can’t be bigger than current year
Number should be divided by 4
Let’s run a command:
php artisan make:rule OlympicYear
Laravel generates a file app/Rules/OlympicYear.php. Change that file to look like this:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class OlympicYear implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
// Set condition that should be filled
return $value >= 1896 && $value <= date('Y') && $value % 4 == 0;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
// Set custom error message.
return ':attribute should be a year of Olympic Games';
}
}
Finally, how we use this class? In controller's store() method we have this code:
public function store(Request $request)
{
$this->validate($request, ['year' => new OlympicYear]);
}
If you want to create validation by Laravel conventions follow tutorial in link below. It is easy and very well explained. It helped me a lot. Link for original tutorial is here Tutorial link.
For me works the solution that give us lukasgeiter, but with a difference that we create a class with our custom validations ,like this, for laravel 5.2.* The next example is for add a validation to a range of date in where the second date has to be equals or more big that the first one
In app/Providers create ValidatorExtended.php
<?php
namespace App\Providers;
use Illuminate\Validation\Validator as IlluminateValidator;
class ValidatorExtended extends IlluminateValidator {
private $_custom_messages = array(
"after_or_equal" => ":attribute debe ser una fecha posterior o igual a
:date.",
);
public function __construct( $translator, $data, $rules, $messages = array(),
$customAttributes = array() ) {
parent::__construct( $translator, $data, $rules, $messages,
$customAttributes );
$this->_set_custom_stuff();
}
protected function _set_custom_stuff() {
//setup our custom error messages
$this->setCustomMessages( $this->_custom_messages );
}
/**
* La fecha final debe ser mayor o igual a la fecha inicial
*
* after_or_equal
*/
protected function validateAfterOrEqual( $attribute, $value, $parameters,
$validator) {
return strtotime($validator->getData()[$parameters[0]]) <=
strtotime($value);
}
} //end of class
Ok. now lets create the Service Provider. Create ValidationExtensionServiceProvider.php inside app/Providers, and we code
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Validator;
class ValidationExtensionServiceProvider extends ServiceProvider {
public function register() {}
public function boot() {
$this->app->validator->resolver( function( $translator, $data, $rules,
$messages = array(), $customAttributes = array() ) {
return new ValidatorExtended( $translator, $data, $rules, $messages,
$customAttributes );
} );
}
} //end of class
Now we to tell Laravel to load this Service Provider, add to providers array at the end in config/app.php and
//Servicio para extender validaciones
App\Providers\ValidationExtensionServiceProvider::class,
now we can use this validation in our request in function rules
public function rules()
{
return [
'fDesde' => 'date',
'fHasta' => 'date|after_or_equal:fDesde'
];
}
or in Validator:make
$validator = Validator::make($request->all(), [
'fDesde' => 'date',
'fHasta' => 'date|after_or_equal:fDesde'
], $messages);
you have to notice that the name of the method that makes the validation has the prefix validate and is in camel case style validateAfterOrEqual but when you use the rule of validation every capital letter is replaced with underscore and the letter in lowercase letter.
All this I take it from https://www.sitepoint.com/data-validation-laravel-right-way-custom-validators// here explain in details. thanks to them.

Proper implementation for multiple validation

Here's my code:
I'm trying to implement it on ServiceProvider but I don't have any luck.
//Contact.php
class Contact extends \Eloquent {
protected $fillable = array('email', 'name', 'subject', 'msg');
public static $rules = array(
'email' => 'required|email',
'name' => 'required',
'subject' => 'required',
'msg' => 'required'
);
public static function validate($input) {
return Validator::make($input, static::$rules);
}
}
//Registration .php
class Registration extends \Eloquent {
protected $fillable = array('name', 'address', 'birthdate', 'gender', 'civil_status', 'nationality', 'contact_number', 'email', 'invited');
protected $guarded = array('id');
public static $rules = array(
"name" => "required|alpha_spaces",
"address" => "required",
"contact_number" => "required|numeric",
"email" => "required|email|unique:registrations"
);
public static function validate($input) {
return Validator::make($input, static::$rules);
}
}
class HomeController extends BaseController
{
public function postContactForm()
{
return Contact::validate(Input::all());
}
public function postRegistrationForm()
{
return Registration ::validate(Input::all());
}
}
Is the a way that I can implement it like this?
$this->validate-check(Input::all());
I'm trying to refactor my code and also still new using laravel 4 as well.
Thanks,
Aldren,
I think a Service Provider is a bit overkill for this task. You can create something like a Validation Service. Let me explain:
Say you put your custom files under app/src and use composer to autoload the classes there.
Create an abstract Validator class. This way you can extend this class for every model you need to validate:
<?php namespace Foo\Services\Validation;
abstract class Validator {
protected $errors;
public function check($validator)
{
if ($validator->fails()) {
$this->errors = $validator->messages();
return false;
}
return true;
}
public function isValidForCreation($input)
{
$validator = \Validator::make($input, static::$insertRules);
return $this->check($validator);
}
public function isValidForUpdate($input)
{
$validator = \Validator::make($input, static::$updateRules);
return $this->check($validator);
}
public function errors()
{
return $this->errors;
}
}
Now, lets say you want to validate your Contact model input, right ? You can then create a ContactValidator class that extends our Validator abstract class:
<?php namespace Foo\Services\Validation;
class ContactValidator extends Validator
{
static $insertRules = [
'name' => 'required'
];
static $updateRules = [
'name' => 'required'
];
}
All right, so now we have our boilerplate done. Now lets go to ContactsController to implement our new ContactValidator.
First of all, we need to inject our validator inside the controller. IMHO the best way to do it is in the controllers constructor.
So, lets go:
<?php
use Foo\Services\Validation\ContactValidator as Validator;
class ContactsController extends \BaseController {
protected $validator;
function __construct(Validator $validator)
{
$this->validator = $validator;
}
Great! Now we have it injected. Next, we have to make sure our ContactValidator is invoked when I try to store a Contact. Lets say your method is called store().
public function store()
{
if(!$this->validator->isValidForCreation(Input::all()))
{
return Redirect::back()->withErrors($this->validator->errors())->withInput();
}
else
{
//store your data here.
}
}
You can use either $this->validator->isValidForCreation or $this->validator->isValidForUpdate to check your input against the Validator Service.
I hope you can understand everything and if you have any doubts please let me know.
Cheers and good coding :D
Thanks for the input GustavoR, I get what you want to explain right here. But is it possible to implement the $this->validator in one controller?
use Foo\Services\Validation\MainValidator as Validator;
class HomeController extends BaseController
{
protected $validator;
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
public function postContactForm()
{
return ( $this->validator->isValidForCreation(Input::all()) );
}
public function postRegistrationForm()
{
return ( $this->validator->isValidForCreation(Input::all()) );
}
}
Again, thanks for the input :)

Categories