mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
350 lines
11 KiB
PHP
350 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* Chill is a software for social workers
|
|
*
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Chill\PersonBundle\Search;
|
|
|
|
use Chill\MainBundle\Form\GenderType;
|
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
|
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
|
use Chill\MainBundle\Search\AbstractSearch;
|
|
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
|
|
use Chill\MainBundle\Search\ParsingException;
|
|
use Chill\MainBundle\Search\SearchInterface;
|
|
use Chill\MainBundle\Search\Utils\ExtractDateFromPattern;
|
|
use Chill\MainBundle\Search\Utils\ExtractPhonenumberFromPattern;
|
|
use Chill\PersonBundle\Entity\Person;
|
|
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
|
|
use libphonenumber\PhoneNumber;
|
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
|
use Symfony\Component\Form\FormBuilderInterface;
|
|
|
|
class PersonSearch extends AbstractSearch implements HasAdvancedSearchFormInterface
|
|
{
|
|
final public const NAME = 'person_regular';
|
|
|
|
private const POSSIBLE_KEYS = [
|
|
'_default', 'firstname', 'lastname', 'birthdate', 'birthdate-before',
|
|
'birthdate-after', 'gender', 'nationality', 'phonenumber', 'city',
|
|
];
|
|
|
|
public function __construct(private readonly \Twig\Environment $templating, private readonly ExtractDateFromPattern $extractDateFromPattern, private readonly ExtractPhonenumberFromPattern $extractPhonenumberFromPattern, private readonly PaginatorFactory $paginatorFactory, private readonly PersonACLAwareRepositoryInterface $personACLAwareRepository) {}
|
|
|
|
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', ChillDateType::class, [
|
|
'label' => 'Birthdate',
|
|
'required' => false,
|
|
])
|
|
->add('birthdate-after', ChillDateType::class, [
|
|
'label' => 'Birthdate after',
|
|
'required' => false,
|
|
])
|
|
->add('birthdate-before', ChillDateType::class, [
|
|
'label' => 'Birthdate before',
|
|
'required' => false,
|
|
])
|
|
->add('phonenumber', ChillPhoneNumberType::class, [
|
|
'required' => false,
|
|
'label' => 'Part of the phonenumber',
|
|
])
|
|
->add('gender', GenderType::class, [
|
|
'label' => 'Gender',
|
|
'required' => false,
|
|
'expanded' => false,
|
|
'placeholder' => 'All genders',
|
|
])
|
|
->add('city', TextType::class, [
|
|
'required' => false,
|
|
'label' => 'City or postal code',
|
|
]);
|
|
}
|
|
|
|
public function convertFormDataToQuery(array $data)
|
|
{
|
|
$string = '@person ';
|
|
|
|
$string .= !isset($data['_default']) ? '' : $data['_default'].' ';
|
|
|
|
foreach (['firstname', 'lastname', 'gender', 'city'] as $key) {
|
|
$string .= !isset($data[$key]) ? '' : $key.':'.
|
|
// add quote if contains spaces
|
|
(str_contains((string) $data[$key], ' ') ? '"'.$data[$key].'"' : $data[$key])
|
|
.' ';
|
|
}
|
|
|
|
foreach (['birthdate', 'birthdate-before', 'birthdate-after'] as $key) {
|
|
$string .= !isset($data[$key]) ?
|
|
''
|
|
:
|
|
$key.':'.$data[$key]->format('Y-m-d').' ';
|
|
}
|
|
|
|
$string .= !isset($data['phonenumber']) ? '' : 'phonenumber:'.$data['phonenumber']->getNationalNumber();
|
|
|
|
return $string;
|
|
}
|
|
|
|
public function convertTermsToFormData(array $terms)
|
|
{
|
|
$data = [];
|
|
|
|
foreach (['firstname', 'lastname', 'gender', '_default', 'phonenumber', 'city'] 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;
|
|
}
|
|
|
|
if (\array_key_exists('phonenumber', $terms)) {
|
|
try {
|
|
$phonenumber = new PhoneNumber();
|
|
$phonenumber->setNationalNumber($terms['phonenumber']);
|
|
} catch (\Exception $ex) {
|
|
throw new ParsingException('The data for phonenumber is not parsable', 0, $ex);
|
|
}
|
|
|
|
$data['phonenumber'] = $phonenumber;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function getAdvancedSearchTitle(): string
|
|
{
|
|
return 'Search within persons';
|
|
}
|
|
|
|
public static function getAlias(): string
|
|
{
|
|
return self::NAME;
|
|
}
|
|
|
|
/**
|
|
* (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;
|
|
}
|
|
|
|
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = [], $format = 'html')
|
|
{
|
|
$terms = $this->findAdditionnalInDefault($terms);
|
|
$total = $this->count($terms);
|
|
$paginator = $this->paginatorFactory->create($total);
|
|
|
|
if ('html' === $format) {
|
|
return $this->templating->render(
|
|
'@ChillPerson/Person/list_with_period.html.twig',
|
|
[
|
|
'persons' => $this->search($terms, $start, $limit, $options),
|
|
'pattern' => $this->recomposePattern(
|
|
$terms,
|
|
\array_filter(self::POSSIBLE_KEYS, static fn ($item) => '_default' !== $item),
|
|
$terms['_domain']
|
|
),
|
|
'total' => $total,
|
|
'start' => $start,
|
|
'search_name' => self::NAME,
|
|
'preview' => $options[SearchInterface::SEARCH_PREVIEW_OPTION],
|
|
'paginator' => $paginator,
|
|
]
|
|
);
|
|
}
|
|
|
|
// format is "json"
|
|
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): int
|
|
{
|
|
[
|
|
'_default' => $default,
|
|
'firstname' => $firstname,
|
|
'lastname' => $lastname,
|
|
'birthdate' => $birthdate,
|
|
'birthdate-before' => $birthdateBefore,
|
|
'birthdate-after' => $birthdateAfter,
|
|
'gender' => $gender,
|
|
'nationality' => $countryCode,
|
|
'phonenumber' => $phonenumber,
|
|
'city' => $city,
|
|
] = $terms + \array_fill_keys(self::POSSIBLE_KEYS, null);
|
|
|
|
if (null !== $birthdate) {
|
|
try {
|
|
$birthdate = new \DateTime($birthdate);
|
|
} catch (\Exception $e) {
|
|
throw new ParsingException('The date is not parsable', 0, $e);
|
|
}
|
|
}
|
|
|
|
if (null !== $birthdateBefore) {
|
|
try {
|
|
$birthdateBefore = new \DateTime($birthdateBefore);
|
|
} catch (\Exception $e) {
|
|
throw new ParsingException('The date is not parsable', 0, $e);
|
|
}
|
|
}
|
|
|
|
if (null !== $birthdateAfter) {
|
|
try {
|
|
$birthdateAfter = new \DateTime($birthdateAfter);
|
|
} catch (\Exception $e) {
|
|
throw new ParsingException('The date is not parsable', 0, $e);
|
|
}
|
|
}
|
|
|
|
return $this->personACLAwareRepository
|
|
->countBySearchCriteria(
|
|
$default,
|
|
$firstname,
|
|
$lastname,
|
|
$birthdate,
|
|
$birthdateBefore,
|
|
$birthdateAfter,
|
|
$gender,
|
|
$countryCode,
|
|
$phonenumber,
|
|
$city
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return Person[]
|
|
*/
|
|
protected function search(array $terms, int $start, int $limit, array $options = [])
|
|
{
|
|
[
|
|
'_default' => $default,
|
|
'firstname' => $firstname,
|
|
'lastname' => $lastname,
|
|
'birthdate' => $birthdate,
|
|
'birthdate-before' => $birthdateBefore,
|
|
'birthdate-after' => $birthdateAfter,
|
|
'gender' => $gender,
|
|
'nationality' => $countryCode,
|
|
'phonenumber' => $phonenumber,
|
|
'city' => $city,
|
|
] = $terms + \array_fill_keys(self::POSSIBLE_KEYS, null);
|
|
|
|
if (null !== $birthdate) {
|
|
try {
|
|
$birthdate = new \DateTime($birthdate);
|
|
} catch (\Exception $e) {
|
|
throw new ParsingException('The date is not parsable', 0, $e);
|
|
}
|
|
}
|
|
|
|
if (null !== $birthdateBefore) {
|
|
try {
|
|
$birthdateBefore = new \DateTime($birthdateBefore);
|
|
} catch (\Exception $e) {
|
|
throw new ParsingException('The date is not parsable', 0, $e);
|
|
}
|
|
}
|
|
|
|
if (null !== $birthdateAfter) {
|
|
try {
|
|
$birthdateAfter = new \DateTime($birthdateAfter);
|
|
} catch (\Exception $e) {
|
|
throw new ParsingException('The date is not parsable', 0, $e);
|
|
}
|
|
}
|
|
|
|
return $this->personACLAwareRepository
|
|
->findBySearchCriteria(
|
|
$start,
|
|
$limit,
|
|
$options['simplify'] ?? false,
|
|
$default,
|
|
$firstname,
|
|
$lastname,
|
|
$birthdate,
|
|
$birthdateBefore,
|
|
$birthdateAfter,
|
|
$gender,
|
|
$countryCode,
|
|
$phonenumber,
|
|
$city
|
|
);
|
|
}
|
|
|
|
private function findAdditionnalInDefault(array $terms): array
|
|
{
|
|
// chaining some extractor
|
|
$datesResults = $this->extractDateFromPattern->extractDates($terms['_default']);
|
|
$phoneResults = $this->extractPhonenumberFromPattern->extractPhonenumber($datesResults->getFilteredSubject());
|
|
$terms['_default'] = $phoneResults->getFilteredSubject();
|
|
|
|
if (
|
|
$datesResults->hasResult() && (!\array_key_exists('birthdate', $terms)
|
|
|| null !== $terms['birthdate'])
|
|
) {
|
|
$terms['birthdate'] = $datesResults->getFound()[0]->format('Y-m-d');
|
|
}
|
|
|
|
if (
|
|
$phoneResults->hasResult() && (!\array_key_exists('phonenumber', $terms)
|
|
|| null !== $terms['phonenumber'])
|
|
) {
|
|
$terms['phonenumber'] = $phoneResults->getFound()[0];
|
|
}
|
|
|
|
return $terms;
|
|
}
|
|
}
|