diff --git a/Controller/PersonController.php b/Controller/PersonController.php
index abec3629f..1ab1f5fa1 100644
--- a/Controller/PersonController.php
+++ b/Controller/PersonController.php
@@ -32,6 +32,7 @@ use Symfony\Component\Security\Core\Role\Role;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Search\SimilarPersonMatcher;
use Symfony\Component\Translation\TranslatorInterface;
+use Chill\MainBundle\Search\SearchProvider;
class PersonController extends Controller
{
@@ -46,7 +47,7 @@ class PersonController extends Controller
* @var TranslatorInterface
*/
protected $translator;
-
+
public function __construct(
SimilarPersonMatcher $similarPersonMatcher,
TranslatorInterface $translator
diff --git a/Form/ChoiceLoader/PersonChoiceLoader.php b/Form/ChoiceLoader/PersonChoiceLoader.php
new file mode 100644
index 000000000..1c2ea9897
--- /dev/null
+++ b/Form/ChoiceLoader/PersonChoiceLoader.php
@@ -0,0 +1,104 @@
+
+ *
+ * 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\PersonBundle\Form\ChoiceLoader;
+
+use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
+use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
+use Doctrine\ORM\EntityRepository;
+use Symfony\Component\Form\ChoiceList\LazyChoiceList;
+use Chill\PersonBundle\Entity\Person;
+
+/**
+ *
+ *
+ * @author Julien Fastré
+ */
+class PersonChoiceLoader implements ChoiceLoaderInterface
+{
+ /**
+ *
+ * @var EntityRepository
+ */
+ protected $personRepository;
+
+ protected $lazyLoadedPersons = [];
+
+ protected $centers = [];
+
+ public function __construct(
+ EntityRepository $personRepository,
+ array $centers = null
+ ) {
+ $this->personRepository = $personRepository;
+ if (NULL !== $centers) {
+ $this->centers = $centers;
+ }
+ }
+
+ protected function hasCenterFilter()
+ {
+ return count($this->centers) > 0;
+ }
+
+ public function loadChoiceList($value = null): ChoiceListInterface
+ {
+ $list = new \Symfony\Component\Form\ChoiceList\ArrayChoiceList(
+ $this->lazyLoadedPersons,
+ function(Person $p) use ($value) {
+ return \call_user_func($value, $p);
+ });
+
+ return $list;
+ }
+
+ public function loadChoicesForValues(array $values, $value = null)
+ {
+ $choices = [];
+
+ foreach($values as $value) {
+ $person = $this->personRepository->find($value);
+
+ if ($this->hasCenterFilter() &&
+ !\in_array($person->getCenter(), $this->centers)) {
+ throw new \RuntimeException("chosen a person not in correct center");
+ }
+
+ $choices[] = $person;
+ }
+
+ return $choices;
+ }
+
+ public function loadValuesForChoices(array $choices, $value = null)
+ {
+ $values = [];
+
+ foreach ($choices as $choice) {
+ if (NULL === $choice) {
+ $values[] = null;
+ continue;
+ }
+
+ $id = \call_user_func($value, $choice);
+ $values[] = $id;
+ $this->lazyLoadedPersons[$id] = $choice;
+ }
+
+ return $values;
+ }
+}
diff --git a/Form/Type/PickPersonType.php b/Form/Type/PickPersonType.php
index 7ee2f3fb4..cd45f7dc7 100644
--- a/Form/Type/PickPersonType.php
+++ b/Form/Type/PickPersonType.php
@@ -26,12 +26,16 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
-
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Chill\MainBundle\Entity\GroupCenter;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Entity\Center;
use Chill\PersonBundle\Entity\PersonRepository;
+use Chill\PersonBundle\Search\PersonSearch;
+use Symfony\Component\Translation\TranslatorInterface;
+use Chill\PersonBundle\Form\ChoiceLoader\PersonChoiceLoader;
+use Symfony\Component\OptionsResolver\Options;
/**
* This type allow to pick a person.
@@ -67,22 +71,36 @@ class PickPersonType extends AbstractType
* @var AuthorizationHelper
*/
protected $authorizationHelper;
+
+ /**
+ *
+ * @var UrlGeneratorInterface
+ */
+ protected $urlGenerator;
+
+ /**
+ *
+ * @var TranslatorInterface
+ */
+ protected $translator;
public function __construct(
PersonRepository $personRepository,
TokenStorageInterface $tokenStorage,
- AuthorizationHelper $authorizationHelper
+ AuthorizationHelper $authorizationHelper,
+ UrlGeneratorInterface $urlGenerator,
+ TranslatorInterface $translator
)
{
$this->personRepository = $personRepository;
$this->user = $tokenStorage->getToken()->getUser();
$this->authorizationHelper = $authorizationHelper;
+ $this->urlGenerator = $urlGenerator;
+ $this->translator = $translator;
}
- public function buildForm(FormBuilderInterface $builder, array $options)
+ protected function filterCentersfom(Options $options)
{
- $qb = $options['query_builder'];
-
if ($options['role'] === NULL) {
$centers = array_map(function (GroupCenter $g) {
@@ -98,10 +116,10 @@ class PickPersonType extends AbstractType
$selectedCenters = $centers;
} else {
$selectedCenters = array();
- $options['centers'] = is_array($options['centers']) ?
+ $optionsCenters = is_array($options['centers']) ?
$options['centers'] : array($options['centers']);
- foreach ($options['centers'] as $c) {
+ foreach ($optionsCenters as $c) {
// check that every member of the array is a center
if (!$c instanceof Center) {
throw new \RuntimeException('Every member of the "centers" '
@@ -115,14 +133,8 @@ class PickPersonType extends AbstractType
$selectedCenters[] = $c;
}
}
-
-
- $qb
- ->orderBy('p.firstName', 'ASC')
- ->orderBy('p.lastName', 'ASC')
- ->where($qb->expr()->in('p.center', ':centers'))
- ->setParameter('centers', $selectedCenters)
- ;
+
+ return $selectedCenters;
}
public function configureOptions(OptionsResolver $resolver)
@@ -151,7 +163,11 @@ class PickPersonType extends AbstractType
);
},
'attr' => array('class' => 'select2 '),
- 'query_builder' => $this->personRepository->createQueryBuilder('p')
+ 'choice_loader' => function(Options $options) {
+ $centers = $this->filterCentersfom($options);
+
+ return new PersonChoiceLoader($this->personRepository, $centers);
+ }
));
}
@@ -159,5 +175,17 @@ class PickPersonType extends AbstractType
{
return EntityType::class;
}
+
+ public function buildView(\Symfony\Component\Form\FormView $view, \Symfony\Component\Form\FormInterface $form, array $options)
+ {
+ $view->vars['attr']['data-person-picker'] = true;
+ $view->vars['attr']['data-select-interactive-loading'] = true;
+ $view->vars['attr']['data-search-url'] = $this->urlGenerator
+ ->generate('chill_main_search', [ 'name' => PersonSearch::NAME, '_format' => 'json' ]);
+ $view->vars['attr']['data-placeholder'] = $this->translator->trans($options['placeholder']);
+ $view->vars['attr']['data-no-results-label'] = $this->translator->trans('select2.no_results');
+ $view->vars['attr']['data-error-load-label'] = $this->translator->trans('select2.error_loading');
+ $view->vars['attr']['data-searching-label'] = $this->translator->trans('select2.searching');
+ }
}
diff --git a/Resources/config/services.yml b/Resources/config/services.yml
index 5a61e8378..ceeeeeaed 100644
--- a/Resources/config/services.yml
+++ b/Resources/config/services.yml
@@ -65,6 +65,8 @@ services:
- "@chill.person.repository.person"
- "@security.token_storage"
- "@chill.main.security.authorization.helper"
+ - '@Symfony\Component\Routing\Generator\UrlGeneratorInterface'
+ - '@Symfony\Component\Translation\TranslatorInterface'
tags:
- { name: form.type }
diff --git a/Search/PersonSearch.php b/Search/PersonSearch.php
index 7e349ba27..0c9d0eab4 100644
--- a/Search/PersonSearch.php
+++ b/Search/PersonSearch.php
@@ -35,6 +35,7 @@ use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
+use Doctrine\ORM\Query;
class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
HasAdvancedSearchFormInterface
@@ -104,7 +105,7 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
return true;
}
- public function supports($domain)
+ public function supports($domain, $format)
{
return 'person' === $domain;
}
@@ -113,23 +114,32 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
* (non-PHPdoc)
* @see \Chill\MainBundle\Search\SearchInterface::renderResult()
*/
- public function renderResult(array $terms, $start = 0, $limit = 50, array $options = array())
+ public function renderResult(array $terms, $start = 0, $limit = 50, array $options = array(), $format = 'html')
{
$total = $this->count($terms);
$paginator = $this->paginatorFactory->create($total);
- return $this->container->get('templating')->render('ChillPersonBundle:Person:list.html.twig',
- array(
- 'persons' => $this->search($terms, $start, $limit, $options),
- 'pattern' => $this->recomposePattern($terms, array('nationality',
- 'firstname', 'lastname', 'birthdate', 'gender',
- 'birthdate-before','birthdate-after'), $terms['_domain']),
- 'total' => $total,
- 'start' => $start,
- 'search_name' => self::NAME,
- 'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION],
- 'paginator' => $paginator
- ));
+ if ($format === 'html') {
+ return $this->container->get('templating')->render('ChillPersonBundle:Person:list.html.twig',
+ array(
+ 'persons' => $this->search($terms, $start, $limit, $options),
+ 'pattern' => $this->recomposePattern($terms, array('nationality',
+ 'firstname', 'lastname', 'birthdate', 'gender',
+ 'birthdate-before','birthdate-after'), $terms['_domain']),
+ 'total' => $total,
+ 'start' => $start,
+ 'search_name' => self::NAME,
+ 'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION],
+ 'paginator' => $paginator
+ ));
+ } elseif ($format === 'json') {
+ return [
+ 'results' => $this->search($terms, $start, $limit, \array_merge($options, [ 'simplify' => true ])),
+ 'pagination' => [
+ 'more' => $paginator->hasNextPage()
+ ]
+ ];
+ }
}
/**
@@ -143,17 +153,35 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
protected function search(array $terms, $start, $limit, array $options = array())
{
$qb = $this->createQuery($terms, 'search');
+
+ if ($options['simplify'] ?? false) {
+ $qb->select(
+ 'p.id',
+ $qb->expr()->concat(
+ 'p.firstName',
+ $qb->expr()->literal(' '),
+ 'p.lastName'
+ ).'AS text'
+ );
+ } else {
+ $qb->select('p');
+ }
- $qb->select('p')
- ->setMaxResults($limit)
- ->setFirstResult($start);
+ $qb
+ ->setMaxResults($limit)
+ ->setFirstResult($start);
//order by firstname, lastname
- $qb->orderBy('p.firstName')
- ->addOrderBy('p.lastName');
-
- return $qb->getQuery()->getResult();
+ $qb
+ ->orderBy('p.firstName')
+ ->addOrderBy('p.lastName');
+
+ if ($options['simplify'] ?? false) {
+ return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
+ } else {
+ return $qb->getQuery()->getResult();
+ }
}
protected function count(array $terms)