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.
Related
I'm using the Laravel Repository Pattern to manage my resources and I was wondering how can I use an interface inside a Nova Action? Since an Interface cannot be instanciated, I was wondering how I could use my Interfaces within my action?
In my Controller constructor I create my repository and then I'm able to use it within my functions, but I can't figure out how to do the same thing inside a Laravel Action.
Any idea how I could do this?
An example in my Controller
private $myRepository;
public function __construct(
MyRepositoryInterface $myRepository,
)
{
$this->myRepository = $myRepository;
}
And then inside a function I can do something like
public function destroy($id)
{
$this->myRepository->delete($id);
return response()->json( array("message" => "success") );
}
Now in my Nova Action, here's what I'm trying to do
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model)
{
$myRepository = new MyRepositoryInterface(); // This doesn't work obviously
$myRepository->customManipulation($model->id);
$this->markAsFinished($model);
}
}
Any idea how I could use my repositories?
Thanks!
You can do $myRepository = App::make(MyRepositoryInterface::class);, IoC will resolve it and will instantiate a class instance.
I assume you have already bound the class to the interface:
App::bind('MyRepositoryInterface', 'MyRepository');
I'm trying to add a custom assertion to the TestReponse class so I can make something like this:
$response = $this->json('POST', '/foo/bar');
$response->myCustomAssertion();
I tried creating an App\TestResponse class that extends the original one and then binding it in the App\Provider\AppServiceProvider class.
public function register()
{
$this->app->bind('Illuminate\Foundation\Testing\TestResponse', function ($app) {
return new App\TestResponse();
});
}
But $response->json() is still returning the original one and not my own implementation.
How can I extend the TestResponse class?
If you want a little more fine-grained control, you can also extend the Illuminate\Foundation\Testing\TestResponse, as you have done, and then override the createTestResponse method in your TestCase class to return an instance of your custom response class:
// Laravel 8 and above
protected function createTestResponse($response)
{
return tap(App\TestResponse::fromBaseResponse($response), function ($response) {
$response->withExceptions(
$this->app->bound(LoggedExceptionCollection::class)
? $this->app->make(LoggedExceptionCollection::class)
: new LoggedExceptionCollection
);
});
}
// Before Laravel 8
protected function createTestResponse($response)
{
return App\TestResponse::fromBaseResponse($response);
}
From Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.
The TestResponse class uses the Macroable trait so you can add macro functions at runtime.
TestResponse::macro('nameOfFunction', function (...) {
...
});
You can add this to a Service Provider's boot method or somewhere before you need to make the call to that macro'ed method.
I have a BaseController that provides the foundation for most HTTP methods for my API server, e.g. the store method:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
I then extend on this BaseController in a more specific controller, such as the UserController, like so:
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
}
This works great. However, I now want to extend UserController to inject Laravel 5's new FormRequest class, which takes care of things like validation and authentication for the User resource. I would like to do this like so, by overwriting the store method and using Laravel's type hint dependency injection for its Form Request class.
UserController.php
public function store(UserFormRequest $request)
{
return parent::store($request);
}
Where the UserFormRequest extends from Request, which itself extends from FormRequest:
UserFormRequest.php
class UserFormRequest 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 [
'name' => 'required',
'email' => 'required'
];
}
}
The problem is that the BaseController requires a Illuminate\Http\Request object whereas I pass a UserFormRequest object. Therefore I get this error:
in UserController.php line 6
at HandleExceptions->handleError('2048', 'Declaration of Bloomon\Bloomapi3\Repositories\User\UserController::store() should be compatible with Bloomon\Bloomapi3\Http\Controllers\BaseController::store(Illuminate\Http\Request $request)', '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php', '6', array('file' => '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php')) in UserController.php line 6
So, how can I type hint inject the UserFormRequest while still adhering to the BaseController's Request requirement? I cannot force the BaseController to require a UserFormRequest, because it should work for any resource.
I could use an interface like RepositoryFormRequest in both the BaseController and the UserController, but then the problem is that Laravel no longer injects the UserFormController through its type hinting dependency injection.
In contrast to many 'real' object oriented languages, this kind of type hinting design in overridden methods is just not possible in PHP, see:
class X {}
class Y extends X {}
class A {
function a(X $x) {}
}
class B extends A {
function a(Y $y) {} // error! Methods with the same name must be compatible with the parent method, this includes the typehints
}
This produces the same kind of error as your code. I would just not put a store() method in your BaseController. If you feel that you are repeating code, consider introducing for example a service class or maybe a trait.
Using a service class
Below a solution that makes use of an extra service class. This might be overkill for your situation. But if you add more functionality to the StoringServices store() method (like validation), it could be useful. You can also add more methods to the StoringService like destroy(), update(), create(), but then you probably want to name the service differently.
class StoringService {
private $repo;
public function __construct(Repository $repo)
{
$this->repo = $repo;
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
$service = new StoringService($this->repo); // Or put this in your BaseController's constructor and make $service a member variable
return $service->store($request);
}
}
Using a trait
You can also use a trait, but you have to rename the trait's store() method then:
trait StoringTrait {
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
use {
StoringTrait::store as baseStore;
}
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
return $this->baseStore($request);
}
}
The advantage of this solution is that if you do not have to add extra functionality to the store() method, you can just use the trait without renaming and you do not have to write an extra store() method.
Using inheritance
In my opinion, inheritance is not so suitable for the kind of code reuse that you need here, at least not in PHP. But if you want to only use inheritance for this code reuse problem, give the store() method in your BaseController another name, make sure that all classes have their own store() method and call the method in the BaseController. Something like this:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
protected function createResource(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
UserController.php
public function store(UserFormRequest $request)
{
return $this->createResource($request);
}
You can move your logic from BaseController to trait, service, facade.
You can not override existing function and force it to use different type of argument, it would break stuff. For example, if you later would write this:
function foo(BaseController $baseController, Request $request) {
$baseController->store($request);
}
It would break with your UserController and OtherRequest because UserController expects UserController, not OtherRequest (which extends Request and is valid argument from foo() perspective).
As others have mentioned, you cannot do what you want to do for a host of reasons. As mentioned, you can solve this problem with traits or similar. I am presenting an alternative approach.
At a guess, it sounds like you are trying to follow the naming convention put forth by Laravel's RESTful Resource Controllers, which is kind of forcing you to use a particular method on a controller, in this case, store.
Looking at the source of ResourceRegistrar.php we can see that in the getResourceMethods method, Laravel does either a diff or intersect with the options array you pass in and against the default values. However, the those defaults are protected, and include store.
What this means is that you can't pass anything to Route::resource to force some override of the route names. So let's rule that out.
A simpler approach would be to simply set up a different method just for this route. This can be achieved by doing:
Route::post('user/save', 'UserController#save');
Route::resource('users', 'UserController');
Note: As per the documentation, the custom routes must come prior to the Route::resource call.
The declaration of UserController::store() should be compatible with BaseController::store(), which means (among other things) that the given parameters for both the BaseController as well as UserController should be exactly the same.
You actually cán force the BaseController to require a UserFormRequest, it's not the prettiest solution, but it works.
By overwriting there is no way you can replace Request with UserFormRequest, so why not use both? Giving both methods an optional parameter for injecting the UserFormRequest object. Which would result in:
BaseController.php
class BaseController {
public function store(Request $request, UserFormRequest $userFormRequest = null)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
public function store(UserFormRequest $request, UserFormRequest $userFormRequest = null)
{
return parent::store($request);
}
}
This way you can ignore the parameter when using BaseController::store() and inject it when using UserController::store().
The easiest and cleanest way I found to circumvent that problem was to prefix the parent methods with an underscore. For example:
BaseController:
_store(Request $request) { ... }
_update(Request $request) { ... }
UserController:
store(UserFormRequest $request) { return parent::_store($request); }
update(UserFormRequest $request) { return parent::_update($request); }
I feel like creating service providers is an overkill. What we're trying to circumvent here is not the Liskov substitution principle, but simply the lack of proper PHP reflection. Type-hinting methods is, in itself, a hack after all.
This will force you to manually implement a store and update in every child controller. I don't know if that's bothersome for your design, but in mine, I use custom requests for each controller, so I had to do it anyway.
Consider this code inside a model:
public function rules() {
return [
[['company_name', 'first_name', 'last_name'], 'sanitize'],
//........
];
}
sanitize is a custom method inside the current class, which is:
public function sanitize($attribute) {
$this->{$attribute} = general::stripTagsConvert($this->{$attribute}, null, true);
}
Now this method obviously will come in handy in many models so I don't want to keep repeating the same code in every model. Is there a way I can reference another class in the rules in place of the current sanitize method name which is binded to the current class?
Yes, it's definitely possible.
Create separate validator. Let assume it's called SanitizeValidator and placed in common/components folder.
Your custom validator must extend from framework base validator and override validateAttribute() method. Put your logic inside this method:
use yii\validators\Validator;
class SanitizeValidator extends Validator
{
/**
* #inheritdoc
*/
public function validateAttribute($model, $attribute)
{
$model->$attribute = general::stripTagsConvert($model->$attribute, null, true);
}
}
Then in model you can attach this validator like this:
use common/components/SanitizeValidator;
/**
* #inheritdoc
*/
public function rules()
{
return [
[['company_name', 'first_name', 'last_name'], SanitizeValidator::className()],
];
}
Check the official documentation about custom validators here and there.
I am trying to use the Extend function that the validation offer,
Here is what I have:
1). HomeController.php :
$rules = array(
'first_name'=>'required|regex:/^[a-z ,."-]+$/i|min:2',
'last_name'=>'required|regex:/^[a-z ,."-]+$/i|min:2',
'gender'=>'required|alpha|gendercheck',
'email'=>'required|email|unique:users,email,'.Input::get('id').',id',
'zip'=>'required|zipcheck|max:10',
);
2). Then to use the extend method I add it to routes.php:
Validator::extend('zipcheck', function($field,$value,$parameters){
// List of regular expressions to use, if a custom one isn't specified.
$countryRegs = array(
"US"=>"/^[\d]{5}(-[\d]{4})?$/",
"GB"=>"/^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$/",
"DE"=>"/\b((?:0[1-46-9]\d{3})|(?:[1-357-9]\d{4})|(?:[4][0-24-9]\d{3})|(?:[6][013-9]\d{3}))\b/",
"CA"=>"/^([ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ])\ {0,1}(\d[ABCEGHJKLMNPRSTVWXYZ]\d)$/",
"FR"=>"/^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$/",
"IT"=>"/^(V-|I-)?[0-9]{5}$/",
"AU"=>"/^(0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|(7[8-9][0-9]{2})$/",
"NL"=>"/^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$/",
"ES"=>"/^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$/",
"DK"=>"/^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$/",
"SE"=>"/^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$/",
"BE"=>"/^[1-9]{1}[0-9]{3}$/"
);
// get country submitted..
$country = Input::get('country');
// check country if in the array..
if (key_exists($country , $countryRegs))
return preg_match($countryRegs[$country], $value);
else // other countries make sure no special characters in there
return preg_match('/^[a-z0-9- ]+$/i' , $value);
});
The problem is I want to keep my code organized and I dont want to add the validation extend to my routes.php
What is the best way where I can have those in its own class and call those from my HomeController and still it will work?
Thanks
There are lots of ways to do this. The way I like is make a file /app/validators.php (So it is in the same location as routes.php and filters.php)
Then go to app/start/global.php and add this at the bottom after the filters require:
require app_path().'/validators.php';
You can now declare all your extended validators in the validators.php file -and Laravel will use them.
There are several ways to do. Personally I like to extends validation by a ValidationService (I think it is much cleaner).
1) We assume you use PSR-4 to load you own company directory in composer.json:
{
"autoload": {
"psr-4": {
"Acme\\": "app/Acme"
}
...
},
}
You have to run composer dumpautoload.
2) Create your validation service provider:
app/Acme/Extension/Validation/ValidationServiceProvider.php
<?php
namespace Acme\Extension\Validation;
use Illuminate\Support\ServiceProvider;
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);
});
}
}
3) Register your service provider in app/config/app.php for autoloading:
<?php
return array(
'providers' => array(
...
'Acme\Extension\Validation\ValidationServiceProvider',
),
);
4) Create your custom validation rule(s):
app/Acme/Extension/Validation/CustomValidator.php
<?php
namespace Acme\Extension\Validation;
use Illuminate\Validation\Validator as IlluminateValidator;
class CustomValidator extends IlluminateValidator {
public function validateAlphaNumSpace($attribute, $value) {
return preg_match('/^([a-z\x20])+$/i', $value);
}
public function validateZip($attribute, $value, $parameters) {
...
}
}
5) You are ready to use your custom rule(s). For example if I want to use my AlphaNumSpace rule (useful in many cases because original AlphaNum rule doesn't allow space!):
$rules = [
'name' => 'required|alpha_num_space',
'zipcode' => 'required|zip',
];