[GSoC 2012] Form and Model Validation

I haven't seen anything about form & model validation here yet, so here is my attempt to get my own head around it and provoke some discussion. Forms are quite central for our work, so I'm assuming quite a lot of people will have opinions about this :)

How far should we
push it the API, and what problems of yours it needs to solve? The trac page quoted below already does a good job of answering many
questions, but maybe there are other answers around?

Here is the quote from a year-old writeup done by core devs at http://open.silverstripe.org/wiki/development/validation-new : we need to "overhaul the current validation system in Sapphire, which is lacking
in flexibility, and ability to express different validation rules such as length. There is also no model validation, so it's possible to write inconsistent data.
"

From what I can see, the current state of affairs in SS3 is as follows:* Validation API includes: ** DataObject validation (triggered from DataObject::write) ** Form validation based on Validator (triggered from Form::httpSubmission)
** FormField validation (used by Validator::php) ** Upload validation, via Upload_Validator (used by FileField::validate)* The only available implementation of Validator is RequiredFields (forced by Form, if no other is provided)
* JS validation has been deprecated (but custom plumbing for old prototype validator.js is still there)* Interestingly, backend validation errors are passed internally via Session between the origin and the template
* Backend constraints imposed on the model are not exposed to the frontend in any way* Error messages are rendered in a very specific way, which tends to be hard to style.

Some related questions, pick and choose!
* model-level vs field-level vs form-level constraints? More specifically can we get away with model-level validation only?
* should multiple-field validation be included (e.g. day field validation could depend on the month setting)?* should we allow multiple messages per field (e.g. "the password is too short", and "the password does not contain any numbers" at the same time)?
* should forms with multiple underlying models be covered (if we go with model-only approach)?* similarly - what about forms without a model (e.g. search or login)?* what are sensible defaults (e.g. do not even touch JS, rely on HTML5)?
* how to best ensure consistency between backend and frontend (e.g.
validation metadata passed to frontend, so it's easy to write default JS
validators)?* how to make error messages easily customisable (e.g. changing the notorious "Email* field is required" and CSS styling)?* validate on submission vs on database transaction?* and last but not least: how to best decouple validation rules from forms and models?

I just wanted to say "hi" to all fellow GSoC applicants and SilverStripe developers and post some basic ideas about the Form and Model Validation project, which I'll be applying for this year.

Since Form/Model validation is a vast topic and will probably take much longer than one GSoC project, I tried to break it down into several parts: I believe that the Form API and ValidationTransaction are vital to form validation, but their presence in the project depends on how fast we'll progress with the more basic stuff.

In the following paragraphs I will be referring mostly to Django, since I
believe its validation system is reliable, flexible and it's certainly
the best one that I worked with.

What I would like to make priorities of the project are:

1) Introducing Form Widgets.

Django uses two very closely related elements, when it comes to forms: Fields are related to the database representation and backend validation of the values. Widgets are frontend representations of fields.

Example:

A TextInput widget is a simple <input type="text" /> field,
which can represent multiple fields. Both CharField and EmailField are
represented by the same widget (TextInput), but they have different
backend validation rules and different default settings/values.\

I believe that we could implement the same mechanism in SilverStripe.
It allows for much more flexibility, makes the code a lot cleaner and
easier to modify.

The fields (and maybe widgets as well) can have multiple and custom
validators. This is also the most handy way to define some common
attributes such as the "required" flag which definitely doesn't belong
to the validator, min/max values, and default values.

Also, widget could have some basic attributes which would allow to add additional css classes or html attributes. To make it as clear as possible, I have prepared an image to illustrate it on a very basic level: http://wojtekszkutnik.com/gsoc/gsoc1.png

2) Refactoring the validators system

This aspect is closely related to my previous point. SilverStripe should allow easily modifiable, highly flexible per-form and per-field validators - this is absolutely crucial for making Forms something more than just a tool for saving data into Models/DataObjects.

Timeline

Considering the complexity of this task, probably both the scope and the timeline will be subject to heavy discussion during the following weeks. I have tried to estimate the obvious tasks and schedule some time before the coding phase for heavy discussion.

I haven't included Unit Tests in any description because, obviously, they are developed on a per-feature basis. This leads to a 20-35% increase of development time but eliminates loads of bugs and provides an easy to maintain codebase.

