Skip to content

Commit c3d69d7

Browse files
committed
Merge branch '3.4' into 4.0
* 3.4: Codefence ClassMetadataFactory Document the required ClassMetadataFactory Update framework.rst Fixed the preferred_choices option for Enttiy type Mentioned the 767 bytes index limit and its solution Refactor a security code example to make it easier to understand Remove deleted RoleInterface Fix How to dynamically Generate Forms Based on user Data Fixed the default value of php_errors.log option
2 parents 6f3ef4d + 5c10d65 commit c3d69d7

File tree

8 files changed

+116
-64
lines changed

8 files changed

+116
-64
lines changed

components/asset.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ In those cases, use the
161161
use Symfony\Component\Asset\Package;
162162
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
163163

164-
$package = new Package(new JsonManifestVersionStrategy(__DIR__'./rev-manifest.json'));
164+
$package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json'));
165165

166166
echo $package->getUrl('css/app.css');
167167
// result: build/css/app.b916426ea1d10021f3f17ce8031f93c2.css

components/security/authorization.rst

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,11 @@ role::
167167
Roles
168168
-----
169169

170-
Roles are objects that give expression to a certain right the user has.
171-
The only requirement is that they implement :class:`Symfony\\Component\\Security\\Core\\Role\\RoleInterface`,
172-
which means they should also have a :method:`Symfony\\Component\\Security\\Core\\Role\\RoleInterface::getRole`
173-
method that returns a string representation of the role itself. The default
174-
:class:`Symfony\\Component\\Security\\Core\\Role\\Role` simply returns its
175-
first constructor argument::
170+
Roles are objects that give expression to a certain right the user has. The only
171+
requirement is that they must define a ``getRole()`` method that returns a
172+
string representation of the role itself. To do so, you can optionally extend
173+
from the default :class:`Symfony\\Component\\Security\\Core\\Role\\Role` class,
174+
which returns its first constructor argument in this method::
176175

177176
use Symfony\Component\Security\Core\Role\Role;
178177

components/serializer.rst

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,15 @@ needs three parameters:
165165
#. The name of the class this information will be decoded to
166166
#. The encoder used to convert that information into an array
167167

168-
By default, additional attributes that are not mapped to the denormalized
169-
object will be ignored by the Serializer component. Set the ``allow_extra_attributes``
170-
key of the deserialization context to ``false`` to let the serializer throw
171-
an exception when additional attributes are passed::
168+
.. versionadded:: 3.3
169+
Support for the ``allow_extra_attributes`` key in the context was introduced
170+
in Symfony 3.3.
171+
172+
By default, additional attributes that are not mapped to the denormalized object
173+
will be ignored by the Serializer component. If you prefer to throw an exception
174+
when this happens, set the ``allow_extra_attributes`` context option to
175+
``false`` and provide an object that implements ``ClassMetadataFactoryInterface``
176+
when constructing the normalizer::
172177

173178
$data = <<<EOF
174179
<person>
@@ -180,6 +185,9 @@ an exception when additional attributes are passed::
180185

181186
// this will throw a Symfony\Component\Serializer\Exception\ExtraAttributesException
182187
// because "city" is not an attribute of the Person class
188+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
189+
$normalizer = new ObjectNormalizer($classMetadataFactory);
190+
$serializer = new Serializer(array($normalizer));
183191
$person = $serializer->deserialize($data, 'Acme\Person', 'xml', array(
184192
'allow_extra_attributes' => false,
185193
));

doctrine.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ Woh! You now have a new ``src/Entity/Product.php`` file::
155155
Confused why the price is an integer? Don't worry: this is just an example.
156156
But, storing prices as integers (e.g. 100 = $1 USD) can avoid rounding issues.
157157

