* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ namespace Chill\CustomFieldsBundle\CustomFields; use Chill\CustomFieldsBundle\Form\Type\ChoicesListType; use Chill\CustomFieldsBundle\Form\Type\ChoicesType; use Chill\CustomFieldsBundle\Form\Type\ChoiceWithOtherType; use Chill\CustomFieldsBundle\CustomFields\CustomFieldInterface; use Symfony\Component\Form\FormBuilderInterface; use Chill\CustomFieldsBundle\Entity\CustomField; use Symfony\Component\HttpFoundation\RequestStack; use Chill\CustomFieldsBundle\Form\DataTransformer\CustomFieldDataTransformer; use Symfony\Bridge\Twig\TwigEngine; use Chill\MainBundle\Templating\TranslatableStringHelper; use Symfony\Component\Translation\Translator; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; /** * * * @author Julien Fastré * @author Marc Ducobu */ class CustomFieldChoice extends AbstractCustomField { const ALLOW_OTHER = 'other'; const OTHER_VALUE_LABEL = 'otherValueLabel'; const MULTIPLE = 'multiple'; const EXPANDED = 'expanded'; const CHOICES = 'choices'; private $defaultLocales; /** * * @var TwigEngine */ private $templating; /** * @var TranslatableStringHelper Helper that find the string in current locale from an array of translation */ private $translatableStringHelper; public function __construct( Translator $translator, TwigEngine $templating, TranslatableStringHelper $translatableStringHelper) { $this->defaultLocales = $translator->getFallbackLocales(); $this->templating = $templating; $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilderInterface $builder, CustomField $customField) { //prepare choices $choices = array(); $customFieldOptions = $customField->getOptions(); foreach($customFieldOptions[self::CHOICES] as $persistedChoices) { if ($persistedChoices['active']){ $choices[$persistedChoices['slug']] = $this->translatableStringHelper->localize($persistedChoices['name']); } } //prepare $options $options = array( 'multiple' => $customFieldOptions[self::MULTIPLE], 'choices' => $choices, 'required' => $customField->isRequired(), 'label' => $this->translatableStringHelper->localize($customField->getName())); //if allow_other = true if ($customFieldOptions[self::ALLOW_OTHER] == true) { $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(), new ChoiceWithOtherType($otherValueLabel), $options) ->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, array( 'expanded' => true, 'multiple' => false, 'choices' => array( '1' => 'Multiple', '0' => 'Unique'), 'empty_data' => '0', 'label' => 'Multiplicity' )) ->add(self::EXPANDED, ChoiceType::class, array( 'expanded' => true, 'multiple' => false, 'choices' => array( '1' => 'Expanded', '0' => 'Non expanded'), 'empty_data' => '0', 'label' => 'Choice display' )) ->add(self::ALLOW_OTHER, ChoiceType::class, array( 'label' => 'Allow other', 'choices' => array( '0' => 'No', '1' => 'Yes'), 'empty_data' => '0', 'expanded' => true, 'multiple' => false )) ->add(self::OTHER_VALUE_LABEL, 'translatable_string', array( 'label' => 'Other value label (empty if use by default)')) ->add(self::CHOICES, new ChoicesType(), array( 'type' => new ChoicesListType($this->defaultLocales), '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]); } else { return $this->deserializeToUnique($serialized, $options[self::ALLOW_OTHER]); } return $serialized; } 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); } else { return $fixedValue; } } /** * deserialized the data from the database to a multiple * field * * @param mixed $serialized * @param boolean $allowOther */ private function deserializeToMultiple($serialized, $allowOther) { $value = $this->guessValue($serialized); // set in an array : we want a multiple $fixedValue = is_array($value) ? $value : array($value); if ($allowOther) { return $this->deserializeWithAllowOther($serialized, $fixedValue); } else { return $fixedValue; } } private function deserializeWithAllowOther($serialized, $value) { $existingOther = isset($serialized['_other']) ? $serialized['_other'] : ''; return array( '_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. * * @param array|string $value * @return mixed * @throws \LogicException if the case is not covered by this */ private function guessValue($value) { if ($value === NULL) { return NULL; } if (!is_array($value)) { return $value; } else { // we have a field with "allow other" if (array_key_exists('_choices', $value)) { return $value['_choices']; } else { // we have a field with "multiple" return $value; } } throw \LogicException("This case is not expected."); } public function getName() { return 'Choices'; } public function isEmptyValue($value, CustomField $customField) { if ($value === NULL) { 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 ($value['_choices'] === NULL) { return true; } return empty($value['_choices']); } else { // we do not have 'allow other' if (count($value) === 1){ return empty($value[0]); } else { return empty($value); } } } else { return empty($value); } throw \LogicException("This case is not expected."); } /** * * @internal this function is able to receive data whichever is the value of "other", "multiple" * @param mixed $value * @param CustomField $customField * @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 = (isset($value['_choices'])) ? $value['_choices'] : $value; $selected = (is_array($data)) ? $data : array($data); $choices = $customField->getOptions()[self::CHOICES]; if (in_array('_other', $selected)){ $choices[] = array('name' => $value['_other'], 'slug' => '_other'); } $template = 'ChillCustomFieldsBundle:CustomFieldsRendering:choice.html.twig'; if($documentType == 'csv') { $template = 'ChillCustomFieldsBundle:CustomFieldsRendering:choice.csv.twig'; } return $this->templating ->render($template, array( 'choices' => $choices, 'selected' => $selected, 'multiple' => $customField->getOptions()[self::MULTIPLE], 'expanded' => $customField->getOptions()[self::EXPANDED], 'locales' => $this->defaultLocales ) ); } public function serialize($value, CustomField $customField) { return $value; } }