[GSoC 2012] Form and Model Validation

386 views
Skip to first unread message

Mateusz Uzdowski

unread,
Apr 6, 2012, 5:09:09 AM4/6/12
to silverst...@googlegroups.com
Dear devs,

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 :)

Refer to https://github.com/silverstripe/gsoc-wiki/wiki/Project-Idea:-Form-validation for the idea description from Ingo.

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?

Thanks :)
Mateusz
--

Mateusz Uzdowski | Developer
SilverStripe
http://silverstripe.com/

Phone: +64 4 978 7330 xtn 68
Skype: MateuszUzdowski


Wojtek Szkutnik

unread,
Apr 6, 2012, 5:59:59 AM4/6/12
to silverst...@googlegroups.com
Aloha!

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)



cheers,

W.


Wojtek Szkutnik

unread,
Apr 6, 2012, 6:15:34 AM4/6/12
to silverst...@googlegroups.com
Ah, sorry - I didn't see Mateusz's post. Referring to a few points:

* 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.

W

Mateusz Uzdowski

unread,
Apr 8, 2012, 4:32:17 AM4/8/12
to silverst...@googlegroups.com
Hey Wojtek, no worries.

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
- widgets
Then 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.
To view this discussion on the web visit https://groups.google.com/d/msg/silverstripe-dev/-/tq0guFWyjYEJ.

To post to this group, send email to silverst...@googlegroups.com.
To unsubscribe from this group, send email to silverstripe-d...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/silverstripe-dev?hl=en.

Wojtek Szkutnik

unread,
Apr 8, 2012, 10:59:04 PM4/8/12
to silverst...@googlegroups.com
That's about right :) 

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.

cheers,


To post to this group, send email to silverstripe-dev@googlegroups.com.
To unsubscribe from this group, send email to silverstripe-dev+unsubscribe@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/silverstripe-dev?hl=en.

Wojtek Szkutnik

unread,
Apr 8, 2012, 11:04:55 PM4/8/12
to silverst...@googlegroups.com
Oh dear, I tried to fit two different thoughts in the same sentence.

Should read:

That's about right :) 

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.

cheers,

Stig Lindqvist

unread,
Apr 9, 2012, 6:14:59 PM4/9/12
to silverst...@googlegroups.com
Hello!

It's good to kick this discussion of since the validation has been nagging me since I've started working with SilverStripe.

* 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)?

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. 

Links:




netefx - Alexander Klein

unread,
Apr 10, 2012, 4:21:08 AM4/10/12
to silverst...@googlegroups.com
Hi,
 
in case you didnt know the NetefxValidator module, you could take a look at it. (written by me and Zauberfisch)
Maybe you will get some inspiration for the new validator.
This one is only for forms, no validation on model-level.
 
 
negative:
 
- no validation on the model level
- no JS/Jquery validation
 
positive:
 
- a lot of ValidationRules for all kind of validation included 
- rules can be combined as complex as you want
- multiple field validation
- use custom methods for validation
- ...
 
 
So I just wanted to mention this module, because maybe you will get some ideas for the new validator. 
 
 


From: silverst...@googlegroups.com [mailto:silverst...@googlegroups.com] On Behalf Of Stig Lindqvist
Sent: Dienstag, 10. April 2012 00:15
To: silverst...@googlegroups.com
Subject: [silverstripe-dev] Re: [GSoC 2012] Form and Model Validation

--
You received this message because you are subscribed to the Google Groups "SilverStripe Core Development" group.
To view this discussion on the web visit https://groups.google.com/d/msg/silverstripe-dev/-/c96-Wx_Ss8YJ.
To post to this group, send email to silverst...@googlegroups.com.
To unsubscribe from this group, send email to silverstripe-d...@googlegroups.com.

Wojtek Szkutnik

unread,
Apr 10, 2012, 7:44:00 AM4/10/12
to silverst...@googlegroups.com
Thanks Alex, will do!

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

W

Mateusz Uzdowski

unread,
Apr 10, 2012, 5:41:25 PM4/10/12
to silverst...@googlegroups.com
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?

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? :)

mateusz


On Tuesday, April 10, 2012 8:21:08 PM UTC+12, lx wrote:
Hi,
 