April - May 21 - discussing the project with developers and the community. I believe that the main points of the discussion would be model vs field validation logic and the degree of using DataObject->validate(). Also, this period will probably let us determine a more strict schedule for the project - it obviously has a lot of potential features and choosing their priorities will be as much important as discussing the technical solutions. For instance, the proof of concept for SilverStripe metadata-driven JavaScript validation depends on how extensive the main tasks will be.

May 21 - June 15 - I believe that during this phase we will mostly concentrate on developing a proper FormField implementation (I feel that there will be a lot of rewrites going on over there).

Also, the duration of this period depends on how extensively we'll decide to develop the "ModelForm".

June 15 - June 30 - possibly implementing the form "widgets" described above and separating the interface forms layer from the data management layer.

July 1 - July 15 - implementing validators, constraints and customisation features for fields and widgets

July 15 - July 30 - First steps related to JavaScript/jQuery validation libraries integration (binding them with optional form attributes etc)

* model-level vs
field-level vs form-level constraints? More specifically can we get away with
model-level validation only?* should multiple-field validation be included
(e.g. day field validation could depend on the month setting)?

I believer that model-level vs field-level vs form-level aren't exactly mutually exclusive. I would suggest going with two kinds of forms: ModelForms, with pre-defined validation rules based on model definitions, used strictly to handle Model/DataObject processing, and simple Forms which would allow for more complex tasks. Forms should be the way to handle all user input, not only data validation/save, so this would allow a far higher amount of flexibility. It'd be great to allow developers overriding basic ModelForm attributes - for example, exclude some fields with "default" settings (like date on which the entry was added for a comments system).

Form-level constraints are very helpful for creation of complex forms - validating one field against another is a pretty common case and it'd be best to have it along with field-level validation features (I believe that form-level constraints are pretty much the same that multiple-field validation?).

should we allow
multiple messages per field (e.g. "the password is too short", and
"the password does not contain any numbers" at the same time)?

Yeah, why not :) We can store an array of errors and just pass it to the frontend. There is no need for making any restrictions at this point IMO.

similarly - what
about forms without a model (e.g. search or login)?

As I mentioned before, such forms would be handled by the standard form, while the validate/save tasks on models would be performed on ModelForms.

how to make error
messages easily customisable (e.g. changing the notorious "Email* field is
required" and CSS styling)?

Simple widget/field attributes would work great, I think. Assuming that we'd have the widgets implemented, passing css to them would be as easy as: (pseudocode) name = TextField(widget=Textarea(array=('classes'=>'sample_class another_one', 'style'=>'font-size:20px;'))))

Error messages could be overridden via validators or field attributes, I'd have to give it some thought - both solutions have their pros and cons.

Let my simple mind catch up with what you are explaining regarding the ModelForm and widgets ;)

On the backend side, you are proposing that by default we treat the existing Form as a model-less interface for the user, where we get to list the FormFields we want to use explicitly in the code. So this is similar to what we have now (if we ignore the saveInto part and display part).

For model manipulation, you propose to create ModelForm that will hook up into model, and scaffold the fields automatically from the DataObject. There is a 1 to 1 mapping between db Field and the FormField so we can always create these explicitly. FormFields no longer control display, so one does not need to change the automatic mapping.

The new feature is proposed in the configuration of the frontend behaviour - it is no longer defined within the FormField but rather is delegated to form widgets, regardless if we are using model-less Form or the ModelForm. All FormFields have default widgets, so it is easy to mock forms. On the other hand widgets can be replaced, so customisation is also possible, as well as using the same widget for several different FormFields.

Then the validation goes on top of that. It is defined (regardless whether it is implemented in JS or backend) on:- model (db Fields)- FormFields (automatically derived from the model for ModelForm)- Form / ModelForm
- widgetsThen all these rules get squashed and rolled into the validation system - whether it is backend validation, HTML5, or JS.

Is that about right, or I missed the point?

Can you explain "Forms should be the way to handle all user input" - do you mean even frontend-rich things such as for example site tree in the CMS?

Also, there is the question whether we would build a JS-heavy validation system and make it a default, or rather make something simple that works for 50% of the cases out there, and expect people to write their own in more complicated scenarios. Backend-only validation as a default is also an option, but I feel that most of us would choose nice and simple JS-validation over backend-only on any given day.

cheers,mateusz

