Symfony Forms, Entities, and Validation

In a pre­vi­ous post, I dis­cussed some of the design pat­terns behind Sym­fony 2’s Form com­po­nent that make it so fast and sta­ble. As requested by Luis Cor­dova from Craft it Online, I have done fur­ther research into how Sym­fony 2 deals with forms, val­i­da­tion, and enti­ties. Much of the infor­ma­tion in this post can be learned from the online doc­u­men­ta­tion, and I highly rec­om­mend read­ing it.

Forms (Symfony\Component\Form)

Forms are cre­ated through a Fac­tory ser­vice (form.factory), which sub­se­quently uses a Builder object, to auto­mate the cre­ation of fields and assem­bly of the Form Com­pos­ite tree. The best way to illus­trate and explain this process is to walk through an exam­ple usage scenario.

$myForm = $this->createForm(new MyType(), $entity, array(...));

Symfony’s Controller class (Symfony\Bundle\FrameworkBundle\Controller\Controller) defines the createForm method short­cut, which accepts the fol­low­ing para­me­ters: a string iden­ti­fier or an instance of a Type class, an entity to which the form will bind its infor­ma­tion, and an array of options to pass to the Type’s buildForm() func­tion. The last two para­me­ters, the entity and the options array, are optional. If no entity is pro­vided, then the data can be retrieved by call­ing $form->getData() after binding.

Controller::createForm() del­e­gates to FormFactory::create(). FormFactory acts as a Façade to the Form com­po­nent by automat­ing and abstract­ing much of the cre­ation process. FormFactory::create() then del­e­gates to a FormBuilder object, which cre­ates the form recur­sively for each of the nested Type objects. After­wards, the FormFactory returns the com­pleted Form tree by call­ing $builder->getForm().

class MyType extends AbstractType {...}

This is the def­i­n­i­tion of a Type class that will con­trol how a node on the Form tree will be con­structed by the builder. Note that Sym­fony 2 dis­cour­ages users from directly defin­ing Form sub­classes: using the builder reduces depen­dency on the Form’s inter­face and allows its inter­nal struc­ture to vary as Sym­fony evolves with­out affect­ing the user’s code.

public function buildForm(FormBuilder $bldr, array $opts) {...}

This func­tion is a mem­ber of the MyType class and is the call­back for when a node of type MyType is cre­ated. The form fac­tory passes a builder to this call­back so this node’s chil­dren can be cre­ated (even recur­sively, if nec­es­sary by call­ing $fieldOrSubForm->buildForm($bldr, array(...))). The options are passed from the par­ent node (or the con­struc­tor if this par­tic­u­lar Type object was instan­ti­ated) and can be used to dynam­i­cally con­fig­ure form elements.

$bldr->add('name', 'text', array('required' => true));

This state­ment would reside in the def­i­n­i­tion of MyType::buildForm() and gives me the oppor­tu­nity to explain how the builder works. The builder’s add func­tion cre­ates a child on the cur­rent Form node with the given argu­ments: name, type, options.

  • The name acts as an iden­ti­fier for the child (and the input field’s name attribute when ren­dered as HTML), much like the key to a PHP array value.
  • The type is optional and can be either a string or an instance of Form (or any of its sub­classes, includ­ing Type objects). If omit­ted, the builder can guess the type using doc­trine meta­data. If a string is given for the type, the builder uses it to look up the type class in a reg­istry of built-​​in Type classes (all of which are defined in Extension\CoreType). I can­not deter­mine whether the reg­istry of built-​​in types fol­lows the Fly­weight pat­tern or the Pro­to­type pat­tern (or a com­bi­na­tion of both), but that’s not impor­tant now. If a Type instance is given, the builder can skip the lookup step and process it imme­di­ately. This is how one can embed a form within a form in Sym­fony 2.
  • The options array will be used for con­fig­u­ra­tion in the child node’s buildForm() method. Note that the ‘required’ option in the code exam­ple above is NOT a part of the val­i­da­tion model. Rather, it is an HTML5 attribute that is added to the ren­dered field for browser-​​based, client-​​side val­i­da­tion. This is not meant to be used in place of server-​​side val­i­da­tion as the ‘required’ attribute is not sup­ported by all browsers.

