This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/UPGRADE-2.1.md
Fabien Potencier 85bb439356 merged branch bschussek/issue3878 (PR #3923)
Commits
-------

6e4ed9e [Form] Fixed regression: bind(null) was not converted to an empty string anymore
fcb2227 [Form] Deprecated FieldType, which has been merged into FormType
bfa7ef2 [Form] Removed obsolete exceptions
2a49449 [Form] Simplified CSRF mechanism and removed "csrf" type

Discussion
----------

[Form] Merged FieldType into FormType

Bug fix: no
Feature addition: no
Backwards compatibility break: yes
Symfony2 tests pass: yes
Fixes the following tickets: #3878
Todo: update the documentation on theming

![Travis Build Status](https://secure.travis-ci.org/bschussek/symfony.png?branch=issue3878)

This PR is a preparatory PR for #3879. See also #3878.

---------------------------------------------------------------------------

by juliendidier at 2012-04-13T14:25:19Z

What's the benefit ?

---------------------------------------------------------------------------

by henrikbjorn at 2012-04-13T14:26:40Z

why `input_widget` ? and not just `widget`

---------------------------------------------------------------------------

by Burgov at 2012-04-13T14:27:49Z

@juliendidier dynamic inheritance is now obsolete which fixes some other issues

---------------------------------------------------------------------------

by stloyd at 2012-04-13T14:37:26Z

What about __not__ breaking API so *badly* and leaving `FieldType` which will be simple like (with marking as deprecated):

```php
<?php

class FieldType extends AbstractType
{
    public function getParent(array $options)
    {
        return 'form';
    }

    public function getName()
    {
        return 'field';
    }
}

---------------------------------------------------------------------------

by bschussek at 2012-04-13T14:43:41Z

@stloyd That's a very good idea.

---------------------------------------------------------------------------

by mvrhov at 2012-04-13T17:41:21Z

IMHO what @stloyd proposed sounds like a good idea, but removing FieldType class, if #3903 will come into life might ensure that more forms will broke and people will check them thoroughly.

---------------------------------------------------------------------------

by r1pp3rj4ck at 2012-04-13T18:46:08Z

@bschussek looks great, but I'm concerned about how quickly will the third-party bundles adapt to this BC break. I hope really quick, because if they don't the whole stuff will be useless :S of course it's not your problem to solve.

---------------------------------------------------------------------------

by stof at 2012-04-13T18:50:32Z

@r1pp3rj4ck there is already another BC break requiring to update custom types for Symfony master. So third party bundles already have to do some work.

---------------------------------------------------------------------------

by r1pp3rj4ck at 2012-04-13T18:59:37Z

@stof which one? I've looked into @bschussek 's RFC about these [foo].bar stuff, but it's not yet implemented. Are you refering to this or another one I've missed?

---------------------------------------------------------------------------

by stof at 2012-04-13T19:04:06Z

@r1pp3rj4ck the change regarding default options

---------------------------------------------------------------------------

by r1pp3rj4ck at 2012-04-13T19:06:10Z

@stof oh, I forgot that one. Weird thing is that I've already changed my default options today and still forgetting these stuff :D

---------------------------------------------------------------------------

by bschussek at 2012-04-14T08:58:29Z

I restored and deprecated FieldType now. I'd appreciate further reviews.

---------------------------------------------------------------------------

by stloyd at 2012-04-14T09:02:32Z

Maybe we should try to avoid this BC in templates ? What do you think about similar move like with `FieldType` ? (hold old, but inside just render new)

---------------------------------------------------------------------------

by bschussek at 2012-04-14T09:07:22Z

@stloyd You mean for those cases where people explicitely render the block "field_*"? We can do that.

---------------------------------------------------------------------------

by stloyd at 2012-04-14T09:09:45Z

@bschussek Yes I mean this case =) Sorry for not being explicit, I need some coffee I think =)

---------------------------------------------------------------------------

by bschussek at 2012-04-17T14:45:35Z

I added the field_* blocks again for BC. Could someone please review again? Otherwise this can be merged.

---------------------------------------------------------------------------

by Burgov at 2012-04-17T15:11:16Z

@bschussek I'm not sure what has changed to cause this, but if I try out your branch on our forms, if I leave the value of an input empty, eventually the reverseTransform method receives a null value, rather than a '' (empty string) value, as on the current symfony master.

DateTimeToLocalizedStringTransformer, for example, will throw an Exception if the value is not a string

```php
if (!is_string($value)) {
   throw new UnexpectedTypeException($value, 'string');
}
```

Other than that, all forms render just the same as they do on symfony master

---------------------------------------------------------------------------

by bschussek at 2012-04-17T15:30:29Z

@Burgov Fixed.
2012-04-18 12:50:00 +02:00

17 KiB

UPGRADE FROM 2.0 to 2.1

General

  • The merging strategy for assets_base_urls and base_urls has changed.

    Unlike most configuration blocks, successive values for assets_base_urls will overwrite each other instead of being merged. This behavior was chosen because developers will typically define base URL's for each environment. Given that most projects tend to inherit configurations (e.g. config_test.yml imports config_dev.yml) and/or share a common base configuration (i.e. config.yml), merging could yield a set of base URL's for multiple environments.

HttpFoundation

  • Locale management was moved from the Session class to the Request class.

    Configuring the default locale

    Before:

    framework:
        session:
            default_locale: fr
    

    After:

    framework:
        default_locale: fr
    
  • The methods getPathInfo(), getBaseUrl() and getBasePath() of a Request now all return a raw value (vs a urldecoded value before). Any call to one of these methods must be checked and wrapped in a rawurldecode() if needed.

    Retrieving the locale from a Twig template

    Before: {{ app.request.session.locale }} or {{ app.session.locale }}

    After: {{ app.request.locale }}

    Retrieving the locale from a PHP template

    Before: $view['session']->getLocale()

    After: $view['request']->getLocale()

    Retrieving the locale from PHP code

    Before: $session->getLocale()

    After: $request->getLocale()

Security

  • Symfony\Component\Security\Core\User\UserInterface::equals() has moved to Symfony\Component\Security\Core\User\EquatableInterface::isEqualTo().

    You must rename the equals() method in your implementation of the User class to isEqualTo() and implement EquatableInterface. Apart from that, no other changes are required.

    Alternatively, you may use the default implementation provided by AbstractToken::hasUserChanged() if you have no need of custom comparison logic. In this case, do not implement EquatableInterface and remove your comparison method.

    Before:

    class User implements UserInterface
    {
        // ...
        public function equals(UserInterface $user) { /* ... */ }
        // ...
    }
    

    After:

    ```
    class User implements UserInterface, EquatableInterface
    {
        // ...
        public function isEqualTo(UserInterface $user) { /* ... */ }
        // ...
    }
    ```
  * The custom factories for the firewall configuration are now
    registered during the build method of bundles instead of being registered
    by the end-user. This means that you will you need to remove the 'factories' 
    keys in your security configuration.

  * The Firewall listener is now registered after the Router listener. This
    means that specific Firewall URLs (like /login_check and /logout) must now
    have proper routes defined in your routing configuration.

  * The user provider configuration has been refactored. The configuration
    for the chain provider and the memory provider has been changed:

     Before:

     ``` yaml
     security:
         providers:
             my_chain_provider:
                 providers: [my_memory_provider, my_doctrine_provider]
             my_memory_provider:
                 users:
                     toto: { password: foobar, roles: [ROLE_USER] }
                     foo: { password: bar, roles: [ROLE_USER, ROLE_ADMIN] }
     ```

     After:

     ``` yaml
     security:
         providers:
             my_chain_provider:
                 chain:
                     providers: [my_memory_provider, my_doctrine_provider]
             my_memory_provider:
                 memory:
                     users:
                         toto: { password: foobar, roles: [ROLE_USER] }
                         foo: { password: bar, roles: [ROLE_USER, ROLE_ADMIN] }
     ```

  * `MutableAclInterface::setParentAcl` now accepts `null`, review any
    implementations of this interface to reflect this change.

### Form

  * Child forms are no longer automatically validated. That means that you must
    explicitly set the `Valid` constraint in your model if you want to validate
    associated objects.

    If you don't want to set the `Valid` constraint, or if there is no reference
    from the data of the parent form to the data of the child form, you can
    enable BC behavior by setting the `cascade_validation` form option to `true`
    on the parent form.

  * Changed implementation of choice lists

    ArrayChoiceList was replaced. If you have custom classes that extend this
    class, you must now extend SimpleChoiceList.

    Before:

    ```
    class MyChoiceList extends ArrayChoiceList
    {
        protected function load()
        {
            parent::load();

            // load choices

            $this->choices = $choices;
        }
    }
    ```

    After:

    ```
    class MyChoiceList extends SimpleChoiceList
    {
        public function __construct()
        {
            // load choices

            parent::__construct($choices);
        }
    }
    ```

    If you need to load the choices lazily -- that is, as soon as they are
    accessed for the first time -- you can extend LazyChoiceList instead.

    ```
    class MyChoiceList extends LazyChoiceList
    {
        protected function loadChoiceList()
        {
            // load choices

            return new SimpleChoiceList($choices);
        }
    }
    ```

    PaddedChoiceList, MonthChoiceList and TimezoneChoiceList were removed.
    Their functionality was merged into DateType, TimeType and TimezoneType.

    EntityChoiceList was adapted. The methods `getEntities()`,
    `getEntitiesByKeys()`, `getIdentifier()` and `getIdentifierValues()` were
    removed or made private. Instead of the first two, you can now use
    `getChoices()` and `getChoicesByValues()`. For the latter two, no
    replacement exists.

  * The strategy for generating the `id` and `name` HTML attributes for
    checkboxes and radio buttons in a choice field has changed.

    Instead of appending the choice value, a generated integer is now appended
    by default. Take care if your JavaScript relies on that.

  * In the choice field type's template, the structure of the `choices` variable
    has changed.

    The `choices` variable now contains ChoiceView objects with two getters,
    `getValue()` and `getLabel()`, to access the choice data. The indices of the
    array are controlled by the choice field's `index_generation` option.

    Before:

    ```
    {% for choice, label in choices %}
        <option value="{{ choice }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
            {{ label }}
        </option>
    {% endfor %}
    ```

    After:

    ```
    {% for choice in choices %}
        <option value="{{ choice.value }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
            {{ choice.label }}
        </option>
    {% endfor %}
    ```

  * In the collection type's template, the default name of the prototype field
    has changed from `$$name$$` to `__name__`.

    For custom names, dollar signs are no longer prepended and appended. You are
    advised to prepend and append two underscores wherever you specify a value
    for the field's `prototype_name` option.

    Before:

    ```
    $builder->add('tags', 'collection', array('prototype' => 'proto'));

    // results in the name "$$proto$$" in the template
    ```

    After:

    ```
    $builder->add('tags', 'collection', array('prototype' => '__proto__'));

    // results in the name "__proto__" in the template
    ```

  * The `read_only` field attribute now renders as `readonly="readonly"`, use
    `disabled` instead for `disabled="disabled"`.

  * Form and field names must now start with a letter, digit or underscore
    and only contain letters, digits, underscores, hyphens and colons

  * `EntitiesToArrayTransformer` and `EntityToIdTransformer` have been removed.
    The former has been replaced by `CollectionToArrayTransformer` in combination
    with `EntityChoiceList`, the latter is not required in the core anymore.

  * The following transformers have been renamed:

      * `ArrayToBooleanChoicesTransformer` to `ChoicesToBooleanArrayTransformer`
      * `ScalarToBooleanChoicesTransformer` to `ChoiceToBooleanArrayTransformer`
      * `ArrayToChoicesTransformer` to `ChoicesToValuesTransformer`
      * `ScalarToChoiceTransformer` to `ChoiceToValueTransformer`

    to be consistent with the naming in `ChoiceListInterface`.

  * `FormUtil::toArrayKey()` and `FormUtil::toArrayKeys()` have been removed.
    They were merged into ChoiceList and have no public equivalent anymore.

  * The options passed to the `getParent()` method of form types no longer
    contain default options. They only contain the options passed by the user.

    You should check if options exist before attempting to read their value.

    Before:

    ```
    public function getParent(array $options)
    {
        return 'single_text' === $options['widget'] ? 'text' : 'choice';
    }
    ```

    After:

    ```
    public function getParent(array $options)
    {
        return isset($options['widget']) && 'single_text' === $options['widget'] ? 'text' : 'choice';
    }
    ```
    
  * The methods `getDefaultOptions()` and `getAllowedOptionValues()` of form
    types no longer receive an option array.
    
    You can specify options that depend on other options using closures instead.
    
    Before:
    
    ```
    public function getDefaultOptions(array $options)
    {
        $defaultOptions = array();
        
        if ($options['multiple']) {
            $defaultOptions['empty_data'] = array();
        }
        
        return $defaultOptions;
    }
    ```
    
    After:
    
    ```
    public function getDefaultOptions()
    {
        return array(
            'empty_data' => function (Options $options, $previousValue) {
                return $options['multiple'] ? array() : $previousValue;
            }
        );
    }
    ```
    
    The second argument `$previousValue` does not have to be specified if not
    needed.

  * The `add()`, `remove()`, `setParent()`, `bind()` and `setData()` methods in
    the Form class now throw an exception if the form is already bound.

    If you used these methods on bound forms, you should consider moving your
    logic to an event listener that observes one of the following events:
    `FormEvents::PRE_BIND`, `FormEvents::BIND_CLIENT_DATA` or
    `FormEvents::BIND_NORM_DATA`.

  * The interface FormValidatorInterface was deprecated and will be removed
    in Symfony 2.3.

    If you implemented custom validators using this interface, you can
    substitute them by event listeners listening to the FormEvents::POST_BIND
    (or any other of the BIND events). In case you used the CallbackValidator
    class, you should now pass the callback directly to `addEventListener`.

  * simplified CSRF protection and removed the csrf type

  * deprecated FieldType and merged it into FormType

  * [BC BREAK] renamed "field_*" theme blocks to "form_*" and "field_widget" to
    "input"

### Validator

  * The methods `setMessage()`, `getMessageTemplate()` and
    `getMessageParameters()` in the Constraint class were deprecated and will
    be removed in Symfony 2.3.

    If you have implemented custom validators, you should use the
    `addViolation()` method on the `ExecutionContext` object instead.

    Before:

    ```
    public function isValid($value, Constraint $constraint)
    {
        // ...
        if (!$valid) {
            $this->setMessage($constraint->message, array(
                '{{ value }}' => $value,
            ));

            return false;
        }
    }
    ```

    After:

    ```
    public function isValid($value, Constraint $constraint)
    {
        // ...
        if (!$valid) {
            $this->context->addViolation($constraint->message, array(
                '{{ value }}' => $value,
            ));

            return false;
        }
    }
    ```

  * The method `setPropertyPath()` in the ExecutionContext class
    was removed.

    You should use the `addViolationAtSubPath()` method on the
    `ExecutionContext` object instead.

    Before:

    ```
    public function isPropertyValid(ExecutionContext $context)
    {
        // ...
        $propertyPath = $context->getPropertyPath() . '.property';
        $context->setPropertyPath($propertyPath);
        $context->addViolation('Error Message', array(), null);
    }
    ```

    After:

    ```
    public function isPropertyValid(ExecutionContext $context)
    {
        // ...
        $context->addViolationAtSubPath('property', 'Error Message', array(), null);

    }
    ```

  * The method `isValid` of `ConstraintValidatorInterface` was renamed to
    `validate` and its return value was dropped.

    `ConstraintValidator` still contains the deprecated `isValid` method and
    forwards `validate` calls to `isValid` by default. This BC layer will be 
    removed in Symfony 2.3. You are advised to rename your methods. You should
    also remove the return values, which have never been used by the framework.

    Before:

    ```
    public function isValid($value, Constraint $constraint)
    {
        // ...
        if (!$valid) {
            $this->context->addViolation($constraint->message, array(
                '{{ value }}' => $value,
            ));

            return false;
        }
    }
    ```

    After:

    ```
    public function validate($value, Constraint $constraint)
    {
        // ...
        if (!$valid) {
            $this->context->addViolation($constraint->message, array(
                '{{ value }}' => $value,
            ));
            
            return;
        }
    }
    ```

  * Core translation messages are changed. Dot is added at the end of each message.
    Overwritten core translations should be fixed if any. More info here.

### Session

  * Flash messages now return an array based on their type. The old method is
    still available but is now deprecated.

    ##### Retrieving the flash messages from a Twig template

    Before:

    ```
    {% if app.session.hasFlash('notice') %}
        <div class="flash-notice">
            {{ app.session.getFlash('notice') }}
        </div>
    {% endif %}
    ```
    After:

    ```
    {% for flashMessage in app.session.flashbag.get('notice') %}
        <div class="flash-notice">
            {{ flashMessage }}
        </div>
    {% endfor %}
    ```

    You can process all flash messages in a single loop with:

    ```
    {% for type, flashMessages in app.session.flashbag.all() %}
        {% for flashMessage in flashMessages %}
            <div class="flash-{{ type }}">
                {{ flashMessage }}
            </div>
        {% endfor %}
    {% endfor %}
    ```

  * Session handler drivers should implement `\SessionHandlerInterface` or extend from
    `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeHandlerInterface` base class and renamed
    to `Handler\FooSessionHandler`.  E.g. `PdoSessionStorage` becomes `Handler\PdoSessionHandler`.

  * Refactor code using `$session->*flash*()` methods to use `$session->getFlashBag()->*()`.

### Serializer

 * The key names craeted by the  `GetSetMethodNormalizer` have changed from
    from all lowercased to camelCased (e.g. `mypropertyvalue` to `myPropertyValue`).

 * The `item` element is now converted to an array when deserializing XML.

    ``` xml
    <?xml version="1.0"?>
    <response>
        <item><title><![CDATA[title1]]></title></item><item><title><![CDATA[title2]]></title></item>
    </response>
    ```

    Before:

        Array()

    After:

        Array(
            [item] => Array(
                [0] => Array(
                    [title] => title1
                )
                [1] => Array(
                    [title] => title2
                )
            )
        )

### Routing

  * The UrlMatcher urldecodes the route parameters only once, they were
    decoded twice before. Note that the `urldecode()` calls have been changed for a
    single `rawurldecode()` in order to support `+` for input paths.

### FrameworkBundle

  * session options: lifetime, path, domain, secure, httponly were deprecated.
    Prefixed versions should now be used instead: cookie_lifetime, cookie_path, cookie_domain, cookie_secure, cookie_httponly

  Before:

framework:
    session:
        lifetime:   3600
        path:       \
        domain:     example.com
        secure:     true
        httponly:   true

After:

framework:
    session:
        cookie_lifetime:   3600
        cookie_path:       \
        cookie_domain:     example.com
        cookie_secure:     true
        cookie_httponly:   true

Added `handler_id`, defaults to `session.handler.native_file`.

 framework:
     session:
         storage_id: session.storage.native
         handler_id: session.handler.native_file

To use mock session storage use the following.  `handler_id` is irrelevant in this context.

 framework:
     session:
         storage_id: session.storage.mock_file

### WebProfilerBundle

* You must clear old profiles after upgrading to 2.1. If you are using a
  database then you will need to remove the table.