<?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;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\PropertyAccess\PropertyPathInterface;
/**
* Adds property path support to a choice list factory.
*
* Pass the decorated factory to the constructor:
*
* $decorator = new PropertyAccessDecorator($factory);
*
* You can now pass property paths for generating choice values, labels, view
* indices, HTML attributes and for determining the preferred choices and the
* choice groups:
*
* // extract values from the $value property
* $list = $createListFromChoices($objects, 'value');
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class PropertyAccessDecorator implements ChoiceListFactoryInterface
{
private $decoratedFactory;
private $propertyAccessor;
public function __construct(ChoiceListFactoryInterface $decoratedFactory, PropertyAccessorInterface $propertyAccessor = null)
{
$this->decoratedFactory = $decoratedFactory;
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
}
/**
* Returns the decorated factory.
*
* @return ChoiceListFactoryInterface
*/
public function getDecoratedFactory()
{
return $this->decoratedFactory;
}
/**
* {@inheritdoc}
*
* @param mixed $value
* @param mixed $filter
*
* @return ChoiceListInterface
*/
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);
}
if ($value instanceof PropertyPathInterface) {
$accessor = $this->propertyAccessor;
$value = function ($choice) use ($accessor, $value) {
// The callable may be invoked with a non-object/array value
// when such values are passed to
// ChoiceListInterface::getValuesForChoices(). Handle this case
// so that the call to getValue() doesn't break.
return \is_object($choice) || \is_array($choice) ? $accessor->getValue($choice, $value) : null;
};
}
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 mixed $value
* @param mixed $filter
*
* @return ChoiceListInterface
*/
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);
}
if ($value instanceof PropertyPathInterface) {
$accessor = $this->propertyAccessor;
$value = function ($choice) use ($accessor, $value) {
// The callable may be invoked with a non-object/array value
// when such values are passed to
// ChoiceListInterface::getValuesForChoices(). Handle this case
// so that the call to getValue() doesn't break.
return \is_object($choice) || \is_array($choice) ? $accessor->getValue($choice, $value) : null;
};
}
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);
}
/**
* {@inheritdoc}
*
* @param mixed $preferredChoices
* @param mixed $label
* @param mixed $index
* @param mixed $groupBy
* @param mixed $attr
* @param mixed $labelTranslationParameters
*
* @return ChoiceListView
*/
public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null/* , $labelTranslationParameters = [] */)
{
$labelTranslationParameters = \func_num_args() > 6 ? func_get_arg(6) : [];
$accessor = $this->propertyAccessor;
if (\is_string($label)) {
$label = new PropertyPath($label);
}
if ($label instanceof PropertyPathInterface) {
$label = function ($choice) use ($accessor, $label) {
return $accessor->getValue($choice, $label);
};
}
if (\is_string($preferredChoices)) {
$preferredChoices = new PropertyPath($preferredChoices);
}
if ($preferredChoices instanceof PropertyPathInterface) {
$preferredChoices = function ($choice) use ($accessor, $preferredChoices) {
try {
return $accessor->getValue($choice, $preferredChoices);
} catch (UnexpectedTypeException $e) {
// Assume not preferred if not readable
return false;
}
};
}
if (\is_string($index)) {
$index = new PropertyPath($index);
}
if ($index instanceof PropertyPathInterface) {
$index = function ($choice) use ($accessor, $index) {
return $accessor->getValue($choice, $index);
};
}
if (\is_string($groupBy)) {
$groupBy = new PropertyPath($groupBy);
}
if ($groupBy instanceof PropertyPathInterface) {
$groupBy = function ($choice) use ($accessor, $groupBy) {
try {
return $accessor->getValue($choice, $groupBy);
} catch (UnexpectedTypeException $e) {
// Don't group if path is not readable
return null;
}
};
}
if (\is_string($attr)) {
$attr = new PropertyPath($attr);
}
if ($attr instanceof PropertyPathInterface) {
$attr = function ($choice) use ($accessor, $attr) {
return $accessor->getValue($choice, $attr);
};
}
if (\is_string($labelTranslationParameters)) {
$labelTranslationParameters = new PropertyPath($labelTranslationParameters);
}
if ($labelTranslationParameters instanceof PropertyPath) {
$labelTranslationParameters = static function ($choice) use ($accessor, $labelTranslationParameters) {
return $accessor->getValue($choice, $labelTranslationParameters);
};
}
return $this->decoratedFactory->createView(
$list,
$preferredChoices,
$label,
$index,
$groupBy,
$attr,
$labelTranslationParameters
);
}
}