diff --git a/best_practices/forms.rst b/best_practices/forms.rst
index 86b57e633d6..e942a40854c 100644
--- a/best_practices/forms.rst
+++ b/best_practices/forms.rst
@@ -22,6 +22,9 @@ form in its own PHP class::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+ use Symfony\Component\Form\Extension\Core\Type\EmailType;
+ use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
class PostType extends AbstractType
{
@@ -29,10 +32,10 @@ form in its own PHP class::
{
$builder
->add('title')
- ->add('summary', 'textarea')
- ->add('content', 'textarea')
- ->add('authorEmail', 'email')
- ->add('publishedAt', 'datetime')
+ ->add('summary', TextareaType::class)
+ ->add('content', TextareaType::class)
+ ->add('authorEmail', EmailType::class)
+ ->add('publishedAt', DateTimeType::class)
;
}
@@ -42,14 +45,9 @@ form in its own PHP class::
'data_class' => 'AppBundle\Entity\Post'
));
}
-
- public function getName()
- {
- return 'post';
- }
}
-To use the class, use ``createForm`` and instantiate the new class::
+To use the class, use ``createForm`` and pass the fully qualified class name::
use AppBundle\Form\PostType;
// ...
@@ -57,7 +55,7 @@ To use the class, use ``createForm`` and instantiate the new class::
public function newAction(Request $request)
{
$post = new Post();
- $form = $this->createForm(new PostType(), $post);
+ $form = $this->createForm(PostType::class, $post);
// ...
}
@@ -97,7 +95,7 @@ directly in your form class, this would effectively limit the scope of that form
{
$builder
// ...
- ->add('save', 'submit', array('label' => 'Create Post'))
+ ->add('save', SubmitType::class, array('label' => 'Create Post'))
;
}
@@ -112,6 +110,7 @@ some developers configure form buttons in the controller::
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use AppBundle\Entity\Post;
use AppBundle\Form\PostType;
@@ -122,8 +121,8 @@ some developers configure form buttons in the controller::
public function newAction(Request $request)
{
$post = new Post();
- $form = $this->createForm(new PostType(), $post);
- $form->add('submit', 'submit', array(
+ $form = $this->createForm(PostType::class, $post);
+ $form->add('submit', SubmitType::class, array(
'label' => 'Create',
'attr' => array('class' => 'btn btn-default pull-right')
));
@@ -207,21 +206,3 @@ Second, we recommend using ``$form->isSubmitted()`` in the ``if`` statement
for clarity. This isn't technically needed, since ``isValid()`` first calls
``isSubmitted()``. But without this, the flow doesn't read well as it *looks*
like the form is *always* processed (even on the GET request).
-
-Custom Form Field Types
------------------------
-
-.. best-practice::
-
- Add the ``app_`` prefix to your custom form field types to avoid collisions.
-
-Custom form field types inherit from the ``AbstractType`` class, which defines the
-``getName()`` method to configure the name of that form type. These names must
-be unique in the application.
-
-If a custom form type uses the same name as any of the Symfony's built-in form
-types, it will override it. The same happens when the custom form type matches
-any of the types defined by the third-party bundles installed in your application.
-
-Add the ``app_`` prefix to your custom form field types to avoid name collisions
-that can lead to hard to debug errors.
diff --git a/best_practices/security.rst b/best_practices/security.rst
index 207499aea07..6b25a7f54b5 100644
--- a/best_practices/security.rst
+++ b/best_practices/security.rst
@@ -264,37 +264,66 @@ the same ``getAuthorEmail`` logic you used above:
namespace AppBundle\Security;
- use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
+ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+ use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
+ use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
+ use AppBundle\Entity\Post;
- // AbstractVoter class requires Symfony 2.6 or higher version
- class PostVoter extends AbstractVoter
+ // Voter class requires Symfony 2.8 or higher version
+ class PostVoter extends Voter
{
const CREATE = 'create';
const EDIT = 'edit';
- protected function getSupportedAttributes()
+ /**
+ * @var AccessDecisionManagerInterface
+ */
+ private $decisionManager;
+
+ public function __construct(AccessDecisionManagerInterface $decisionManager)
{
- return array(self::CREATE, self::EDIT);
+ $this->decisionManager = $decisionManager;
}
- protected function getSupportedClasses()
+ protected function supports($attribute, $subject)
{
- return array('AppBundle\Entity\Post');
+ if (!in_array($attribute, array(self::CREATE, self::EDIT))) {
+ return false;
+ }
+
+ if (!$subject instanceof Post) {
+ return false;
+ }
+
+ return true;
}
- protected function isGranted($attribute, $post, $user = null)
+ protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
+ $user = $token->getUser();
+ /** @var Post */
+ $post = $subject; // $subject must be a Post instance, thanks to the supports method
+
if (!$user instanceof UserInterface) {
return false;
}
- if ($attribute === self::CREATE && in_array('ROLE_ADMIN', $user->getRoles(), true)) {
- return true;
- }
-
- if ($attribute === self::EDIT && $user->getEmail() === $post->getAuthorEmail()) {
- return true;
+ switch ($attribute) {
+ case self::CREATE:
+ // if the user is an admin, allow them to create new posts
+ if ($this->decisionManager->decide($token, array('ROLE_ADMIN'))) {
+ return true;
+ }
+
+ break;
+ case self::EDIT:
+ // if the user is the author of the post, allow them to edit the posts
+ if ($user->getEmail() === $post->getAuthorEmail()) {
+ return true;
+ }
+
+ break;
}
return false;
@@ -310,6 +339,7 @@ To enable the security voter in the application, define a new service:
# ...
post_voter:
class: AppBundle\Security\PostVoter
+ arguments: ['@security.access.decision_manager']
public: false
tags:
- { name: security.voter }
@@ -337,7 +367,7 @@ via the even easier shortcut in a controller:
*/
public function editAction($id)
{
- $post = // query for the post ...
+ $post = ...; // query for the post
$this->denyAccessUnlessGranted('edit', $post);
diff --git a/book/forms.rst b/book/forms.rst
index 63c0d37651e..599ef7dae91 100644
--- a/book/forms.rst
+++ b/book/forms.rst
@@ -80,6 +80,9 @@ from inside a controller::
use AppBundle\Entity\Task;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
+ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class DefaultController extends Controller
{
@@ -91,9 +94,11 @@ from inside a controller::
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
- ->add('task', 'text')
- ->add('dueDate', 'date')
- ->add('save', 'submit', array('label' => 'Create Task'))
+ ->add('task', TextType::class)
+ // If you use PHP 5.3 or 5.4 you must use
+ // ->add('task', 'Symfony\Component\Form\Extension\Core\Type\TextType')
+ ->add('dueDate', DateType::class)
+ ->add('save', SubmitType::class, array('label' => 'Create Task'))
->getForm();
return $this->render('default/new.html.twig', array(
@@ -116,8 +121,16 @@ building the form.
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.
+You've also assigned each a "type" (e.g. ``TextType`` and ``DateType``),
+represented by its fully qualified class name. Among other things, it determines
+which HTML form tag(s) is rendered for that field.
+
+.. versionadded:: 2.8
+ To denote the form type, you have to use the fully qualified class name - like
+ ``TextType::class`` in PHP 5.5+ or ``Symfony\Component\Form\Extension\Core\Type\TextType``.
+ Before Symfony 2.8, you could use an alias for each type like ``text`` or
+ ``date``. The old alias syntax will still work until Symfony 3.0. For more details,
+ see the `2.8 UPGRADE Log`_.
Finally, you added a submit button with a custom label for submitting the form to
the server.
@@ -224,9 +237,9 @@ controller::
$task = new Task();
$form = $this->createFormBuilder($task)
- ->add('task', 'text')
- ->add('dueDate', 'date')
- ->add('save', 'submit', array('label' => 'Create Task'))
+ ->add('task', TextType::class)
+ ->add('dueDate', DateType::class)
+ ->add('save', SubmitType::class, array('label' => 'Create Task'))
->getForm();
$form->handleRequest($request);
@@ -241,7 +254,7 @@ controller::
'form' => $form->createView(),
));
}
-
+
.. caution::
Be aware that the ``createView()`` method should be called *after* ``handleRequest``
@@ -310,10 +323,10 @@ 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::
$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'))
+ ->add('task', TextType::class)
+ ->add('dueDate', DateType::class)
+ ->add('save', SubmitType::class, array('label' => 'Create Task'))
+ ->add('saveAndAdd', SubmitType::class, array('label' => 'Save and Add'))
->getForm();
In your controller, use the button's
@@ -622,8 +635,8 @@ First, we need to add the two buttons to the form::
$form = $this->createFormBuilder($task)
// ...
- ->add('nextStep', 'submit')
- ->add('previousStep', 'submit')
+ ->add('nextStep', SubmitType::class)
+ ->add('previousStep', SubmitType::class)
->getForm();
Then, we configure the button for returning to the previous step to run
@@ -632,7 +645,7 @@ so we set its ``validation_groups`` option to false::
$form = $this->createFormBuilder($task)
// ...
- ->add('previousStep', 'submit', array(
+ ->add('previousStep', SubmitType::class, array(
'validation_groups' => false,
))
->getForm();
@@ -669,7 +682,7 @@ boxes. However, the :doc:`date field ` can be
configured to be rendered as a single text box (where the user would enter
the date as a string in the box)::
- ->add('dueDate', 'date', array('widget' => 'single_text'))
+ ->add('dueDate', DateType::class, array('widget' => 'single_text'))
.. image:: /images/book/form-simple2.png
:align: center
@@ -701,7 +714,7 @@ the documentation for each type.
The label for the form field can be set using the ``label`` option,
which can be applied to any field::
- ->add('dueDate', 'date', array(
+ ->add('dueDate', DateType::class, array(
'widget' => 'single_text',
'label' => 'Due Date',
))
@@ -722,7 +735,7 @@ Now that you've added validation metadata to the ``Task`` class, Symfony
already knows a bit about your fields. If you allow it, Symfony can "guess"
the type of your field and set it up for you. In this example, Symfony can
guess from the validation rules that both the ``task`` field is a normal
-``text`` field and the ``dueDate`` field is a ``date`` field::
+``TextType`` field and the ``dueDate`` field is a ``DateType`` field::
public function newAction()
{
@@ -731,7 +744,7 @@ guess from the validation rules that both the ``task`` field is a normal
$form = $this->createFormBuilder($task)
->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
- ->add('save', 'submit')
+ ->add('save', SubmitType::class)
->getForm();
}
@@ -992,9 +1005,9 @@ ways. If you build your form in the controller, you can use ``setAction()`` and
$form = $this->createFormBuilder($task)
->setAction($this->generateUrl('target_route'))
->setMethod('GET')
- ->add('task', 'text')
- ->add('dueDate', 'date')
- ->add('save', 'submit')
+ ->add('task', TextType::class)
+ ->add('dueDate', DateType::class)
+ ->add('save', SubmitType::class)
->getForm();
.. note::
@@ -1006,7 +1019,10 @@ In :ref:`book-form-creating-form-classes` you will learn how to move the
form building code into separate classes. When using an external form class
in the controller, you can pass the action and method as form options::
- $form = $this->createForm(new TaskType(), $task, array(
+ use AppBundle\Form\Type\TaskType;
+ // ...
+
+ $form = $this->createForm(TaskType::class, $task, array(
'action' => $this->generateUrl('target_route'),
'method' => 'GET',
));
@@ -1025,7 +1041,9 @@ to the ``form()`` or the ``form_start()`` helper:
start($form, array(
- 'action' => $view['router']->generate('target_route'),
+ // The path() method was introduced in Symfony 2.8. Prior to 2.8,
+ // you had to use generate().
+ 'action' => $view['router']->path('target_route'),
'method' => 'GET',
)) ?>
@@ -1056,6 +1074,7 @@ that will house the logic for building the task form::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class TaskType extends AbstractType
{
@@ -1064,36 +1083,21 @@ that will house the logic for building the task form::
$builder
->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
- ->add('save', 'submit')
+ ->add('save', SubmitType::class)
;
}
-
- public function getName()
- {
- return 'task';
- }
}
-.. caution::
-
- The ``getName()`` method returns the identifier of this form "type". These
- identifiers must be unique in the application. Unless you want to override
- a built-in type, they should be different from the default Symfony types
- and from any type defined by a third-party bundle installed in your application.
- Consider prefixing your types with ``app_`` to avoid identifier collisions.
-
This new class contains all the directions needed to create the task form. It can
be used to quickly build a form object in the controller::
// src/AppBundle/Controller/DefaultController.php
-
- // add this new use statement at the top of the class
use AppBundle\Form\Type\TaskType;
public function newAction()
{
$task = ...;
- $form = $this->createForm(new TaskType(), $task);
+ $form = $this->createForm(TaskType::class, $task);
// ...
}
@@ -1140,7 +1144,7 @@ the choice is ultimately up to you.
$builder
->add('task')
->add('dueDate', null, array('mapped' => false))
- ->add('save', 'submit')
+ ->add('save', SubmitType::class)
;
}
@@ -1160,8 +1164,8 @@ the choice is ultimately up to you.
Defining your Forms as Services
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Defining your form type as a service is a good practice and makes it really
-easy to use in your application.
+Your form type might have some external dependencies. You can define your form
+type as a service, and inject inject all dependencies you need.
.. note::
@@ -1169,6 +1173,39 @@ easy to use in your application.
:doc:`later on in this book `. Things will be
more clear after reading that chapter.
+You might want to use a service defined as ``app.my_service`` in your form
+type. Create a constructor to your form type to receive the service::
+
+ // src/AppBundle/Form/Type/TaskType.php
+ namespace AppBundle\Form\Type;
+
+ use App\Utility\MyService;
+ use Symfony\Component\Form\AbstractType;
+ use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+
+ class TaskType extends AbstractType
+ {
+ private $myService;
+
+ public function __construct(MyService $mySevice)
+ {
+ $this->myService = $myService;
+ }
+
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ // You can now use myService.
+ $builder
+ ->add('task')
+ ->add('dueDate', null, array('widget' => 'single_text'))
+ ->add('save', SubmitType::class)
+ ;
+ }
+ }
+
+Define your form type as a service.
+
.. configuration-block::
.. code-block:: yaml
@@ -1177,8 +1214,9 @@ easy to use in your application.
services:
app.form.type.task:
class: AppBundle\Form\Type\TaskType
+ arguments: ["@app.my_service"]
tags:
- - { name: form.type, alias: task }
+ - { name: form.type }
.. code-block:: xml
@@ -1190,7 +1228,8 @@ easy to use in your application.
-
+
+
@@ -1198,43 +1237,11 @@ easy to use in your application.
.. code-block:: php
// src/AppBundle/Resources/config/services.php
- $container
- ->register(
- 'app.form.type.task',
- 'AppBundle\Form\Type\TaskType'
- )
- ->addTag('form.type', array(
- 'alias' => 'task',
- ))
- ;
-
-That's it! Now you can use your form type directly in a controller::
+ use ;
- // src/AppBundle/Controller/DefaultController.php
- // ...
-
- public function newAction()
- {
- $task = ...;
- $form = $this->createForm('task', $task);
-
- // ...
- }
-
-or even use from within the form type of another form::
-
- // src/AppBundle/Form/Type/ListType.php
- // ...
-
- class ListType extends AbstractType
- {
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- // ...
-
- $builder->add('someTask', 'task');
- }
- }
+ $container->register('app.form.type.task', 'AppBundle\Form\Type\TaskType')
+ ->addArgument(new Reference('app.my_service'))
+ ->addTag('form.type')
Read :ref:`form-cookbook-form-field-service` for more information.
@@ -1360,11 +1367,6 @@ create a form class so that a ``Category`` object can be modified by the user::
'data_class' => 'AppBundle\Entity\Category',
));
}
-
- public function getName()
- {
- return 'category';
- }
}
The end goal is to allow the ``Category`` of a ``Task`` to be modified right
@@ -1375,12 +1377,13 @@ class:
.. code-block:: php
use Symfony\Component\Form\FormBuilderInterface;
+ use AppBundle\Form\Type\CategoryType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
- $builder->add('category', new CategoryType());
+ $builder->add('category', CategoryType::class);
}
The fields from ``CategoryType`` can now be rendered alongside those from
@@ -1851,10 +1854,10 @@ an array of the submitted data. This is actually really easy::
{
$defaultData = array('message' => 'Type your message here');
$form = $this->createFormBuilder($defaultData)
- ->add('name', 'text')
- ->add('email', 'email')
- ->add('message', 'textarea')
- ->add('send', 'submit')
+ ->add('name', TextType::class)
+ ->add('email', EmailType::class)
+ ->add('message', TextareaType::class)
+ ->add('send', SubmitType::class)
->getForm();
$form->handleRequest($request);
@@ -1915,12 +1918,13 @@ but here's a short example:
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
$builder
- ->add('firstName', 'text', array(
+ ->add('firstName', TextType::class, array(
'constraints' => new Length(array('min' => 3)),
))
- ->add('lastName', 'text', array(
+ ->add('lastName', TextType::class, array(
'constraints' => array(
new NotBlank(),
new Length(array('min' => 3)),
@@ -1974,3 +1978,4 @@ Learn more from the Cookbook
.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
.. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery
.. _`view on GitHub`: https://github.com/symfony/symfony/tree/master/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form
+.. _`2.8 UPGRADE Log`: https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md#form
\ No newline at end of file
diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst
index d243b390434..5420a31ea1b 100644
--- a/book/from_flat_php_to_symfony2.rst
+++ b/book/from_flat_php_to_symfony2.rst
@@ -598,7 +598,7 @@ database and the Templating component to render a template and return a
-
- path(
'blog_show',
array('id' => $post->getId())
) ?>">
diff --git a/book/http_cache.rst b/book/http_cache.rst
index bf91ff2345d..e01baff8c6d 100644
--- a/book/http_cache.rst
+++ b/book/http_cache.rst
@@ -1087,23 +1087,22 @@ matter), Symfony uses the standard ``render`` helper to configure ESI tags:
- // you can use a controller reference
- use Symfony\Component\HttpKernel\Controller\ControllerReference;
+
render(
- new ControllerReference(
+ new \Symfony\Component\HttpKernel\Controller\ControllerReference(
'AppBundle:News:latest',
array('maxPerPage' => 5)
),
array('strategy' => 'esi')
) ?>
- // ... or a URL
- use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+
render(
- $view['router']->generate(
+ // The url() method was introduced in Symfony 2.8. Prior to 2.8,
+ // you had to use generate($name, $parameters, UrlGeneratorInterface::ABSOLUTE_URL)
+ $view['router']->url(
'latest_news',
array('maxPerPage' => 5),
- UrlGeneratorInterface::ABSOLUTE_URL
),
array('strategy' => 'esi'),
) ?>
diff --git a/book/installation.rst b/book/installation.rst
index 407cfc36704..6e213203936 100644
--- a/book/installation.rst
+++ b/book/installation.rst
@@ -54,6 +54,8 @@ execute it as follows:
c:\> move symfony c:\projects
c:\projects\> php symfony
+.. _installation-creating-the-app:
+
Creating the Symfony Application
--------------------------------
diff --git a/book/routing.rst b/book/routing.rst
index ed0b4bf809c..9ffbd210933 100644
--- a/book/routing.rst
+++ b/book/routing.rst
@@ -1531,12 +1531,16 @@ a template helper function:
.. code-block:: html+php
- path('blog_show', array(
'slug' => 'my-blog-post',
)) ?>">
Read this blog post.
+.. versionadded:: 2.8
+ The ``path()`` PHP templating helper was introduced in Symfony 2.8. Prior
+ to 2.8, you had to use the ``generate()`` helper method.
+
.. index::
single: Routing; Absolute URLs
@@ -1550,9 +1554,8 @@ method::
$this->generateUrl('blog_show', array('slug' => 'my-blog-post'), true);
// http://www.example.com/blog/my-blog-post
-From a template, in Twig, simply use the ``url()`` function (which generates an absolute URL)
-rather than the ``path()`` function (which generates a relative URL). In PHP, pass ``true``
-to ``generate()``:
+From a template, simply use the ``url()`` function (which generates an absolute
+URL) rather than the ``path()`` function (which generates a relative URL):
.. configuration-block::
@@ -1564,16 +1567,18 @@ to ``generate()``:
.. code-block:: html+php
-
-
- url('blog_show', array(
'slug' => 'my-blog-post',
- ), UrlGeneratorInterface::ABSOLUTE_URL) ?>">
+ )) ?>">
Read this blog post.
+.. versionadded:: 2.8
+ The ``url()`` PHP templating helper was introduced in Symfony 2.8. Prior
+ to 2.8, you had to use the ``generate()`` helper method with
+ ``Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL``
+ passed as the third argument.
+
.. note::
The host that's used when generating an absolute URL is automatically
diff --git a/book/service_container.rst b/book/service_container.rst
index 2cb08ae4078..2c777c8106e 100644
--- a/book/service_container.rst
+++ b/book/service_container.rst
@@ -145,7 +145,7 @@ As a bonus, the ``Mailer`` service is only created once and the same
instance is returned each time you ask for the service. This is almost always
the behavior you'll need (it's more flexible and powerful), but you'll learn
later how you can configure a service that has multiple instances in the
-":doc:`/cookbook/service_container/scopes`" cookbook article.
+":doc:`/cookbook/service_container/shared`" cookbook article.
.. note::
diff --git a/book/templating.rst b/book/templating.rst
index 4c1de72f752..f50085219c1 100644
--- a/book/templating.rst
+++ b/book/templating.rst
@@ -709,8 +709,11 @@ tags:
array('renderer' => 'hinclude')
) ?>
+
render(
- $view['router']->generate('...'),
+ $view['router']->url('...'),
array('renderer' => 'hinclude')
) ?>
@@ -918,7 +921,9 @@ To link to the page, just use the ``path`` Twig function and refer to the route:
.. code-block:: html+php
- Home
+
+ Home
As expected, this will generate the URL ``/``. Now, for a more complicated
route:
@@ -997,7 +1002,9 @@ correctly:
- path('article_show', array(
'slug' => $article->getSlug(),
)) ?>">
getTitle() ?>
@@ -1006,26 +1013,26 @@ correctly:
.. tip::
- You can also generate an absolute URL by using the ``url`` Twig function:
+ You can also generate an absolute URL by using the ``url`` function:
- .. code-block:: html+twig
+ .. configuration-block::
- Home
+ .. code-block:: html+twig
- The same can be done in PHP templates by passing a third argument to
- the ``generate()`` method:
+ Home
- .. code-block:: html+php
+ .. code-block:: html+php
-
+ Home
- Home
+ .. versionadded:: 2.8
+ The ``url()`` PHP templating helper was introduced in Symfony 2.8. Prior
+ to 2.8, you had to use the ``generate()`` helper method with
+ ``Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL``
+ passed as the third argument.
.. index::
single: Templating; Linking to assets
@@ -1696,7 +1703,9 @@ key in the parameter hash:
.. code-block:: html+php
- path('article_show', array(
'id' => 123,
'_format' => 'pdf',
)) ?>">
diff --git a/book/validation.rst b/book/validation.rst
index 8856de1d304..a30ea5e0e9d 100644
--- a/book/validation.rst
+++ b/book/validation.rst
@@ -230,7 +230,7 @@ workflow looks like the following from inside a controller::
public function updateAction(Request $request)
{
$author = new Author();
- $form = $this->createForm(new AuthorType(), $author);
+ $form = $this->createForm(AuthorType::class, $author);
$form->handleRequest($request);
diff --git a/components/form/form_events.rst b/components/form/form_events.rst
index 5fb51d59fa1..5bde8df6b42 100644
--- a/components/form/form_events.rst
+++ b/components/form/form_events.rst
@@ -288,10 +288,13 @@ Creating and binding an event listener to the form is very easy::
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
+ use Symfony\Component\Form\Extension\Core\Type\EmailType;
$form = $formFactory->createBuilder()
- ->add('username', 'text')
- ->add('show_email', 'checkbox')
+ ->add('username', TextType::class)
+ ->add('show_email', CheckboxType::class)
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$user = $event->getData();
$form = $event->getForm();
@@ -304,7 +307,7 @@ Creating and binding an event listener to the form is very easy::
// If the data was submitted previously, the additional value that is
// included in the request variables needs to be removed.
if (true === $user['show_email']) {
- $form->add('email', 'email');
+ $form->add('email', EmailType::class
} else {
unset($user['email']);
$event->setData($user);
@@ -317,14 +320,17 @@ Creating and binding an event listener to the form is very easy::
When you have created a form type class, you can use one of its methods as a
callback for better readability::
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
+
// ...
class SubscriptionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
- $builder->add('username', 'text');
- $builder->add('show_email', 'checkbox');
+ $builder->add('username', TextType::class);
+ $builder->add('show_email', CheckboxType::class);
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
array($this, 'onPreSetData')
@@ -351,6 +357,7 @@ Event subscribers have different uses:
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
+ use Symfony\Component\Form\Extension\Core\Type\EmailType;
class AddEmailFieldListener implements EventSubscriberInterface
{
@@ -370,7 +377,7 @@ Event subscribers have different uses:
// Check whether the user from the initial data has chosen to
// display his email or not.
if (true === $user->isShowEmail()) {
- $form->add('email', 'email');
+ $form->add('email', EmailType::class);
}
}
@@ -387,7 +394,7 @@ Event subscribers have different uses:
// If the data was submitted previously, the additional value that
// is included in the request variables needs to be removed.
if (true === $user['show_email']) {
- $form->add('email', 'email');
+ $form->add('email', EmailType::class);
} else {
unset($user['email']);
$event->setData($user);
@@ -397,11 +404,14 @@ Event subscribers have different uses:
To register the event subscriber, use the addEventSubscriber() method::
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
+
// ...
$form = $formFactory->createBuilder()
- ->add('username', 'text')
- ->add('show_email', 'checkbox')
+ ->add('username', TextType::class)
+ ->add('show_email', CheckboxType::class)
->addEventSubscriber(new AddEmailFieldListener())
->getForm();
diff --git a/components/form/introduction.rst b/components/form/introduction.rst
index b654f7bee0a..36a13fb9d28 100644
--- a/components/form/introduction.rst
+++ b/components/form/introduction.rst
@@ -391,9 +391,16 @@ is created from the form factory.
.. code-block:: php-standalone
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
+
+ // ...
+
$form = $formFactory->createBuilder()
- ->add('task', 'text')
- ->add('dueDate', 'date')
+ ->add('task', TextType::class)
+ // If you use PHP 5.3 or 5.4, you must use
+ // ->add('task', 'Symfony\Component\Form\Extension\Core\Type\TextType')
+ ->add('dueDate', DateType::class)
->getForm();
var_dump($twig->render('new.html.twig', array(
@@ -407,6 +414,8 @@ is created from the form factory.
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
class DefaultController extends Controller
{
@@ -414,9 +423,12 @@ is created from the form factory.
{
// createFormBuilder is a shortcut to get the "form factory"
// and then call "createBuilder()" on it
+
$form = $this->createFormBuilder()
- ->add('task', 'text')
- ->add('dueDate', 'date')
+ ->add('task', TextType::class)
+ // If you use PHP 5.3 or 5.4, you must use
+ // ->add('task', 'Symfony\Component\Form\Extension\Core\Type\TextType')
+ ->add('dueDate', DateType::class)
->getForm();
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
@@ -427,8 +439,9 @@ is created from the form factory.
As you can see, creating a form is like writing a recipe: you call ``add``
for each new field you want to create. The first argument to ``add`` is the
-name of your field, and the second is the field "type". The Form component
-comes with a lot of :doc:`built-in types `.
+name of your field, and the second is the fully qualified class name. If you
+use PHP 5.5 or above, you can use ``::class`` constant of a form type. The Form
+component comes with a lot of :doc:`built-in types `.
Now that you've built your form, learn how to :ref:`render `
it and :ref:`process the form submission `.
@@ -444,24 +457,34 @@ builder:
.. code-block:: php-standalone
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
+
+ // ...
+
$defaults = array(
'dueDate' => new \DateTime('tomorrow'),
);
$form = $formFactory->createBuilder('form', $defaults)
- ->add('task', 'text')
- ->add('dueDate', 'date')
+ ->add('task', TextType::class)
+ ->add('dueDate', DateType::class)
->getForm();
.. code-block:: php-symfony
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
+
+ // ...
+
$defaults = array(
'dueDate' => new \DateTime('tomorrow'),
);
$form = $this->createFormBuilder($defaults)
- ->add('task', 'text')
- ->add('dueDate', 'date')
+ ->add('task', TextType::class)
+ ->add('dueDate', DateType::class)
->getForm();
.. tip::
@@ -513,7 +536,11 @@ by ``handleRequest()`` to determine whether a form has been submitted):
.. code-block:: php-standalone
- $formBuilder = $formFactory->createBuilder('form', null, array(
+ use Symfony\Component\Form\Extension\Core\Type\FormType;
+
+ // ...
+
+ $formBuilder = $formFactory->createBuilder(FormType::class, null, array(
'action' => '/search',
'method' => 'GET',
));
@@ -522,11 +549,13 @@ by ``handleRequest()`` to determine whether a form has been submitted):
.. code-block:: php-symfony
+ use Symfony\Component\Form\Extension\Core\Type\FormType;
+
// ...
public function searchAction()
{
- $formBuilder = $this->createFormBuilder('form', null, array(
+ $formBuilder = $this->createFormBuilder(FormType::class, null, array(
'action' => '/search',
'method' => 'GET',
));
@@ -548,10 +577,14 @@ method:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
+
+ // ...
$form = $formFactory->createBuilder()
- ->add('task', 'text')
- ->add('dueDate', 'date')
+ ->add('task', TextType::class)
+ ->add('dueDate', DateType::class)
->getForm();
$request = Request::createFromGlobals();
@@ -573,13 +606,16 @@ method:
.. code-block:: php-symfony
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
+
// ...
public function newAction(Request $request)
{
$form = $this->createFormBuilder()
- ->add('task', 'text')
- ->add('dueDate', 'date')
+ ->add('task', TextType::class)
+ ->add('dueDate', DateType::class)
->getForm();
$form->handleRequest($request);
@@ -624,12 +660,14 @@ option when building each field:
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Type;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
$form = $formFactory->createBuilder()
- ->add('task', 'text', array(
+ ->add('task', TextType::class, array(
'constraints' => new NotBlank(),
))
- ->add('dueDate', 'date', array(
+ ->add('dueDate', DateType::class, array(
'constraints' => array(
new NotBlank(),
new Type('\DateTime'),
@@ -641,12 +679,14 @@ option when building each field:
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Type;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\DateType;
$form = $this->createFormBuilder()
- ->add('task', 'text', array(
+ ->add('task', TextType::class, array(
'constraints' => new NotBlank(),
))
- ->add('dueDate', 'date', array(
+ ->add('dueDate', DateType::class, array(
'constraints' => array(
new NotBlank(),
new Type('\DateTime'),
diff --git a/components/form/type_guesser.rst b/components/form/type_guesser.rst
index 13a64400118..e1f88f3e96d 100644
--- a/components/form/type_guesser.rst
+++ b/components/form/type_guesser.rst
@@ -91,6 +91,10 @@ With this knowledge, you can easily implement the ``guessType`` method of the
use Symfony\Component\Form\Guess\Guess;
use Symfony\Component\Form\Guess\TypeGuess;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\IntegerType;
+ use Symfony\Component\Form\Extension\Core\Type\NumberType;
+ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
class PHPDocTypeGuesser implements FormTypeGuesserInterface
{
@@ -107,25 +111,25 @@ With this knowledge, you can easily implement the ``guessType`` method of the
case 'string':
// there is a high confidence that the type is text when
// @var string is used
- return new TypeGuess('text', array(), Guess::HIGH_CONFIDENCE);
+ return new TypeGuess(TextType::class, array(), Guess::HIGH_CONFIDENCE);
case 'int':
case 'integer':
// integers can also be the id of an entity or a checkbox (0 or 1)
- return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);
+ return new TypeGuess(IntegerType::class, array(), Guess::MEDIUM_CONFIDENCE);
case 'float':
case 'double':
case 'real':
- return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
+ return new TypeGuess(NumberType::class, array(), Guess::MEDIUM_CONFIDENCE);
case 'boolean':
case 'bool':
- return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);
+ return new TypeGuess(CheckboxType::class, array(), Guess::HIGH_CONFIDENCE);
default:
// there is a very low confidence that this one is correct
- return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
+ return new TypeGuess(TextType::class, array(), Guess::LOW_CONFIDENCE);
}
}
diff --git a/components/security/authorization.rst b/components/security/authorization.rst
index 3895592b95e..fc51b57f8aa 100644
--- a/components/security/authorization.rst
+++ b/components/security/authorization.rst
@@ -90,10 +90,10 @@ of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterf
which means they have to implement a few methods which allows the decision
manager to use them:
-``supportsAttribute($attribute)``
+``supportsAttribute($attribute)`` (deprecated as of 2.8)
will be used to check if the voter knows how to handle the given attribute;
-``supportsClass($class)``
+``supportsClass($class)`` (deprecated as of 2.8)
will be used to check if the voter is able to grant or deny access for
an object of the given class;
@@ -103,6 +103,12 @@ manager to use them:
i.e. ``VoterInterface::ACCESS_GRANTED``, ``VoterInterface::ACCESS_DENIED``
or ``VoterInterface::ACCESS_ABSTAIN``;
+.. note::
+
+ The ``supportsAttribute()`` and ``supportsClass()`` methods are deprecated
+ as of Symfony 2.8 and no longer required in 3.0. These methods should not
+ be called outside the voter class.
+
The Security component contains some standard voters which cover many use
cases:
diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst
index db60e778bba..375f2678504 100644
--- a/cookbook/bundles/override.rst
+++ b/cookbook/bundles/override.rst
@@ -105,17 +105,16 @@ associations. Learn more about this feature and its limitations in
Forms
-----
-In order to override a form type, it has to be registered as a service (meaning
-it is tagged as ``form.type``). You can then override it as you would override any
-service as explained in `Services & Configuration`_. This, of course, will only
-work if the type is referred to by its alias rather than being instantiated,
-e.g.::
+As of Symfony 2.8, form types are referred to by their fully-qualified class name::
- $builder->add('name', 'custom_type');
+ $builder->add('name', CustomType::class);
-rather than::
+This means that you cannot override this by creating a sub-class of ``CustomType``
+and registering it as a service, and tagging it with ``form.type`` (you *could*
+do this in earlier version).
- $builder->add('name', new CustomType());
+Instead, you should use a "form type extension" to modify the existing form type.
+For more information, see :doc:`/cookbook/form/create_form_type_extension`.
.. _override-validation:
diff --git a/cookbook/configuration/index.rst b/cookbook/configuration/index.rst
index 11838c6fa38..741fe5be4e4 100644
--- a/cookbook/configuration/index.rst
+++ b/cookbook/configuration/index.rst
@@ -5,6 +5,7 @@ Configuration
:maxdepth: 2
environments
+ micro-kernel-trait
override_dir_structure
using_parameters_in_dic
front_controllers_and_kernel
diff --git a/cookbook/configuration/micro-kernel-trait.rst b/cookbook/configuration/micro-kernel-trait.rst
new file mode 100644
index 00000000000..79f1d474f6d
--- /dev/null
+++ b/cookbook/configuration/micro-kernel-trait.rst
@@ -0,0 +1,327 @@
+Building your own Framework with the MicroKernelTrait
+=====================================================
+
+A :ref:`traditional Symfony app ` contains a sensible
+directory structure, various configuration files and an ``AppKernel`` with several
+bundles already-registered. This is a fully-featured app that's ready to go.
+
+But did you know, you can create a fully-functional Symfony application in as little
+as one file? This is possible thanks to the new
+:class:`Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait`. This allows
+you to start with a tiny application, and then add features and structure as you
+need to.
+
+.. note::
+
+ The MicroKernelTrait requires PHP 5.4. However, there's nothing special about
+ this trait. If you're using PHP 5.3, simply copy its methods into *your* kernel
+ class to get the same functionality.
+
+A Single-File Symfony Application
+---------------------------------
+
+Start with a completely empty directory. Get ``symfony/symfony`` as a dependency
+via Composer:
+
+.. code-block:: bash
+
+ $ composer require symfony/symfony
+
+Next, create an ``index.php`` file that creates a kernel class and executes it::
+
+ use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
+ use Symfony\Component\Config\Loader\LoaderInterface;
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\HttpFoundation\JsonResponse;
+ use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Component\HttpKernel\Kernel;
+ use Symfony\Component\Routing\RouteCollectionBuilder;
+
+ // require Composer's autoloader
+ require __DIR__.'/vendor/autoload.php';
+
+ class AppKernel extends Kernel
+ {
+ use MicroKernelTrait;
+
+ public function registerBundles()
+ {
+ return array(
+ new Symfony\Bundle\FrameworkBundle\FrameworkBundle()
+ );
+ }
+
+ protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
+ {
+ // PHP equivalent of config.yml
+ $c->loadFromExtension('framework', array(
+ 'secret' => 'S0ME_SECRET'
+ ));
+ }
+
+ protected function configureRoutes(RouteCollectionBuilder $routes)
+ {
+ // kernel is a service that points to this class
+ // optional 3rd argument is the route name
+ $routes->add('/random/{limit}', 'kernel:randomAction');
+ }
+
+ public function randomAction($limit)
+ {
+ return new JsonResponse(array(
+ 'number' => rand(0, $limit)
+ ));
+ }
+ }
+
+ $kernel = new AppKernel('dev', true);
+ $request = Request::createFromGlobals();
+ $response = $kernel->handle($request);
+ $response->send();
+ $kernel->terminate($request, $response);
+
+That's it! To test it, you can start the built-in web server:
+
+.. code-block:: bash
+
+ $ php -S localhost:8000
+
+Then see the JSON response in your browser:
+
+> http://localhost:8000/random/10
+
+The Methods of a "Micro" Kernel
+-------------------------------
+
+When you use the ``MicroKernelTrait``, your kernel needs to have exactly three methods
+that define your bundles, your services and your routes:
+
+**registerBundles()**
+ This is the same ``registerBundles()`` that you see in a normal kernel.
+
+**configureContainer(ContainerBuilder $c, LoaderInterface $loader)**
+ This methods builds and configures the container. In practice, you will use
+ ``loadFromExtension`` to configure different bundles (this is the equivalent
+ of what you see in a normal ``config.yml`` file). You can also register services
+ directly in PHP or load external configuration files (shown below).
+
+**configureRoutes(RouteCollectionBuilder $routes)**
+ Your job in this method is to add routes to the application. The ``RouteCollectionBuilder``
+ is new in Symfony 2.8 and has methods that make adding routes in PHP more fun.
+ You can also load external routing files (shown below).
+
+
+Advanced Example: Twig, Annotations and the Web Debug Toolbar
+-------------------------------------------------------------
+
+The purpose of the ``MicroKernelTrait`` is *not* to have a single-file application.
+Instead, its goal to give you the power to choose your bundles and structure.
+
+First, you'll probably want to put your PHP classes in an ``src/`` directory. Configure
+your ``composer.json`` file to load from there:
+
+.. code-block:: json
+
+ {
+ "require": {
+ "...": "..."
+ },
+ "autoload": {
+ "psr-4": {
+ "": "src/"
+ }
+ }
+ }
+
+Now, suppose you want to use Twig and load routes via annotations. For annotation
+routing, you need SensioFrameworkExtraBundle. This comes with a normal Symfony project.
+But in this case, you need to download it:
+
+.. code-block:: bash
+
+ $ composer require sensio/framework-extra-bundle
+
+Instead of putting *everything* in ``index.php``, create a new ``app/AppKernel.php``
+to hold the kernel. Now it looks like this::
+
+ // app/AppKernel.php
+
+ use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
+ use Symfony\Component\Config\Loader\LoaderInterface;
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\HttpKernel\Kernel;
+ use Symfony\Component\Routing\RouteCollectionBuilder;
+ use Doctrine\Common\Annotations\AnnotationRegistry;
+
+ // require Composer's autoloader
+ $loader = require __DIR__.'/../vendor/autoload.php';
+ // auto-load annotations
+ AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
+
+ class AppKernel extends Kernel
+ {
+ use MicroKernelTrait;
+
+ public function registerBundles()
+ {
+ $bundles = array(
+ new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
+ new Symfony\Bundle\TwigBundle\TwigBundle(),
+ new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle()
+ );
+
+ if ($this->getEnvironment() == 'dev') {
+ $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
+ }
+
+ return $bundles;
+ }
+
+ protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
+ {
+ $loader->load(__DIR__.'/config/config.yml');
+
+ // configure WebProfilerBundle only if the bundle is enabled
+ if (isset($this->bundles['WebProfilerBundle'])) {
+ $c->loadFromExtension('web_profiler', array(
+ 'toolbar' => true,
+ 'intercept_redirects' => false,
+ ));
+ }
+ }
+
+ protected function configureRoutes(RouteCollectionBuilder $routes)
+ {
+ // import the WebProfilerRoutes, only if the bundle is enabled
+ if (isset($this->bundles['WebProfilerBundle'])) {
+ $routes->mount('/_wdt', $routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml'));
+ $routes->mount('/_profiler', $routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml'));
+ }
+
+ // load the annotation routes
+ $routes->mount(
+ '/',
+ $routes->import(__DIR__.'/../src/App/Controller/', 'annotation')
+ );
+ }
+ }
+
+Unlike the previous kernel, this loads an external ``app/config/config.yml`` file,
+because the configuration started to get bigger:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ framework:
+ secret: S0ME_SECRET
+ templating:
+ engines: ['twig']
+ profiler: { only_exceptions: false }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+ twig
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('framework', array(
+ 'secret' => 'S0ME_SECRET',
+ 'templating' => array(
+ 'engines' => array('twig'),
+ ),
+ 'profiler' => array(
+ 'only_exceptions' => false,
+ ),
+ ));
+
+This also loads annotation routes from an ``src/App/Controller/`` directory, which
+has one file in it::
+
+ // src/App/Controller/MicroController.php
+ namespace App\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+
+ class MicroController extends Controller
+ {
+ /**
+ * @Route("/random/{limit}")
+ */
+ public function randomAction($limit)
+ {
+ $number = rand(0, $limit);
+
+ return $this->render('micro/random.html.twig', array(
+ 'number' => $number
+ ));
+ }
+ }
+
+Template files should live in the ``Resources/views`` directory of whatever directory
+your *kernel* lives in. Since ``AppKernel`` lives in ``app/``, this template lives
+at ``app/Resources/views/micro/random.html.twig``.
+
+Finally, you need a front controller to boot and run the application. Create a
+``web/index.php``:
+
+ // web/index.php
+
+ use Symfony\Component\HttpFoundation\Request;
+
+ require __DIR__.'/../app/AppKernel.php';
+
+ $kernel = new AppKernel('dev', true);
+ $request = Request::createFromGlobals();
+ $response = $kernel->handle($request);
+ $response->send();
+ $kernel->terminate($request, $response);
+
+That's it! This ``/random/10`` URL will work, Twig will render, and you'll even
+get the web debug toolbar to show up at the bottom. The final structure looks like
+this:
+
+.. code-block:: text
+
+ your-project/
+ ├─ app/
+ | ├─ AppKernel.php
+ │ ├─ cache/
+ │ ├─ config/
+ │ ├─ logs/
+ │ └─ Resources
+ | └─ views
+ | ├─ base.html.twig
+ | └─ micro
+ | └─ random.html.twig
+ ├─ src/
+ │ └─ App
+ | └─ Controller
+ | └─ MicroController.php
+ ├─ vendor/
+ │ └─ ...
+ ├─ web/
+ | └─ index.php
+ ├─ composer.json
+ └─ composer.lock
+
+Hey, that looks a lot like a *traditional* Symfony application! You're right: the
+``MicroKernelTrait`` *is* still Symfony: but you can control your structure and
+features quite easily.
diff --git a/cookbook/controller/upload_file.rst b/cookbook/controller/upload_file.rst
index 065b4bbaf0a..43cb18a4d5f 100644
--- a/cookbook/controller/upload_file.rst
+++ b/cookbook/controller/upload_file.rst
@@ -57,6 +57,7 @@ Then, add a new ``brochure`` field to the form that manages the ``Product`` enti
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\FileType;
class ProductType extends AbstractType
{
@@ -64,7 +65,7 @@ Then, add a new ``brochure`` field to the form that manages the ``Product`` enti
{
$builder
// ...
- ->add('brochure', 'file', array('label' => 'Brochure (PDF file)'))
+ ->add('brochure', FileType::class, array('label' => 'Brochure (PDF file)'))
// ...
;
}
@@ -116,7 +117,7 @@ Finally, you need to update the code of the controller that handles the form::
public function newAction(Request $request)
{
$product = new Product();
- $form = $this->createForm(new ProductType(), $product);
+ $form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isValid()) {
diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst
index 062969242c1..9e19eb205f5 100644
--- a/cookbook/doctrine/registration_form.rst
+++ b/cookbook/doctrine/registration_form.rst
@@ -157,15 +157,18 @@ Next, create the form for the ``User`` entity::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\EmailType;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('email', 'email')
- ->add('username', 'text')
- ->add('plainPassword', 'repeated', array(
+ ->add('email', EmailType::class)
+ ->add('username', TextType::class)
+ ->add('plainPassword', RepeatedType::class, array(
'type' => 'password',
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
@@ -213,7 +216,7 @@ controller for displaying the registration form::
{
// 1) build the form
$user = new User();
- $form = $this->createForm(new UserType(), $user);
+ $form = $this->createForm(UserType::class, $user);
// 2) handle the submit (will only happen on POST)
$form->handleRequest($request);
@@ -368,15 +371,17 @@ To do this, add a ``termsAccepted`` field to your form, but set its
// src/AppBundle/Form/UserType.php
// ...
use Symfony\Component\Validator\Constraints\IsTrue;
+ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
+ use Symfony\Component\Form\Extension\Core\Type\EmailType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('email', 'email');
+ ->add('email', EmailType::class);
// ...
- ->add('termsAccepted', 'checkbox', array(
+ ->add('termsAccepted', CheckboxType::class, array(
'mapped' => false,
'constraints' => new IsTrue(),
))
diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst
index ab933f6e8ea..b95e1d70683 100644
--- a/cookbook/form/create_custom_field_type.rst
+++ b/cookbook/form/create_custom_field_type.rst
@@ -25,6 +25,7 @@ for form fields, which is ``\Form\Type``. Make sure the field extend
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class GenderType extends AbstractType
{
@@ -40,12 +41,7 @@ for form fields, which is ``\Form\Type``. Make sure the field extend
public function getParent()
{
- return 'choice';
- }
-
- public function getName()
- {
- return 'gender';
+ return ChoiceType::class;
}
}
@@ -54,8 +50,12 @@ for form fields, which is ``\Form\Type``. Make sure the field extend
The location of this file is not important - the ``Form\Type`` directory
is just a convention.
+.. versionadded:: 2.8
+ In 2.8, the ``getName()`` method was removed. Now, fields are always referred
+ to by their fully-qualified class name.
+
Here, the return value of the ``getParent`` function indicates that you're
-extending the ``choice`` field type. This means that, by default, you inherit
+extending the ``ChoiceType`` field. This means that, by default, you inherit
all of the logic and rendering of that field type. To see some of the logic,
check out the `ChoiceType`_ class. There are three methods that are particularly
important:
@@ -89,10 +89,6 @@ important:
Also, if you need to modify the "view" of any of your child types from
your parent type, use the ``finishView()`` method.
-The ``getName()`` method returns an identifier which should be unique in
-your application. This is used in various places, such as when customizing
-how your form type will be rendered.
-
The goal of this field was to extend the choice type to enable selection of
a gender. This is achieved by fixing the ``choices`` to a list of possible
genders.
@@ -100,13 +96,19 @@ genders.
Creating a Template for the Field
---------------------------------
-Each field type is rendered by a template fragment, which is determined in
-part by the value of your ``getName()`` method. For more information, see
+Each field type is rendered by a template fragment, which is determined in part by
+the class name of your type. For more information, see
:ref:`cookbook-form-customization-form-themes`.
-In this case, since the parent field is ``choice``, you don't *need* to do
-any work as the custom field type will automatically be rendered like a ``choice``
-type. But for the sake of this example, suppose that when your field is "expanded"
+.. note::
+
+ The first part of the prefix (e.g. ``gender``) comes from the class name
+ (``GenderType`` -> ``gender``). This can be controlled by overriding ``getBlockPrefix()``
+ in ``GenderType``.
+
+In this case, since the parent field is ``ChoiceType``, you don't *need* to do
+any work as the custom field type will automatically be rendered like a ``ChoiceType``.
+But for the sake of this example, suppose that when your field is "expanded"
(i.e. radio buttons or checkboxes, instead of a select field), you want to
always render it in a ``ul`` element. In your form theme template (see above
link for details), create a ``gender_widget`` block to handle this:
@@ -154,7 +156,7 @@ link for details), create a ``gender_widget`` block to handle this:
.. note::
Make sure the correct widget prefix is used. In this example the name should
- be ``gender_widget``, according to the value returned by ``getName``.
+ be ``gender_widget`` (see :ref:`cookbook-form-customization-form-themes`).
Further, the main config file should point to the custom form template
so that it's used when rendering all forms.
@@ -241,18 +243,19 @@ new instance of the type in one of your forms::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
+ use AppBundle\Form\Type\GenderType;
class AuthorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
- $builder->add('gender_code', new GenderType(), array(
+ $builder->add('gender_code', GenderType::class, array(
'placeholder' => 'Choose a gender',
));
}
}
-But this only works because the ``GenderType()`` is very simple. What if
+But this only works because the ``GenderType`` is very simple. What if
the gender codes were stored in configuration or in a database? The next
section explains how more complex field types solve this problem.
@@ -311,14 +314,14 @@ the ``genders`` parameter value as the first argument to its to-be-created
arguments:
- "%genders%"
tags:
- - { name: form.type, alias: gender }
+ - { name: form.type }
.. code-block:: xml
%genders%
-
+
.. code-block:: php
@@ -331,9 +334,7 @@ the ``genders`` parameter value as the first argument to its to-be-created
'AppBundle\Form\Type\GenderType',
array('%genders%')
))
- ->addTag('form.type', array(
- 'alias' => 'gender',
- ))
+ ->addTag('form.type')
;
.. tip::
@@ -341,10 +342,8 @@ the ``genders`` parameter value as the first argument to its to-be-created
Make sure the services file is being imported. See :ref:`service-container-imports-directive`
for details.
-Be sure that the ``alias`` attribute of the tag corresponds with the value
-returned by the ``getName`` method defined earlier. You'll see the importance
-of this in a moment when you use the custom field type. But first, add a ``__construct``
-method to ``GenderType``, which receives the gender configuration::
+First, add a ``__construct`` method to ``GenderType``, which receives the gender
+configuration::
// src/AppBundle/Form/Type/GenderType.php
namespace AppBundle\Form\Type;
@@ -374,28 +373,28 @@ method to ``GenderType``, which receives the gender configuration::
}
Great! The ``GenderType`` is now fueled by the configuration parameters and
-registered as a service. Additionally, because you used the ``form.type`` alias in its
-configuration, using the field is now much easier::
+registered as a service. Because you used the ``form.type`` alias in its configuration,
+your service will be used instead of creating a *new* ``GenderType``. In other words,
+your controller *does not need to change*, it still looks like this::
// src/AppBundle/Form/Type/AuthorType.php
namespace AppBundle\Form\Type;
+ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
-
- // ...
+ use AppBundle\Form\Type\GenderType;
class AuthorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
- $builder->add('gender_code', 'gender', array(
+ $builder->add('gender_code', GenderType::class, array(
'placeholder' => 'Choose a gender',
));
}
}
-Notice that instead of instantiating a new instance, you can just refer to
-it by the alias used in your service configuration, ``gender``. Have fun!
+Have fun!
.. _`ChoiceType`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
.. _`FieldType`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php
diff --git a/cookbook/form/create_form_type_extension.rst b/cookbook/form/create_form_type_extension.rst
index ec22d8a94a3..df290c1e4f4 100644
--- a/cookbook/form/create_form_type_extension.rst
+++ b/cookbook/form/create_form_type_extension.rst
@@ -15,7 +15,7 @@ extensions come in.
Form type extensions have 2 main use-cases:
#. You want to add a **specific feature to a single type** (such
- as adding a "download" feature to the "file" field type);
+ as adding a "download" feature to the ``FileType`` field type);
#. You want to add a **generic feature to several types** (such as
adding a "help" text to every "input text"-like type).
@@ -51,6 +51,7 @@ class. In most cases, it's easier to extend the abstract class::
namespace AppBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
+ use Symfony\Component\Form\Extension\Core\Type\FileType;
class ImageTypeExtension extends AbstractTypeExtension
{
@@ -61,7 +62,7 @@ class. In most cases, it's easier to extend the abstract class::
*/
public function getExtendedType()
{
- return 'file';
+ return FileType::class;
}
}
@@ -105,14 +106,14 @@ tag:
app.image_type_extension:
class: AppBundle\Form\Extension\ImageTypeExtension
tags:
- - { name: form.type_extension, alias: file }
+ - { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FileType }
.. code-block:: xml
-
+
.. code-block:: php
@@ -122,11 +123,15 @@ tag:
'app.image_type_extension',
'AppBundle\Form\Extension\ImageTypeExtension'
)
- ->addTag('form.type_extension', array('alias' => 'file'));
+ ->addTag('form.type_extension', array('extended_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType'));
-The ``alias`` key of the tag is the type of field that this extension should
-be applied to. In your case, as you want to extend the ``file`` field type,
-you will use ``file`` as an alias.
+.. versionadded:: 2.8
+ The ``extended_type`` option is new in Symfony 2.8. Before, the option was
+ called ``alias``.
+
+The ``extended_type`` key of the tag is the type of field that this extension should
+be applied to. In your case, as you want to extend the ``Symfony\Component\Form\Extension\Core\Type\FileType``
+field type, you will use that as the ``extended_type``.
Adding the extension Business Logic
-----------------------------------
@@ -175,14 +180,14 @@ database)::
}
Your form type extension class will need to do two things in order to extend
-the ``file`` form type:
+the ``FileType::class`` form type:
#. Override the ``configureOptions`` method in order to add an ``image_path``
option;
#. Override the ``buildForm`` and ``buildView`` methods in order to pass the image
URL to the view.
-The logic is the following: when adding a form field of type ``file``,
+The logic is the following: when adding a form field of type ``FileType::class``,
you will be able to specify a new option: ``image_path``. This option will
tell the file field how to get the actual image URL in order to display
it in the view::
@@ -195,6 +200,7 @@ it in the view::
use Symfony\Component\Form\FormInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\FileType;
class ImageTypeExtension extends AbstractTypeExtension
{
@@ -205,7 +211,7 @@ it in the view::
*/
public function getExtendedType()
{
- return 'file';
+ return FileType::class;
}
/**
@@ -291,7 +297,7 @@ Specifically, you need to override the ``file_widget`` block:
Using the Form Type Extension
-----------------------------
-From now on, when adding a field of type ``file`` in your form, you can
+From now on, when adding a field of type ``FileType::class`` in your form, you can
specify an ``image_path`` option that will be used to display an image
next to the file field. For example::
@@ -300,19 +306,16 @@ next to the file field. For example::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\FileType;
class MediaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('name', 'text')
- ->add('file', 'file', array('image_path' => 'webPath'));
- }
-
- public function getName()
- {
- return 'media';
+ ->add('name', TextType::class)
+ ->add('file', FileType::class, array('image_path' => 'webPath'));
}
}
@@ -324,13 +327,13 @@ Generic Form Type Extensions
You can modify several form types at once by specifying their common parent
(:doc:`/reference/forms/types`). For example, several form types natively
-available in Symfony inherit from the ``text`` form type (such as ``email``,
-``search``, ``url``, etc.). A form type extension applying to ``text``
-(i.e. whose ``getExtendedType`` method returns ``text``) would apply to all of
-these form types.
+available in Symfony inherit from the ``TextType`` form type (such as ``email``,
+``SearchType``, ``UrlType``, etc.). A form type extension applying to ``TextType``
+(i.e. whose ``getExtendedType`` method returns ``TextType::class``) would apply
+to all of these form types.
In the same way, since **most** form types natively available in Symfony inherit
-from the ``form`` form type, a form type extension applying to ``form`` would
-apply to all of these. A notable exception are the ``button`` form types. Also
-keep in mind that a custom form type which extends neither the ``form`` nor
-the ``button`` type could always be created.
+from the ``FormType`` form type, a form type extension applying to ``FormType``
+would apply to all of these. A notable exception are the ``ButtonType`` form
+types. Also keep in mind that a custom form type which extends neither the
+``FormType`` nor the ``ButtonType`` type could always be created.
diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst
index 72af8108659..c63bce0e54f 100644
--- a/cookbook/form/data_transformers.rst
+++ b/cookbook/form/data_transformers.rst
@@ -26,13 +26,14 @@ Suppose you have a Task form with a description ``textarea`` type::
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
// ...
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
- $builder->add('description', 'textarea');
+ $builder->add('description', TextareaType::class);
}
public function configureOptions(OptionsResolver $resolver)
@@ -62,13 +63,14 @@ class::
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
// ...
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
- $builder->add('description', 'textarea');
+ $builder->add('description', TextareaType::class);
$builder->get('description')
->addModelTransformer(new CallbackTransformer(
@@ -104,8 +106,10 @@ in your code.
You can also add the transformer, right when adding the field by changing the format
slightly::
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+
$builder->add(
- $builder->create('description', 'textarea')
+ $builder->create('description', TextareaType::class)
->addModelTransformer(...)
);
@@ -123,14 +127,17 @@ Start by setting up the text field like normal::
// src/AppBundle/Form/TaskType.php
namespace AppBundle\Form\Type;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+
// ...
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('description', 'textarea')
- ->add('issue', 'text')
+ ->add('description', TextareaType::class)
+ ->add('issue', TextType::class)
;
}
@@ -247,13 +254,15 @@ Next, you need to instantiate the ``IssueToNumberTransformer`` class from inside
of the entity manager (because ``IssueToNumberTransformer`` needs this).
No problem! Just add a ``__construct()`` function to ``TaskType`` and force this
-to be passed in. Then, you can easily create and add the transformer::
+to be passed in by registering ``TaskType`` as a service.::
// src/AppBundle/Form/TaskType.php
namespace AppBundle\Form\Type;
use AppBundle\Form\DataTransformer\IssueToNumberTransformer;
use Doctrine\Common\Persistence\ObjectManager;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
// ...
class TaskType extends AbstractType
@@ -268,8 +277,8 @@ to be passed in. Then, you can easily create and add the transformer::
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('description', 'textarea')
- ->add('issue', 'text', array(
+ ->add('description', TextareaType::class)
+ ->add('issue', TextType::class, array(
// validation message if the data transformer fails
'invalid_message' => 'That is not a valid issue number',
));
@@ -283,18 +292,62 @@ to be passed in. Then, you can easily create and add the transformer::
// ...
}
-Now, when you create your ``TaskType``, you'll need to pass in the entity manager::
+Define the form type as a service in your configuration files.
- // e.g. in a controller somewhere
- $manager = $this->getDoctrine()->getManager();
- $form = $this->createForm(new TaskType($manager), $task);
+.. configuration-block::
- // ...
+ .. code-block:: yaml
-.. note::
+ # src/AppBundle/Resources/config/services.yml
+ services:
+ app.form.type.task:
+ class: AppBundle\Form\Type\TaskType
+ arguments: ["@doctrine.orm.entity_manager"]
+ tags:
+ - { name: form.type }
- To make this step easier (especially if ``TaskType`` is embedded into other
- form type classes), you might choose to :ref:`register your form type as a service `.
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // src/AppBundle/Resources/config/services.php
+ use AppBundle\Form\Type\TaskType;
+
+ $definition = new Definition(TaskType::class, array(
+ new Reference('doctrine.orm.entity_manager'),
+ ));
+ $container
+ ->setDefinition(
+ 'app.form.type.task',
+ $definition
+ )
+ ->addTag('form.type')
+ ;
+.. tip::
+
+ For more information about defining form types as services, read
+ :ref:`register your form type as a service `.
+
+Now, you can easily use your ``TaskType``::
+
+ // e.g. in a controller somewhere
+ $form = $this->createForm(TaskType::class, $task);
+
+ // ...
Cool, you're done! Your user will be able to enter an issue number into the
text field and it will be transformed back into an Issue object. This means
@@ -312,7 +365,7 @@ its error message can be controlled with the ``invalid_message`` field option.
// THIS IS WRONG - TRANSFORMER WILL BE APPLIED TO THE ENTIRE FORM
// see above example for correct code
- $builder->add('issue', 'text')
+ $builder->add('issue', TextType::class)
->addModelTransformer($transformer);
.. _using-transformers-in-a-custom-field-type:
@@ -360,12 +413,7 @@ First, create the custom field type class::
public function getParent()
{
- return 'text';
- }
-
- public function getName()
- {
- return 'issue_selector';
+ return TextType::class;
}
}
@@ -385,7 +433,7 @@ it's recognized as a custom field type:
class: AppBundle\Form\IssueSelectorType
arguments: ["@doctrine.orm.entity_manager"]
tags:
- - { name: form.type, alias: issue_selector }
+ - { name: form.type }
.. code-block:: xml
@@ -401,7 +449,7 @@ it's recognized as a custom field type:
-
+
@@ -421,9 +469,7 @@ it's recognized as a custom field type:
new Reference('doctrine.orm.entity_manager'),
)
)
- ->addTag('form.type', array(
- 'alias' => 'issue_selector',
- ))
+ ->addTag('form.type')
;
Now, whenever you need to use your special ``issue_selector`` field type,
@@ -433,6 +479,7 @@ it's quite easy::
namespace AppBundle\Form\Type;
use AppBundle\Form\DataTransformer\IssueToNumberTransformer;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
// ...
class TaskType extends AbstractType
@@ -440,8 +487,8 @@ it's quite easy::
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('description', 'textarea')
- ->add('issue', 'issue_selector')
+ ->add('description', TextareaType::class)
+ ->add('issue', IssueSelectorType::class)
;
}
diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst
index 4772ae871c2..6483ed73532 100644
--- a/cookbook/form/dynamic_form_modification.rst
+++ b/cookbook/form/dynamic_form_modification.rst
@@ -128,7 +128,7 @@ the event listener might look like the following::
// If no data is passed to the form, the data is "null".
// This should be considered a new "Product"
if (!$product || null === $product->getId()) {
- $form->add('name', 'text');
+ $form->add('name', TextType::class);
}
});
}
@@ -178,6 +178,7 @@ class::
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
class AddNameFieldSubscriber implements EventSubscriberInterface
{
@@ -194,7 +195,7 @@ class::
$form = $event->getForm();
if (!$product || null === $product->getId()) {
- $form->add('name', 'text');
+ $form->add('name', TextType::class);
}
}
}
@@ -224,24 +225,21 @@ Using an event listener, your form might look like this::
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class FriendMessageFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('subject', 'text')
- ->add('body', 'textarea')
+ ->add('subject', TextType::class)
+ ->add('body', TextareaType::class)
;
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
// ... add a choice list of friends of the current application user
});
}
-
- public function getName()
- {
- return 'friend_message';
- }
}
The problem is now to get the current user and create a choice field that
@@ -277,6 +275,10 @@ and fill in the listener logic::
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Doctrine\ORM\EntityRepository;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+
// ...
class FriendMessageFormType extends AbstractType
@@ -291,8 +293,8 @@ and fill in the listener logic::
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('subject', 'text')
- ->add('body', 'textarea')
+ ->add('subject', TextType::class)
+ ->add('body', TextareaType::class)
;
// grab the user, do a quick sanity check that one exists
@@ -323,7 +325,7 @@ and fill in the listener logic::
// create the field, this is similar the $builder->add()
// field name, field type, data, options
- $form->add('friend', 'entity', $formOptions);
+ $form->add('friend', EntityType::class, $formOptions);
}
);
}
@@ -339,44 +341,13 @@ and fill in the listener logic::
.. note::
The ``multiple`` and ``expanded`` form options will default to false
- because the type of the friend field is ``entity``.
+ because the type of the friend field is ``EntityType::class``.
Using the Form
~~~~~~~~~~~~~~
-Our form is now ready to use and there are two possible ways to use it inside
-of a controller:
-
-a) create it manually and remember to pass the token storage to it;
-
-or
-
-b) define it as a service.
-
-a) Creating the Form manually
-.............................
-
-This is very simple, and is probably the better approach unless you're using
-your new form type in many places or embedding it into other forms::
-
- class FriendMessageController extends Controller
- {
- public function newAction(Request $request)
- {
- $tokenStorage = $this->container->get('security.token_storage');
- $form = $this->createForm(
- new FriendMessageFormType($tokenStorage)
- );
-
- // ...
- }
- }
-
-b) Defining the Form as a Service
-.................................
-
-To define your form as a service, just create a normal service and then tag
-it with :ref:`dic-tags-form-type`.
+Our form is now ready to use. But first, because it has a ``__construct()`` method,
+you need to register it as a service and tag it with :ref:`form.type `:
.. configuration-block::
@@ -388,7 +359,7 @@ it with :ref:`dic-tags-form-type`.
class: AppBundle\Form\Type\FriendMessageFormType
arguments: ["@security.token_storage"]
tags:
- - { name: form.type, alias: friend_message }
+ - { name: form.type }
.. code-block:: xml
@@ -396,7 +367,7 @@ it with :ref:`dic-tags-form-type`.
-
+
@@ -404,18 +375,13 @@ it with :ref:`dic-tags-form-type`.
// app/config/config.php
$definition = new Definition('AppBundle\Form\Type\FriendMessageFormType');
- $definition->addTag('form.type', array('alias' => 'friend_message'));
+ $definition->addTag('form.type');
$container->setDefinition(
'app.form.friend_message',
$definition,
array('security.token_storage')
);
-If you wish to create it from within a service that has access to the form factory,
-you then use::
-
- $form = $formFactory->create('friend_message');
-
In a controller that extends the :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller`
class, you can simply call::
@@ -425,7 +391,7 @@ class, you can simply call::
{
public function newAction(Request $request)
{
- $form = $this->createForm('friend_message');
+ $form = $this->createForm(FriendMessageFormType::class);
// ...
}
@@ -436,7 +402,7 @@ You can also easily embed the form type into another form::
// inside some other "form type" class
public function buildForm(FormBuilderInterface $builder, array $options)
{
- $builder->add('message', 'friend_message');
+ $builder->add('message', FriendMessageFormType::class);
}
.. _cookbook-form-events-submitted-data:
@@ -462,6 +428,7 @@ sport like this::
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
+ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
// ...
class SportMeetupType extends AbstractType
@@ -469,7 +436,7 @@ sport like this::
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('sport', 'entity', array(
+ ->add('sport', EntityType::class, array(
'class' => 'AppBundle:Sport',
'placeholder' => '',
))
@@ -486,7 +453,7 @@ sport like this::
$sport = $data->getSport();
$positions = null === $sport ? array() : $sport->getAvailablePositions();
- $form->add('position', 'entity', array(
+ $form->add('position', EntityType::class, array(
'class' => 'AppBundle:Position',
'placeholder' => '',
'choices' => $positions,
@@ -533,6 +500,7 @@ The type would now look like::
// ...
use Symfony\Component\Form\FormInterface;
+ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use AppBundle\Entity\Sport;
class SportMeetupType extends AbstractType
@@ -540,7 +508,7 @@ The type would now look like::
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('sport', 'entity', array(
+ ->add('sport', EntityType::class, array(
'class' => 'AppBundle:Sport',
'placeholder' => '',
));
@@ -549,7 +517,7 @@ The type would now look like::
$formModifier = function (FormInterface $form, Sport $sport = null) {
$positions = null === $sport ? array() : $sport->getAvailablePositions();
- $form->add('position', 'entity', array(
+ $form->add('position', EntityType::class, array(
'class' => 'AppBundle:Position',
'placeholder' => '',
'choices' => $positions,
@@ -606,7 +574,7 @@ your application. Assume that you have a sport meetup creation controller::
public function createAction(Request $request)
{
$meetup = new SportMeetup();
- $form = $this->createForm(new SportMeetupType(), $meetup);
+ $form = $this->createForm(SportMeetupType::class, $meetup);
$form->handleRequest($request);
if ($form->isValid()) {
// ... save the meetup, redirect etc.
diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst
index e186f642f17..8779406f239 100644
--- a/cookbook/form/form_collections.rst
+++ b/cookbook/form/form_collections.rst
@@ -99,11 +99,6 @@ Then, create a form class so that a ``Tag`` object can be modified by the user::
'data_class' => 'AppBundle\Entity\Tag',
));
}
-
- public function getName()
- {
- return 'tag';
- }
}
With this, you have enough to render a tag form by itself. But since the end
@@ -119,6 +114,7 @@ Notice that you embed a collection of ``TagType`` forms using the
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class TaskType extends AbstractType
{
@@ -126,7 +122,9 @@ Notice that you embed a collection of ``TagType`` forms using the
{
$builder->add('description');
- $builder->add('tags', 'collection', array('entry_type' => new TagType()));
+ $builder->add('tags', CollectionType::class, array(
+ 'entry_type' => TagType::class
+ ));
}
public function configureOptions(OptionsResolver $resolver)
@@ -135,14 +133,9 @@ Notice that you embed a collection of ``TagType`` forms using the
'data_class' => 'AppBundle\Entity\Task',
));
}
-
- public function getName()
- {
- return 'task';
- }
}
-In your controller, you'll now initialize a new instance of ``TaskType``::
+In your controller, you'll create a new form from the ``TaskType``::
// src/AppBundle/Controller/TaskController.php
namespace AppBundle\Controller;
@@ -169,7 +162,7 @@ In your controller, you'll now initialize a new instance of ``TaskType``::
$task->getTags()->add($tag2);
// end dummy code
- $form = $this->createForm(new TaskType(), $task);
+ $form = $this->createForm(TaskType::class, $task);
$form->handleRequest($request);
@@ -284,8 +277,8 @@ add the ``allow_add`` option to your collection field::
{
$builder->add('description');
- $builder->add('tags', 'collection', array(
- 'entry_type' => new TagType(),
+ $builder->add('tags', CollectionType::class, array(
+ 'entry_type' => TagType::class,
'allow_add' => true,
));
}
@@ -453,7 +446,7 @@ Next, add a ``by_reference`` option to the ``tags`` field and set it to ``false`
{
// ...
- $builder->add('tags', 'collection', array(
+ $builder->add('tags', CollectionType::class, array(
// ...
'by_reference' => false,
));
@@ -569,7 +562,7 @@ you will learn about next!).
.. _cookbook-form-collections-remove:
Allowing Tags to be Removed
-----------------------------
+---------------------------
The next step is to allow the deletion of a particular item in the collection.
The solution is similar to allowing tags to be added.
@@ -583,7 +576,7 @@ Start by adding the ``allow_delete`` option in the form Type::
{
// ...
- $builder->add('tags', 'collection', array(
+ $builder->add('tags', CollectionType::class, array(
// ...
'allow_delete' => true,
));
@@ -696,7 +689,7 @@ the relationship between the removed ``Tag`` and ``Task`` object.
$originalTags->add($tag);
}
- $editForm = $this->createForm(new TaskType(), $task);
+ $editForm = $this->createForm(new TaskType::class, $task);
$editForm->handleRequest($request);
diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst
index b90f22455db..025297bee32 100644
--- a/cookbook/form/form_customization.rst
+++ b/cookbook/form/form_customization.rst
@@ -211,6 +211,9 @@ this folder.
In this example, the customized fragment name is ``integer_widget`` because
you want to override the HTML ``widget`` for all ``integer`` field types. If
you need to customize ``textarea`` fields, you would customize ``textarea_widget``.
+
+ The ``integer`` part comes from the class name: ``IntegerType`` becomes ``integer``,
+ based on a standard.
As you can see, the fragment name is a combination of the field type and
which part of the field is being rendered (e.g. ``widget``, ``label``,
@@ -698,12 +701,13 @@ field whose *id* is ``product_name`` (and name is ``product[name]``).
form type::
use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
- $builder->add('name', 'text', array(
+ $builder->add('name', TextType::class, array(
'block_name' => 'custom_name',
));
}
diff --git a/cookbook/form/inherit_data_option.rst b/cookbook/form/inherit_data_option.rst
index 7429baae325..f4e6fdf88d6 100644
--- a/cookbook/form/inherit_data_option.rst
+++ b/cookbook/form/inherit_data_option.rst
@@ -52,14 +52,15 @@ Start with building two forms for these entities, ``CompanyType`` and ``Customer
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
class CompanyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('name', 'text')
- ->add('website', 'text');
+ ->add('name', TextType::class)
+ ->add('website', TextType::class);
}
}
@@ -70,14 +71,15 @@ Start with building two forms for these entities, ``CompanyType`` and ``Customer
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
class CustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('firstName', 'text')
- ->add('lastName', 'text');
+ ->add('firstName', TextType::class)
+ ->add('lastName', TextType::class);
}
}
@@ -91,16 +93,18 @@ for that::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+ use Symfony\Component\Form\Extension\Core\Type\TextType;
class LocationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
- ->add('address', 'textarea')
- ->add('zipcode', 'text')
- ->add('city', 'text')
- ->add('country', 'text');
+ ->add('address', TextareaType::class)
+ ->add('zipcode', TextType::class)
+ ->add('city', TextType::class)
+ ->add('country', TextType::class);
}
public function configureOptions(OptionsResolver $resolver)
@@ -109,11 +113,6 @@ for that::
'inherit_data' => true
));
}
-
- public function getName()
- {
- return 'location';
- }
}
The location form has an interesting option set, namely ``inherit_data``. This
@@ -135,7 +134,7 @@ Finally, make this work by adding the location form to your two original forms::
{
// ...
- $builder->add('foo', new LocationType(), array(
+ $builder->add('foo', LocationType::class, array(
'data_class' => 'AppBundle\Entity\Company'
));
}
@@ -147,7 +146,7 @@ Finally, make this work by adding the location form to your two original forms::
{
// ...
- $builder->add('bar', new LocationType(), array(
+ $builder->add('bar', LocationType::class, array(
'data_class' => 'AppBundle\Entity\Customer'
));
}
diff --git a/cookbook/form/use_empty_data.rst b/cookbook/form/use_empty_data.rst
index 342341e5b7a..79026a573f0 100644
--- a/cookbook/form/use_empty_data.rst
+++ b/cookbook/form/use_empty_data.rst
@@ -15,11 +15,11 @@ your form. For example::
// $blog is passed in as the data, so the empty_data
// option is not needed
- $form = $this->createForm(new BlogType(), $blog);
+ $form = $this->createForm(BlogType::class, $blog);
// no data is passed in, so empty_data is
// used to get the "starting data"
- $form = $this->createForm(new BlogType());
+ $form = $this->createForm(BlogType::class);
}
By default, ``empty_data`` is set to ``null``. Or, if you have specified
@@ -61,10 +61,14 @@ that constructor with no arguments::
}
}
-You can instantiate your class however you want. In this example, we pass
-some dependency into the ``BlogType`` when we instantiate it, then use that
-to instantiate the ``Blog`` class. The point is, you can set ``empty_data``
-to the exact "new" object that you want to use.
+You can instantiate your class however you want. In this example, you pass
+some dependency into the ``BlogType`` then use that to instantiate the ``Blog`` class.
+The point is, you can set ``empty_data`` to the exact "new" object that you want to use.
+
+.. tip::
+
+ In order to pass arguments to the ``BlogType`` constructor, you'll need to
+ :ref:`register it as a service and tag with form.type `.
Option 2: Provide a Closure
---------------------------
diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc
index 6f48c8247ae..98496abd7be 100644
--- a/cookbook/map.rst.inc
+++ b/cookbook/map.rst.inc
@@ -30,6 +30,7 @@
* :doc:`/cookbook/configuration/index`
* :doc:`/cookbook/configuration/environments`
+ * :doc:`/cookbook/configuration/micro-kernel-trait`
* :doc:`/cookbook/configuration/override_dir_structure`
* :doc:`/cookbook/configuration/using_parameters_in_dic`
* :doc:`/cookbook/configuration/front_controllers_and_kernel`
@@ -161,6 +162,7 @@
* :doc:`/cookbook/security/form_login_setup`
* :doc:`/cookbook/security/entity_provider`
+ * :doc:`/cookbook/security/guard-authentication`
* :doc:`/cookbook/security/remember_me`
* :doc:`/cookbook/security/impersonating_user`
* :doc:`/cookbook/security/form_login`
@@ -191,6 +193,7 @@
* :doc:`/cookbook/service_container/index`
+ * :doc:`/cookbook/service_container/shared`
* :doc:`/cookbook/service_container/scopes`
* :doc:`/cookbook/service_container/compiler_passes`
* (Event Dispatcher) :doc:`/cookbook/event_dispatcher/event_listener`
diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst
index f2e9fc7e824..18027be259f 100644
--- a/cookbook/security/api_key_authentication.rst
+++ b/cookbook/security/api_key_authentication.rst
@@ -4,6 +4,11 @@
How to Authenticate Users with API Keys
=======================================
+.. tip::
+
+ Check out :doc:`/cookbook/security/guard-authentication` for a simpler and more
+ flexible way to accomplish custom authentication tasks like this.
+
Nowadays, it's quite usual to authenticate the user via an API key (when developing
a web service for instance). The API key is provided for every request and is
passed as a query string parameter or via an HTTP header.
@@ -11,8 +16,14 @@ passed as a query string parameter or via an HTTP header.
The API Key Authenticator
-------------------------
+.. versionadded:: 2.8
+ The ``SimplePreAuthenticatorInterface`` interface was moved to the
+ ``Symfony\Component\Security\Http\Authentication`` namespace in Symfony
+ 2.8. Prior to 2.8, it was located in the
+ ``Symfony\Component\Security\Core\Authentication`` namespace.
+
Authenticating a user based on the Request information should be done via a
-pre-authentication mechanism. The :class:`Symfony\\Component\\Security\\Core\\Authentication\\SimplePreAuthenticatorInterface`
+pre-authentication mechanism. The :class:`Symfony\\Component\\Security\\Http\\Authentication\\SimplePreAuthenticatorInterface`
allows you to implement such a scheme really easily.
Your exact situation may differ, but in this example, a token is read
@@ -22,13 +33,13 @@ value and then a User object is created::
// src/AppBundle/Security/ApiKeyAuthenticator.php
namespace AppBundle\Security;
- use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
+ use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
- use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
- use Symfony\Component\HttpFoundation\Request;
- use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
+ use Symfony\Component\Security\Core\User\UserProviderInterface;
+ use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
{
@@ -268,9 +279,9 @@ you can use to create an error ``Response``.
// src/AppBundle/Security/ApiKeyAuthenticator.php
namespace AppBundle\Security;
- use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
+ use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
@@ -501,8 +512,8 @@ for security reasons. To take advantage of the session, update ``ApiKeyAuthentic
to see if the stored token has a valid User object that can be used::
// src/AppBundle/Security/ApiKeyAuthenticator.php
- // ...
+ // ...
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
{
// ...
diff --git a/cookbook/security/csrf_in_login_form.rst b/cookbook/security/csrf_in_login_form.rst
index b7649aa92f7..5853bbec647 100644
--- a/cookbook/security/csrf_in_login_form.rst
+++ b/cookbook/security/csrf_in_login_form.rst
@@ -107,7 +107,9 @@ using the login form:
-