I don't think that my validator extentions are working, but I can't fathom why.
Service Provider.
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('fail', function ($attribute, $value, $parameters, $validator) {
return false;
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
}
I know the Service Provider's boot is firing because if I dd(); inside the boot method I get output. If I add a dd(); to the extend closure function, I do not get any output.
Request
class SaveOrder 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()
{
$rules = [
'customer_id' => 'in:' . Customer::getImplodedCurrentTeamKeys(),
'pin' => 'fail'
];
return $rules;
}
}
I know the request is validating correctly because if I change the rule to 'pin' => 'required' and don't give in put I get a fail.
Why is my custom validation rule not working?
I found my solution at the very bottom of the Laravel Validation docs page: (https://laravel.com/docs/5.4/validation)
For a rule to run even when an attribute is empty, the rule must imply that the attribute is required. To create such an "implicit" extension, use the Validator::extendImplicit() method:
By changing the method from extend to extendImplicit , my problem was solved.
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extendImplicit('fail', function ($attribute, $value, $parameters, $validator) {
return false;
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
}
Related
I created my request in which I validate the data. Some of this data I need to convert to JSON.
For this, I decided to create middleware. But when I try to get a request in the controller, it doesn't have anything I added in the middleware.
This seems to be because it is not my own request 'MyRequest $request' that gets into the middleware. How can this be resolved?
middlevare
class TransformData
{
/**
* #param $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$next($request);
$request->merge(['user_id' => \Auth::user()->id]);
$request->merge(['select_products' => json_encode($request->select_products)]);
return $request;
}
}
my request is called OfferRequest, there are just validation rules
controller
class BaseController extends Controller
{
public function __construct()
{
$this->middleware('transform.offer.data')->only('store');
}
}
public function store(OfferRequest $request)
{
$all = $request->all();
dd($all); // there is nothing here that I added in the middleware
}
I added the middleware to the kernel - protected $routeMiddleware
Update your Middleware like the following :
TransformData.php
class TransformData
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$request->merge([
'user_id' => \Auth::user()->id,
'select_products' => json_encode($request->select_products)
]);
return $next($request);
}
}
The above would work, but I would suggest you to use prepareForValidation() method link in form request class for update request data.
Using prepareForValidation() method you could add or update your request parameters.
OfferRequest.php
class AccountFilterRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
//your rules
}
/**
* Modify the input.
*/
public function prepareForValidation()
{
$this->merge([
'user_id' => \Auth::user()->id,
'select_products' => json_encode($request->select_products)
]);
}
}
prepareForValidation() method is executed before validation so you can add new data or update data and validate those.
I am just starting to get the hang of Service Providers and the IoC container, however one thing is confusing me. I have a SpamServiceProvider that requires two other classes to function. However one of those classes, InvalidKeywords, has a array $blacklist parameter which needs to be passed to its constructor.
If I register that class in the AppServiceProvider and pass in the $blacklist array, everything works fine. However, if I try to bind the class in the SpamServiceProvider instead it will not inject the $blacklist into InvalidKeywords constructor.
So I guess my question is why is this? And is there a way to keep bindings like this together in a single container or do I simply have to bind InvalidKeywords inside the AppServiceProvider?
This works
class SpamServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(SpamManager::class, function ($app) {
return new SpamManager(new InvalidKeywords, new RepeatedCharacters);
});
}
}
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(InvalidKeywords::class, function ($app) {
return new InvalidKeywords(config('spam.blacklist'));
});
}
}
This does not work
class SpamServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(InvalidKeywords::class, function ($app) {
return new InvalidKeywords(config('spam.blacklist'));
});
$this->app->bind(SpamManager::class, function ($app) {
return new SpamManager(new InvalidKeywords, new RepeatedCharacters);
});
}
}
In the second case you're not resolving the InvalidKeywords class from the container, simply creating a new instance. Instead, try using app or resolve helpers when creating the SpamManager:
$this->app->bind(SpamManager::class, function ($app) {
return new SpamManager(resolve(InvalidKeywords::class), resolve(RepeatedCharacters::class));
});
// or
$this->app->bind(SpamManager::class, function ($app) {
return new SpamManager(app(InvalidKeywords::class), app(RepeatedCharacters::class));
});
I would create a singleton with InvalidKeywords as well:
$this->app->singleton(InvalidKeywords::class, function ($app) {
return new InvalidKeywords(config('spam.blacklist'));
});
I am using a FormRequest to validate a delete request, but I do not need to access the request inside of my controller. I just need it for validation.
I find the unused variable annoying, but I can't see anything in the docs that suggests I can call anything on the class to validate it.
Form request:
class DeleteList extends FormRequest
{
protected $errorBag = 'delete_list';
/**
* 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 [
'confirm' => 'required|accepted'
];
}
}
Controller:
public function postDelete(DeleteList $request, ListService $listService, $site, $list)
{
$listService->delete($site, $list);
return redirect()->route('site.lists', ['site' => $site]);
}
You could do this:
public function postDelete(ListService $listService, $site, $list)
{
$this->validate(request(), ['confirm'=>'required|accepted']);
$listService->delete($site, $list);
return redirect()->route('site.lists', ['site' => $site]);
}
But personally I like your solution better, even if it means having an unused variable.
I'm using the Form Request classes to validate data being passed into my controllers.
Additionally, I'm using Policies to determine if a current user is allowed to show / update / destroy etc the object in question.
If I am using Policies, does this mean I can simply use:
public function authorize()
{
return true;
}
within my Request classes? Or should I be doing the check twice / writing them in different ways?
If someone could shed some light on this, that would be great.
Thanks.
See \Illuminate\Validation\ValidatesWhenResolvedTrait
<?php
namespace Illuminate\Validation;
use Illuminate\Contracts\Validation\ValidationException;
use Illuminate\Contracts\Validation\UnauthorizedException;
/**
* Provides default implementation of ValidatesWhenResolved contract.
*/
trait ValidatesWhenResolvedTrait
{
/**
* Validate the class instance.
*
* #return void
*/
public function validate()
{
$instance = $this->getValidatorInstance();
if (! $this->passesAuthorization()) {
$this->failedAuthorization();
} elseif (! $instance->passes()) {
$this->failedValidation($instance);
}
}
/**
* Get the validator instance for the request.
*
* #return \Illuminate\Validation\Validator
*/
protected function getValidatorInstance()
{
return $this->validator();
}
/**
* Handle a failed validation attempt.
*
* #param \Illuminate\Validation\Validator $validator
* #return mixed
*/
protected function failedValidation(Validator $validator)
{
throw new ValidationException($validator);
}
/**
* Determine if the request passes the authorization check.
*
* #return bool
*/
protected function passesAuthorization()
{
if (method_exists($this, 'authorize')) {
return $this->authorize();
}
return true;
}
/**
* Handle a failed authorization attempt.
*
* #return mixed
*/
protected function failedAuthorization()
{
throw new UnauthorizedException;
}
}
And \Illuminate\Foundation\Http\FormRequest
/**
* Determine if the request passes the authorization check.
*
* #return bool
*/
protected function passesAuthorization()
{
if (method_exists($this, 'authorize')) {
return $this->container->call([$this, 'authorize']);
}
return false;
}
It only checks the returning result and determine to continue or not when the request is resolved. It doesn't pass the policies or any middleware or sth. strange like that.
I want to override a method (isValidatable) in the Illuminate\Validation\Validator class. I have done this by creating a class (outside Illuminate) that extends the Validator and only overrides the isValidatable method.
I think this will work, except I'm not sure how to create the service provider for the Validator class (or actually CustomLaravelValidator class). I have created service providers before, but there seems to be going on a lot inside the Validator serviceprovider (Illuminate\Validation\ValidationServiceProvider). Therefore I don't have a clue on how my custom service provider for this class should look like.
This is my CustomLaravelValidator class:
<?php namespace API\Extensions\Core;
use Illuminate\Validation\Validator;
class CustomLaravelValidator extends Validator {
/**
* Determine if the attribute is validatable.
*
* #param string $rule
* #param string $attribute
* #param mixed $value
* #return bool
*/
protected function isValidatable($rule, $attribute, $value)
{
// Validate integers on empty strings as well
if($rule == 'IntStrict')
{
return true;
}
return $this->presentOrRuleIsImplicit($rule, $attribute, $value) &&
$this->passesOptionalCheck($attribute);
}
}
This is the default ValidationServiceProvider from Laravel:
<?php namespace Illuminate\Validation;
use Illuminate\Support\ServiceProvider;
class ValidationServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerPresenceVerifier();
$this->app->bindShared('validator', function($app)
{
$validator = new Factory($app['translator'], $app);
// The validation presence verifier is responsible for determining the existence
// of values in a given data collection, typically a relational database or
// other persistent data stores. And it is used to check for uniqueness.
if (isset($app['validation.presence']))
{
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
/**
* Register the database presence verifier.
*
* #return void
*/
protected function registerPresenceVerifier()
{
$this->app->bindShared('validation.presence', function($app)
{
return new DatabasePresenceVerifier($app['db']);
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return array('validator', 'validation.presence');
}
}
Can anyone tell me how my custom serviceprovider have to look like?
Your service provider doesn't need to mimic the native Validator service provider. You just need to register your custom validator using the resolver method on the validator factory.
use API\Extensions\Core\CustomLaravelValidator;
class CustomValidationServiceProvider extends ServiceProvider {
public function boot()
{
$this->app['validator']
->resolver(function($translator, $data, $rules, $messages)
{
return new CustomLaravelValidator(
$translator,
$data,
$rules,
$messages
);
});
}
}
That's it...