Commit de52c23a authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Recherche textuelle de compte utilisateur : possibilité de chercher 2 mots ou...

Recherche textuelle de compte utilisateur : possibilité de chercher 2 mots ou plus, portant aussi sur l'individu lié éventuel.
parent 8759b1f6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ Journal des modifications
- Annuaire des thèses/HDR : affichage systématique de l'établissement d'inscription et plus seulement si le filtre est vierge.
- Formulaire de création de compte local : obligation de choisir une civilité (Mme, M. ou Neutre).
- Formulaire Admission : mention de la civilité neutre en plus de Mme M.
- Recherche textuelle de compte utilisateur : possibilité de chercher 2 mots ou plus, portant aussi sur l'individu lié éventuel.
- [FIX] Modification d'une structure impossible
- [FIX] Recherche d'individus : suppression de la limite de 100 résultats maximum.
- [FIX] Dépôt de thèse : plantage de l'annulation de la validation des corrections de la thèse.
+2 −2
Original line number Diff line number Diff line
@@ -44,10 +44,10 @@ use UnicaenAuthentification\Options\ModuleOptions;
use UnicaenAuthentification\Service\User as AuthentificationUserService;
use UnicaenAuthToken\Controller\TokenController;
use UnicaenAuthToken\Service\TokenServiceAwareTrait;
use UnicaenAuthToken\Service\TokenServiceException;
use Webmozart\Assert\Assert;

/**
 * @property \Application\Service\Utilisateur\UtilisateurSearchService $searchService
 * @method \Application\Controller\Plugin\Forward forward()
 */
class UtilisateurController extends \UnicaenAuthentification\Controller\UtilisateurController implements SearchControllerInterface
@@ -122,7 +122,7 @@ class UtilisateurController extends \UnicaenAuthentification\Controller\Utilisat
     *
     * @return ViewModel|Response
     */
    public function indexAction()
    public function indexAction(): Response|ViewModel
    {
        $result = $this->search();
        if ($result instanceof Response) {
+58 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

namespace Application\Entity\Db\Repository;

use Doctrine\DBAL\Exception;
use Individu\Entity\Db\Individu;
use Application\Entity\Db\Utilisateur;
use Doctrine\ORM\NonUniqueResultException;
@@ -62,6 +63,63 @@ class UtilisateurRepository extends DefaultEntityRepository
        return $utilisateur;
    }

    /**
     * Recherche textuelle d'utilisateurs, en SQL pur.
     *
     * @param string $text
     * @param int $limit
     *
     * @return int[] Ids des utilisateurs trouvés
     */
    public function findByText(string $text, int $limit = 100): array
    {
        if (strlen($text) < 2) return [];

        $pieces = explode(' ', $text);

        $sql =
            "select u.* " .
            "from utilisateur u " .
            "left join individu i on i.id = u.individu_id " .
            "left join individu_rech ir on ir.id = i.id ";

        $criteria = [
            'u.username',
            'u.email',
            'u.display_name',
            'ir.haystack',
        ];

        $sqlCri = [];
        foreach ($pieces as $p) {
            $sqlCri[$p] = [];
            foreach ($criteria as $c) {
                $sqlCri[$p][] = "$c like str_reduce($$%" . $p . "%$$)";
            }
            $sqlCri[$p] = '(' . implode(' or ', $sqlCri[$p]) . ')';
        }
        $sqlCri = implode(' and ', $sqlCri);

        $sql .= 'where (' . $sqlCri . ') ';
        if ($limit > 0) {
            $sql .= " limit " . $limit;
        }

        try {
            $stmt = $this->getEntityManager()->getConnection()->executeQuery($sql);
        } catch (Exception $e) {
            throw new RuntimeException(
                "Erreur rencontrée lors de l'exécution de la requête de recherche", null, $e);
        }

        try {
            return $stmt->fetchAllAssociative();
        } catch (Exception $e) {
            throw new RuntimeException(
                "Impossible d'obtenir les résultats de la requête de recherche", null, $e);
        }
    }

    /**
     * Recherche un utilisateur par le token permettant de changer de mot de passe.
     */
+20 −11
Original line number Diff line number Diff line
@@ -13,9 +13,6 @@ class UtilisateurSearchService extends SearchService
{
    use UtilisateurServiceAwareTrait;

    /**
     * @inheritDoc
     */
    protected function createQueryBuilder(): QueryBuilder
    {
        $qb = $this->utilisateurService->getRepository()->createQueryBuilder('u');
@@ -30,18 +27,30 @@ class UtilisateurSearchService extends SearchService
    {
        // filtre textuel
        $textFilter = new TextSearchFilter("Recherche textuelle", 'text');
        $textFilter->setAttributes(['title' => "Recherche sur le Display name, Username, Email ou Individu"]);
        $textFilter->setAttributes(['title' => "Recherche sur l'identité, l'identifiant de connexion, l'adresse électronique, ou l'utilisateur lié"]);
        $textFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) {
            $qb
                ->andWhere($qb->expr()->orX(
                    'UPPER(u.username)    LIKE UPPER(:text)',
                    'UPPER(u.displayName) LIKE UPPER(:text)',
                    'UPPER(u.email)       LIKE UPPER(:text)'
                ))
                ->setParameter('text', '%' . $filter->getValue() . '%');
            $utilisateurs = $this->utilisateurService->getRepository()->findByText($filter->getValue(), limit: 0);
            if (count($utilisateurs) > 0) {
                $utilisateursIds = array_map(fn(array $utilisateur) => $utilisateur['id'], $utilisateurs);
                $qb->andWhere($qb->expr()->in('u.id', $utilisateursIds));
            }
        });
        $this->addFilter($textFilter);

//        // filtre textuel
//        $textFilter = new TextSearchFilter("Recherche textuelle", 'text');
//        $textFilter->setAttributes(['title' => "Recherche sur le Display name, Username, Email ou Individu"]);
//        $textFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) {
//            $qb
//                ->andWhere($qb->expr()->orX(
//                    'UPPER(u.username)    LIKE UPPER(:text)',
//                    'UPPER(u.displayName) LIKE UPPER(:text)',
//                    'UPPER(u.email)       LIKE UPPER(:text)'
//                ))
//                ->setParameter('text', '%' . $filter->getValue() . '%');
//        });
//        $this->addFilter($textFilter);

        // filtre "est lié à un individu"
        $individuFilter = new CheckboxSearchFilter("Lié à un<br>individu", 'individu');
        $individuFilter->setQueryBuilderApplier(function(SearchFilter $filter, QueryBuilder $qb) {