Skip to content

Commit 5c10d65

Browse files
committed
Merge branch '2.8' into 3.4
* 2.8: 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 Fix How to dynamically Generate Forms Based on user Data
2 parents 64c2cce + c32378f commit 5c10d65

File tree

4 files changed

+83
-53
lines changed

4 files changed

+83
-53
lines changed

doctrine.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ can automatically generate an empty ``test_project`` database for you:
194194
support 4-byte unicode characters, and strings containing them will be
195195
truncated. This is fixed by the `newer utf8mb4 character set`_.
196196

197+
.. caution::
198+
199+
MySQL sets a `limit of 767 bytes for the index key prefix`_. When using
200+
``utf8mb4``, string columns with 255 character length surpass that limit.
201+
This means that any column of type ``string`` and ``unique=true`` must
202+
set its maximum ``length`` to ``190``. Otherwise, you'll see this error:
203+
*"[PDOException] SQLSTATE[42000]: Syntax error or access violation:
204+
1071 Specified key was too long; max key length is 767 bytes"*.
205+
197206
.. note::
198207

199208
If you want to use SQLite as your database, you need to set the path
@@ -885,3 +894,4 @@ Learn more
885894
.. _`FrameworkExtraBundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
886895
.. _`newer utf8mb4 character set`: https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
887896
.. _`Transactions and Concurrency`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html
897+
.. _`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 & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -245,15 +245,6 @@ done in the constructor::
245245
$this->security = $security;
246246
}
247247

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

@@ -294,38 +285,41 @@ security helper to fill in the listener logic::
294285
);
295286
}
296287

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

322312
// ...
323313
}
324314

325315
.. note::
326316

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

330324
Using the Form
331325
~~~~~~~~~~~~~~

reference/forms/types/entity.rst

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

268268
.. include:: /reference/forms/types/options/placeholder.rst.inc
269269

270-
.. include:: /reference/forms/types/options/preferred_choices.rst.inc
270+
preferred_choices
271+
~~~~~~~~~~~~~~~~~
271272

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

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)