Laravel custom class validator: TranslatorInterface is not instantiable - php

I have a problem with a custom class validator.
Here is my CustomValidator.php
<?php
use Illuminate\Validation\Validator;
class CustomValidator extends Validator {
public function validateEmailExistence($attribute, $value, $parameters)
{
//check email existence
}
}
Here in validators.php call the class
Validator::extend('check_email', 'CustomValidator#validateEmailExistence');
In bindings.php resolve the validator
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new CustomValidator($translator, $data, $rules, $messages);
});
When I run I get this error:
Target [Symfony\Component\Translation\TranslatorInterface] is not instantiable.
If I extend validator through anonymous function it works fine. I know this is problem with interface binding, but I don't know which implementation should I use.

Your code seems to be ok for me, but try creating a service provider and register it in the app config file.
class ValidationServiceProvider extends ServiceProvider
{
public function register()
{
}
public function boot()
{
$this->app->validator->resolver(function($translator, $data, $rules, $messages) {
return new CustomValidator($translator, $data, $rules, $messages);
});
}
}
Note: do a composer dumpauto after creating the service provider.
Let me know if you are still getting the error.

Related

Laravel 7 - How do I rebind repository on Validator::extend() for tests?

I have set up a custom validation rule like this:
// Custom created RepositoryProvider.php also registered in app.php 'providers' array.
public $bindings = [
UserRepository::class => EloquentUserRepository::class,
];
public function boot(UserRepository $userRepository)
{
Validator::extend('user_email_unique', function($attribute, $value, $parameters) use($userRepository) {
return !$userRepository->findByEmailAddress($value)->exists();
});
}
// my test
class SignUpUserActionTest extends TestCase
{
public function setUp() : void
{
parent::setUp();
$this->app->bind(UserRepository::class, function() {
return new UserRepositoryMock();
});
}
}
In my test I rebind the UserRepository to a mock. It works fine in for the fetching of data, but it maintains the original binding for the validation extension and does not rebind the repository used. They therefore use two different implementations when unit tests are run.
How can I extend the validator so that the automatic resolution is rebound on tests?
Thanks.
Since you do you logic in your RepositoryProvider, this will be instantiated in your parent::setUp() call and thereby fetch your UserRepository before the binding has been mocked. Move the binding before this call and i will assume you will get a different result.
public function setUp() : void
{
$this->app->bind(UserRepository::class, function() {
return new UserRepositoryMock();
});
parent::setUp();
}
EDIT
Based on your comment, the cause was right the solution was not. Resolve UserRepository in the closure, will most likely make it resolve at a time where your binding is set.
Validator::extend('user_email_unique', function($attribute, $value, $parameters) {
$userRepository = resolve(UserRepository::class);
return !$userRepository->findByEmailAddress($value)->exists();
});

Custom validator in Laravel 5 fails

I'm trying to use a custom validator of laravel 5, I intend to validate two fields so that one is greater than the other. Follow the steps in this link http://goo.gl/3236xn but could not make the application call the validation method. Can anyone give any tips?
Step 1: Make a bind class where you can implement each rule you want extending Validator class, as follow:
<?php
namespace App\Services;
use \Illuminate\Validation\Validator;
class GreaterThanValidator extends Validator
{
public function validateGreaterThan($attribute, $value, $parameters)
{
if (isset($parameters[1])) {
$other = $parameters[1];
return (floatval($value) > floatval($other)) ;
} else {
return true;
}
}
}
Step 2:Make a service provider that extends ServiceProvider, as follow:
<?php
namespace App\Providers;
use Illuminate\Validation\Validator;
use Illuminate\Support\ServiceProvider;
use App\Services\GreaterThanValidator;
class ValidatorsServiceProvider extends ServiceProvider
{
public function boot()
{
Log::info('passando pelo provider...');
\Validator::resolver(function($translator, $data, $rules, $messages) {
Log::info('chamando new...');
return new \App\Services\GreaterThanValidator($translator, $data, $rules, $messages);
});
}
public function register()
{
}
}
Step 3:Add your custom validator provider at config/app.php file.
/*
* Application Service Providers...
*/
'App\Providers\AppServiceProvider',
'App\Providers\BusServiceProvider',
'App\Providers\ConfigServiceProvider',
'App\Providers\EventServiceProvider',
'App\Providers\RouteServiceProvider',
'App\Providers\ValidatorsServiceProvider',
I forgot to specify, but the controller have the following rule
$rules = array(
'vlFixado' => 'greater_than:vl_anulacao,' . $vlAnulacao,
);
To validate:
$validator = Validator::make(Input::all(), $rules, $messages);

