So I've finally got to the bottom of this, and I think it's a all a bit over-engineered - though I would love to hear differently - it may be that I'm just cross having spent the last 4 hours working all this out.
The key to this whole problem is that Validators HAVE to be made using Validator::make() Factory facade which creates the validator instance, then sets a bunch of properties on it, which otherwise could not be set - including imbuing new instances with the custom user validation callbacks I mentioned above.
This means you cannot just create a Validator instance using the "new" keyword - unless of course you start copying chunks of code from various classes, and including them in your custom validator in the constructor. If you don't do this, all you get a vanilla validation instance, without all the custom goodness.
This would be fine if there was a way to specify a Validator class to be used, for example a makeCustom(), but you can't - as internally, the make() method checks for an internal property resolver which is a callback that has to be set via Validator::resolver()at some earlier point in time.
This point of this closure is to give the user control to return a new Validator instance back to the factory, so it can have all these new properties added to it.
This is your one and only way to create a new Validator instance.
The callback has various parameters passed to it by the factory, all of which SHOULD be passed to your custom Validator, which then SHOULD be passed to the parent validator constructor (unless you want to override them, like I did to get custom messages):
function($translator, $data, $rules, $messages)
{
return new CustomValidator($translator, $data, $rules, $messages);
}
Yes, this is all documented, but it's not particularly clear.
What also isn't clear is if you DO need a choice of validators, this is the ONLY place you have to make that choice - you get one closure for your app, and the choice MUST be made here. The problems with this are at least:
- There's no context to help you decide what validator to create
- As there's only one single resolver callback, there's no way to have different parts of your app, or different packages, update this function
The only ways round this I can see now are:
- pass a dummy key in with the form
$dataand use this to decide the validator - call a mediating class, that is used externally to return a
Validatorinstance - call the
Validator::resolver()method immediately before calling theValidator::make()method, changing each time you need a new Validation instance
It seems to me that there are two solutions that would offer more flexibility:
- Give the core Validator instance an
initialize()method which sets up all the machinery, and lets you create "new" Validation instances - Add a public
initialize()method to the factory that allows you to initialize any validation instance - Add a custom
::make()method to the factory that allows you to pass in a class reference to be "new"ed on your behalf
It just seems to me that this is yet another area which should be simple, or should have better / simpler documentation, or better source code comments.
Maybe I'm the first one to notice :(