in case you didnt know the NetefxValidator module, you could take a look at it. (written by me and Zauberfisch)
Maybe you will get some inspiration for the new validator.
This one is only for forms, no validation on model-level.
 
 
negative:
 
- no validation on the model level
- no JS/Jquery validation
 
positive:
 
- a lot of ValidationRules for all kind of validation included 
- rules can be combined as complex as you want
- multiple field validation
- use custom methods for validation
- ...
 
 
So I just wanted to mention this module, because maybe you will get some ideas for the new validator. 
 
 


From: silverstripe-dev@googlegroups.com [mailto:silverstripe-dev@googlegroups.com] On Behalf Of Stig Lindqvist

Sent: Dienstag, 10. April 2012 00:15

Subject: [silverstripe-dev] Re: [GSoC 2012] Form and Model Validation
Hello!

It's good to kick this discussion of since the validation has been nagging me since I've started working with SilverStripe.

* 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)?

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. 

Links:




--
You received this message because you are subscribed to the Google Groups "SilverStripe Core Development" group.
To view this discussion on the web visit https://groups.google.com/d/msg/silverstripe-dev/-/c96-Wx_Ss8YJ.
To post to this group, send email to silverstripe-dev@googlegroups.com.
To unsubscribe from this group, send email to silverstripe-dev+unsubscribe@googlegroups.com.

Sam Minnée

unread,
Apr 10, 2012, 5:56:02 PM4/10/12
to silverst...@googlegroups.com
> Just how often would you use these two features?

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 :)

Totally agree.

netefx - Alexander Klein

unread,
Apr 11, 2012, 4:45:53 AM4/11/12
to silverst...@googlegroups.com
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.

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?
 
I use the comparison of fields rarely. But i need the chaining a lot. It often ends in many many rules :)
Mostly i use all these validation rules in the backend in modeladmin. Its very important for our customers that the modeladmin tries to find any errors when they add or edit items :)
 
