Blog / Article #25
[FosUserBundle] Trier ses users alphabétiquement dans son interface admin

star

31 Mai 2013

[fosuserbundle] trier ses users alphabétiquement dans son interface admin

[FosUserBundle] Trier ses users alphabétiquement dans son interface admin

Comme beaucoup de gens qui travaillent avec Symfony2, j'utilise parfois le célèbre bundle FosUserBundle pour gérer les membres de mes sites web. Pas toujours bien sur, car je me suis fait le mien et il fonctionne à peu près aussi bien pour les grosses lignes ; cela dit, des fois, c'est comme ça, je passe quand même par FosUserBundle. Ca fonctionne bien, c'est vite configuré, mon code est maintenable et si jamais un confrère moins cher reprend mon projet, il saura s'y retrouver rapidos. (...) Bref, mon problème, c'est qu'en utilisant le très pratique userManager livré avec le bundle, je n'ai pas trop de choix sur la manière dont sont triés les users lorsque je les appelle en utilisant la méthode findUsers() proposée dans la doc...

$users = $userManager->findUsers(); 

Tout simplement

Cet user manager me renvoie les users triés par ID, car si on remonte un peu dans la classe maman on s’aperçoit que la méthode de repository appelée n'est n'y plus ni moins qu'un findAll()...

    /**
     * {@inheritDoc}
     */
    public function findUsers(){
        return $this->repository->findAll();
    }

Un choix a faire