W

--
You received this message because you are subscribed to the Google Groups "SilverStripe Core Development" group.

I would also add that ModelForms could have additional fields, as long as the dev adds them to the ModelForm declaration.

By "Forms should be the way to handle all user input" I meant that every time that some data is transferred to the server via GET or POST, processed and some results are returned, it probably is a good use for a form. Search and your Day/Month validation examples pretty much explain it. On second thought, I would like to add that I obviously didn't mean any kinds of APIs - these shouldn't necessarily be handled this way ;-)

I think, that we could implement some basic frontend validation system and provide an easy option to turn it off. We could use HTML5 to implement basic validation (lightweight but not supported by all browsers yet), some jQuery plugin, or maybe a solution in between which would handle the validation via HTML5 or jQuery, depending on browser version.

I would also add that ModelForms could have additional fields, as long as the dev adds them to the ModelForm declaration.

By "Forms should be the way to handle all user input" I meant that every time that some data is transferred to the server via GET or POST, processed and some results are returned, it probably is a good use for a form. Search and your login validation examples pretty much explain it, sometimes it's also useful to have forms delegated to specific tasks, which aren't necessarily directly related to the forms visible on the frontend. On second thought, I would like to add that I obviously didn't mean some kinds of APIs - these shouldn't necessarily be handled this way ;-)

I think, that we could implement some basic frontend validation system and provide an easy option to turn it off. We could use HTML5 to implement basic validation (lightweight but not supported by all browsers yet), some jQuery plugin, or maybe a solution in between which would handle the validation via HTML5 or jQuery, depending on browser version.

* should multiple-field validation be included (e.g. day field validation could depend on the month setting)?

From my point of view we have to separate what kind of validation we want on the different levels. My thoughts are that:

- Forms should make sure that the data is consistent overall. Example: If checkbox 'Use creditcard', then textfield 'Credit card number' must be filled in.

- Fields should ensure that the data is formated according to what specific fields. Example: A textfield for credit card number is a valid credit card.

- Models should ensure that the data entered by the user actually "fits" the database. Example: A Boolean isn't a string.

* should we allow multiple messages per field (e.g. "the password is too short", and "the password does not contain any numbers" at the same time)?

I think so, I want forms that tells a form user what went wrong so I can correct them at once, instead of posting the form until it passes.

* and last but not least: how to best decouple validation rules from forms and models?

I hope this would include a possibility to inject validators at runtime on Form, Fields and Models.

Another suggestion is to use another frameworks validators, like Zend Framework, since it's silly to roll our own. This would separate the validation logic from the presentation of validation messages.

By the way, just a side note to remember during implementation: I juststumbled upon a missing feature in Django, which would allow togroup-override error messages. Basically you can't change the defaultmessage (e.g. "This field is required"), you have to specify themessages on a per-case basis, which just turned out to be a hugeproblem for me ;-)

Thanks - I see it has a fairly comprehensive coverage for validation rules. HTML5 defines just the following rules though, which is less than you have:

* required

* regex pattern

* min/max

* field-type validators (email, url, color, date, time, tel)

Do you think your basic rules are implementable using HTML5?

Two interesting things that stick out in your implementation:

* Comparison to another field: custom syntax "@Persons~-1".

* Chaining rules - e.g. two rules can be bunched together by adding the as an array to the "OR" rule.

These are addressing some basic scenarios for the form-level validation already.

Just how often would you use these two features?

We need to keep in mind that if we implement these complex rules in the API, we will need to be able to communicate them to the frontend, and probably also support them there on a basic level. This might mean we will be inventing a SS Custom Validation Language (tm) which I think we should avoid :)

So this is the case of deciding at which point we ask devs to implement their own functions vs. providing predefined rules.

Dear devs, any opinions on just how complex/simple the built-in validation system should be? :)

I'd probably suggest that rules like this would be better implemented as code rather than options. However, that does make it more difficult to validate on the front-end. One thing we talked about was the idea of validating via an ajax submission, in much the same way that auto-complete lookups happen.

If we post the form data to <formurl>/validate, it could return an 204 OK response, or 412 Validation error response. The 412 response might include a JSON summary of validation errors.

> We need to keep in mind that if we implement these complex rules in the API, we will need to be able to communicate them to the frontend, and probably also support them there on a basic level. This might mean we will be inventing a SS Custom Validation Language (tm) which I think we should avoid :)