here is a small extract from our latest project (validation in modeladmin), that shows the usage of both - even combined.
 
 
public function getCMSValidator() {
...
$rule_TargetViewsSSP_greater_zero        = new NetefxValidatorRuleGREATER        ("TargetViewsSSP", "", "error", 0);
$rule_TargetViewsPP_greater_zero         = new NetefxValidatorRuleGREATER        ("TargetViewsPP", "", "error", 0);
$rule_TargetViewsSSP_OR_PP_greater_zero  = new NetefxValidatorRuleOR             ("Budgetdummy", "You cant have 0 TargetViews SSP and also 0 Target Views PP", "error", array($rule_TargetViewsSSP_greater_zero, $rule_TargetViewsPP_greater_zero));
$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.

mateusz 
 
regards,
lx
To view this discussion on the web visit https://groups.google.com/d/msg/silverstripe-dev/-/lN-7hfFOKf0J.
To post to this group, send email to silverst...@googlegroups.com.
To unsubscribe from this group, send email to silverstripe-d...@googlegroups.com.

Wojtek Szkutnik

unread,
Apr 11, 2012, 8:40:07 AM4/11/12
to silverst...@googlegroups.com
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.

I think that I mentioned some plugins before, here is one of them:
http://www.matiasmancini.com.ar/jquery-plugin-ajax-form-validation-html5.html
If we decided to do frontend validation, we could do something
similiar and implement only the lacking features. I think that it's
doable.

W

Marcus Nyeholt

unread,
Apr 11, 2012, 7:37:37 PM4/11/12
to silverst...@googlegroups.com
Just putting my thoughts into the mix....

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.

Sam Minnée

unread,
Apr 11, 2012, 7:50:56 PM4/11/12
to silverst...@googlegroups.com
> 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.

Something like this:

class Member {
static $db = array(
"Email" => "Varchar",
);
static $constraints = array(
'Email' => array('UniqueField', 'Required')
);
}

class MemberController {
public function EditForm() {
$form = new Form();

$constraints = Member::constraints();
$constraints->add(array("Email" => "ValidEmail"));
$form->addValidation($constraints);
}
}

In terms of implementation, ConstraintSet could be represented as a subclass of a Validator, and both forms and models can have as many Validators as they like.

Alternatively, you could do this:

class MemberController {
public function EditForm() {
$form = new Form();

$form->addValidation(Member::constraints());
$form->addValidation(new ValidEmail("Email"));
}
}

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.

Marcus Nyeholt

unread,
Apr 12, 2012, 2:36:27 AM4/12/12
to silverst...@googlegroups.com
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. 



Mateusz Uzdowski

unread,
Apr 12, 2012, 7:04:42 PM4/12/12
to silverst...@googlegroups.com
Couple more thoughts about that.

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?

Shouldn't the constraints also be definable on the FormFields?

class Member {
        static $db = array(
                "Email" => "Varchar",
        );
        static $constraints = array(
                'Email' => array('UniqueField', 'Required')
        );
}

class FancyEmailField extends FormField {
        static $constraints = array(
                "ValidEmail", "FancyEmail"
        );
}

class FancyEmail extends ValidatorRule {
...
}

class MemberController {
        public function EditForm() {
                $form = new ModelForm("Member", new FieldList(new EmailField("Email")));

                $constraints = $form->constraints();
                // $constraints has 'Email' => array('UniqueField', 'Required', 'ValidEmail', 'FancyEmail')
                $form->addValidation($constraints);
        }
}

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:

$form->addValidation(function($validationContext) { ... });


mateusz
To post to this group, send email to silverstripe-dev@googlegroups.com.
To unsubscribe from this group, send email to silverstripe-dev+unsubscribe@googlegroups.com.
To post to this group, send email to silverstripe-dev@googlegroups.com.
To unsubscribe from this group, send email to silverstripe-dev+unsubscribe@googlegroups.com.

Wojtek Szkutnik

unread,
Apr 13, 2012, 4:14:42 AM4/13/12
to silverst...@googlegroups.com
> 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-mail
fields is te...@example.com or something similiar, and if you try to
put 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 an
E-mail - obviously, you can always perform a raw sql query since the
field is usually represented as a varchar field, but ORM should always
validate 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 basic
validation rules (obviously, the validation rules for role-specific
fields such as an E-mail field, should use more strict validation than
a imple TextField), but the developer should be allowed to define
additional validators for these.

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

W

Dan Rye

unread,
Apr 24, 2012, 6:09:10 PM4/24/12
to silverst...@googlegroups.com
This might be of interest to those following this thread:

W

Marcus Nyeholt

unread,
Apr 25, 2012, 2:34:00 AM4/25/12
to silverst...@googlegroups.com
> 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. 



 

Frank Mullenger

unread,
Apr 25, 2012, 5:22:14 AM4/25/12
to silverst...@googlegroups.com
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.

--

Wojtek Szkutnik

unread,
Apr 25, 2012, 5:26:32 AM4/25/12
to silverst...@googlegroups.com
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 :)

W

Ingo Schommer

unread,
Apr 26, 2012, 7:21:49 AM4/26/12
to silverst...@googlegroups.com
First of all: I think this is a great (and way overdue) discussion :)

I want to second Stig's suggestion that we should look into reusing existing implementations,
before going off too far into a tangent on new APIs.

Symfony's Validator component is already decoupled from their form/model logic.
More importantly, it has pluggable "metadata mappers", which means we
can hook build up validators from DataObject statics, as well as our own FormField collections.

Reusing the Symfony form component is a different story, and much more complex.
Couple of risk areas: Nested controllers, templating, request handling differences,
general differences in approaching config between Symfony and SS, localization of messages,
localization of format validation.

I think the question is: How much do we see the form API as part of the
overall validation effort, how soon do we want it in core,
and how long do we want to stick with whatever new API we come up with?
We don't want to break backwards compat twice in subsequent releases, 
so now's the time to discuss the broader strokes.

A bit more general note: The discussion is starting to exceed the scope of Wojtek's GSOC project in my opinion. 
That's generally OK, as its a medium term effort. As long as  we don't start out with overly high expectations 
what can be achieved in three months development.

I don't doubt his abilities, but am painfully aware how long it took us to get certain APIs "done done" for SS3.
He's got another three weeks to set the GSOC scope alongside his mentors, Sean and Mateusz.
Wojtek, how do you feel about the scope so far? I think in parallel to the "big picture" discussion,
you need to start thinking about dependencies, and which parts can be solved properly in isolation,
in a releasable+coherent fashion.

Ingo

Wojtek Szkutnik

unread,
Apr 26, 2012, 7:29:45 AM4/26/12
to silverst...@googlegroups.com
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 :)

W