When the builder cre­ates a child node, it sets the new node’s par­ent ref­er­ence to the cur­rent node that called for the child’s con­struc­tion. The builder is actu­ally a lot more com­pli­cated than how I have explained it here: it uses lazy load­ing so that it does not cre­ate any child objects until it absolutely has to. Call­ing $builder->getForm() trig­gers this mech­a­nism. The result­ing Form object will allow for data bind­ing and val­i­da­tion and acts as a wrap­per for the data object underneath.

return array('view' => $form->createView());

Because the Form object only serves as a wrap­per to an entity, array, or other data form, it can­not be ren­dered into HTML as-​​is. To resolve this, the con­troller passes an instance of FormView to the tem­plat­ing engine at render-​​time. A FormView is a PHP abstrac­tion of the HTML input tags and pro­vides a way for the tem­plat­ing engine to iter­ate over the fields in the form.

In sum­mary, the FormFactory is the main inter­face for the Form com­po­nent; the FormBuilder is respon­si­ble for adding fields (Types) to the Form object being built; the Form is a light­weight com­pos­ite wrap­per for the data object that stores the infor­ma­tion, and the FormView encap­su­lates infor­ma­tion per­ti­nent to ren­der­ing HTML input fields.

Enti­ties

Dynamic web appli­ca­tions depend heav­ily on stor­ing and retriev­ing data. Sym­fony has altered the tra­di­tional norm in favor of Domain Dri­ven Design.

Enti­ties are noth­ing more than plain-​​old PHP classes that mimic the prop­er­ties and behav­ior of real-​​life objects. They do not extend any base Entity class and are thus entirely self-​​contained. This is another rea­son Sym­fony is so fast and light­weight. Enti­ties help the Sym­fony frame­work feel more like work­ing with a sta­t­i­cally typed object-​​oriented lan­guage like Java or C++.

Enti­ties are per­sisted by the Doc­trine ORM. The ORM under­stands how to com­mu­ni­cate with the enti­ties because of the meta­data infor­ma­tion asso­ci­ated with the fields to be saved and by con­ven­tional get­ter and set­ter names. I pre­fer using anno­ta­tions because I can define map­ping and val­i­da­tion meta­data for each field in the same file. I am also amused by the fact that Doc­trine (and other Sym­fony com­po­nents) can read and cache the PHP­doc com­ments that are ignored by the PHP inter­preter. Sym­fony also pro­vides ways to con­fig­ure entity meta­data using YAML, XML, or PHP files.

Val­i­da­tion (Symfony\Component\Validator)

Val­i­da­tion, the act of check­ing data legit­i­macy, has tra­di­tion­ally been closely inte­grated with forms: when a user sub­mits data, a val­i­da­tion script checks that all required fields have been filled, cer­tain numeric or date val­ues are within a pre­de­ter­mined range, and email addresses are cor­rectly for­mat­ted. Val­i­da­tion also applies on a cod­ing level: a REST­ful web ser­vice could sub­mit infor­ma­tion directly into the con­troller with­out the use of an HTML form; unit tests pop­u­late enti­ties directly through set­ter methods.

Val­i­da­tion must be made avail­able to both Forms and Enti­ties. Sym­fony 2 solves this prob­lem by extract­ing the val­i­da­tion sub­sys­tem into its own Val­ida­tor com­po­nent. Accord­ing to the Sym­fony 2 online doc­u­men­ta­tion, “This com­po­nent is based on the JSR303 Bean Val­i­da­tion spec­i­fi­ca­tion.”

Like Doc­trine, the Val­ida­tor com­po­nent knows how to val­i­date an object based on meta­data, which can be defined on the Entity using Anno­ta­tions or in sep­a­rate YAML, XML, or PHP con­fig­u­ra­tion files. Regard­less of how the meta­data is defined, the Val­ida­tor caches it for faster performance.

