Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Form] Added a "choice_filter" option to ChoiceType #35733

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 UPGRADE-5.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Form
is deprecated. The method will be added to the interface in 6.0.
* Implementing the `FormConfigBuilderInterface` without implementing the `setIsEmptyCallback()` method
is deprecated. The method will be added to the interface in 6.0.
* Added argument `callable|null $filter` to `ChoiceListFactoryInterface::createListFromChoices()` and `createListFromLoader()` - not defining them is deprecated.

FrameworkBundle
---------------
Expand Down
1 change: 1 addition & 0 deletions 1 UPGRADE-6.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Form

* Added the `getIsEmptyCallback()` method to the `FormConfigInterface`.
* Added the `setIsEmptyCallback()` method to the `FormConfigBuilderInterface`.
* Added argument `callable|null $filter` to `ChoiceListFactoryInterface::createListFromChoices()` and `createListFromLoader()`.

FrameworkBundle
---------------
Expand Down
2 changes: 2 additions & 0 deletions 2 src/Symfony/Component/Form/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ CHANGELOG
5.1.0
-----

* Added a `choice_filter` option to `ChoiceType`
HeahDude marked this conversation as resolved.
Show resolved Hide resolved
* Added argument `callable|null $filter` to `ChoiceListFactoryInterface::createListFromChoices()` and `createListFromLoader()` - not defining them is deprecated.
* Added a `ChoiceList` facade to leverage explicit choice list caching based on options
* Added an `AbstractChoiceLoader` to simplify implementations and handle global optimizations
* The `view_timezone` option defaults to the `model_timezone` if no `reference_date` is configured.
Expand Down
11 changes: 11 additions & 0 deletions 11 src/Symfony/Component/Form/ChoiceList/ChoiceList.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceAttr;
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFieldName;
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceFilter;
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLabel;
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceLoader;
use Symfony\Component\Form\ChoiceList\Factory\Cache\ChoiceValue;
Expand Down Expand Up @@ -66,6 +67,16 @@ public static function value($formType, $value, $vary = null): ChoiceValue
return new ChoiceValue($formType, $value, $vary);
}

/**
* @param FormTypeInterface|FormTypeExtensionInterface $formType A form type or type extension configuring a cacheable choice list
* @param callable $filter Any pseudo callable to filter a choice list
* @param mixed|null $vary Dynamic data used to compute a unique hash when caching the callback
*/
public static function filter($formType, $filter, $vary = null): ChoiceFilter
{
return new ChoiceFilter($formType, $filter, $vary);
}

/**
* Decorates a "choice_label" option to make it cacheable.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\ChoiceList\Factory\Cache;

use Symfony\Component\Form\FormTypeExtensionInterface;
use Symfony\Component\Form\FormTypeInterface;

/**
* A cacheable wrapper for any {@see FormTypeInterface} or {@see FormTypeExtensionInterface}
HeahDude marked this conversation as resolved.
Show resolved Hide resolved
* which configures a "choice_filter" option.
*
* @internal
*
* @author Jules Pietri <jules@heahprod.com>
*/
final class ChoiceFilter extends AbstractStaticOption
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
/**
* Caches the choice lists created by the decorated factory.
*
* To cache a list based on its options, arguments must be decorated
* by a {@see Cache\AbstractStaticOption} implementation.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Jules Pietri <jules@heahprod.com>
*/
Expand Down Expand Up @@ -80,35 +83,61 @@ public function getDecoratedFactory()

/**
* {@inheritdoc}
*
* @param callable|Cache\ChoiceValue|null $value The callable or static option for
* generating the choice values
* @param callable|Cache\ChoiceFilter|null $filter The callable or static option for
* filtering the choices
*/
public function createListFromChoices(iterable $choices, $value = null)
public function createListFromChoices(iterable $choices, $value = null/*, $filter = null*/)
{
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;

if ($choices instanceof \Traversable) {
$choices = iterator_to_array($choices);
}

// Only cache per value when needed. The value is not validated on purpose.
$cache = true;
// Only cache per value and filter when needed. The value is not validated on purpose.
// The decorated factory may decide which values to accept and which not.
if ($value instanceof Cache\ChoiceValue) {
$value = $value->getOption();
} elseif ($value) {
return $this->decoratedFactory->createListFromChoices($choices, $value);
$cache = false;
}
if ($filter instanceof Cache\ChoiceFilter) {
$filter = $filter->getOption();
} elseif ($filter) {
$cache = false;
}

if (!$cache) {
return $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
}

$hash = self::generateHash([$choices, $value], 'fromChoices');
$hash = self::generateHash([$choices, $value, $filter], 'fromChoices');

if (!isset($this->lists[$hash])) {
$this->lists[$hash] = $this->decoratedFactory->createListFromChoices($choices, $value);
$this->lists[$hash] = $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
}

return $this->lists[$hash];
}

