Grammar and formatting in upgrade doc
Added logical component headings to changes. Grouped changes by bullets, with indented text and code blocks. Applied consistent formatting to method names and code references. Re-flowed paragraph text to abide an 80-character column.
This commit is contained in:
parent
b80951c21c
commit
cfddbba4af
434
UPGRADE-2.1.md
434
UPGRADE-2.1.md
|
@ -3,299 +3,341 @@ UPGRADE FROM 2.0 to 2.1
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
* assets_base_urls and base_urls merging strategy has changed
|
* The merging strategy for `assets_base_urls` and `base_urls` has changed.
|
||||||
|
|
||||||
Unlike most configuration blocks, successive values for
|
Unlike most configuration blocks, successive values for `assets_base_urls`
|
||||||
``assets_base_urls`` will overwrite each other instead of being merged.
|
will overwrite each other instead of being merged. This behavior was chosen
|
||||||
This behavior was chosen because developers will typically define base
|
because developers will typically define base URL's for each environment.
|
||||||
URL's for each environment. Given that most projects tend to inherit
|
Given that most projects tend to inherit configurations (e.g.
|
||||||
configurations (e.g. ``config_test.yml`` imports ``config_dev.yml``)
|
`config_test.yml` imports `config_dev.yml`) and/or share a common base
|
||||||
and/or share a common base configuration (i.e. ``config.yml``), merging
|
configuration (i.e. `config.yml`), merging could yield a set of base URL's
|
||||||
could yield a set of base URL's for multiple environments.
|
for multiple environments.
|
||||||
|
|
||||||
### [HttpFoundation]
|
### HttpFoundation
|
||||||
|
|
||||||
* moved management of the locale from the Session class to the Request class
|
* Locate management was moved from the Session class to the Request class.
|
||||||
|
|
||||||
Configuring the default locale:
|
##### Configuring the default locale
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
framework:
|
```
|
||||||
session:
|
framework:
|
||||||
default_locale: fr
|
session:
|
||||||
|
default_locale: fr
|
||||||
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
framework:
|
```
|
||||||
default_locale: fr
|
framework:
|
||||||
|
default_locale: fr
|
||||||
|
```
|
||||||
|
|
||||||
Retrieving the locale from a Twig template:
|
##### Retrieving the locale from a Twig template
|
||||||
|
|
||||||
Before: `{{ app.request.session.locale }}` or `{{ app.session.locale }}`
|
Before: `{{ app.request.session.locale }}` or `{{ app.session.locale }}`
|
||||||
|
|
||||||
After: `{{ app.request.locale }}`
|
After: `{{ app.request.locale }}`
|
||||||
|
|
||||||
Retrieving the locale from a PHP template:
|
##### Retrieving the locale from a PHP template
|
||||||
|
|
||||||
Before: `$view['session']->getLocale()`
|
Before: `$view['session']->getLocale()`
|
||||||
|
|
||||||
After: `$view['request']->getLocale()`
|
After: `$view['request']->getLocale()`
|
||||||
|
|
||||||
Retrieving the locale from PHP code:
|
##### Retrieving the locale from PHP code
|
||||||
|
|
||||||
Before: `$session->getLocale()`
|
Before: `$session->getLocale()`
|
||||||
|
|
||||||
After: `$request->getLocale()`
|
After: `$request->getLocale()`
|
||||||
|
|
||||||
* Method `equals` of `Symfony\Component\Security\Core\User\UserInterface` has
|
### Security
|
||||||
moved to `Symfony\Component\Security\Core\User\EquatableInterface`.
|
|
||||||
|
|
||||||
You have to change the name of the `equals` function in your implementation
|
* `Symfony\Component\Security\Core\User\UserInterface::equals()` has moved to
|
||||||
of the `User` class to `isEqualTo` and implement `EquatableInterface`.
|
`Symfony\Component\Security\Core\User\EquatableInterface::isEqualTo()`.
|
||||||
Apart from that, no other changes are required to make it behave as before.
|
|
||||||
Alternatively, you can use the default implementation provided
|
You must rename the `equals()` method in your implementation of the `User`
|
||||||
by `AbstractToken:hasUserChanged` if you do not need any custom comparison logic.
|
class to `isEqualTo()` and implement `EquatableInterface`. Apart from that,
|
||||||
In this case do not implement the interface and remove your comparison function.
|
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:
|
Before:
|
||||||
|
|
||||||
class User implements UserInterface
|
```
|
||||||
{
|
class User implements UserInterface
|
||||||
// ...
|
{
|
||||||
public function equals(UserInterface $user) { /* ... */ }
|
// ...
|
||||||
// ...
|
public function equals(UserInterface $user) { /* ... */ }
|
||||||
}
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
class User implements UserInterface, EquatableInterface
|
```
|
||||||
{
|
class User implements UserInterface, EquatableInterface
|
||||||
// ...
|
{
|
||||||
public function isEqualTo(UserInterface $user) { /* ... */ }
|
// ...
|
||||||
// ...
|
public function isEqualTo(UserInterface $user) { /* ... */ }
|
||||||
}
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
* Form children aren't automatically validated anymore. That means that you
|
### Form and Validator
|
||||||
explicitely need to set the `Valid` constraint in your model if you want to
|
|
||||||
validate associated objects.
|
* 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
|
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
|
from the data of the parent form to the data of the child form, you can
|
||||||
enable BC behaviour by setting the option "cascade_validation" to `true` on
|
enable BC behavior by setting the `cascade_validation` form option to `true`
|
||||||
the parent form.
|
on the parent form.
|
||||||
|
|
||||||
* Changed implementation of choice lists
|
* Changed implementation of choice lists
|
||||||
|
|
||||||
ArrayChoiceList was replaced. If you have custom classes that extend
|
ArrayChoiceList was replaced. If you have custom classes that extend this
|
||||||
this class, you can now extend SimpleChoiceList.
|
class, you must now extend SimpleChoiceList.
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
class MyChoiceList extends ArrayChoiceList
|
```
|
||||||
|
class MyChoiceList extends ArrayChoiceList
|
||||||
|
{
|
||||||
|
protected function load()
|
||||||
{
|
{
|
||||||
protected function load()
|
parent::load();
|
||||||
{
|
|
||||||
parent::load();
|
// load choices
|
||||||
|
|
||||||
// load choices
|
$this->choices = $choices;
|
||||||
|
|
||||||
$this->choices = $choices;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
class MyChoiceList extends SimpleChoiceList
|
```
|
||||||
|
class MyChoiceList extends SimpleChoiceList
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
{
|
{
|
||||||
public function __construct()
|
// load choices
|
||||||
{
|
|
||||||
// load choices
|
parent::__construct($choices);
|
||||||
|
|
||||||
parent::__construct($choices);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If you need to load the choices lazily - that is, as soon as they are
|
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.
|
accessed for the first time -- you can extend LazyChoiceList instead.
|
||||||
|
|
||||||
class MyChoiceList extends LazyChoiceList
|
```
|
||||||
|
class MyChoiceList extends LazyChoiceList
|
||||||
|
{
|
||||||
|
protected function loadChoiceList()
|
||||||
{
|
{
|
||||||
protected function loadChoiceList()
|
// load choices
|
||||||
{
|
|
||||||
// load choices
|
return new SimpleChoiceList($choices);
|
||||||
|
|
||||||
return new SimpleChoiceList($choices);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
PaddedChoiceList, MonthChoiceList and TimezoneChoiceList were removed.
|
PaddedChoiceList, MonthChoiceList and TimezoneChoiceList were removed.
|
||||||
Their functionality was merged into DateType, TimeType and
|
Their functionality was merged into DateType, TimeType and TimezoneType.
|
||||||
TimezoneType.
|
|
||||||
|
|
||||||
EntityChoiceList was adapted. The methods `getEntities`,
|
EntityChoiceList was adapted. The methods `getEntities()`,
|
||||||
`getEntitiesByKeys`, `getIdentifier` and `getIdentifierValues` were
|
`getEntitiesByKeys()`, `getIdentifier()` and `getIdentifierValues()` were
|
||||||
removed/made private. Instead of the first two, you can now use
|
removed or made private. Instead of the first two, you can now use
|
||||||
`getChoices` and `getChoicesByValues`. For the latter two, no
|
`getChoices()` and `getChoicesByValues()`. For the latter two, no
|
||||||
replacement exists.
|
replacement exists.
|
||||||
|
|
||||||
* The strategy for generating the HTML attributes "id" and "name"
|
* The strategy for generating the `id` and `name` HTML attributes for choices
|
||||||
of choices in a choice field has changed
|
in a choice field has changed.
|
||||||
|
|
||||||
Instead of appending the choice value, a generated integer is now appended
|
Instead of appending the choice value, a generated integer is now appended
|
||||||
by default. Take care if your Javascript relies on that. If you can
|
by default. Take care if your JavaScript relies on the old behavior. If you
|
||||||
guarantee that your choice values only contain ASCII letters, digits,
|
can guarantee that your choice values only contain ASCII letters, digits,
|
||||||
letters, colons and underscores, you can restore the old behaviour by
|
colons and underscores, you can restore the old behavior by setting the
|
||||||
setting the option "index_strategy" of the choice field to
|
`index_strategy` choice field option to `ChoiceList::COPY_CHOICE`.
|
||||||
`ChoiceList::COPY_CHOICE`.
|
|
||||||
|
|
||||||
* The strategy for generating the HTML attributes "value" of choices in a
|
* The strategy for generating the `value` HTML attribute for choices in a
|
||||||
choice field has changed
|
choice field has changed.
|
||||||
|
|
||||||
Instead of using the choice value, a generated integer is now stored.
|
Instead of using the choice value, a generated integer is now stored. Again,
|
||||||
Again, take care if your Javascript reads this value. If your choice field
|
take care if your JavaScript reads this value. If your choice field is a
|
||||||
is a non-expanded single-choice field, or if the choices are guaranteed not
|
non-expanded single-choice field, or if the choices are guaranteed not to
|
||||||
to contain the empty string '' (which is the case when you added it manually
|
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
|
or when the field is a single-choice field and is not required), you can
|
||||||
restore the old behaviour by setting the option "value_strategy" to
|
restore the old behavior by setting the `value_strategy` choice field option
|
||||||
`ChoiceList::COPY_CHOICE`.
|
to `ChoiceList::COPY_CHOICE`.
|
||||||
|
|
||||||
* In the template of the choice type, the structure of the "choices" variable
|
* In the choice field type's template, the structure of the `choices` variable
|
||||||
has changed
|
has changed.
|
||||||
|
|
||||||
"choices" now contains ChoiceView objects with two getters `getValue`
|
The `choices` variable now contains ChoiceView objects with two getters,
|
||||||
and `getLabel` to access the choice data. The indices of the array
|
`getValue()` and `getLabel()`, to access the choice data. The indices of the
|
||||||
store an index whose generation is controlled by the "index_generation"
|
array are controlled by the choice field's `index_generation` option.
|
||||||
option of the choice field.
|
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
{% for choice, label in choices %}
|
```
|
||||||
<option value="{{ choice }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
|
{% for choice, label in choices %}
|
||||||
{{ label }}
|
<option value="{{ choice }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
|
||||||
</option>
|
{{ label }}
|
||||||
{% endfor %}
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
{% for choice in choices %}
|
```
|
||||||
<option value="{{ choice.value }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
|
{% for choice in choices %}
|
||||||
{{ choice.label }}
|
<option value="{{ choice.value }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
|
||||||
</option>
|
{{ choice.label }}
|
||||||
{% endfor %}
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
```
|
||||||
|
|
||||||
* In the template of the collection type, the default name of the prototype
|
* In the collection type's template, the default name of the prototype field
|
||||||
field has changed from "$$name$$" to "__name__"
|
has changed from `$$name$$` to `__name__`.
|
||||||
|
|
||||||
For custom names, no dollars are prepended/appended anymore. You are advised
|
For custom names, dollar signs are no longer prepended and appended. You are
|
||||||
to prepend and append double underscores wherever you have configured the
|
advised to prepend and append two underscores wherever you specify a value
|
||||||
prototype name manually.
|
for the field's `prototype_name` option.
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
$builder->add('tags', 'collection', array('prototype' => 'proto'));
|
```
|
||||||
|
$builder->add('tags', 'collection', array('prototype' => 'proto'));
|
||||||
// results in the name "$$proto$$" in the template
|
|
||||||
|
// results in the name "$$proto$$" in the template
|
||||||
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
$builder->add('tags', 'collection', array('prototype' => '__proto__'));
|
```
|
||||||
|
$builder->add('tags', 'collection', array('prototype' => '__proto__'));
|
||||||
|
|
||||||
|
// results in the name "__proto__" in the template
|
||||||
|
```
|
||||||
|
|
||||||
// results in the name "__proto__" in the template
|
* The methods `setMessage()`, `getMessageTemplate()` and
|
||||||
|
`getMessageParameters()` in the Constraint class were deprecated.
|
||||||
|
|
||||||
* The methods `setMessage`, `getMessageTemplate` and `getMessageParameters`
|
If you have implemented custom validators, you should use the
|
||||||
in Constraint were deprecated
|
`addViolation()` method on the `ExecutionContext` object instead.
|
||||||
|
|
||||||
If you have implemented custom validators, you should use either of the
|
|
||||||
`addViolation*` methods of the context object instead.
|
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
public function isValid($value, Constraint $constraint)
|
```
|
||||||
{
|
public function isValid($value, Constraint $constraint)
|
||||||
// ...
|
{
|
||||||
if (!$valid) {
|
// ...
|
||||||
$this->setMessage($constraint->message, array(
|
if (!$valid) {
|
||||||
'{{ value }}' => $value,
|
$this->setMessage($constraint->message, array(
|
||||||
));
|
'{{ value }}' => $value,
|
||||||
|
));
|
||||||
return false;
|
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
public function isValid($value, Constraint $constraint)
|
```
|
||||||
{
|
public function isValid($value, Constraint $constraint)
|
||||||
// ...
|
{
|
||||||
if (!$valid) {
|
// ...
|
||||||
$this->context->addViolation($constraint->message, array(
|
if (!$valid) {
|
||||||
'{{ value }}' => $value,
|
$this->context->addViolation($constraint->message, array(
|
||||||
));
|
'{{ value }}' => $value,
|
||||||
|
));
|
||||||
return false;
|
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
* The options passed to `getParent` of the form types don't contain default
|
* The options passed to the `getParent()` method of form types no longer
|
||||||
options anymore
|
contain default options.
|
||||||
|
|
||||||
You should check with `isset` if options exist before checking their value.
|
You should check if options exist before attempting to read their value.
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
public function getParent(array $options)
|
```
|
||||||
{
|
public function getParent(array $options)
|
||||||
return 'single_text' === $options['widget'] ? 'text' : 'choice';
|
{
|
||||||
}
|
return 'single_text' === $options['widget'] ? 'text' : 'choice';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
public function getParent(array $options)
|
```
|
||||||
{
|
public function getParent(array $options)
|
||||||
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'text' : 'choice';
|
{
|
||||||
}
|
return isset($options['widget']) && 'single_text' === $options['widget'] ? 'text' : 'choice';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
* Flash Messages now returns and array based on type (the old method are still available but deprecated)
|
* The `add()`, `remove()`, `setParent()`, `bind()` and `setData()` methods in
|
||||||
|
the Form class now throw an exception if the form is already bound.
|
||||||
Before (Twig):
|
|
||||||
|
|
||||||
{% if app.session.hasFlash('notice') %}
|
|
||||||
<div class="flash-notice">
|
|
||||||
{{ app.session.flash('notice') }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
After (Twig):
|
|
||||||
|
|
||||||
{% if app.session.flashbag.has('notice') %}
|
|
||||||
<div class="flash-notice">
|
|
||||||
{{ app.session.flashbag.get('notice') }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
Again you can process all flash messages in one go with
|
|
||||||
|
|
||||||
{% for type, flashMessage in app.session.flashbag.all() %}
|
|
||||||
<div class="flash-{{ type }}">
|
|
||||||
{{ flashMessage }}
|
|
||||||
</div>
|
|
||||||
{% endforeach %}
|
|
||||||
|
|
||||||
* Session storage drivers
|
|
||||||
|
|
||||||
Session storage drivers should inherit from
|
|
||||||
`Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage`
|
|
||||||
and no longer should implement `read()`, `write()`, `remove()` which were removed from the
|
|
||||||
`SessionStorageInterface`.
|
|
||||||
|
|
||||||
Any session storage driver that wants to use custom save handlers should
|
|
||||||
implement `Symfony\Component\HttpFoundation\Session\Storage\SaveHandlerInterface`
|
|
||||||
|
|
||||||
* The methods `add`, `remove`, `setParent`, `bind` and `setData` in class Form
|
|
||||||
now throw an exception if the form is already bound
|
|
||||||
|
|
||||||
If you used these methods on bound forms, you should consider moving your
|
If you used these methods on bound forms, you should consider moving your
|
||||||
logic to an event listener listening to either of the events
|
logic to an event listener that observes one of the following events:
|
||||||
FormEvents::PRE_BIND, FormEvents::BIND_CLIENT_DATA or
|
`FormEvents::PRE_BIND`, `FormEvents::BIND_CLIENT_DATA` or
|
||||||
FormEvents::BIND_NORM_DATA instead.
|
`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.flash('notice') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
{% if app.session.flashbag.has('notice') %}
|
||||||
|
<div class="flash-notice">
|
||||||
|
{{ app.session.flashbag.get('notice') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can process all flash messges in a single loop with:
|
||||||
|
|
||||||
|
```
|
||||||
|
{% for type, flashMessage in app.session.flashbag.all() %}
|
||||||
|
<div class="flash-{{ type }}">
|
||||||
|
{{ flashMessage }}
|
||||||
|
</div>
|
||||||
|
{% endforeach %}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Session storage drivers should inherit from
|
||||||
|
`Symfony\Component\HttpFoundation\Session\Storage\AbstractSessionStorage`
|
||||||
|
and should no longer implement `read()`, `write()`, and `remove()`, which
|
||||||
|
were removed from `SessionStorageInterface`.
|
||||||
|
|
||||||
|
Any session storage driver that wants to use custom save handlers should
|
||||||
|
implement `Symfony\Component\HttpFoundation\Session\Storage\SaveHandlerInterface`.
|
||||||
|
|
Reference in New Issue