refactor: Update source code based on PHP conventions.

This commit is contained in:
Pol Dellaiera
2021-04-26 15:45:05 +02:00
parent 6b2eda0f94
commit 054a28ecf4
766 changed files with 52425 additions and 53000 deletions

View File

@@ -1,80 +1,77 @@
<?php
/*
/**
* Chill is a software for social workers.
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* 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 <http://www.gnu.org/licenses/>.
* @see https://www.champs-libres.coop/
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Search;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Search\AbstractSearch;
use Doctrine\ORM\EntityManagerInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
use Chill\MainBundle\Search\ParsingException;
use Chill\MainBundle\Search\SearchInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\Person;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Exception;
use LogicException;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Chill\MainBundle\Search\ParsingException;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Symfony\Component\Security\Core\Role\Role;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
use Doctrine\ORM\Query;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Role\Role;
use function array_key_exists;
use function in_array;
class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
class PersonSearch extends AbstractSearch implements
ContainerAwareInterface,
HasAdvancedSearchFormInterface
{
use ContainerAwareTrait;
public const NAME = 'person_regular';
/**
* @var PaginatorFactory
*/
protected $paginatorFactory;
private $_cacheQuery = [];
/**
*
* @var EntityManagerInterface
*/
private $em;
/**
*
* @var \Chill\MainBundle\Entity\User
*/
private $user;
/**
*
* @var AuthorizationHelper
*/
private $helper;
/**
*
* @var PaginatorFactory
* @var \Chill\MainBundle\Entity\User
*/
protected $paginatorFactory;
const NAME = "person_regular";
private $user;
public function __construct(
EntityManagerInterface $em,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $helper,
PaginatorFactory $paginatorFactory)
{
EntityManagerInterface $em,
TokenStorageInterface $tokenStorage,
AuthorizationHelper $helper,
PaginatorFactory $paginatorFactory
) {
$this->em = $em;
$this->user = $tokenStorage->getToken()->getUser();
$this->helper = $helper;
@@ -82,13 +79,211 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
// 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'
throw new LogicException('The user provided must be an instance'
. ' of Chill\MainBundle\Entity\User');
}
}
/*
* (non-PHPdoc)
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()
@@ -96,8 +291,9 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
return 100;
}
/*
* (non-PHPdoc)
/**
* (non-PHPdoc).
*
* @see \Chill\MainBundle\Search\SearchInterface::isActiveByDefault()
*/
public function isActiveByDefault()
@@ -105,64 +301,80 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
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;
}
/*
* (non-PHPdoc)
* @see \Chill\MainBundle\Search\SearchInterface::renderResult()
*/
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = array(), $format = 'html')
protected function count(array $terms)
{
$total = $this->count($terms);
$paginator = $this->paginatorFactory->create($total);
$qb = $this->createQuery($terms);
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()
]
];
}
$qb->select('COUNT(p.id)');
return $qb->getQuery()->getSingleScalarResult();
}
/**
*
* @param string $pattern
* @param int $start
* @param int $limit
* @param array $options
*
* @return Person[]
*/
protected function search(array $terms, $start, $limit, array $options = array())
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'
);
'p.id',
$qb->expr()->concat(
'p.firstName',
$qb->expr()->literal(' '),
'p.lastName'
) . 'AS text'
);
} else {
$qb->select('p');
}
@@ -176,219 +388,11 @@ class PersonSearch extends AbstractSearch implements ContainerAwareInterface,
$qb
->orderBy('p.firstName')
->addOrderBy('p.lastName');
if ($options['simplify'] ?? false) {
return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
} else {
return $qb->getQuery()->getResult();
}
return $qb->getQuery()->getResult();
}
protected function count(array $terms)
{
$qb = $this->createQuery($terms);
$qb->select('COUNT(p.id)');
return $qb->getQuery()->getSingleScalarResult();
}
private $_cacheQuery = array();
/**
*
* @param array $terms
* @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'], array(Person::MALE_GENDER, Person::FEMALE_GENDER))) {
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 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;
}
public function getAdvancedSearchTitle()
{
return 'Search within persons';
}
}