/**
* {@inheritdoc}
*
* @param ChoiceLoaderInterface|Cache\ChoiceLoader $loader The loader or static loader to load
* the choices lazily
* @param callable|Cache\ChoiceValue|null $value The callable or static option for
* generating the choice values
* @param callable|Cache\ChoiceFilter|null $filter The callable or static option for
* filtering the choices
*/
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/*, $filter = null*/)
{
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;

$cache = true;

if ($loader instanceof Cache\ChoiceLoader) {
Expand All @@ -123,21 +152,33 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
$cache = false;
}

if ($filter instanceof Cache\ChoiceFilter) {
$filter = $filter->getOption();
} elseif ($filter) {
$cache = false;
}

if (!$cache) {
return $this->decoratedFactory->createListFromLoader($loader, $value);
return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
}

$hash = self::generateHash([$loader, $value], 'fromLoader');
$hash = self::generateHash([$loader, $value, $filter], 'fromLoader');

if (!isset($this->lists[$hash])) {
$this->lists[$hash] = $this->decoratedFactory->createListFromLoader($loader, $value);
$this->lists[$hash] = $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
}

return $this->lists[$hash];
}

/**
* {@inheritdoc}
*
* @param array|callable|Cache\PreferredChoice|null $preferredChoices The preferred choices
* @param callable|false|Cache\ChoiceLabel|null $label The option or static option generating the choice labels
* @param callable|Cache\ChoiceFieldName|null $index The option or static option generating the view indices
* @param callable|Cache\GroupBy|null $groupBy The option or static option generating the group names
* @param array|callable|Cache\ChoiceAttr|null $attr The option or static option generating the HTML attributes
*/
public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ interface ChoiceListFactoryInterface
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
* @param callable|null $filter The callable filtering the choices
*
* @return ChoiceListInterface The choice list
*/
public function createListFromChoices(iterable $choices, callable $value = null);
public function createListFromChoices(iterable $choices, callable $value = null/*, callable $filter = null*/);

/**
* Creates a choice list that is loaded with the given loader.
Expand All @@ -42,9 +44,11 @@ public function createListFromChoices(iterable $choices, callable $value = null)
* The callable receives the choice as only argument.
* Null may be passed when the choice list contains the empty value.
*
* @param callable|null $filter The callable filtering the choices
*
* @return ChoiceListInterface The choice list
*/
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null);
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null/*, callable $filter = null*/);

/**
* Creates a view for the given choice list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\LazyChoiceList;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\Loader\FilterChoiceLoaderDecorator;
use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
Expand All @@ -23,22 +25,44 @@
* Default implementation of {@link ChoiceListFactoryInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Jules Pietri <jules@heahprod.com>
*/
class DefaultChoiceListFactory implements ChoiceListFactoryInterface
{
/**
* {@inheritdoc}
*
* @param callable|null $filter
*/
public function createListFromChoices(iterable $choices, callable $value = null)
public function createListFromChoices(iterable $choices, callable $value = null/*, callable $filter = null*/)
{
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;

if ($filter) {
// filter the choice list lazily
return $this->createListFromLoader(new FilterChoiceLoaderDecorator(
new CallbackChoiceLoader(static function () use ($choices) {
return $choices;
}
), $filter), $value);
}

return new ArrayChoiceList($choices, $value);
}

/**
* {@inheritdoc}
*
* @param callable|null $filter
*/
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null)
public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null/*, callable $filter = null*/)
{
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;

if ($filter) {
$loader = new FilterChoiceLoaderDecorator($loader, $filter);
}

return new LazyChoiceList($loader, $value);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@ public function getDecoratedFactory()
/**
* {@inheritdoc}
*
* @param callable|string|PropertyPath|null $value The callable or path for
* generating the choice values
* @param callable|string|PropertyPath|null $value The callable or path for
* generating the choice values
* @param callable|string|PropertyPath|null $filter The callable or path for
* filtering the choices
*
* @return ChoiceListInterface The choice list
*/
public function createListFromChoices(iterable $choices, $value = null)
public function createListFromChoices(iterable $choices, $value = null/*, $filter = null*/)
{
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;

if (\is_string($value)) {
$value = new PropertyPath($value);
}
Expand All @@ -81,19 +85,34 @@ public function createListFromChoices(iterable $choices, $value = null)
};
}

return $this->decoratedFactory->createListFromChoices($choices, $value);
if (\is_string($filter)) {
$filter = new PropertyPath($filter);
}

if ($filter instanceof PropertyPath) {
$accessor = $this->propertyAccessor;
$filter = static function ($choice) use ($accessor, $filter) {
return (\is_object($choice) || \is_array($choice)) && $accessor->getValue($choice, $filter);
};
}

return $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
}

/**
* {@inheritdoc}
*
* @param callable|string|PropertyPath|null $value The callable or path for
* generating the choice values
* @param callable|string|PropertyPath|null $value The callable or path for
* generating the choice values
* @param callable|string|PropertyPath|null $filter The callable or path for
* filtering the choices
*
* @return ChoiceListInterface The choice list
*/
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null)
public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/*, $filter = null*/)
{
$filter = \func_num_args() > 2 ? func_get_arg(2) : null;

if (\is_string($value)) {
$value = new PropertyPath($value);
}
Expand All @@ -109,7 +128,18 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul
};
}

return $this->decoratedFactory->createListFromLoader($loader, $value);
if (\is_string($filter)) {
$filter = new PropertyPath($filter);
}

if ($filter instanceof PropertyPath) {
$accessor = $this->propertyAccessor;
$filter = static function ($choice) use ($accessor, $filter) {
return (\is_object($choice) || \is_array($choice)) && $accessor->getValue($choice, $filter);
};
}

return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
}

/**
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.