$rule_Capping_greater_hundret
= new NetefxValidatorRuleGREATEREQUAL ("Capping", "", "error",
'100');$rule_Capping_equals_zero
= new NetefxValidatorRuleEQUALS ("Capping", "",
"error", '0');$rule_Capping_zero_OR_greater_hundret = new
NetefxValidatorRuleOR ("Capping", "Please set the
capping to a minimum of 100 or 0 if you dont want a capping.", "error",
array($rule_Capping_equals_zero,
$rule_Capping_greater_hundret));$rule_Capping_Smaller_TargetViews
= new NetefxValidatorRuleSMALLEREQUAL ("Capping", "", "error",
'(@TargetViewsSSP~+@TargetViewsPP~)*0.25');
$rule_Capping_implies_smaller_payed = new
NetefxValidatorRuleIMPLIES ("Capping", "It doesnt make
sense to have a capping per day and blog that is more than 25% of total Paid
views.", "error", array($rule_Capping_zero_OR_greater_hundret,
$rule_Capping_Smaller_TargetViews));...

}

We need to keep in mind that if we implement these complex rules in the
API, we will need to be able to communicate them to the frontend, and
probably also support them there on a basic level. This might mean we will be
inventing a SS Custom Validation Language (tm) which I think we should avoid
:)

So this is the case of deciding at which point we ask devs to implement
their own functions vs. providing predefined rules.

Dear devs, any opinions on just how complex/simple the built-in validation
system should be? :)

I also
had in mind, to use ajax to send all fields to the server and let the
server do the validation. But this needs to be fast, so you wouldnt upload files
to the server (Uploadfield). But what if you are sending all fields except
file (image) fields to the server but you have a validation rule for the file
field.

On Wed, Apr 11, 2012 at 10:45 AM, netefx - Alexander Klein<al...@netefx.de> wrote:> Hi lx,>> Thanks - I see it has a fairly comprehensive coverage for validation rules.> HTML5 defines just the following rules though, which is less than you have:> * required> * regex pattern> * min/max> * field-type validators (email, url, color, date, time, tel)>> Do you think your basic rules are implementable using HTML5?>> I have no idea. I didnt try it because i didnt want that some rules are> already checked in html5 while others are just checked on the server.> I wouldnt like it, because the user would think that a form is filled> correctly when all html5 errors disappear. But then other error messages> appear because more complex rules are taken into account on the server side.

Model layer validation (prior to performing any (con/de)structive database calls) and frontend validation need to be treated separately, but with some way to generate a suitable default for the the latter from the former. Given data objects can be manipulated in many different ways, the frontend validation needs to be user action centric, while model layer validation needs to be code centric.

Something to consider - model validation doesn't necessarily have to be as rigid as you might think. For example, on a member object I wouldn't make the 'email' field need to be a valid email address (what if I want to create a user with some dummy data in there via code?) but from a frontend form perspective, I don't want users being able to do the same

class Member {

public $email;

public $constraints = array(

'email'=> array('UniqueField', 'Required')

);

}

class MemberController {

public function EditForm() {

$form = new Form();

$form->addValidation('email', new EmailFieldValidator());

}

}

Cheers,

Marcus

W

--
You received this message because you are subscribed to the Google Groups "SilverStripe Core Development" group.

> Model layer validation (prior to performing any (con/de)structive database calls) and frontend validation need to be treated separately, but with some way to generate a suitable default for the the latter from the former. Given data objects can be manipulated in many different ways, the frontend validation needs to be user action centric, while model layer validation needs to be code centric.

I agree. In my mind, "controller validation" / front-end validation needs to include all the validation that the model provides, *and* any extra validation suitable for the specific action. It does seem like the same notion of validating a set of field values can be used across both model and controller validation.

> Something to consider - model validation doesn't necessarily have to be as rigid as you might think. For example, on a member object I wouldn't make the 'email' field need to be a valid email address (what if I want to create a user with some dummy data in there via code?) but from a frontend form perspective, I don't want users being able to do the same> class Member {> public $email;> > public $constraints = array(> 'email' => array('UniqueField', 'Required')> );> }> > class MemberController {> public function EditForm() {> $form = new Form();> $form->addValidation('email', new EmailFieldValidator());> }> }