158+
.. caution::
159+
160+
MySQL sets a `limit of 767 bytes for the index key prefix`_. When using
161+
``utf8mb4``, string columns with 255 character length surpass that limit.
162+
This means that any column of type ``string`` and ``unique=true`` must
163+
set its maximum ``length`` to ``190``. Otherwise, you'll see this error:
164+
*"[PDOException] SQLSTATE[42000]: Syntax error or access violation:
165+
1071 Specified key was too long; max key length is 767 bytes"*.
166+
158167
This class is called an "entity". And soon, you'll be able to save and query Product
159168
objects to a ``product`` table in your database. Each property in the ``Product``
160169
entity can be mapped to a column in that table. This is usually done with annotations:
@@ -741,3 +750,4 @@ Learn more
741750
.. _`NativeQuery`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html
742751
.. _`SensioFrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html
743752
.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
753+
.. _`limit of 767 bytes for the index key prefix`: https://dev.mysql.com/doc/refman/5.6/en/innodb-restrictions.html

form/dynamic_form_modification.rst

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,6 @@ service into the form type so you can get the current user object::
243243
$this->security = $security;
244244
}
245245

246-
.. note::
247-
248-
You might wonder, now that you have access to the ``User`` object, why not
249-
just use it directly in ``buildForm()`` and omit the event listener? This is
250-
because doing so in the ``buildForm()`` method would result in the whole
251-
form type being modified and not just this one form instance. This may not
252-
usually be a problem, but technically a single form type could be used on a
253-
single request to create many forms or fields.
254-
255246
Customizing the Form Type
256247
~~~~~~~~~~~~~~~~~~~~~~~~~
257248

@@ -292,37 +283,41 @@ security helper to fill in the listener logic::
292283
);
293284
}
294285

295-
$builder->addEventListener(
296-
FormEvents::PRE_SET_DATA,
297-
function (FormEvent $event) use ($user) {
298-
$form = $event->getForm();
299-
300-
$formOptions = array(
301-
'class' => User::class,
302-
'choice_label' => 'fullName',
303-
'query_builder' => function (EntityRepository $userRepository) use ($user) {
304-
// build a custom query
305-
// return $userRepository->createQueryBuilder('u')->addOrderBy('fullName', 'DESC');
306-
307-
// or call a method on your repository that returns the query builder
308-
// return $userRepository->createOrderByFullNameQueryBuilder();
309-
},
310-
);
311-
312-
// create the field, this is similar the $builder->add()
313-
// field name, field type, data, options
314-
$form->add('friend', EntityType::class, $formOptions);
286+
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($user) {
287+
if (null !== $event->getData()->getFriend()) {
288+
// we don't need to add the friend field because
289+
// the message will be addressed to a fixed friend
290+
return;
315291
}
316-
);
292+
293+
$form = $event->getForm();
294+
295+
$formOptions = array(
296+
'class' => User::class,
297+
'choice_label' => 'fullName',
298+
'query_builder' => function (UserRepository $userRepository) use ($user) {
299+
// call a method on your repository that returns the query builder
300+
// return $userRepository->createFriendsQueryBuilder($user);
301+
},
302+
);
303+
304+
// create the field, this is similar the $builder->add()
305+
// field name, field type, field options
306+
$form->add('friend', EntityType::class, $formOptions);
307+
});
317308
}
318309

319310
// ...
320311
}
321312

322313
.. note::
323314

324-
The ``multiple`` and ``expanded`` form options will default to false
325-
because the type of the friend field is ``EntityType::class``.
315+
You might wonder, now that you have access to the ``User`` object, why not
316+
just use it directly in ``buildForm()`` and omit the event listener? This is
317+
because doing so in the ``buildForm()`` method would result in the whole
318+
form type being modified and not just this one form instance. This may not
319+
usually be a problem, but technically a single form type could be used on a
320+
single request to create many forms or fields.
326321

327322
Using the Form
328323
~~~~~~~~~~~~~~

reference/configuration/framework.rst

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Configuration
159159
* `test`_
160160
* `translator`_
161161

162+
* :ref:`default_path <reference-translator-default_path>`
162163
* :ref:`enabled <reference-translator-enabled>`
163164
* `fallbacks`_
164165
* `logging`_
@@ -1539,6 +1540,19 @@ paths
15391540
This option allows to define an array of paths where the component will look
15401541
for translation files.
15411542

1543+
.. _reference-translator-default_path:
1544+
1545+
default_path
1546+
............
1547+
1548+
.. versionadded:: 3.4
1549+
The ``default_path`` option was introduced in Symfony 3.4.
1550+
1551+
**type**: ``string`` **default**: ``%kernel.project_dir%/translations``
1552+
1553+
This option allows to define the path where the application translations files
1554+
are stored.
1555+
15421556
property_access
15431557
~~~~~~~~~~~~~~~
15441558

@@ -1747,7 +1761,7 @@ php_errors
17471761
log
17481762
...
17491763

1750-
**type**: ``boolean`` **default**: ``false``
1764+
**type**: ``boolean`` **default**: ``%kernel.debug%``
17511765

17521766
Use the application logger instead of the PHP logger for logging PHP errors.
17531767

reference/forms/types/entity.rst

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,33 @@ These options inherit from the :doc:`ChoiceType </reference/forms/types/choice>`
272272

273273
.. include:: /reference/forms/types/options/placeholder.rst.inc
274274

275-
.. include:: /reference/forms/types/options/preferred_choices.rst.inc
275+
preferred_choices
276+
~~~~~~~~~~~~~~~~~
276277

277-
.. note::
278+
**type**: ``array`` or ``callable`` **default**: ``array()``
279+
280+
This option allows you to move certain choices to the top of your list with a visual
281+
separator between them and the rest of the options. This option expects an array
282+
of entity objects::
283+
284+
use AppBundle\Entity\User;
285+
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
286+
// ...
287+
288+
$builder->add('users', EntityType::class, array(
289+
'class' => User::class,
290+
// this method must return an array of User entities
291+
'preferred_choices' => $group->getPreferredUsers(),
292+
));
293+
294+
The preferred choices are only meaningful when rendering a ``select`` element
295+
(i.e. ``expanded`` false). The preferred choices and normal choices are separated
296+
visually by a set of dotted lines (i.e. ``-------------------``). This can be customized
297+
when rendering the field:
298+
299+
.. code-block:: twig
278300
279-
This option expects an array of entity objects (that's actually the same as with
280-
the ``ChoiceType`` field, which requires an array of the preferred "values").
301+
{{ form_widget(form.publishAt, { 'separator': '=====' }) }}
281302
282303
.. include:: /reference/forms/types/options/choice_type_translation_domain.rst.inc
283304

security/custom_provider.rst

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -133,21 +133,7 @@ Here's an example of how this might look::
133133
{
134134
public function loadUserByUsername($username)
135135
{
136-
// make a call to your webservice here
137-
$userData = ...
138-
// pretend it returns an array on success, false if there is no user
139-
140-
if ($userData) {
141-
$password = '...';
142-
143-
// ...
144-
145-
return new WebserviceUser($username, $password, $salt, $roles);
146-
}
147-
148-
throw new UsernameNotFoundException(
149-
sprintf('Username "%s" does not exist.', $username)
150-
);
136+
return $this->fetchUser($username);
151137
}
152138

153139
public function refreshUser(UserInterface $user)
@@ -158,13 +144,32 @@ Here's an example of how this might look::
158144
);
159145
}
160146

161-
return $this->loadUserByUsername($user->getUsername());
147+
return $this->fetchUser($username);
162148
}
163149

164150
public function supportsClass($class)
165151
{
166152
return WebserviceUser::class === $class;
167153
}
154+
155+
private function fetchUser($username)
156+
{
157+
// make a call to your webservice here
158+
$userData = ...
159+
// pretend it returns an array on success, false if there is no user
160+
161+
if ($userData) {
162+
$password = '...';
163+
164+
// ...
165+
166+
return new WebserviceUser($username, $password, $salt, $roles);
167+
}
168+
169+
throw new UsernameNotFoundException(
170+
sprintf('Username "%s" does not exist.', $username)
171+
);
172+
}
168173
}
169174

170175
Create a Service for the User Provider

0 commit comments

Comments
 (0)