I'm using Phalcon framework, and I'm trying to make a custom validator:
<?php
use Phalcon\Validation\Validator,
Phalcon\Validation\ValidatorInterface,
Phalcon\Validation\Message;
class Currency extends Validator implements ValidatorInterface
{
/**
* Executes the validation
*
* #param Phalcon\Validation $validator
* #param string $attribute
* #return boolean
*/
public function validate($validator, $attribute)
{
$value = $validator->getValue($attribute);
if(! is_numeric($value))
{
$message = $this->getOption('message');
if(! $message){
$message = 'Not a valid currency.';
}
$validator->appendMessage(new Message($message, $attribute, 'Currency'));
return false;
}
return true;
}
}
I get this error when trying to use the validator above:
Unexpected value type: expected object implementing
Phalcon\Mvc\Model\ValidatorInterface, object of type Currency given
I put the validator class in /plugins/validators/Currency.php and auto-loaded it of course using DI.
Any clues?
this kind of validator cannot be used on model validation.
" Phalcon\Validation is an independent validation component that validates an arbitrary set of data. This component can be used to implement validation rules on data objects that do not belong to a model or collection. "
more info here: http://docs.phalconphp.com/en/latest/reference/validation.html#validators
for model validators you need to use another interface.
info here:
http://docs.phalconphp.com/en/latest/reference/models.html#validating-data-integrity
Related
A question has arisen.
Suppose I have an api route that is
get: api/v1/conferences/{conference_id}/languages
The purpose would be to get the languages linked to this conference.
My program, almost always starts with conferences/{conference_id} so the conference_id must always be real. In case the conference_id does not exist, I should throw an exception.
I want to do this without putting any logic in the controllers or any class that has any particular logic of mine. I would like it to be validated by default from the laravel kernel, is that possible?
I mean, i want that every time somebody access to a route which starts with conferences/{conference_id} the program would be able to check if this id is real
Thanks
Use exists in validation rules like:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ConferencesRequest extends FormRequest
{
/**
* #inheritDoc
*/
public function all($keys = null)
{
$data = parent::all();
$data['conference_id'] = $this->route('conference_id');
return $data;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'conference_id' => ['required', 'integer', 'exists:' . App\Models\Conference::class . ',id'],
];
}
}
Or, if you dont want to use a FormRequest class, use this:
p.s. param should be changed to conference instead of conference_id:
use App\Models\Conference;
Route::get('conferences/{conference}/languages', function (Conference $conference) {
//...
});
See: https://laravel.com/docs/9.x/routing#route-model-binding
I am trying to implement a custom validation rule within lumen and I am following the docs for lumen 5.6. It says to refer to laravel validation to see how to use the validation. I am currently trying to make a validation to check if the value is a true null or not. So $x === "" would mean it fails Here is my rule located in App\Rules folder I created.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class TrueNull implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
if($value === "") {
return false;
} else {
return true;
}
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute cannot be an empty string.';
}
}
I copied this straight from lumen docs and make my modification to the passes function. Within my modal have
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
use Laravel\Lumen\Auth\Authorizable;
use App\Rules\TrueNull;
use Validator;
Then
public function validate($data)
{
// make a new validator object
$v = Validator::make($data,
[
'x' => ['regex:/^(?=.+)(?:[1-9]\d*|0)?(?:\.\d+)?$/', new TrueNull]
]
}
But the validation for TrueNull never happens am I missing a connection or something its really frustrating because the docs says this should work.
Here is my controller calling the update I am validating.
public function update(Request $request, $id)
{
/*
In middleware need to add community id to request.
*/
try {
$site = Site::findOrFail($id);
if ($site->validate($request->all())) {
$site->fill($request->all());
// save
$site->save();
} else {
return response()->json($site->errors(), 422);
}
} catch (Exception $e) {
return response()->json($e, 422);
}
return response()->json($site, 200);
}
For future reference I found a random snippet of code that offset the basic docs of Lumen. In my class for TrueNull instead of implements Rule I changed this to implements ImplicitRule and changed the use to \ImplicitRule and it is now catching that "" is not a null.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\ImplicitRule;
class TrueNull implements ImplicitRule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
if($value === "") {
return false;
} else {
return true;
}
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute cannot be an empty string.';
}
}
The answer is greate by #Alexander Beyers but it doesn't work for Lumen 5.3. Here is how to create organised custom rules for the Lumen 5.3.
Create a Directory name Rules under app dir and Create the Following File:
namespace App\Rules;
use Illuminate\Support\Facades\Validator;
class AlphaSpace
{
public static function validate(){
//Extending the custom validation rule.
Validator::extend('alpha_spaces', function ($attribute, $value) {
// This will only accept alpha and spaces.
// If you want to accept hyphens use: /^[\pL\s-]+$/u.
return preg_match('/^[\pL\s]+$/u', $value);
});
}
}
Open the file resources/lang/en/validation and add the following under the Custom Validation:
Note:(under the Custom Validation is only for maintenance)
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'alpha_spaces' => 'The :attribute may only contain letters and spaces.',
Call the rule in app/Providers/AppServiceProvider::boot():
use HRM\Core\Rules\AlphaSpace;
class AppServiceProvider extends ServiceProvider
{
public function boot() {
AlphaSpace::validate();
}
// class will carry on with the stuffs!
Now you can use it anywhere you want to like this:
'first_name' => 'required|alpha_spaces|min:3|max:50',
'last_name' => 'required|alpha_spaces|min:3|max:50',
Version: Lumen 7.X
First, declare your rule in app/Providers/AppServiceProvider.php.
Create a boot() with your rule method as following (I registered a rule for phone number).
public function register()
{
//
}
public function boot()
{
app('validator')->extend('phone', function ($attribute, $value) {
return preg_match('%^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\.\ \\\/]?)?((?:\(?\d{1,}\)?[\-\.\ \\\/]?){0,})(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(\d+))?$%i', $value) && strlen($value) >= 10;
});
app('validator')->replacer('phone', function ($message, $attribute, $rule, $parameters) {
return 'Phone number has wrong format.';
});
}
Different part in opposite of Laravel, and most important is, that AppServiceProvider is not registered in Lumen by default.
You need to go to bootstrap/app.php and uncomment following line:
$app->register(App\Providers\AppServiceProvider::class);
Maybe you would like to do composer dumpautoload, just in case, but it should work without it.
Then, in your code:
$validator = Validator::make(
$request->all(),
[
'tel' => 'required|phone'
],
[
'tel.required' => 'Phone is required',
'tel.phone' => 'Phone has wrong format'
]
);
That should be it!
I have input $data =['identifier' = 'xxxxxxxxxx'];, and want to save the encrypt($data['identifier']) to the table info primary id column.
I've to validate before save it. Rule unique:info, id isn't suitable here, so I want to write a custom validation rule. And in the custom validation rule, I encrypt() the value first, then use the unique validation rule.
I know how to write a custom validation rule, but how to use the unique validation rule in my custom validation rule?
Rules "unique" and "exists" use the DatabasePresenceVerifier class. So, you don't need to really extend the unique rule, just access this presence verifier. For instance:
Validator::extend('encrypted_unique', function ($attribute, $value, $parameters, $validator) {
list ($table, $column, $ignore_id) = $parameters; // or hard-coded if fixed
$count = $validator->getPresenceVerifier()->getCount($table, $column, encrypt($value), $ignore_id);
return $count === 0;
});
Then you can use it as usual:
'identifier' => "encrypted_unique:table,column,$this_id"
Suppose you have A ModuleRequest that validates your inputs,you can write this method in this class
protected function validationData()
{
$all = parent::validationData();
$all['email'] = encrypt($all['email']);
return $all;
}
Laravel has Custom Validation Rules (https://laravel.com/docs/8.x/validation#using-rule-objects)
For example I have a table named clients who has two unique fields ecnrypt using Laravel's encryption services (https://laravel.com/docs/8.x/encryption) and because its encrypted, i can't aply the unique directive of validation method (https://laravel.com/docs/8.x/validation#rule-unique). The fields are code_client and email
That's the reason of implements a Custom Validation Rules.
this Service has two methods: passes and message. The method passes take two variables: $attributes (take de field to validate) and $value (take de value of field), and return true or false. Te method message retrieve message in case of failure.
In clients example i mentioned, folow the next steps:
php artisan make:rule ValidateFieldsClients
in class that composer creates ValidateFieldsClients, I have to declare a method for validate the fields in passes, I use this method for validate both fields (code_client and email).
next i complete de method message to retrieve the issue to user in views
additionally i declare a property $field to identify what´s the field it have the errors
The class ValidateFieldsClients example:
/***/class ValidateFieldsClients implements Rule{protected $field; /**
* 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)
{
$clients = client::all();
$this->field = $attribute;
foreach ($clients as $client ) {
if ($value == Crypt::decryptString($client->$attribute)) return false;
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return strtoupper($this->field).' exists, check.';
}
}
Then to validate I use Form Request Validation (https://laravel.com/docs/8.x/validation#form-request-validation)
php artisan make:request ClientRequest
And in the validate method of the recently created class:
class ClientRequest 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 [
'code_client'=> ['required', new ValidateFieldsClients],
'email'=>['required', new ValidateFieldsClients],
];
}
Finally in controller:
public function store(ClientRequest $request)
{ $clientRequest = $request->validated();
foreach ($clientRequest as $key => $client) {
$encryptedClient[$key] = Crypt::encryptString($client);
}; client::create($encryptedClient+ [
'idorga' => 1,
'idcrea' => 1,
'idmodifica' => 1
]);
return redirect('clientes/create')->with('success', 'Registro creado correctamente');
//return redirect('cuadros')->with('success', 'Registro exitoso!');
}
Namespaces omitted for brevity...
I have written the following service provider and registered in config/app.php:
class OfferServiceProvider extends ServiceProvider
{
public function register()
{
$this->registerLossControlManager();
}
protected function registerLossControlManager()
{
$this->app->bind('LossControlInterface', 'LossControl');
}
}
Here is my LossControlInterface
interface LossControlInterface
{
/**
* #param int $demandId
* #param float $offerTotal
* #param float $productTotal
* #param null|int $partnerId
* #return mixed
*/
public function make($demandId, $offerTotal, $productTotal, $partnerId = null);
/**
* #return float
*/
public function getAcceptableLoss();
/**
* #return bool
*/
public function isAcceptable();
/**
* #return bool
*/
public function isUnacceptable();
/**
* #return null
*/
public function reject();
}
Now within the controller, I can inject the LossController as follows:
use LossControlInterface as LossControl;
class HomeController extends BaseController {
public function __construct(LossControl $lossControl)
{
$this->lossControl = $lossControl;
}
public function getLossThresholds()
{
$lossControl = $this->lossControl->make(985, 1000, null);
var_dump('Acceptable Loss: ' . $lossControl->getAcceptableLoss());
var_dump('Actual Loss: ' . $lossControl->calculateLoss());
var_dump('Acceptable? ' . $lossControl->isAcceptable());
}
}
However if I try to dependency inject the LossControlInterface from within a custom class called by a command:
[2014-09-02 13:09:52] development.ERROR: exception 'ErrorException' with message 'Argument 11 passed to Offer::__construct() must be an instance of LossControlInterface, none given, called in /home/vagrant/Code/.../ProcessOffer.php on line 44 and defined' in /home/vagrant/Code/.../Offer.php:79
It appears as though I am unable to dependency inject the interface into a custom class, but I can when dependency injecting into a controller.
Any thoughts on what Im doing wrong or have omitted to get the automatic resolution working?
The IoC is automatic within controllers, and you don't see the injection because Laravel handles the construction of controllers for you. When creating any other custom class by using the new keyword, you will still need to send in all of the parameters needed to it's constructor:
$myClass = new ClassWithDependency( app()->make('Dependency') );
You can hide this, to a degree, by funneling creation of your custom class through a service provider:
// Your service provider
public function register()
{
$this->app->bind('ClassWithDependency', function($app) {
return new ClassWithDependency( $app->make('Dependency') );
});
}
Then just have the IoC make it whenever you need it:
$myClass = app()->make('ClassWithDepenency');
In your case, you can change your code to look like this:
private function setOffer(Offer $offer = null) {
$this->processOffer = $offer ?:
new Offer( app()->make('LossControlInterface') );
}
A perhaps cleaner approach could be to create a service provider and an OfferFactory which gets injected into your controller. The controller can then request the factory to create the offer whenever it needs one:
// Controller
public function __construct(OfferFactory $offerFactory)
{
$this->offerFactory = $offerFactory;
}
public function setOffer(Offer $offer = null)
{
$this->processOffer = $offer ?: $this->offerFactory->createOffer();
}
// OfferFactory
class OfferFactory
{
public function createOffer()
{
return app()->make('Offer');
}
}
This has the benefit of completely decoupling your controller from the logic behind the creation of the offer, yet allowing you to have a spot to add any amount of complexity necessary to the process of creating offers.
In Laravel 5.2 the simplest solution for your particular problem would be to replace
new Offer();
with
App::make('Offer');
or even shorter
app('Offer');
which will use Laravel Container to take care of dependencies.
If however you want to pass additional parameters to the Offer constructor it is necessary to bind it in your service provider
App::bind('Offer', function($app, $args) {
return new Offer($app->make('LossControl'), $args);
});
And voila, now you can write
app('Offer', [123, 456]);
In laravel 5.4 (https://github.com/laravel/framework/pull/18271) you need to use the new makeWith method of the IoC container.
App::makeWith( 'App\MyNameSpace\MyClass', [ $id ] );
if you still use 5.3 or below, the above answers will work.
In a form i working on, i have this rules set which i want to validate. rate should be a a floating number. and price should be numbers with , and .
here is what i have so far
public function rules()
{
array('rate','type', 'type'=>'float', 'on' => 'loan-calculator'),
// EPriceValidator is a custom validation class
array('price', 'site.common.components.validate.EPriceValidator'),
}
In site.common.components.validate.EPriceValidator i have this
class EPriceValidator extends CRegularExpressionValidator
{
public $pattern = '/[^0-9,.]/';
}
when i change
array('price', 'site.common.components.validate.EPriceValidator'),
to
array('price', 'match', 'not' => true, 'pattern' => '/[^0-9,.]/'),
it works perfectly when validating on the fly. but i would rather put it into a class, this way i can reuse it thru out my site.
array('rate','type', 'type'=>'float', 'on' => 'loan-calculator'),
the code above on the other hand doesn't work at all. Any idea how i can fix these two problems? Or what am i'm doing wrong? Thanks
try something like this
class EPriceValidator extends CValidator
{
//Regular Expressions for numbers
private $pattern = '/[^0-9,.]/';
//Default error messages
private $err_msg = '{attribute} is an invalid.';
/**
* Validates the attribute of the object.
* If there is any error, the error message is added to the object.
* #param CModel $object the object being validated
* #param string $attribute the attribute being validated
*/
protected function validateAttribute($object,$attribute)
{
$pattern = $this->pattern;
// extract the attribute value from it's model object
$value = $object->$attribute;
if(!preg_match($pattern, $value))
{
$this->addError($object, $attribute, $this->err_msg);
}
}
/**
* Implementing Client Validation
*
* Returns the JavaScript needed for performing client-side validation.
* #param CModel $object the data object being validated
* #param string $attribute the name of the attribute to be validated.
* #return string the client-side validation script.
* #see CActiveForm::enableClientValidation
*/
public function clientValidateAttribute($object,$attribute)
{
// check the strength parameter used in the validation rule of our model
$pattern = $this->pattern;
//replace {attribute} with correct label
$params['{attribute}']=$object->getAttributeLabel($attribute);
$error_message = strtr($this->err_msg,$params);
return "
if(value.match(".$pattern.")) {
messages.push(".CJSON::encode($error_message).");
}
";
}
}
the validateAttribute() function overrides the CValidator class function for server side validation. And the clientValidateAttribute() function overrides the CValidator class function for client side validation.
for more information read this