Skip to content

Latest commit

 

History

History
389 lines (290 loc) · 10.8 KB

UPGRADE-2.1.md

File metadata and controls

389 lines (290 loc) · 10.8 KB

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
    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) { /* ... */ }
        // ...
    }
    ```

### Form and Validator

  * 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 choices
    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 the old behavior. If you
    can guarantee that your choice values only contain ASCII letters, digits,
    colons and underscores, you can restore the old behavior by setting the
    `index_strategy` choice field option to `ChoiceList::COPY_CHOICE`.

  * The strategy for generating the `value` HTML attribute for choices in a
    choice field has changed.

    Instead of using the choice value, a generated integer is now stored. Again,
    take care if your JavaScript reads this value. If your choice field is a
    non-expanded single-choice field, or if the choices are guaranteed not to
    contain the empty string '' (which is the case when you added it manually
    or when the field is a single-choice field and is not required), you can
    restore the old behavior by setting the `value_strategy` choice field option
    to `ChoiceList::COPY_CHOICE`.

  * 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 methods `setMessage()`, `getMessageTemplate()` and
    `getMessageParameters()` in the Constraint class were deprecated.

    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 options passed to the `getParent()` method of form types no longer
    contain default options.

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

### 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()->*()`.

### 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