mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
377 lines
12 KiB
PHP
377 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* Chill is a software for social workers
|
|
*
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Chill\CustomFieldsBundle\CustomFields;
|
|
|
|
use Chill\CustomFieldsBundle\Entity\CustomField;
|
|
use Chill\CustomFieldsBundle\Form\DataTransformer\CustomFieldDataTransformer;
|
|
use Chill\CustomFieldsBundle\Form\Type\ChoicesListType;
|
|
use Chill\CustomFieldsBundle\Form\Type\ChoicesType;
|
|
use Chill\CustomFieldsBundle\Form\Type\ChoiceWithOtherType;
|
|
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
|
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
|
use Symfony\Component\Form\FormBuilderInterface;
|
|
use Twig\Environment;
|
|
|
|
class CustomFieldChoice extends AbstractCustomField
|
|
{
|
|
final public const ALLOW_OTHER = 'other';
|
|
|
|
final public const CHOICES = 'choices';
|
|
|
|
final public const EXPANDED = 'expanded';
|
|
|
|
final public const MULTIPLE = 'multiple';
|
|
|
|
final public const OTHER_VALUE_LABEL = 'otherValueLabel';
|
|
|
|
/**
|
|
* CustomFieldChoice constructor.
|
|
*/
|
|
public function __construct(
|
|
private readonly Environment $templating,
|
|
/**
|
|
* @var TranslatableStringHelper Helper that find the string in current locale from an array of translation
|
|
*/
|
|
private readonly TranslatableStringHelper $translatableStringHelper
|
|
) {}
|
|
|
|
public function allowOtherChoice(CustomField $cf)
|
|
{
|
|
return $cf->getOptions()[self::ALLOW_OTHER];
|
|
}
|
|
|
|
public function buildForm(FormBuilderInterface $builder, CustomField $customField)
|
|
{
|
|
// prepare choices
|
|
$choices = [];
|
|
$customFieldOptions = $customField->getOptions();
|
|
|
|
foreach ($customFieldOptions[self::CHOICES] as $persistedChoices) {
|
|
if ($persistedChoices['active']) {
|
|
$choices[$persistedChoices['slug']] = $this->translatableStringHelper->localize($persistedChoices['name']);
|
|
}
|
|
}
|
|
|
|
// prepare $options
|
|
$options = [
|
|
'multiple' => $customFieldOptions[self::MULTIPLE],
|
|
'choices' => array_combine(array_values($choices), array_keys($choices)),
|
|
'required' => $customField->isRequired(),
|
|
'label' => $this->translatableStringHelper->localize($customField->getName()),
|
|
];
|
|
|
|
// if allow_other = true
|
|
if (true === $customFieldOptions[self::ALLOW_OTHER]) {
|
|
$otherValueLabel = null;
|
|
|
|
if (\array_key_exists(self::OTHER_VALUE_LABEL, $customFieldOptions)) {
|
|
$otherValueLabel = $this->translatableStringHelper->localize(
|
|
$customFieldOptions[self::OTHER_VALUE_LABEL]
|
|
);
|
|
}
|
|
|
|
$builder->add(
|
|
$builder
|
|
->create(
|
|
$customField->getSlug(),
|
|
ChoiceWithOtherType::class,
|
|
$options,
|
|
['other_value_label' => $otherValueLabel]
|
|
)
|
|
->addModelTransformer(new CustomFieldDataTransformer($this, $customField))
|
|
);
|
|
} else { // if allow_other = false
|
|
// we add the 'expanded' to options
|
|
$options['expanded'] = $customFieldOptions[self::EXPANDED];
|
|
|
|
$builder->add(
|
|
$builder->create($customField->getSlug(), ChoiceType::class, $options)
|
|
->addModelTransformer(new CustomFieldDataTransformer($this, $customField))
|
|
);
|
|
}
|
|
}
|
|
|
|
public function buildOptionsForm(FormBuilderInterface $builder)
|
|
{
|
|
$builder
|
|
->add(self::MULTIPLE, ChoiceType::class, [
|
|
'expanded' => true,
|
|
'multiple' => false,
|
|
'choices' => [
|
|
'Multiple' => '1',
|
|
'Unique' => '0', ],
|
|
'empty_data' => '0',
|
|
'label' => 'Multiplicity',
|
|
])
|
|
->add(self::EXPANDED, ChoiceType::class, [
|
|
'expanded' => true,
|
|
'multiple' => false,
|
|
'choices' => [
|
|
'Expanded' => '1',
|
|
'Non expanded' => '0', ],
|
|
'empty_data' => '0',
|
|
'label' => 'Choice display',
|
|
])
|
|
->add(self::ALLOW_OTHER, ChoiceType::class, [
|
|
'label' => 'Allow other',
|
|
'choices' => [
|
|
'No' => '0',
|
|
'Yes' => '1', ],
|
|
'empty_data' => '0',
|
|
'expanded' => true,
|
|
'multiple' => false,
|
|
])
|
|
->add(self::OTHER_VALUE_LABEL, TranslatableStringFormType::class, [
|
|
'label' => 'Other value label (empty if use by default)', ])
|
|
->add(self::CHOICES, ChoicesType::class, [
|
|
'entry_type' => ChoicesListType::class,
|
|
'allow_add' => true,
|
|
]);
|
|
|
|
return $builder;
|
|
}
|
|
|
|
public function deserialize($serialized, CustomField $customField)
|
|
{
|
|
// we always have to adapt to what the current data should be
|
|
$options = $customField->getOptions();
|
|
|
|
if ($options[self::MULTIPLE]) {
|
|
return $this->deserializeToMultiple($serialized, $options[self::ALLOW_OTHER]);
|
|
}
|
|
|
|
return $this->deserializeToUnique($serialized, $options[self::ALLOW_OTHER]);
|
|
|
|
return $serialized;
|
|
}
|
|
|
|
public function extractOtherValue(CustomField $cf, array $data = null)
|
|
{
|
|
return $data['_other'];
|
|
}
|
|
|
|
public function getChoices(CustomField $cf)
|
|
{
|
|
if ($cf->getOptions()[self::MULTIPLE]) {
|
|
$choices = [];
|
|
|
|
foreach ($cf->getOptions()[self::CHOICES] as $choice) {
|
|
if (false === $choice['active']) {
|
|
continue;
|
|
}
|
|
$choices[$choice['slug']] = $this->translatableStringHelper
|
|
->localize($choice['name']);
|
|
}
|
|
|
|
if ($this->allowOtherChoice($cf)) {
|
|
$labels = $cf->getOptions()[self::OTHER_VALUE_LABEL];
|
|
|
|
if (!\is_array($labels) || 0 === \count($labels)) {
|
|
$labels['back'] = 'other value';
|
|
}
|
|
$choices['_other'] = $this->translatableStringHelper
|
|
->localize($labels);
|
|
}
|
|
|
|
return $choices;
|
|
}
|
|
|
|
return [
|
|
$cf->getSlug() => $this->translatableStringHelper->localize($cf->getName()),
|
|
];
|
|
}
|
|
|
|
public function getName()
|
|
{
|
|
return 'Choices';
|
|
}
|
|
|
|
/**
|
|
* Return true if the choice given in $choiceSlug is checked inside $data.
|
|
*
|
|
* Used in list exports.
|
|
*
|
|
* @param string $choiceSlug the slug of the choice we want to know if it was checked
|
|
* @param array|string $data the data of the field
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isChecked(CustomField $cf, $choiceSlug, array|string $data)
|
|
{
|
|
if (null === $data) {
|
|
return false;
|
|
}
|
|
|
|
if ($cf->getOptions()[self::MULTIPLE]) {
|
|
if ($cf->getOptions()[self::ALLOW_OTHER]) {
|
|
return \in_array($choiceSlug, $this->deserialize($data, $cf)['_choices'], true);
|
|
}
|
|
|
|
return \in_array($choiceSlug, $this->deserialize($data, $cf), true);
|
|
}
|
|
|
|
if ($cf->getOptions()[self::ALLOW_OTHER]) {
|
|
return $this->deserialize($data, $cf)['_choices'] === $choiceSlug;
|
|
}
|
|
|
|
return $this->deserialize($data, $cf) === $choiceSlug;
|
|
}
|
|
|
|
public function isEmptyValue($value, CustomField $customField)
|
|
{
|
|
if (null === $value) {
|
|
return true;
|
|
}
|
|
|
|
// if multiple choice OR multiple/single choice with other
|
|
if (\is_array($value)) {
|
|
// if allow other
|
|
if (\array_key_exists('_choices', $value)) {
|
|
if (null === $value['_choices']) {
|
|
return true;
|
|
}
|
|
|
|
return empty($value['_choices']);
|
|
} // we do not have 'allow other'
|
|
|
|
if (1 === \count($value)) {
|
|
return empty($value[0]);
|
|
}
|
|
|
|
return empty($value);
|
|
}
|
|
|
|
return empty($value);
|
|
|
|
throw \LogicException('This case is not expected.');
|
|
}
|
|
|
|
public function isMultiple(CustomField $cf)
|
|
{
|
|
return $cf->getOptions()[self::MULTIPLE];
|
|
}
|
|
|
|
/**
|
|
* @internal this function is able to receive data whichever is the value of "other", "multiple"
|
|
*
|
|
* @return string html representation
|
|
*/
|
|
public function render($value, CustomField $customField, $documentType = 'html')
|
|
{
|
|
// extract the data. They are under a _choice key if they are stored with allow_other
|
|
$data = $value['_choices'] ?? $value;
|
|
$selected = (\is_array($data)) ? $data : [$data];
|
|
$choices = $customField->getOptions()[self::CHOICES];
|
|
|
|
if (\in_array('_other', $selected, true)) {
|
|
$choices[] = ['name' => $value['_other'], 'slug' => '_other'];
|
|
}
|
|
|
|
$template = '@ChillCustomFields/CustomFieldsRendering/choice.html.twig';
|
|
|
|
if ('csv' === $documentType) {
|
|
$template = '@ChillCustomFields/CustomFieldsRendering/choice.csv.twig';
|
|
}
|
|
|
|
return $this->templating
|
|
->render(
|
|
$template,
|
|
[
|
|
'choices' => $choices,
|
|
'selected' => $selected,
|
|
'multiple' => $customField->getOptions()[self::MULTIPLE],
|
|
'expanded' => $customField->getOptions()[self::EXPANDED],
|
|
]
|
|
);
|
|
}
|
|
|
|
public function serialize($value, CustomField $customField)
|
|
{
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* deserialized the data from the database to a multiple
|
|
* field.
|
|
*
|
|
* @param bool $allowOther
|
|
*/
|
|
private function deserializeToMultiple(mixed $serialized, $allowOther)
|
|
{
|
|
$value = $this->guessValue($serialized);
|
|
|
|
// set in an array : we want a multiple
|
|
$fixedValue = \is_array($value) ? $value : [$value];
|
|
|
|
if ($allowOther) {
|
|
return $this->deserializeWithAllowOther($serialized, $fixedValue);
|
|
}
|
|
|
|
return $fixedValue;
|
|
}
|
|
|
|
private function deserializeToUnique($serialized, $allowOther)
|
|
{
|
|
$value = $this->guessValue($serialized);
|
|
|
|
// set in a single value. We must have a single string
|
|
$fixedValue = \is_array($value) ?
|
|
// check if the array has an element, if not replace by empty string
|
|
\count($value) > 0 ? end($value) : ''
|
|
:
|
|
$value;
|
|
|
|
if ($allowOther) {
|
|
return $this->deserializeWithAllowOther($serialized, $fixedValue);
|
|
}
|
|
|
|
return $fixedValue;
|
|
}
|
|
|
|
private function deserializeWithAllowOther($serialized, $value)
|
|
{
|
|
$existingOther = $serialized['_other'] ?? '';
|
|
|
|
return [
|
|
'_other' => $existingOther,
|
|
'_choices' => $value,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Guess the value from the representation of it.
|
|
*
|
|
* If the value had an 'allow_other' = true option, the returned value
|
|
* **is not** the content of the _other field, but the `_other` string.
|
|
*/
|
|
private function guessValue(null|array|string $value)
|
|
{
|
|
if (null === $value) {
|
|
return null;
|
|
}
|
|
|
|
if (!\is_array($value)) {
|
|
return $value;
|
|
}
|
|
// we have a field with "allow other"
|
|
if (\array_key_exists('_choices', $value)) {
|
|
return $value['_choices'];
|
|
}
|
|
|
|
// we have a field with "multiple"
|
|
return $value;
|
|
|
|
throw \LogicException('This case is not expected.');
|
|
}
|
|
}
|