Current File : //var/www/prestashop/src/PrestaShopBundle/Form/Admin/Type/EntitySearchInputType.php |
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
declare(strict_types=1);
namespace PrestaShopBundle\Form\Admin\Type;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\TranslatorInterface;
/**
* This form type is used for a OneToMany (or ManyToMany) association, it allows to search a list of entities
* (based on a remote url) and associate it. It is based on the CollectionType form type which provides prototype
* features to display a custom template for each associated items.
*
* A default entry type is provided with this form type @see EntityItemType which is composed of three inputs:
* - id
* - name
* - image
*
* Thus matches the default mapping of this form type via prototype_mapping, but you can change this entry type
* to change the included data, the rendering and/or the mapping. In front the EntitySearchInput js component
* will automatically adapt to the new mapping.
*/
class EntitySearchInputType extends CollectionType
{
public const LIST_LAYOUT = 'list';
public const TABLE_LAYOUT = 'table';
/**
* @var TranslatorInterface
*/
private $translator;
/**
* @param TranslatorInterface $translator
*/
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* {@inheritDoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults([
// These are parameters from collection type which default values are modified
'allow_add' => true,
'allow_delete' => true,
'allow_search' => true,
'prototype_name' => '__entity_index__',
// Default entry type that matches the default template from the prestashop ui kit form theme
'entry_type' => EntityItemType::class,
'entry_options' => [
// Force block prefix to easily profit from the UI kit theme (without changing it in the entity type itself)
'block_prefix' => 'entity_item',
],
// This is an optional entity type that can be useful to identify which type of entity is searched
'entity_type' => null,
// The remote url is used internally by a javascript component which performs a request when search input is used
'remote_url' => null,
// Max number of selectable entities (0 is unlimited)
'limit' => 0,
// Min length before suggestions start getting rendered
'min_length' => 2,
// Search input attributes (if needed to be customized)
'search_attr' => [],
// List container attributes (if needed to be customized)
'list_attr' => [],
// Search input placeholder
'placeholder' => '',
// This mapping array indicate which field from the entity must be used and what placeholder use to replace
// it (the placeholder must be used in the prototype so that the value is in the right place)
'prototype_mapping' => null,
'identifier_field' => 'id',
// Specify IDs that must be filtered out of suggestions
'filtered_identities' => [],
// Layout
'layout' => static::LIST_LAYOUT,
// Remove modal wording
'remove_modal' => null,
// Empty state wording
'empty_state' => null,
// field name in record dataset which should be used to show suggestion in search dropdown
'suggestion_field' => 'name',
]);
$resolver->setAllowedTypes('allow_search', ['bool']);
$resolver->setAllowedTypes('search_attr', ['array']);
$resolver->setAllowedTypes('list_attr', ['array']);
$resolver->setAllowedTypes('placeholder', ['string']);
$resolver->setAllowedTypes('remote_url', ['string', 'null']);
$resolver->setAllowedTypes('limit', ['int']);
$resolver->setAllowedTypes('min_length', ['int']);
$resolver->setAllowedTypes('entity_type', ['string', 'null']);
$resolver->setAllowedTypes('prototype_mapping', ['array', 'null']);
$resolver->setAllowedTypes('identifier_field', ['string']);
$resolver->setAllowedTypes('filtered_identities', ['array']);
$resolver->setAllowedTypes('remove_modal', ['array', 'null']);
$resolver->setNormalizer('remove_modal', function (Options $options, $value) {
return $this->getRemoveModalResolver()->resolve($value ?? []);
});
$resolver->setAllowedTypes('layout', ['string']);
$resolver->setAllowedValues('layout', [static::LIST_LAYOUT, static::TABLE_LAYOUT]);
$resolver->setAllowedTypes('empty_state', ['string', 'null']);
$resolver->setAllowedTypes('suggestion_field', ['string', 'null']);
}
/**
* {@inheritDoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
// If no mapping has been defined it is built based on the prototype field names
/** @var FormInterface $prototype */
$prototype = $form->getConfig()->getAttribute('prototype');
if (empty($options['prototype_mapping'])) {
$options['prototype_mapping'] = [];
foreach ($prototype->all() as $prototypeChild) {
$options['prototype_mapping'][$prototypeChild->getName()] = sprintf(
'__%s__',
$prototypeChild->getName()
);
}
}
// Force the data in prototype so that placeholders are injected in the prototype template then render the view
$prototype->setData($options['prototype_mapping']);
parent::buildView($view, $form, $options);
// Reformat parameter name for javascript (PHP and JS don't have same naming conventions)
$removeModal = $options['remove_modal'];
$removeModal['buttonClass'] = $removeModal['button_class'];
unset($removeModal['button_class']);
$view->vars = array_replace($view->vars, [
'allow_search' => $options['allow_search'],
'remote_url' => $options['remote_url'],
'limit' => $options['limit'],
'min_length' => $options['min_length'],
'search_attr' => $options['search_attr'],
'list_attr' => $options['list_attr'],
'placeholder' => $options['placeholder'],
'prototype_mapping' => $options['prototype_mapping'],
'remove_modal' => $removeModal,
'list_layout' => $options['layout'],
'empty_state' => $options['empty_state'],
'identifier_field' => $options['identifier_field'],
'filtered_identities' => $options['filtered_identities'],
'suggestion_field' => $options['suggestion_field'],
]);
}
/**
* Returns the block prefix of this type.
*
* @return string The prefix name
*/
public function getBlockPrefix()
{
return 'entity_search_input';
}
/**
* @param string $key
* @param string $domain
* @param array $parameters
*
* @return string
*/
protected function trans(string $key, string $domain, array $parameters = []): string
{
return $this->translator->trans($key, $parameters, $domain);
}
/**
* @return OptionsResolver
*/
private function getRemoveModalResolver(): OptionsResolver
{
$externalLinkResolver = new OptionsResolver();
$externalLinkResolver
->setRequired(['title', 'message', 'apply', 'cancel', 'button_class'])
->setDefaults([
'id' => 'modal-confirm-remove-entity',
'title' => $this->trans('Delete item', 'Admin.Notifications.Warning'),
'message' => $this->trans('Are you sure you want to delete this item?', 'Admin.Notifications.Warning'),
'apply' => $this->trans('Delete', 'Admin.Actions'),
'cancel' => $this->trans('Cancel', 'Admin.Actions'),
'button_class' => 'btn-danger',
])
->setAllowedTypes('id', 'string')
->setAllowedTypes('title', 'string')
->setAllowedTypes('message', 'string')
->setAllowedTypes('apply', 'string')
->setAllowedTypes('cancel', 'string')
->setAllowedTypes('button_class', 'string')
;
return $externalLinkResolver;
}
}