Donc on a plusieurs choix. Soit on fait la grosse erreur de modifier directement  le contrôleur pour changer la méthode de repository, (faut pas faire ça c'est pas beau) soit on surcharge la classe UserManager pour se faire un service personnalisé, qui ira chercher une méthode personnalisée dans le repository personnalisé. Oh oui, ca serait vraiment Symfonique, ...mais non.
Y a trop de personnalisation pour pas grand chose et je n'ai pas envie de créer des trucs et des machins, juste pour trier mon simple tableau users retourné.

Je trierais tout seul !

Après tout, j'ai un dédié qui envoie du pâté de coq, autant utiliser un peu ses ressources ! Je vais donc trier mon tableau users moi même. Bon je vous rassure tout de suite, toutes mes pages ne listent pas tous mes users à chaque affichages, il s'agit juste de la partie utilisateurs de mon interface d'administration qui n'est visible que pour l'admin du site en question. Sinon, sur une base de plusieurs milliers d'utilisateurs, on se doute bien que faire un tri en temps réel coté serveur sur des listes utilisateurs serait tout sauf une idée ingénieuse en terme de charge mémoire et Cpu...

Le principe

Pour faire mon tri, je vais utiliser deux fonctions principales qui sont la fonction usort()  et la fonction strcmp() . Le principe est de trier le tableau users en utilisant une fonction de comparaison sur les chaines de caractères (sur lesquelles nous souhaitons effectuer notre tri), ici j'ai choisi d'afficher mes users par noms d'utilisateurs (username), tout ce qu'il y a de plus classique. Pour des raisons plus intimes je ferais ma comparaison sur le champ usernameCanonical, car j'ai modifié le canonicalizer par défaut , mais bon, ici, ce n'est pas très important, une comparaison sur le champ username peut tout a fait être suffisante vu que dans une grande majorité de cas, les comparaisons elles mêmes ne se feront vraiment que sur quelques caractères la plupart du temps. Donc pour faire simple, le tableau users une fois passé dans la moulinette sera donc trié par ordre alphabétique, basé sur son attribut usernameCanonical.

Les méthodes

Dans le contrôleur chargé de récupérer les users, je rajoute deux méthodes privées qui me serviront pour l'une à comparer les champs désignés et pour l'autre à mettre mon tableau dans l'ordre en fonction du retour de la première.

private function comparerUsername($a, $b) {
    return strcmp($a->getUsernameCanonical(), $b->getUsernameCanonical());
}
private function trierUsersAlpha(){
    usort ($this->users, array($this,'comparerUsername'));
}

Donc sans rentrer dans les détails des fonctions et de leurs arguments afin d'éviter de s'éloigner du sujet, il faut bien pointer l'attribut (son getter ici) sur lequel on souhaite effectuer la recherche dans la première fonction, la deuxième (trierUsersAlpha) se charge de modifier le tableau. En fait on peut quand même dire que la deuxième fonction boucle sur le tableau (ici $this->users) et renvoie à la première deux indexes du tableau en argument. Cette dernière après avoir comparé renvoie un signal indiquant si la première est supérieure (alphabétiquement) à la deuxième ou inversement ; en fonction de cette valeur de retour, les deux entrées testées sont soit laissées dans l'ordre ou elles sont arrivées, soit permutées. Et puisque c'est une boucle, toutes les entrées y passent pour s'assurer que toutes les lignes soient correctement disposées, de A a Z, ...l'alphabet quoi !

J'en ai vu trois qui se sont barrés en soufflant, alors je vais expliquer avec un exemple car au fond c'est assez simple c'est juste que j'explique assez mal ce que je n'avait pas envie d'expliquer initialement vu que ce n'est pas le but de l'article mais bon, tant que j'y suis...

Explication improvisée du tri

Quand on doit trier une liste de mots, on doit les comparer par paires pour savoir lequel des deux doit se placer avant l'autre dans la liste finale. On imagine que nous devons trier de cette même façon les trois mots Canard, Bateau et Avion. Comme je l'ai dit tout a l'heure on doit boucler sur chaque mots et les comparer par paires donc on peut commencer par comparer dans l'ordre d'arrivée Canard et Bateau. Puisque Canard est plus grand que Bateau, on doit les permuter. On a donc désormais Bateau, Canard, Avion. On continue de boucler en choisissant maintenant le deuxième mot Canard, qu'on comparera avec le troisième, (son suivant) Avion. Canard est plus grand, sans grand suspens, on permute pour alors obtenir à la fin du premier bouclage l'ordre suivant, Bateau, Avion, Canard.
Qui a dit coincoin ?
A chaque fins de boucles, lorsqu'il y a eu besoin de faire des permutations, la fonction reprend de zéro en reprenant le premier mot qu'elle comparera avec le second. Bateau étant plus grand que Avion, les deux mots seront permutés à leur tour. La comparaison des mots Bateau et Canard n’occasionnera pas d'échanges de places. La dernière boucle sur ces mots non plus, on peut donc considérer que le tableau est trié.

Trier les users (enfin)

Donc en fait une fois que ces deux fonction sont agrafées dans votre classe, il s'agit maintenant de s'en servir, et c'est surement ce qu'on aura de plus simple à faire :

    /**
     * La liste des users
     */
    public function listeAction(){
        $userManager = $this->get('fos_user.user_manager');
        $this->users = $userManager->findUsers();        
        $this->trierUsersAlpha();
        
        return $this->render('MonBundle:FosUser:liste.html.twig',array(
            'users'     => $this->users,
        ));
    }

Remarquez que j'ai placé mon tableau d'users en attribut, je trouve plus pratique d'agir sur un attribut directement que devoir passer un tableau en argument puis le récupérer etc.. On peut aussi noter qu'on peu vraiment effectuer le tri sur ce qu'on veut, il suffi juste de modifier la fonction de comparaison, car c'est elle qui compare vraiment. Vous pouvez aussi bien définir votre propre fonction avec sa propre comparaison, sur des dates ou des chiffres ou wtf you want... (oui je parle américain un peu) Le seul truc que la fonction usort demande c'est qu'on lui renvoie -1 , 0 , ou 1 suivant les paires qu'elle envoie à la fonction de comparaison. A vous voyez que ce n'est pas sorcier finalement !


Donc voila, essayez, c'est vite fait et vous allez être bluffé ! Ça prend 3 minutes et tous vos users seront bien rangés ! C'est quand même plus pratique pour retrouver un utilisateur lorsqu'il est présenté par ordre alphabétique :)

PS: Ça existe du pâté de coq en vrai ?


J'ai mis un bouton +1 en dessous je sais pas si il marche...

Auteur de l'article
Retour a la liste