I agree that this is something that a SilverStripe developer should be able to do. For my own apps, I'd probably put email validation on the model and use put @x.com on the end of my dummy entries, but that's not something the framework should mandate.

However, I don't like that the example talks about "constraints" in one half of the app and "validation" in the other. This seems like an unnecessary distinction. However, the difference between constraints and validation could be useful as follows:

- "constraints" define validation rules that can be expressed as a set of properties rather than procedural code. - "validation" extends this, adding the kind of validation that needs to be coded.

In other words "constraints" are our "simple" or "out of the box" validation, but that constraints can be used in controllers too.

I'm not sure how constraints would interact with JavaScript validation. In principle the data contained in constraints could be passed through to the front-end, but the implementation details could get messy.

Yeah, in my thinking I just used the term constraint because it describes something, but doesn't actually do anything itself. The underlying model layer code responsible for writing data would be responsible for actually performing validation based on those constraints.

Your thinking seems to be going in a direction that the developer who provides the specific DBFields/FormFields/FormWidgets is not responsible for defining the specific validation rules for these fields - and these only get configured on the compound structures such as model or form?

We also need to remember that Forms and FormFields can inherit their constraints from parents, so to actually get a full list of constraints, one would need to traverse the whole hierarchy of the Form, of the underlying Model, and for all FormFields... or we'd simply assume that there is no inheritance of constraints and one has to explicitly define the full list on the child object if one wishes to amend it?

On another note, it seems a bit overkill to define class for each case of validator. They'd have to be quite configurable:

static $constraints = array(

"MaxValue(7500)", "MinLength(2)"

);

and would be nice to have a way to quickly define validation rules without the psychological overhead of creating a whole new class somewhere else in the code:

> I agree that this is something that a SilverStripe developer should be able to do. For my own apps, I'd probably put email validation on the model and use put @x.com on the end of my dummy entries, but that's not something the framework should mandate.

Actually, I beg to disagree on this one :) Dummy data for E-mailfields is te...@example.com or something similiar, and if you try toput invalid data into the ORM, it should fail validation. Basically,if you decide to use an E-mail field, the ORM should validate it as anE-mail - obviously, you can always perform a raw sql query since thefield is usually represented as a varchar field, but ORM should alwaysvalidate the data based on the field specs - that's what it is for ;-)

> However, I don't like that the example talks about "constraints" in one half of the app and "validation" in the other. This seems like an unnecessary distinction. However, the difference between constraints and validation could be useful as follows:>> - "constraints" define validation rules that can be expressed as a set of properties rather than procedural code.> - "validation" extends this, adding the kind of validation that needs to be coded.>

That's pretty much what I had in mind :)

> Your thinking seems to be going in a direction that the developer who provides the specific DBFields/FormFields/FormWidgets is not responsible for defining the specific> validation rules for these fields - and these only get configured on the compound structures such as model or form?

Actually, from my point of view, the fields should have some basicvalidation rules (obviously, the validation rules for role-specificfields such as an E-mail field, should use more strict validation thana imple TextField), but the developer should be allowed to defineadditional validators for these.

As for $form->addValidation(function($validationContext) { ... });, Ithink it's a code design decision - should we allow to change thevalidators from any point in the code? Enforcing validation rules onform definition seems reasonable in most cases - makes the code muchcleaner. We could probably have some backdoor for specific uses, butin 99,9% of the cases specifying validation rules on form definitionshould be enough.

> However, I don't like that the example talks about "constraints" in one half of the app and "validation" in the other. This seems like an unnecessary distinction. However, the difference between constraints and validation could be useful as follows:
>
> - "constraints" define validation rules that can be expressed as a set of properties rather than procedural code.
> - "validation" extends this, adding the kind of validation that needs to be coded.
>

The reason for the distinction is to be explicit about the difference of defining constraints for a value, and the process (validation) that checks these constraints are valid. Though they are regularly treated as the same, which in many cases is okay.

> Your thinking seems to be going in a direction that the developer who provides the specific DBFields/FormFields/FormWidgets is not responsible for defining the specific

> validation rules for these fields - and these only get configured on the compound structures such as model or form?

Not the developer, but that the class itself (eg DBField/FormField) shouldn't be responsible for providing the validation logic; so an EmailField class may compose several validators instead of an isValid() method.

