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!');
}
Related
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!
Within Laravel you can easily define abilities and then hook into them later on a user request regarding to do different actions:
$gate->define('update-post', function ($user, $post) {
return $user->id === $post->user_id;
});
But almost all my defined abilities has this part $user->id === $model->user_id in it. I don't like it as it's a kind of repeating a condition over and over which I think could be more abstract.
Most of my defined abilities are according to updating/deleting records, so it would be better if I could make a global condition applied to all of them or if there could be a group ability defining which is like to what we do in routing.
Is there any workaround for it? I really like it DRY.
Everything in Laravel is extendable, that's the power of its service providers.
You can extend the Gate object to a MyCustomGate object and do whatever you want in that object. Here's an example:
MyCustomGate.php
class MyCustomGate extends \Illuminate\Auth\Access\Gate
{
protected $hasOwnershipVerification = [];
/**
* Define a new ability.
*
* #param string $ability
* #param callable|string $callback
* #return $this
*
* #throws \InvalidArgumentException
*/
public function defineWithOwnership($ability, $callback, $foreignUserIdKey = "user_id")
{
// We will add this
$this->hasOwnershipVerification[$ability] = $foreignUserIdKey;
return $this->define($ability, $callback);
}
/**
* Resolve and call the appropriate authorization callback.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $ability
* #param array $arguments
* #return bool
*/
protected function callAuthCallback($user, $ability, array $arguments)
{
$callback = $this->resolveAuthCallback(
$user, $ability, $arguments
);
// We will assume that the model is ALWAYS the first key
$model = is_array($arguments) ? $arguments[0] : $arguments;
return $this->checkDirectOwnership($ability, $user, $model) && call_user_func_array(
$callback, array_merge([$user], $arguments)
);
}
/**
* Check if the user owns a model.
*
* #param string $ability
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param \Illuminate\Database\Eloquent\Model $model
* #return bool
*/
protected function checkDirectOwnership($ability, $user, $model)
{
if(!isset($this->hasOwnershipVerification[$ability])) {
return true
}
$userIdKey = $this->hasOwnershipVerification[$ability];
// getAuthIdentifier() is just ->id, but it's better in case the pk of a user is different that id
return $user->getAuthIdentifier() == $model->{$userIdKey};
}
}
Then, you will have to tell Laravel to use your gate instead of the default one. You ca do that in your AuthServiceProvider (assuming that it's extending Illuminate\Auth\AuthServiceProvider, just add the following method.
AuthServiceProvider
/**
* Register the access gate service.
*
* #return void
*/
protected function registerAccessGate()
{
$this->app->singleton(\Illuminate\Contracts\Auth\Access\Gate::class, function ($app) {
return new MyCustomGate($app, function () use ($app) {
return $app['auth']->user();
});
});
}
And this way, you can define abilities using defineWithOwnership() method instead of define(). You can still use define() for abilities that don't require ownership verification. There's a third parameter defineWithOwnership() accepts which is $foreignUserIdKey; that's used for the case when a model has a different field for the user id.
Note: I wrote the code on the fly and did not try it, it may have errors, but you get the idea.
I checked your question quite a bit, but I've found no "easy" way to do it.
Instead, what I would probably do is this:
<?php
namespace App\Policies;
use App\User;
use App\Post;
trait CheckOwnership {
protected function checkOwnership($user, $model) {
$owned = $user->id === $model->user_id;
if ($owned === false)
throw new NotOwnedException;
}
}
class PostPolicy
{
use CheckOwnership;
public function update(User $user, Post $post)
{
try {
$this->checkOwnership($user, $post);
//continue other checks
} catch (NotOwnedException $ex) {
return false;
}
}
}
Add this function to your AuthServiceProvider
public function defineAbilities(array $abilities, $gate)
{
foreach($abilities as $name => $model){
$gate->define($name, function ($user, $model){
return $user->id === ${$model}->user_id;
});
}
}
and then inside boot method
$this->defineAbilities(['ability1' => 'model1', 'ability2' => 'model2'], $gate);
You can define another function and call it within the anonymous function. This will allow you to have commonly-used code in one central location while still allowing any resource-specific logic.
Add this function to your AuthServiceProvider class:
public function userCheck(User $user, $target)
{
// do the user id check
$result = isset($target->user_id) && isset($user) && $user->id === $target->user_id;
return $result;
}
Your code, modified:
$gate->define('update-post', function ($user, $post) {
// call the function
$result = $this->userCheck($user, $post);
// do some kind of 'update-post' specific check
return $result/* && some_bool_statement*/;
});
I think you can use middlewares.
Simply make a admin middleware and use it in your routes and routes group.
And there is no security bug on your project (delete, create & ... actions) because Laravel has csrf token!
You can use before() function, also.
And then an important note:
if you don't define a correspond function on Policy class and call it $this->authorize($post) on a controller an unauthorized Action error will be thrown unless before()methodreturnstrue.
for example call $this->authorize on Dashboard\PostsController:
public function edit($id)
{
$post = Post::find($id)->first();
$this->authorize($post);
return view('dashboard.post')->with(compact('post'));
}
and if we defined a PostPolicy Class:
class PostPolicy
{
use HandlesAuthorization;
public function before($user, $ability)
{
return $user->is_admin;
}
}
If user be admin he/she can edit post because we returned true in before() method despite of have not a method with same name (as edit method in PostsController).
In fact Laravel will check for before method mthod on Policy Class. if before return'snull will check for correspond method with same name on controller method and if this method not found user cannot perform action.
Thank you laravel for DRY us!♥
I'm trying to modify an user submitted input before validation success. I've followed this easy instructions, but when I test it on Laravel 5.1, It's not working. Am I doing something wrong?
This is my Request class on SSHAM\Http\Requests\UserCreateRequest.php
<?php
namespace SSHAM\Http\Requests;
use SSHAM\Http\Requests\Request;
class UserCreateRequest extends Request
{
// Some stuff not related with this problem
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
// Only for debug
$prova = $this->all();
echo "<pre>Inside Request - Before sanitize\n[" . $prova['public_key'] . "]</pre>\n";
// Call a function to sanitize user input
$this->sanitize();
// Only for debug
$prova = $this->all();
echo "<pre>Inside Request - After sanitize\n[" . $prova['public_key'] . "]</pre>\n";
return [
'username' => 'required|max:255|unique:users',
'public_key' => 'openssh_key:public',
];
}
/**
* Sanitizes user input. In special 'public_key' to remove carriage returns
*/
public function sanitize()
{
$input = $this->all();
// Removes carriage returns from 'public_key' input
$input['public_key'] = str_replace(["\n", "\t", "\r"], '', $input['public_key']);
$this->replace($input);
}
}
This is my custom validation rule on SSHAM\Providers\OpenSSHKeyValidatorServiceProvider.php
<?php
namespace SSHAM\Providers;
use Illuminate\Support\ServiceProvider;
class OpenSSHKeyValidatorServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
// Registering the validator extension with the validator factory
\Validator::extend('openssh_key', function ($attribute, $value, $parameters) {
// Some stuff not related with this problem
// Only for debug
echo "<pre>Inside Validator value\n[" . $value ."]</pre>\n";
dd();
return true;
});
}
// Some stuff not related with this problem
}
When I call for debugging I obtain this output:
Inside Request - Before sanitize
[blah
second line
third line]
Inside Request - After sanitize
[blah second line third line]
Inside Validator value
[blah
second line
third line]
Seems that sanitize() is working, but when value is treated on validation class it has not been sanitized.
This is a tricky one. I only figured out one way to achieve what you want.
The main point is, that it has no effect for the Validator if you change the Request Values in the rules() function.
You could do a workaround by adding a function to your UserCreateRequest:
protected function getValidatorInstance() {
$this->sanitize();
return parent::getValidatorInstance();
}
This overrides the parent's getValidatorInstance();
The parent's getValidatorInstance() method includes
return $factory->make(
$this->all(), $this->container->call([$this, 'rules']), $this->messages(), $this->attributes());
Which is reached before your code in the rules() function, so the old values (not affected by the changes in rules()) of $this->all() are used.
If you override that function in your own RequestClass you can manipulate the Request values before calling the actual parent's method.
UPDATE (L5.5)
If you are using the Controllers validate function you could do something like that:
$requestData = $request->all();
// modify somehow
$requestData['firstname'] = trim($requestData['firstname']);
$request->replace($requestData);
$values = $this->validate($request, $rules);
You can do this by modifying the request and setting the input value.
$request->request->set('key', 'value');
Or, if you prefer the request helper method.
request()->request->set('key', 'value');
If you are using a request MyClassRequest for keeping your validation then simply override all() method of Request class
public function all()
{
$attributes = parent::all();
//you can modify your inputs here before it is validated
$attribute['firstname'] = trim($attribute['firstname']);
$attribute['lastname'] = trim($attribute['lastname']);
return $attributes;
}
Hope this helps.
These answers no longer work for me in 5.5
you can use
protected function validationData()
{
$this->request->add([
'SomeField' => '..some code to modify it goes here'
]);
return $this->request->all();
}
the add method on request overwrites any existing input for that key.
You can see why this works in Illuminate\Foundation\Http\FormRequest, if you follow the trail
/**
* Get data to be validated from the request.
*
* #return array
*/
protected function validationData()
{
return $this->all();
}
You can use the prepareForValidation method
protected function prepareForValidation()
{
$this->merge(['field' => 'field value' ]) ;
}
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
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