How to test custom validation rules in Laravel 5?

I created a custom validation rule in Laravel, extending it in the register() method of a service provider, and I'd like to test it, but don't know how.
I took a look at Laravel framework's validation tests, but I couldn't understand the purpose of the getTranslator() and getRealTranslator() methods.
Could someone give me a hint on how to test Laravel's custom validation rules?
EDIT
That's what I did:
Created a ValidatorServiceProvider as follows:
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ValidatorServiceProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
$this->app['validator']->extend('greater_than', function($attr, $val, $params)
{
return false;
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
Also added it to the providers array and issued composer dump-autoload -o.
Added the following to tests\CustomValidationRulesTest.php:
<?php
use Mockery as m;
use Illuminate\Validation\Validator;
class CustomValidationRulesTest extends TestCase {
public function tearDown()
{
m::close();
}
public function testValidateGreaterThan()
{
$trans = $this->getTranslator();
$rules = [
'field2' => 'greater_than:field1'
];
$data = [
'field1' => 1,
'field2' => 2
];
$v = new Validator($trans, $data, $rules);
$this->assertTrue($v->passes());
}
protected function getTranslator()
{
return m::mock('Symfony\Component\Translation\TranslatorInterface');
}
protected function getRealTranslator()
{
$trans = new Symfony\Component\Translation\Translator('en', new Symfony\Component\Translation\MessageSelector);
$trans->addLoader('array', new Symfony\Component\Translation\Loader\ArrayLoader);
return $trans;
}
}
Running PHPUnit gives me the following:
PHPUnit 4.6.6 by Sebastian Bergmann and contributors.
Configuration read from /home/ubuntu/workspace/phpunit.xml
E.
Time: 248 ms, Memory: 14.75Mb
There was 1 error:
1) CustomValidationRulesTest::testValidateGreaterThan
BadMethodCallException: Method [validateGreaterThan] does not exist.
/home/ubuntu/workspace/vendor/laravel/framework/src/Illuminate/Validation/Validator.php:2615
/home/ubuntu/workspace/vendor/laravel/framework/src/Illuminate/Validation/Validator.php:372
/home/ubuntu/workspace/vendor/laravel/framework/src/Illuminate/Validation/Validator.php:372
/home/ubuntu/workspace/vendor/laravel/framework/src/Illuminate/Validation/Validator.php:325
/home/ubuntu/workspace/tests/CustomValidationRulesTest.php:27
What I'm doing wrong?
Thanks!
You need to understand a bit more the Container or IoC and how the Validator is registered there.
Laravel registers an instance of Illuminate\Validation\Factory as validator. So if you check the \Illuminate\Support\Facades\Validator, you find that it resolves to the Factory. When you extend the validator, you are in fact adding an extension in the Factory. Now, calling Validator::make() calls the Factory which has the extensions and it creates the Validator with the extensions, while instantiating a new Illuminate\Validation\Validator won't be able to resolve the extentions from the Factory.
You should not instantiate a new Validator, but use app('validator')->make().
Take it:
protected function getRealTranslator()
{
$loader = new \Illuminate\Translation\ArrayLoader;
$translator = new \Illuminate\Translation\Translator($loader, 'ja');
return $translator;
}

Custom validator in Laravel 5

I am upgrading my Laravel application from 4 to 5. However, I have a custom validator that I cannot get to work.
In L4, I made a validators.php file and included it in global.php using require app_path().'/validators.php';.
I tried doing somewhat the same in L5. I dropped a validator in app/Validators/Validators.php, and updated my composer.json.
"files": [
"app/Validators/Validators.php"
]
However, now nothing renders on any page. What've I done wrong?
Try the following:
Make a bind class where you can implement each rule you want extending Validator class.
Make a service provider that extends ServiceProvider.
Add your custom validator provider at config/app.php file.
You can create the bind at Services folder like this:
namespace MyApp\Services;
class Validator extends \Illuminate\Validation\Validator{
public function validateFoo($attribute, $value, $parameters){
return $value == "foo"
}
}
Then, use a service provider to extends the core:
namespace MyApp\Providers;
use MyApp\Services\Validator;
use Illuminate\Support\ServiceProvider;
class ValidatorServiceProvider extends ServiceProvider{
public function boot()
{
\Validator::resolver(function($translator, $data, $rules, $messages)
{
return new Validator($translator, $data, $rules, $messages);
});
}
public function register()
{
}
}
Finally, import your service provider at config/app.php like so:
'providers' => [
...
...
'MyApp\Providers\ValidatorServiceProvider';
]
so here's what I did on adding a custom validation. this is for laravel 5.1
run PHP Artisan make:request MyFormValidationRequest file is created under app\Requests\MyFormValidationRequest.php
Here's the initial code:
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class MyFormValidationRequest extends Request
{
/**
* 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 [
//
];
}
}
IMPORTANT: Change the return value of authorize() method to true, if you're not doing any authentication. it's initial value is false. else you get a white page with a "Forbidden" error message.
I added a rule under the function rules(), here's what it looks like
public function rules() {
return [
'activeuntil' => 'today_onwards'
];
}
today_onwards is my new validation.
I created a folder named 'Services' under App folder
I created a file named 'ValidatorExtended.php' under App\Services folder , here's the code below:
<?php
namespace App\Services;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
class ValidatorExtended extends Validator {
private $_custom_messages = array(
"today_onwards" => "The :attribute must be today onwards",
);
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 );
}
protected function validateTodayOnwards( $attribute, $value ) {
$now = strtotime('-1 day');
$valueDateFormat = strtotime($value);
if($valueDateFormat > $now){
return true;
}
else {
return false;
}
}
}
Note: the validateTodayOnwards method is where you put your logic.
the name of the method should always start in "validate" then the name of your new validation key which should be in title case,
Another note your validation key should be separated by underscore and all small letters, in this case, "today_onwards". the underscore should be put before all first capital letters in the method name. I hope I explained it good.
TodayOnwards method is equivalent to validation name of "today_onwards",
another example, if I created validateOldPassword, your validation key should be "old_password".
I added below code in app\Providers\AppServiceProvider.php inside boot() method.
Validator::resolver(function($translator, $data, $rules, $messages = array(), $customAttributes = array())
{
return new ValidatorExtended($translator, $data, $rules, $messages, $customAttributes);
});
Don't forget to add below library, one is the Validator class and the other is your own class which is the "ValidatorExtended".
use App\Services\ValidatorExtended;
use Illuminate\Support\Facades\Validator;
Here's what the whole file looks like, [app\Providers\AppServiceProvider.php]
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\ValidatorExtended;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
Validator::resolver(function($translator, $data, $rules, $messages = array(), $customAttributes = array())
{
return new ValidatorExtended($translator, $data, $rules, $messages, $customAttributes);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
That's it. done. you created your own custom validation.
Additionally, if you want to use it in your controller, below is the code:
class testController extends Controller
{
public function updatePass(MiscValidation $request){
//code here
}
}
Instead of using Request Class you use your own class which is an extension of the Request class.

Laravel 4 custom validation - [method] does not exist

I am trying to implement and use a couple of my own custom validation methods in a class called WBValidation that extends Illuminate\Validation\Validator
I have this method validateCombinedRequired:
class WBValidation extends Illuminate\Validation\Validator{
public function validateCombinedRequired($attribute,$value,$parameters){
return ( $this->validateRequired($attribute,$value) )
and ( $this->validateRequired($parameters[0],$this->data[$parameters[0]]) );
}
}
I have placed this class in the libraries folder. For the framework to automatically pick up this class, it might be getting picked up because I can see it in autoload_classmap.php ( I might be wrong ).
When I try to use it in my model, I am getting this error which says BadMethodCallException","message":"Method [validateCombinedRequired] does not exist:
class UserModel extends Eloquent{
protected $table='user';
public static function VerifyUserAdd($data){
$rules = array('password'=>'required|combined_required:repassword');
// stuff
return Validator::make($data,$rules,$errormessages);
}
}
Is there anything else I should be doing? Please help me!
You need to register your custom Validator extension:
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new WBValidation($translator, $data, $rules, $messages);
});
I suggest reading the documentation as it covers several was of adding your own custom validation rules.

Categories