Skip to content

Commit 1eb9a9d

Browse files
committed
Merge branch '4.0' into 4.1
* 4.0: 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 d65f2ff + c3d69d7 commit 1eb9a9d

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
@@ -160,6 +160,7 @@ Configuration
160160
* `test`_
161161
* `translator`_
162162

163+
* :ref:`default_path <reference-translator-default_path>`
163164
* :ref:`enabled <reference-translator-enabled>`
164165
* `fallbacks`_
165166
* `logging`_
@@ -1542,6 +1543,19 @@ paths
15421543
This option allows to define an array of paths where the component will look
15431544
for translation files.
15441545

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

@@ -1774,7 +1788,7 @@ php_errors
17741788
log
17751789
...
17761790

1777-
**type**: ``boolean|int`` **default**: ``false``
1791+
**type**: ``boolean|int`` **default**: ``%kernel.debug%``
17781792

17791793
Use the application logger instead of the PHP logger for logging PHP errors.
17801794
When an integer value is used, it also sets the log level. Those integer

reference/forms/types/entity.rst

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

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

276-
.. include:: /reference/forms/types/options/preferred_choices.rst.inc
276+
preferred_choices
277+
~~~~~~~~~~~~~~~~~
277278

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

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)