Skip to content

Commit

Permalink
Merge branch '2.8' into 3.4
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
javiereguiluz committed Jul 17, 2018
2 parents 64c2cce + c32378f commit 5c10d65
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 53 deletions.
10 changes: 10 additions & 0 deletions doctrine.rst
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ can automatically generate an empty ``test_project`` database for you:
support 4-byte unicode characters, and strings containing them will be
truncated. This is fixed by the `newer utf8mb4 character set`_.

.. caution::

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

.. note::

If you want to use SQLite as your database, you need to set the path
Expand Down Expand Up @@ -885,3 +894,4 @@ Learn more
.. _`FrameworkExtraBundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
.. _`newer utf8mb4 character set`: https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
.. _`Transactions and Concurrency`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html
.. _`limit of 767 bytes for the index key prefix`: https://dev.mysql.com/doc/refman/5.6/en/innodb-restrictions.html
60 changes: 27 additions & 33 deletions form/dynamic_form_modification.rst
Original file line number Diff line number Diff line change
Expand Up @@ -245,15 +245,6 @@ done in the constructor::
$this->security = $security;
}

.. note::

You might wonder, now that you have access to the User, why not just use it
directly in ``buildForm()`` and omit the event listener? This is because
doing so in the ``buildForm()`` method would result in the whole form type
being modified and not just this one form instance. This may not usually be
a problem, but technically a single form type could be used on a single
request to create many forms or fields.

Customizing the Form Type
~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -294,38 +285,41 @@ security helper to fill in the listener logic::
);
}

$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($user) {
$form = $event->getForm();

$formOptions = array(
'class' => User::class,
'choice_label' => 'fullName',
'query_builder' => function (EntityRepository $er) use ($user) {
// build a custom query
// return $er->createQueryBuilder('u')->addOrderBy('fullName', 'DESC');

// or call a method on your repository that returns the query builder
// the $er is an instance of your UserRepository
// return $er->createOrderByFullNameQueryBuilder();
},
);

// create the field, this is similar the $builder->add()
// field name, field type, data, options
$form->add('friend', EntityType::class, $formOptions);
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($user) {
if (null !== $event->getData()->getFriend()) {
// we don't need to add the friend field because
// the message will be addressed to a fixed friend
return;
}
);

$form = $event->getForm();

$formOptions = array(
'class' => User::class,
'choice_label' => 'fullName',
'query_builder' => function (UserRepository $userRepository) use ($user) {
// call a method on your repository that returns the query builder
// return $userRepository->createFriendsQueryBuilder($user);
},
);

// create the field, this is similar the $builder->add()
// field name, field type, field options
$form->add('friend', EntityType::class, $formOptions);
});
}

// ...
}

.. note::

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

Using the Form
~~~~~~~~~~~~~~
Expand Down
29 changes: 25 additions & 4 deletions reference/forms/types/entity.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,33 @@ These options inherit from the :doc:`ChoiceType </reference/forms/types/choice>`

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

.. include:: /reference/forms/types/options/preferred_choices.rst.inc
preferred_choices
~~~~~~~~~~~~~~~~~

.. note::
**type**: ``array`` or ``callable`` **default**: ``array()``

This option allows you to move certain choices to the top of your list with a visual
separator between them and the rest of the options. This option expects an array
of entity objects::

use AppBundle\Entity\User;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
// ...

$builder->add('users', EntityType::class, array(
'class' => User::class,
// this method must return an array of User entities
'preferred_choices' => $group->getPreferredUsers(),
));

The preferred choices are only meaningful when rendering a ``select`` element
(i.e. ``expanded`` false). The preferred choices and normal choices are separated
visually by a set of dotted lines (i.e. ``-------------------``). This can be customized
when rendering the field:

.. code-block:: twig
This option expects an array of entity objects (that's actually the same as with
the ``ChoiceType`` field, which requires an array of the preferred "values").
{{ form_widget(form.publishAt, { 'separator': '=====' }) }}
.. include:: /reference/forms/types/options/choice_type_translation_domain.rst.inc

Expand Down
37 changes: 21 additions & 16 deletions security/custom_provider.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,21 +133,7 @@ Here's an example of how this might look::
{
public function loadUserByUsername($username)
{
// make a call to your webservice here
$userData = ...
// pretend it returns an array on success, false if there is no user

if ($userData) {
$password = '...';

// ...

return new WebserviceUser($username, $password, $salt, $roles);
}

throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
return $this->fetchUser($username);
}

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

return $this->loadUserByUsername($user->getUsername());
return $this->fetchUser($username);
}

public function supportsClass($class)
{
return WebserviceUser::class === $class;
}

private function fetchUser($username)
{
// make a call to your webservice here
$userData = ...
// pretend it returns an array on success, false if there is no user

if ($userData) {
$password = '...';

// ...

return new WebserviceUser($username, $password, $salt, $roles);
}

throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
}

Create a Service for the User Provider
Expand Down

0 comments on commit 5c10d65

Please sign in to comment.