Sam Minnée

unread,
Apr 29, 2012, 7:54:52 PM4/29/12
to silverst...@googlegroups.com

I want to second Stig's suggestion that we should look into reusing existing implementations,
before going off too far into a tangent on new APIs.

Symfony's Validator component is already decoupled from their form/model logic.
More importantly, it has pluggable "metadata mappers", which means we
can hook build up validators from DataObject statics, as well as our own FormField collections.

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.

On a more positive site, the main thing that the Symfony Validator could provide us that we don't have already is a set of constraint validators.  You can see these here - https://github.com/symfony/Validator/tree/master/Constraints - and there's a substantial list, including fun things like https://github.com/symfony/Validator/blob/master/Constraints/ImageValidator.php.

Sam Minnée

unread,
Apr 29, 2012, 8:19:20 PM4/29/12
to silverst...@googlegroups.com
Related to this is a comparatively small change that I started work on last week:

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.

stojg

unread,
Apr 30, 2012, 7:15:46 PM4/30/12
to silverst...@googlegroups.com
@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

Sam Minnée

unread,
Apr 30, 2012, 11:13:46 PM4/30/12
to silverst...@googlegroups.com
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.

Yeah, that's a good point.  Something like Hamcrest might also be good: http://code.google.com/p/hamcrest/wiki/TutorialPHP (also kind of available in JavaScript here https://github.com/abe33/hamcrest4qunit)

-----

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.
    * 
    */
   function validate($fieldName, $value, SS_Map $object, ValidatonResult &$validationResult);
}  

5) ConstraintSets can be passed around as the validation constructs.  We could add one to a Form:

$form->setConstraints(new ConstraintSet(array(
  "Title" => "Required",
  "URLSegment" => "Regex('/^[A-Za-z\-]+$/')",
))

..or validate against a random one as a part of an action handler or something:

ConstraintSet::create(array(
  "Title" => "Required",
  "URLSegment" => "Regex('/^[A-Za-z\-]+$/')",
))->validate($myDataObject);

6) If we could add callbacks to ConstraintSets, then we could inject any other custom code.  Note that I'm not particularly happy with the specific syntax below:

ConstraintSet::create(array(
  "Title" => "Required",
  "URLSegment" => "Regex('/^[A-Za-z\-]+$/')",
  "MenuTitle" => function($fieldName, $value, $object, &$validationResult) {
    if($value != "Foo") $validationResult->addError("MenuTitle must only ever be set to Foo.");
  },
  function($object, &$validationResult) {
    if($object->Title != $object->MenuTitle) {
      $validationResult->addError("MenuTitle and Title must be identical.");
    }
  }
));

-----

Assuming that we go down that road, what would we need to add to SilverStripe?  Well, there's a bunch of stuff that we'd have to do even if we used a 3rd party validator:

 * A constraint config static on DataObject
 * A constraint setter on Form
 * Amend Form::validate() and DataObject::validate() to call ConstraintSet::validate() where appropriate
 * A way of creating ConstraintSets via Object::create_from_string().

If we were to roll it from scratch, we'd have to do this stuff that Symfony Validator provides:

 * Implement ConstraintSet, which would be a thin wrapper around the individual constraints.
 * Amend ValidationResult to have appropriate methods for tracking errors.  Not much code to write here, but designing a future-proof API design would be tricky.
 * Implement all of the Constraints -- this would be most of the work.

-----

In conclusion:

 * I think that Marcus' general approach is the right one
 * ConstraintSets can be passed around as the object-form of validation behaviour
 * If we are looking to pull in 3rd party code, we should limit it to the set of constraints and not an entire validation system.

Mateusz Uzdowski

unread,
May 1, 2012, 8:58:48 PM5/1/12
to silverst...@googlegroups.com
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.

If we are talking about a portable ConstraintSet, what do you think about basic sanitisation filters like here http://kohanaframework.org/3.0/guide/kohana/security/validation#highlighter_691925 ? Seems to be useful at the first glance...

mateusz

Mateusz Uzdowski

unread,
May 9, 2012, 9:57:48 PM5/9/12
to silverst...@googlegroups.com
Here is a set of stories based on what people wrote, and based on feedback from a frontend dev. Specifically the second part might be more interesting to you because it's from another perspective :)

Reply all
Reply to author
Forward
0 new messages