em = $em; $this->user = $tokenStorage->getToken()->getUser(); $this->helper = $helper; $this->paginatorFactory = $paginatorFactory; // throw an error if user is not a valid user if (!$this->user instanceof \Chill\MainBundle\Entity\User) { throw new LogicException('The user provided must be an instance' . ' of Chill\MainBundle\Entity\User'); } } public function buildForm(FormBuilderInterface $builder) { $builder ->add('_default', TextType::class, [ 'label' => 'First name or Last name', 'required' => false, ]) ->add('firstname', TextType::class, [ 'label' => 'First name', 'required' => false, ]) ->add('lastname', TextType::class, [ 'label' => 'Last name', 'required' => false, ]) ->add('birthdate-after', ChillDateType::class, [ 'label' => 'Birthdate after', 'required' => false, ]) ->add('birthdate', ChillDateType::class, [ 'label' => 'Birthdate', 'required' => false, ]) ->add('birthdate-before', ChillDateType::class, [ 'label' => 'Birthdate before', 'required' => false, ]) ->add('gender', ChoiceType::class, [ 'choices' => [ 'Man' => Person::MALE_GENDER, 'Woman' => Person::FEMALE_GENDER, ], 'label' => 'Gender', 'required' => false, ]); } public function convertFormDataToQuery(array $data) { $string = '@person '; $string .= empty($data['_default']) ? '' : $data['_default'] . ' '; foreach (['firstname', 'lastname', 'gender'] as $key) { $string .= empty($data[$key]) ? '' : $key . ':' . // add quote if contains spaces (strpos($data[$key], ' ') !== false ? '"' . $data[$key] . '"' : $data[$key]) . ' '; } foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) { $string .= empty($data[$key]) ? '' : $key . ':' . $data[$key]->format('Y-m-d') . ' '; } return $string; } public function convertTermsToFormData(array $terms) { foreach (['firstname', 'lastname', 'gender', '_default'] as $key) { $data[$key] = $terms[$key] ?? null; } // parse dates foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) { if (array_key_exists($key, $terms)) { try { $date = new DateTime($terms[$key]); } catch (Exception $ex) { throw new ParsingException("The date for {$key} is " . 'not parsable', 0, $ex); } } $data[$key] = $date ?? null; } return $data; } /** * @return \Doctrine\ORM\QueryBuilder */ public function createQuery(array $terms) { //get from cache $cacheKey = md5(serialize($terms)); if (array_key_exists($cacheKey, $this->_cacheQuery)) { return clone $this->_cacheQuery[$cacheKey]; } $qb = $this->em->createQueryBuilder(); $qb->from('ChillPersonBundle:Person', 'p'); if (array_key_exists('firstname', $terms)) { $qb->andWhere($qb->expr()->like('UNACCENT(LOWER(p.firstName))', ':firstname')) ->setParameter('firstname', '%' . $terms['firstname'] . '%'); } if (array_key_exists('lastname', $terms)) { $qb->andWhere($qb->expr()->like('UNACCENT(LOWER(p.lastName))', ':lastname')) ->setParameter('lastname', '%' . $terms['lastname'] . '%'); } foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) { if (array_key_exists($key, $terms)) { try { $date = new DateTime($terms[$key]); } catch (Exception $ex) { throw new ParsingException('The date is ' . 'not parsable', 0, $ex); } switch ($key) { case 'birthdate': $qb->andWhere($qb->expr()->eq('p.birthdate', ':birthdate')) ->setParameter('birthdate', $date); break; case 'birthdate-before': $qb->andWhere($qb->expr()->lt('p.birthdate', ':birthdatebefore')) ->setParameter('birthdatebefore', $date); break; case 'birthdate-after': $qb->andWhere($qb->expr()->gt('p.birthdate', ':birthdateafter')) ->setParameter('birthdateafter', $date); break; default: throw new LogicException("this case {$key} should not exists"); } } } if (array_key_exists('gender', $terms)) { if (!in_array($terms['gender'], [Person::MALE_GENDER, Person::FEMALE_GENDER], true)) { throw new ParsingException('The gender ' . $terms['gender'] . ' is not accepted. Should be "' . Person::MALE_GENDER . '" or "' . Person::FEMALE_GENDER . '"'); } $qb->andWhere($qb->expr()->eq('p.gender', ':gender')) ->setParameter('gender', $terms['gender']); } if (array_key_exists('nationality', $terms)) { try { $country = $this->em->createQuery('SELECT c FROM ' . 'ChillMainBundle:Country c WHERE ' . 'LOWER(c.countryCode) LIKE :code') ->setParameter('code', $terms['nationality']) ->getSingleResult(); } catch (\Doctrine\ORM\NoResultException $ex) { throw new ParsingException('The country code "' . $terms['nationality'] . '" ' . ', used in nationality, is unknow', 0, $ex); } $qb->andWhere($qb->expr()->eq('p.nationality', ':nationality')) ->setParameter('nationality', $country); } if ('' !== $terms['_default']) { $grams = explode(' ', $terms['_default']); foreach ($grams as $key => $gram) { $qb->andWhere($qb->expr() ->like('p.fullnameCanonical', 'UNACCENT(LOWER(:default_' . $key . '))')) ->setParameter('default_' . $key, '%' . $gram . '%'); } } //restraint center for security $reachableCenters = $this->helper->getReachableCenters( $this->user, new Role('CHILL_PERSON_SEE') ); $qb->andWhere($qb->expr() ->in('p.center', ':centers')) ->setParameter('centers', $reachableCenters); $this->_cacheQuery[$cacheKey] = $qb; return clone $qb; } public function getAdvancedSearchTitle() { return 'Search within persons'; } /** * (non-PHPdoc). * * @see \Chill\MainBundle\Search\SearchInterface::getOrder() */ public function getOrder() { return 100; } /** * (non-PHPdoc). * * @see \Chill\MainBundle\Search\SearchInterface::isActiveByDefault() */ public function isActiveByDefault() { return true; } /** * (non-PHPdoc). * * @see \Chill\MainBundle\Search\SearchInterface::renderResult() * * @param mixed $start * @param mixed $limit * @param mixed $format */ public function renderResult(array $terms, $start = 0, $limit = 50, array $options = [], $format = 'html') { $total = $this->count($terms); $paginator = $this->paginatorFactory->create($total); if ('html' === $format) { return $this->container->get('templating')->render( 'ChillPersonBundle:Person:list.html.twig', [ 'persons' => $this->search($terms, $start, $limit, $options), 'pattern' => $this->recomposePattern($terms, ['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 ('json' === $format) { return [ 'results' => $this->search($terms, $start, $limit, array_merge($options, ['simplify' => true])), 'pagination' => [ 'more' => $paginator->hasNextPage(), ], ]; } } public function supports($domain, $format) { return 'person' === $domain; } protected function count(array $terms) { $qb = $this->createQuery($terms); $qb->select('COUNT(p.id)'); return $qb->getQuery()->getSingleScalarResult(); } /** * @param int $start * @param int $limit * * @return Person[] */ protected function search(array $terms, $start, $limit, array $options = []) { $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 ->setMaxResults($limit) ->setFirstResult($start); //order by firstname, lastname $qb ->orderBy('p.firstName') ->addOrderBy('p.lastName'); if ($options['simplify'] ?? false) { return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY); } return $qb->getQuery()->getResult(); } }