Формы
Работа с формами очень распространенная и утомительная задача для веб мастеров. Symfony предлагает вам компонент Form способный сделать работу с формами очень простой. В этой статье мы будет строить форму с нуля и рассмотрим все основные возможности компонента.
Разумеется поскольку речь идет о добавление и редактировании вам понадобиться форма. Но прежде чем мы начнем давайте создадим Сущность
Этот клас обычный PHP объект, он не имеет ничего общего ни с Symfony, ни с другими библиотеками. Этот объект довольно простой и нужен для удобной(человекопонятной) презентации такой сущности как Задача. Конечно в конце этой статьи вы сможете представлять данные объекта Task через формы, проверять входящие данные и сохранять их в базу данных.
Создание формы
Now that you've created a
Creating a form requires relatively little code because Symfony form objects
are built with a "form builder". The form builder's purpose is to allow you
to write simple form "recipes", and have it do all the heavy-lifting of actually
building the form.
In this example, you've added two fields to your form -
Finally, you added a submit button with a custom label for submitting the form to the server.
That's it! Just three lines are needed to render the complete form:
Before moving on, notice how the rendered
In your controller, use the button's
Validation is done by adding a set of rules (called constraints) to a class. To see this in action, add validation constraints so that the
That's it! If you re-submit the form with invalid data, you'll see the
corresponding errors printed out with the form.
Validation is a very powerful feature of Symfony and has its own
dedicated chapter.
If you're creating form classes (a
good practice), then you'll need to add the following to the
In both of these cases, only the
Note that when you do that, the form will still run basic integrity checks,
for example whether an uploaded file was too large or whether non-existing
fields were submitted. If you want to suppress validation, you can use the
POST_SUBMIT event.
This will call the static method
Using the
You can find more information about how the validation groups and the default constraints
work in the book section about validation groups.
First, we need to add the two buttons to the form:
Then, we configure the button for returning to the previous step to run
specific validation groups. In this example, we want it to suppress validation,
so we set its
Now the form will skip your validation constraints. It will still validate
basic integrity constraints, such as checking whether an uploaded file was too
large or whether you tried to submit text in a number field.
Компонент Form является автономной библиотекой, которая может быть использованна в любом проекте.
Создаем простую форму
Предположим что вы будете создавать органайзер, который будет отображать задачи,а так же позволять их добавлять и редактировать.Разумеется поскольку речь идет о добавление и редактировании вам понадобиться форма. Но прежде чем мы начнем давайте создадим Сущность
Task
который представляет 1 задачу.// src/AppBundle/Entity/Task.php
namespace AppBundle\Entity;
class Task
{
protected $task;
protected $dueDate;
public function getTask()
{
return $this->task;
}
public function setTask($task)
{
$this->task = $task;
}
public function getDueDate()
{
return $this->dueDate;
}
public function setDueDate(\DateTime $dueDate = null)
{
$this->dueDate = $dueDate;
}
}
|
Создание формы
Now that you've created a Task
class, the next step is to create and
render the actual HTML form. In Symfony, this is done by building a form
object and then rendering it in a template. For now, this can all be done
from inside a controller:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use AppBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
// create a task and give it some dummy data for this example
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit', array('label' => 'Create Task'))
->getForm();
return $this->render('default/new.html.twig', array(
'form' => $form->createView(),
));
}
}
|
This example shows you how to build your form directly in the controller.
Later, in the "Creating Form Classes" section, you'll learn
how to build your form in a standalone class, which is recommended as
your form becomes reusable.
In this example, you've added two fields to your form -
task
and dueDate
-
corresponding to the task
and dueDate
properties of the Task
class.
You've also assigned each a "type" (e.g. text
, date
), which, among
other things, determines which HTML form tag(s) is rendered for that field.Finally, you added a submit button with a custom label for submitting the form to the server.
2.3Support for submit buttons was introduced in Symfony 2.3. Before that, you had
to add buttons to the form's HTML manually.
Symfony comes with many built-in types that will be discussed shortly
(see Built-in Field Types).Rendering the Form¶
Now that the form has been created, the next step is to render it. This is done by passing a special form "view" object to your template (notice the$form->createView()
in the controller above) and using a set of form
helper functions:
This example assumes that you submit the form in a "POST" request and to
the same URL that it was displayed in. You will learn later how to
change the request method and the target URL of the form.
form_start(form)
- Renders the start tag of the form, including the correct enctype attribute when using file uploads.
form_widget(form)
- Renders all the fields, which includes the field element itself, a label and any validation error messages for the field.
form_end(form)
- Renders the end tag of the form and any fields that have not yet been rendered, in case you rendered each field yourself. This is useful for rendering hidden fields and taking advantage of the automatic CSRF Protection.
As easy as this is, it's not very flexible (yet). Usually, you'll want to
render each form field individually so you can control how the form looks.
You'll learn how to do that in the "Rendering a Form in a Template" section.
task
input field has the value
of the task
property from the $task
object (i.e. "Write a blog post").
This is the first job of a form: to take data from an object and translate
it into a format that's suitable for being rendered in an HTML form.
The form system is smart enough to access the value of the protected
task
property via the getTask()
and setTask()
methods on the
Task
class. Unless a property is public, it must have a "getter" and
"setter" method so that the Form component can get and put data onto the
property. For a Boolean property, you can use an "isser" or "hasser" method
(e.g. isPublished()
or hasReminder()
) instead of a getter (e.g.
getPublished()
or getReminder()
).Handling Form Submissions¶
The second job of a form is to translate user-submitted data back to the properties of an object. To make this happen, the submitted data from the user must be written into the form. Add the following functionality to your controller:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // ...
use Symfony\Component\HttpFoundation\Request;
public function newAction(Request $request)
{
// just setup a fresh $task object (remove the dummy data)
$task = new Task();
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit', array('label' => 'Create Task'))
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// perform some action, such as saving the task to the database
return $this->redirectToRoute('task_success');
}
// ...
}
|
2.3The
This controller follows a common pattern for handling forms, and has three
possible paths:handleRequest()
method
was introduced in Symfony 2.3. Previously, the $request
was passed
to the submit
method - a strategy which is deprecated and will be
removed in Symfony 3.0. For details on that method, see Passing a Request to Form::submit() (Deprecated).- When initially loading the page in a browser, the form is simply created and rendered.
handleRequest()
recognizes that the form was not submitted and does nothing.isValid()
returnsfalse
if the form was not submitted. - When the user submits the form,
handleRequest()
recognizes this and immediately writes the submitted data back into thetask
anddueDate
properties of the$task
object. Then this object is validated. If it is invalid (validation is covered in the next section),isValid()
returnsfalse
again, so the form is rendered together with all validation errors;You can use the methodisSubmitted()
to check whether a form was submitted, regardless of whether or not the submitted data is actually valid. - When the user submits the form with valid data, the submitted data is again written into the form, but this time
isValid()
returnstrue
. Now you have the opportunity to perform some actions using the$task
object (e.g. persisting it to the database) before redirecting the user to some other page (e.g. a "thank you" or "success" page).Redirecting a user after a successful form submission prevents the user from being able to hit the "Refresh" button of their browser and re-post the data.
If you need more control over exactly when your form is submitted or which
data is passed to it, you can use the
submit()
for this. Read more about it in the cookbook.Submitting Forms with Multiple Buttons¶
2.3Support for buttons in forms was introduced in Symfony 2.3.
When your form contains more than one submit button, you will want to check
which of the buttons was clicked to adapt the program flow in your controller.
To do this, add a second button with the caption "Save and add" to your form:1 2 3 4 5 6 | $form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit', array('label' => 'Create Task'))
->add('saveAndAdd', 'submit', array('label' => 'Save and Add'))
->getForm();
|
isClicked()
method for
querying if the "Save and add" button was clicked:1 2 3 4 5 6 7 8 9 | if ($form->isValid()) {
// ... perform some action, such as saving the task to the database
$nextAction = $form->get('saveAndAdd')->isClicked()
? 'task_new'
: 'task_success';
return $this->redirectToRoute($nextAction);
}
|
Form Validation¶
In the previous section, you learned how a form can be submitted with valid or invalid data. In Symfony, validation is applied to the underlying object (e.g.Task
). In other words, the question isn't whether the "form" is
valid, but whether or not the $task
object is valid after the form has
applied the submitted data to it. Calling $form->isValid()
is a shortcut
that asks the $task
object whether or not it has valid data.Validation is done by adding a set of rules (called constraints) to a class. To see this in action, add validation constraints so that the
task
field cannot
be empty and the dueDate
field cannot be empty and must be a valid DateTime
object.- Annotations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// AppBundle/Entity/Task.php use Symfony\Component\Validator\Constraints as Assert; class Task { /** * @Assert\NotBlank() */ public $task; /** * @Assert\NotBlank() * @Assert\Type("\DateTime") */ protected $dueDate; }
- YAML
- XML
- PHP
Validation Groups¶
If your object takes advantage of validation groups, you'll need to specify which validation group(s) your form should use:1 2 3 | $form = $this->createFormBuilder($users, array(
'validation_groups' => array('registration'),
))->add(...);
|
setDefaultOptions()
method:1 2 3 4 5 6 7 8 | use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array('registration'),
));
}
|
registration
validation group will
be used to validate the underlying object.Disabling Validation¶
2.3The ability to set
Sometimes it is useful to suppress the validation of a form altogether. For
these cases you can set the validation_groups
to false was introduced in Symfony 2.3.validation_groups
option to false
:1 2 3 4 5 6 7 8 | use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => false,
));
}
|
Groups based on the Submitted Data¶
If you need some advanced logic to determine the validation groups (e.g. based on submitted data), you can set thevalidation_groups
option
to an array callback:1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\OptionsResolver\OptionsResolverInterface;
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array(
'AppBundle\Entity\Client',
'determineValidationGroups',
),
));
}
|
determineValidationGroups()
on the
Client
class after the form is submitted, but before validation is executed.
The Form object is passed as an argument to that method (see next example).
You can also define whole logic inline by using a Closure
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | use Acme\AcmeBundle\Entity\Client;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) {
$data = $form->getData();
if (Client::TYPE_PERSON == $data->getType()) {
return array('person');
}
return array('company');
},
));
}
|
validation_groups
option overrides the default validation
group which is being used. If you want to validate the default constraints
of the entity as well you have to adjust the option as follows:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | use Acme\AcmeBundle\Entity\Client;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) {
$data = $form->getData();
if (Client::TYPE_PERSON == $data->getType()) {
return array('Default', 'person');
}
return array('Default', 'company');
},
));
}
|
Groups based on the Clicked Button¶
2.3Support for buttons in forms was introduced in Symfony 2.3.
When your form contains multiple submit buttons, you can change the validation
group depending on which button is used to submit the form. For example,
consider a form in a wizard that lets you advance to the next step or go back
to the previous step. Also assume that when returning to the previous step,
the data of the form should be saved, but not validated.First, we need to add the two buttons to the form:
1 2 3 4 5 | $form = $this->createFormBuilder($task)
// ...
->add('nextStep', 'submit')
->add('previousStep', 'submit')
->getForm();
|
validation_groups
option to false:1 2 3 4 5 6 | $form = $this->createFormBuilder($task)
// ...
->add('previousStep', 'submit', array(
'validation_groups' => false,
))
->getForm();
|
Комментариев нет:
Отправить комментарий