There are two ways to use the Val­ida­tor com­po­nent: either directly or through the Form com­po­nent. Using it directly is very straight­for­ward. From the con­troller level, the Val­ida­tor ser­vice can be obtained by call­ing $this->get('validator'). Inside a Unit test, the Val­ida­tor is cre­ated with ValidatorFactory::buildDefault()->getValidator(). Once we have a Val­ida­tor, we can pass it an entity to be validated:

$validator = $this->get('validator');
$errors = $validator->validate($entity);

When bind­ing data to a Form, the Form object auto­mat­i­cally trans­fers the data to the under­ly­ing entity object and calls the Val­i­da­tion com­po­nent to check it.


September 11: Tenth Anniversary

It’s hard to believe that it has been ten years since the attacks of Sep­tem­ber 11, 2001. Today we pray for all of the vic­tims, those who sac­ri­ficed them­selves for the safety of oth­ers, our armed forces pro­tect­ing our free­dom, and all of the fam­i­lies of these peo­ple. We also pray for all of the


Brave New Country

I sel­dom talk and write about pol­i­tics because I get mad every time that I do. In this case, I’m writ­ing because I am not only enraged at but deeply con­cerned of what I see hap­pen­ing to our coun­try. I just received word that the gov­ern­ment is mov­ing to pass a bill that would man­date


Symfony Design Patterns

As rec­om­mended by Dr. Andreas Klap­pe­necker, my CSCE 222 pro­fes­sor last semes­ter, I have been read­ing through the Gang of Four’s Design Pat­terns: Ele­ments of Reusable Object-​​​​Oriented Soft­ware book this sum­mer. In agree­ment with Dr. Klap­pe­necker, this is a must-​​​​read for any­one who works with code. I’ve pre­vi­ously writ­ten about the Sym­fony 2 PHP frame­work, and


One Ocean Wallpaper

To com­mem­o­rate the first show­ing of One Ocean at Sea­World San Anto­nio today, I pieced together some wall­pa­per that I just had to share. Enjoy! (Click on a thumb­nail to view a larger image) Cred­its Phở­tos by Jason Col­lier Copy­right Sea­World Parks and Entertainment


Symfony 2 Walkthrough

Sen­sio unveiled the first pre­view releases of the Sym­fony 2 frame­work a while ago (note the cap­i­tal ‘S’). I couldn’t help but think, great, and just after I was get­ting the hang of sym­fony 1.4. At the time I was con­vert­ing from CakePHP to sym­fony in search of more flex­i­ble rout­ing, but even now I


Summer Time!

My first year of col­lege ended only three weeks ago, and I’ve been busy wrap­ping up projects and defeat­ing Aper­ture Lab­o­ra­to­ries’ blun­ders. Yes, some of my fel­low com­puter sci­en­tists intro­duced me to Por­tal at the end of last semes­ter, and I’ve been play­ing both the old and new ver­sions ever since col­lege ended. How­ever, I


Ash Wednesday

Today, Ash Wednes­day marks the begin­ning of Lent—40 days of prayer, fast­ing, and penance—the Church’s remem­brance of Jesus’ 40 days in the desert before He began His pub­lic min­istry. All mem­bers of the Church (includ­ing non-​​​​Catholics) are encour­aged to receive the ashen cross on their fore­head sym­bol­iz­ing our mor­tal­ity and as a reminder of our iden­tity in


In Memory of Dawn Brancheau — A Year Later

One year ago today, Sea­World Orlando orca trainer Dawn Brancheau was trag­i­cally drowned by Tilikum, one of the orcas there. This came as a huge shock to the whole world—especially to the Sea­World Parks and Enter­tain­ment sys­tem and Sea­World fans like me.


Verba e Picturis

Okay, the Latin titles thing just hap­pens. I can’t help it if I had great Latin teach­ers for my last two years of High School—thank you Mrs. Gher­ardi and Mrs. Slowey, and I truly mean that ab imo pec­tore! Nonethe­less, I’ll try to con­tain myself next time. It is said that a pic­ture is worth a