Utiliser un select type dans la config Oro!

Pour utiliser un champ select héritant de OroEntitySelectOrCreateInlineType, on ne peux simplement utiliser des transformers pour convertir un objet en Id pour que l'on puisse enregistrer un id au lieu d'un objet en base.(Sinon vous allez avoir des surprises en prod :) )

Explication

Le champ Select vas utiliser la ModelData du formulaire pour la transmettre a l'autocomplete qui utilise un objet. ( Oro/Bundle/ConfigBundle/Form/Handler/ConfigHandler.php pour plus de précisions). OroEntitySelectOrCreateInlineType utilise lui meme un EntityToIdTransformer pour transformer la donnée. Donc il ne faut pas rajouter d'autre DataTransformer sinon vous aurez des erreurs. Il faut donc transformer la donnée autre part, mais ou ?

Solution

Tout d'abord créer un formulaire comme ceci :

<?php  
namespace SliderBundle\Form\Type;  

use Doctrine\Common\Persistence\ObjectRepository;  
use Oro\Bundle\FormBundle\Form\Type\OroEntitySelectOrCreateInlineType;  
use SliderBundle\Form\DataTransformer\SliderModelTransformer;  
use SliderBundle\Form\DataTransformer\SliderViewTransformer;  
use Symfony\Component\Form\AbstractType;  
use Symfony\Component\Form\FormBuilderInterface;  
use Symfony\Component\OptionsResolver\OptionsResolver;  

class SliderSelectType extends AbstractType  
{  
  const NAME = 'kiboko_slider_select';  

  /**  
 * {@inheritdoc}  
 */  public function configureOptions(OptionsResolver $resolver)  
 {  $resolver->setDefaults(  
 [  'autocomplete_alias' => 'kiboko_slider',  
  'create_form_route' => 'kiboko_slider_create',  
  'grid_name' => 'kiboko-slider-grid',  
  'configs' => [  
  'placeholder' => 'kiboko.slider.form.choose',  
  ]  
 ] );  
  }  

  /**  
 * {@inheritdoc}  
 */  public function getParent()  
 {  return OroEntitySelectOrCreateInlineType::class;  
  }  

  /**  
 * {@inheritdoc}  
 */  public function getName()  
 {  return $this->getBlockPrefix();  
  }  

  /**  
 * {@inheritdoc}  
 */  public function getBlockPrefix()  
 {  return self::NAME;  
  }  
}

Assurez vous bien d'avoir un search handler:

services.yml : ```kiboko.form.autocomplete.slider.search_handler:
public: false
parent: oro_form.autocomplete.search_handler
arguments:
- '%kiboko.entity.slider.class%'
- ["name"]
tags:
- { name: oro_form.autocomplete.search_handler, alias: kiboko_slider }

search.yml :

search:
SliderBundle\Entity\Slider:
alias: kiboko_slider
title_fields: [name]
route:
name: kiboko_slider_view
parameters:
id: id
search_template: SliderBundle:Search:result.html.twig
fields:
-
name: name
target_type: text
target_fields: [name]


Dans votre fichier de Configuration dans DependencyInjection votre config doit être de type **string**    pour stocker l'id de l'entité.

    'slider' => ['type' => 'string', 'value' => null],

et dans votre fichier system_configuration.yml

slider_bundle.slider:
data_type: string
type: SliderBundle\Form\Type\SliderSelectType
options:
label: kiboko.slider.system_configuration.fields.slider.label
tooltip: kiboko.slider.system_configuration.fields.slider.label.tooltip
required: true
configs:
placeholder: 'kiboko.slider.form.choose_slider'
result_template_twig: 'SliderBundle:Slider:Autocomplete/result.html.twig'
selection_template_twig: 'SliderBundle:Slider:Autocomplete/selection.html.twig'
constraints:
- NotBlank: ~


A vous de personnalisez vos templates twig a votre sauce :)

Bon, jusqu'ici nous avons notre champ select mais ils nous faut la logique de transformation :

Pour cela  créer une classe SystemConfigListener dans un dossier EventListener, comme ceci :
namespace SliderBundle\EventListener;

use Doctrine\Common\Persistence\ManagerRegistry;
use Oro\Bundle\ConfigBundle\Config\ConfigManager;
use Oro\Bundle\ConfigBundle\Event\ConfigSettingsUpdateEvent;
use SliderBundle\DependencyInjection\SliderExtension;

class SystemConfigListener
{
const SETTING = 'slider';

/*
* @var ManagerRegistry
/ protected $registry;

/*
* @var string
/ protected $sliderClass;

/*
* @param ManagerRegistry $registry
* @param string $userClass
/
public function __construct(ManagerRegistry $registry, $userClass)
{ $this->registry = $registry;
$this->sliderClass = $userClass;
}

/
* @param ConfigSettingsUpdateEvent $event
*/
public function onFormPreSetData(ConfigSettingsUpdateEvent $event)
{ $settingsKey = implode(ConfigManager::SECTION_VIEW_SEPARATOR, [SliderExtension::ALIAS, self::SETTING]);
$settings = $event->getSettings();
if (is_array($settings)
&& !empty($settings[$settingsKey]['value'])
) { $settings[$settingsKey]['value'] = $this->registry
->getManagerForClass($this->sliderClass)
->find($this->sliderClass, $settings[$settingsKey]['value']);
$event->setSettings($settings);
}
}
/

* @param ConfigSettingsUpdateEvent $event
*/
public function onSettingsSaveBefore(ConfigSettingsUpdateEvent $event)
{ $settings = $event->getSettings();

if (!array_key_exists('value', $settings)) {
return;
}

if (!is_a($settings['value'], $this->sliderClass)) {
return;
}

/* @var object $owner /
$slider = $settings['value'];
$settings['value'] = $slider->getId();
$event->setSettings($settings);
}
}


Le but de cette classe et de récupérer l'entité en fonction de son id lors du chargement de la page et de transformer cet objet en id lors d'un submit.
et du coup pour ça il faut déclarer le service comme ceci 
````
kiboko.slider.event_listener.system_config:  
    class: SliderBundle\EventListener\SystemConfigListener  
    arguments:  
        - "@doctrine"  
        - '%kiboko.entity.slider.class%'  
  tags:  
        - { name: kernel.event_listener, event: oro_config.settings_form_preset, method: onFormPreSetData }  
        - { name: kernel.event_listener, event: oro_config.settings_before_save.slider_bundle.slider, method: onSettingsSaveBefore }
````

Sachant que  **slider_bundle** c'est votre alias de votre extension et **slider** votre config spécifique :

class SliderExtension extends Extension
{
const ALIAS = 'slider_bundle';

```