A few years ago I worked on a project using CakePHP which did validation on the model, problems arose when different data in the model class would need to be validated in different circumstances.

Such as a User model, when a User registered: Firstname, Surname, Email, Terms and Conditions is required. When a User edits their profile the Terms and Conditions checkbox was no longer relevant.

Or when a Company was registering, the Company has_one User and the User details from the form were used to create a User record. Different data was required from the form.

CakePHP didn't have a very good way to handle these cases at the time, Jonathan Snook had a method to get around it. I used that method but it gets a bit out of hand in the Model class (example). CakePHP provided a way to only use a validation rule on 'create' or on 'edit' but that didn't really cut it and also got to be quite complicated.

Because SS has validation on the form fields its a bit of a different situation, but worth mentioning in regards to constraints on the model class.

I'll try to put together a list of links related to the project on Delicious or somewhere to have a good base for discussing this topic, I want to investigate a few more frameworks in terms of validation to get the best out it :)

Obviously, the scope is pretty big and reworking the Forms/Models validation API is something that could take months or even years. I took the project mostly because I feel that it's a little more complex than the rest and will require a significant amount of work and knowledge, so I'm prepared to make the most out of it. However, I know that we have to set a strict number of features for the GSoC project to make sure that we take it forward step by step - I will have a little more time from Monday, I'll try to compile a list of features that I would like to take on as the first step and we'll take the discussion from there :)

Hrm, with the caveat that I'm no longer CTO, this concerns me. Validation is very closely related to the data model, which is a key piece of SSF. My concern is that, while we, it turns SilverStripe Framework into a bloated chimera that, when taken as a whole, is substantially inferior to other frameworks. It's one thing to say "well we have some rough edges right now but we're working on them" and quite different to say "our vision for the future is an inferior system".

If the overall system (including the code we pull from Symfony) is elegant and maintainable using the Symfony Validator, then that's fine, but we can't kid ourselves by saying "oh it's really simple because we can ignore everything that Symonfy Validator provides". SilverStripe developers will inevitably need to understand and follow the internals Symfony Validator when debugging issues, and we therefore need to treat any Symonfy Validator code as "part of SilverStripe framework" when assessing complexity.

For comparison: Zend has been used for a few pieces of plumbing, but even there the result has been that following the path of, for example, what happens when you use the cache, is very complex. Now, I'm not suggesting that it was the wrong call to use Zend_Cache for this, but the resulting code is more complex that it otherwise can be.

Perhaps it's better to look to Symfony Validator to start discussions about the API? They have some interesting ideas worth discussing:

* A ConstraintCollection seems to be the place where you define what you're validator is. As the name suggests, it's composed of a number of constraints. This makes perfect sense for many situations, although for more complex situations I would suggest that it should be easier to insert custom code. Symfony does this by wrapping such code in a CallbackConstraint.

* Validators are kept separate from ConstraintCollections. You can pass a constraint collection to the validateField() method of a constraint. This means that each Constraint has a corresponding ConstraintValidator. There is an analog with MVC here - if constraints are the "model" then validators are the "controller". Of course, it's just an analog with MVC, since in many cases both the constraint and the validator will live in your model. Or they might both live in the controller for a UI-specific validation. The separation is curious, and possibly valuable, but I don't really see the use case for it and frankly it seems like bloat. If alternative implementations of validators, chosen without altering the constraint, was an important use-case, then this might be worthwhile, but I just don't see it.

* The CallbackConstraint, of course, would appear to violate this separation, because the Constraint defines the code to call when validating.

* The role of the Validator object is basically to look up the right ConstraintValidator object for each constraint (although ConstraintValidatorFactory actually does this), execute it, and collate the results (although ExecutionContext does this). It's really a shell, which is why it reminded me of a Controller.

* Constraint evaluation happens by calling an addViolation method on a $this->context object. $this->context is an ExecutionContext object which in turn passes violations to a GlobalExecutionContext object. Again, this seems like an unnecessary extra layer. However, having constraint register violations by calling a method on some object (rather than returning a value, or throwing an exception) might make sense. Throwing exceptions has its own benefits - it makes it easier for methods at any depth to register violations - but it would be worth debating whether the Symfony approach makes more sense here. With reference to existing SilverStripe code, this is a matter of asking whether we throw a ValidationException or call methods on a ValidationResult.

* Both the ConstraintCollections and the Validators are kept separate from any data model. I haven't looked into the ClassMetaData system, but this seems to be where constraints are linked to models, and the linking appears to be at the level of PHP properties.

To perform a validation, you will be making use of the following objects:

- 1x ClassMetaDataFactory

- 1x ConstraintValidatorFactory

- 1x Validator

- 1x ConstraintCollection

- A number of Constraint subclasses

- A number of ConstraintValidator subclasses

- An ExecutionContext and a GlobalExecutionContext

- Probably some other stuff.

If we are to integrate this into SilverStripe, we would probably add something extra on top of this. I can't say I'm in love with this code, but maybe I'm oversimplifying things, and that there really are good reasons to have all of those classes involved in a validation. It is, however, important to remain pragmatic about this.

In short, what it does is ensure that form actions will respect and handle ValidationExceptions, using the ValidationResult contained within as the source of information for error messages on the form.

Although the definition of constraints doesn't affect this change, the structure of ValidationResult would be. Regardless of which way we go, it would probably be beneficial to compare the API of ValidationResult with that of Symfony Validator to see if could be improved. The closest classes in Symfony Validator are probably ConstraintViolation and ConstraintViolationList. https://github.com/symfony/Validator/blob/master/ConstraintViolation.php

Looking briefly over it, it seems as though ConstraintViolations have two components:

- A parameterised message (supports simple variable substitution)

- A "property path", which presumably is used for field-specific messages. Given it's a path, I assume there is some kind of dot syntax for referencing nested properties.

@Sam: Great walkthrough on Symphony's validator architecture, I never delved that deep into it. :)

When I was naively suggesting to 'reusing existing implementations', I was mostly referring to that we should perhaps not rewrite the validation constrains, since It's something I feel I've been doing over and over.

This is just if we can get away without using the a lot of boilerplate code from another framework, it's hard to match two different approaches to framework architecture without falling (and failing) between them.

As it is now SS 3.0 relies quite a lot on third party code. 29M out of 45M of /sapphire/ is thirdparty, though most of it is Zend_Locale data and AFAIK not used at all.

IMO it's up to the implementors to find the good enough way to enhance the validation system. It's a daunting task. O7

When I was naively suggesting to 'reusing existing implementations', I was mostly referring to that we should perhaps not rewrite the validation constrains, since It's something I feel I've been doing over and over.

Setting aside the list of constraint classes to one side for a moment, Marcus had suggested something like this:

static SiteTree extends DataObject {

static $db = array(

"Title" => "Varchar",

);

static $constraints = array(

"Title" => "Required",

"URLSegment" => "Regex('/^[A-Za-z\-]+$/')",

);

}

Given this is the case, we could do the following:

1) SiteTree::constraints() would return a ConstraintSet object

2) This would be used by DataObject::assertValid() (throws exception) and/or DataObject::validate() (updates a ValidationResult passed by reference)

class DataObject {

// Return a ConstraintSet object representing the constraints for this object.

function constraints();

// Throws a ValidationException if there's a problem. Calls $this->validate. Requires less boilerplate on behalf of the caller.

function assertValid();

// Amend the ValidationResult object with the results. Easier to chain with other validation operations.

// Most of the work done by ConstraintSet::validate()

function validate(ValidationResult &$result);

}

3) ConstraintSet would execute the validation against the given object (DataObject::validate would pass itself). Use an interface, SS_Map, so that you could pass both Forms and DataObjects to the validator. We'd need to ensure that SS_Map can be applied to both these classes without trouble. Maybe we'd use ArrayAccess instead of SS_Map?

class ConstraintSet {

// Validate the given object. Calls $this->validate.

function assertValid(SS_Map $obj);

// Validate the given object

function validate(SS_Map $obj, ValidationToResult &$result);

}

4) A ConstraintSet would call the call the validate method of each constraint, passing the field name and value, as well as the whole object just in case a constraint wasn't limited to a single field (e.g. a constraint that ensure that Password is equal to ConfirmedPassword).

abstract class Constraint {

/**

* Validate that this constraint is met. Call methods on $validationResult if not.

So there would be a compilation step that would pull the constraints in from everywhere, and one could apply it wherever needed - ConstraintSet could be applied to ArrayData, or passed on to the frontend (through some kind of a transformation). I like it in principle.