Tri Alphanumérique en PHP
Dans le développement web, il est fréquent d’afficher des listes d’éléments qui peuvent contenir des mots composés uniquement de lettres, de chiffres ou une combinaison des deux. Par exemple, nous pourrions avoir des éléments tels que « A », « B », « U1 », « U2 », « U10 », « P », « R », « N1 », et « Z ».
Cependant, lorsqu’un tri standard est appliqué, les résultats peuvent être inattendus. Par exemple, dans certains cas, « U2 » pourrait être placé après « U10 » en raison d’un tri basé sur l’ordre lexicographique pur. Ce type de tri ne reflète pas toujours l’ordre numérique logique que l’on pourrait attendre.
Cet article explore comment effectuer un tri alphanumérique correct en PHP pour gérer ces cas de manière appropriée.
Nous allons donc aborder les étapes suivantes :
- Récupérer des données à partir d’une base de données MySQL.
- Afficher les données brutes
- Afficher un tri basé uniquement sur SQL
- Appliquer un tri alphanumérique approprié.
Récupération des Données
maginons que nous ayons une table MySQL nommée tab_options
qui contient des éléments avec différents labels. Notre objectif est de récupérer ces labels pour les trier et les afficher dans le bon ordre, en prenant en compte à la fois les chaînes alphabétiques et alphanumériques.
CREATE TABLE `tab_options` (
`ch_opts_id` INT AUTO_INCREMENT PRIMARY KEY,
`ch_opts_type` VARCHAR(50),
`ch_opts_slug` VARCHAR(50), -- renommé de `ch_opts_nature`
`ch_opts_label` VARCHAR(50), -- le libellé que l'on affiche
`ch_opts_etat` TINYINT(1) -- statut de l'option (actif/inactif)
);
INSERT INTO `tab_options` (`ch_opts_id`, `ch_opts_type`, `ch_opts_slug`, `ch_opts_label`, `ch_opts_etat`) VALUES
(3, 'ch_champ_categorie', 'u1', 'U1', '1'),
(4, 'ch_champ_categorie', 'u2', 'U2', '1'),
(5, 'ch_champ_categorie', 'u10', 'U10', '1'),
(6, 'ch_champ_categorie', 'p', 'P', '1'),
(7, 'ch_champ_categorie', 'r', 'R', '1'),
(8, 'ch_champ_categorie', 'n1', 'N1', '1'),
(9, 'ch_champ_categorie', 'n2', 'N2', '1'),
(10, 'ch_champ_categorie', 'a', 'A', '1'),
(11, 'ch_champ_categorie', 'z', 'Z', '1'),
(12, 'ch_champ_categorie', 'espoir', 'Espoir', '1'),
(13, 'ch_champ_categorie', 'elite', 'Élite', '1');
Maintenant que la structure de la base de données est en place, nous allons écrire un script PHP pour récupérer les données et les traiter sous forme d’un tableau d’objets. Pour cela, il est essentiel d’établir d’abord une connexion à la base de données. PHP permet de le faire de manière simple et sécurisée grâce à l’extension PDO (PHP Data Objects). Cette méthode est recommandée car elle assure une connexion flexible et sécurisée, et elle supporte également les requêtes préparées, ce qui est crucial pour éviter les injections SQL.
Dans cet exemple, nous utiliserons des identifiants génériques pour la base de données. Cependant, vous devrez les remplacer par vos propres paramètres en fonction de votre environnement : l’hôte de la base de données (souvent localhost
pour un serveur local), le nom de la base de données, l’utilisateur et le mot de passe.
<?php
// Remplacez les valeurs suivantes par les paramètres de votre propre base de données
$host = 'localhost'; // L'adresse de votre serveur de base de données (souvent 'localhost' pour un serveur local)
$db = 'nom_de_votre_base'; // Le nom de la base de données que vous utilisez
$user = 'utilisateur'; // Votre nom d'utilisateur pour accéder à la base de données
$pass = 'mot_de_passe'; // Le mot de passe de l'utilisateur
// DSN (Data Source Name) : construction de la chaîne de connexion PDO
$dsn = "mysql:host=$host;dbname=$db;charset=utf8mb4";
try {
// Création d'une instance de PDO avec les paramètres
$dbh = new PDO($dsn, $user, $pass);
// Configuration de PDO pour générer des exceptions en cas d'erreur
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Message de confirmation en cas de connexion réussie (à retirer en production)
echo "Connexion réussie à la base de données.";
} catch (PDOException $e) {
// Gérer l'erreur en cas de problème de connexion
die("Erreur de connexion : " . $e->getMessage());
}
?>
Cette méthode établit une connexion sécurisée à la base de données et permet de récupérer et manipuler les données via PDO. Nous allons maintenant exécuter une requête SQL pour sélectionner les éléments.
$query_level = "
SELECT
LEVEL.ch_opts_id AS LEVEL_ID,
LEVEL.ch_opts_label AS LEVEL_LABEL,
LEVEL.ch_opts_etat AS LEVEL_ETAT
FROM `tab_options` AS LEVEL
WHERE LEVEL.ch_opts_type = :optsLEVEL AND LEVEL.ch_opts_etat = :optsETAT
";
Nous utilisons ensuite une instruction préparée pour exécuter la requête, ce qui nous permet d’obtenir les éléments actifs dans notre table. Les résultats sont stockés dans le tableau $rows_level
.
$stmt_level = $dbh->prepare($query_level);
$stmt_level->execute(['optsETAT' => '1', 'optsLEVEL' => 'ch_champ_categorie']);
$rows_level = $stmt_level->fetchAll(PDO::FETCH_ASSOC);
Affichage des Données Avant le Tri
Avant d’appliquer le tri, il est judicieux de visualiser les données récupérées telles quelles. Cela nous aidera à comprendre ce que nous obtenons avant d’effectuer le tri.
// Vérification des données récupérées
echo "<pre>";
echo "Données récupérées avant tri :\n";
print_r($rows_level);
echo "</pre>";
En affichant le tableau, nous pouvons voir l’état des données avant toute transformation, ce qui est essentiel pour vérifier la cohérence des résultats après le tri.
ID | Label | État |
---|---|---|
3 | U1 | 1 |
4 | U2 | 1 |
5 | U10 | 1 |
6 | P | 1 |
7 | R | 1 |
8 | N1 | 1 |
9 | N2 | 1 |
10 | A | 1 |
11 | Z | 1 |
12 | Espoir | 1 |
13 | Élite | 1 |
Tri Alphabétique avec SQL
Maintenant que nous constatons que les données brutes ne s’affichent pas dans l’ordre que nous escomptions, il est temps de les organiser. Un tri de base en SQL peut s’effectuer sur le champ ch_opts_label
afin de classer les valeurs par ordre alphabétique. Cette méthode est simple et utilise la fonction ORDER BY
de SQL, mais elle a certaines limites, notamment lorsqu’il s’agit de trier des chaînes alphanumériques comme « U2 », avec « U10 », ou « N2 », où le résultat peut ne pas correspondre à ce que l’on attend intuitivement.
$query_level = "
SELECT
LEVEL.ch_opts_id AS LEVEL_ID,
LEVEL.ch_opts_label AS LEVEL_LABEL,
LEVEL.ch_opts_etat AS LEVEL_ETAT
FROM `tab_options` AS LEVEL
WHERE LEVEL.ch_opts_etat = :optsETAT AND LEVEL.ch_opts_type = :optsLEVEL
ORDER BY LEVEL.ch_opts_label ASC;
";
En exécutant cette requête, nous obtenons un tableau trié de manière strictement alphabétique. Ce type de tri est utile pour des valeurs composées uniquement de lettres, mais pose problème pour les valeurs alphanumériques. Jetons un œil aux résultats après ce tri.
ID | Label | État |
---|---|---|
11 | A | 1 |
13 | Élite | 1 |
12 | Espoir | 1 |
8 | N1 | 1 |
9 | N2 | 1 |
6 | P | 1 |
7 | R | 1 |
3 | U1 | 1 |
5 | U10 | 1 |
4 | U2 | 1 |
10 | Z | 1 |
Comme vous pouvez le constater, « U2 » apparait après « U10 » dans cet ordre strictement alphabétique. Cet ordre peut ne pas être adéquat lorsque nous avons des valeurs alphanumériques à traiter. Nous allons explorer comment corriger cela dans les sections suivantes avec des méthodes de tri alphanumérique.
Fonction de Tri Alphanumérique
Lorsque nous voulons trier des chaînes qui contiennent à la fois des lettres et des chiffres (comme « U1 », « U10 », et « U2 »), un tri alphabétique de base ne suffit pas. Comme nous l’avons vu avec la commande SQL ORDER BY
, les résultats ne sont pas toujours intuitifs : « U10 » peut apparaître avant « U2 ». Cela s’explique par le fait que SQL trie ces chaînes selon leur ordre lexicographique, c’est-à-dire en comparant chaque caractère un par un, sans tenir compte de la signification numérique des chiffres.
Pour résoudre ce problème en PHP, nous allons créer une fonction de tri alphanumérique personnalisée au travers de l’instruction native usort()
.
Qu’est-ce que usort()
?
usort()
est une fonction native de PHP qui permet de trier un tableau en utilisant une fonction de comparaison que l’on définit. Cette fonction de comparaison reçoit deux arguments (dans notre cas, deux éléments de notre tableau) et doit retourner un nombre :
- Un nombre négatif si le premier élément doit être classé avant le second.
- 0 si les deux éléments sont équivalents.
- Un nombre positif si le premier élément doit être classé après le second.
C’est cette flexibilité qui rend usort()
si puissant pour implémenter un tri alphanumérique, car cela nous permet de contrôler précisément comment les valeurs doivent être comparées. POur l’utiliser rien de plus simple :
usort($rows_level, 'alphanum_sort');
Dans cet exemple, usort()
prend deux arguments :
- Le tableau à trier :
$rows_level
. - La fonction de comparaison :
alphanum_sort
, la fonction qui va gérer le tri et que nous allons définir juste après.
Maintenant, nous allons donc créer notre fonction de comparaison alphanum_sort
, qui sera utilisée pour le tri.
Étape 1 : Comparer les Parties Alphabétiques
Lorsque nous comparons deux chaînes alphanumériques comme « U1 », « U10 » ou « N2 », la première chose à faire est de séparer la partie alphabétique de la partie numérique. Nous pouvons utiliser une expression régulière, avec la fonction preg_replace()
, paramétrer de telle sorte que elle nous permettra de supprimer les chiffres et de ne conserver que les lettres.
function alphanum_sort($a, $b) {
// Extraire la partie alphabétique des deux chaînes
$alphaA = preg_replace('/[0-9]/', '', $a['LEVEL_LABEL']);
$alphaB = preg_replace('/[0-9]/', '', $b['LEVEL_LABEL']);
// Comparer ces parties alphabétiques
$cmp = strcmp($alphaA, $alphaB);
// Si les parties alphabétiques sont différentes, renvoyer le résultat de cette comparaison
if ($cmp !== 0) {
return $cmp;
}
}
preg_replace('/[0-9]/', '', ...)
: Cette expression supprime tous les chiffres de la chaîne, ne conservant que les lettres. Par exemple, pour « U1 », cela renverra « U ».strcmp()
: Cette fonction compare deux chaînes en fonction de leur ordre alphabétique et retourne un nombre négatif, 0 ou positif en conséquence.
À ce stade, si les deux parties alphabétiques sont différentes, la fonction retourne directement le résultat de la comparaison alphabétique. Cela signifie que si vous comparez, par exemple, « N1 » et « U1 », la fonction strcmp()
analysera d’abord les lettres « N » et « U ». Puisque « N » vient avant « U » dans l’ordre alphabétique, la fonction retournera une valeur négative, indiquant que « N1 » doit être trié avant « U1 ». De la même manière, si nous comparons « P5 » et « N3 », « N » vient avant « P », donc « N3 » sera placé avant « P5 » dans le tableau trié. Si les parties alphabétiques sont identiques (comme « U1 » et « U10 »), la fonction passera ensuite à la comparaison des parties numériques, ce que nous verrons à l’étape suivante.
Étape 2 : Comparer les Parties Numériques
Si les parties alphabétiques sont identiques (par exemple, entre « U1 » et « U10 »), nous devons maintenant comparer les parties numériques. Pour cela, nous allons procéder à l’inverse, et extraire les chiffres des chaînes afin de les convertir en entiers avec intval()
.
// Si les parties alphabétiques sont égales, comparer les parties numériques
if ($cmp === 0) {
// Extraire la partie numérique des deux chaînes
$numA = intval(preg_replace('/[^0-9]/', '', $a['LEVEL_LABEL']));
$numB = intval(preg_replace('/[^0-9]/', '', $b['LEVEL_LABEL']));
// Comparer les parties numériques
return $numA <=> $numB;
}
}
preg_replace('/[^0-9]/', '', ...)
: Cette expression fonctionne à l’inverse de la précédente. Elle supprime tout ce qui n’est pas un chiffre, ne conservant que les nombres. Par exemple, pour « U10 », cela renverra « 10 ».intval()
: Nous convertissons la chaîne obtenue en entier, afin de pouvoir faire une vraie comparaison numérique.<=>
: L’opérateur « vaisseau spatial » de PHP compare deux valeurs numériques et retourne -1, 0, ou 1.
Notez au passage le changement de l’expression régulière qui passe de
'/[0-9]/'
à'/[^0-9]/'
: l’accent circonflexe^
à l’intérieur des crochets inverse la sélection, ce qui signifie que, au lieu de supprimer les chiffres, nous supprimons tout sauf les chiffres, permettant ainsi d’extraire uniquement la partie numérique de la chaîne.
Étape 3 : Affichage des résultats triés
Après avoir implémenté notre fonction de tri alphanumérique et appliqué la fonction usort()
à notre tableau de données, il est temps de vérifier si le tri a bien fonctionné.
<?php
// Appliquer le tri alphanumérique
usort($rows_level, 'alphanum_sort');
// Vérification des données triées (sortie brute dans un <pre>)
echo "<pre>";
echo "Données après tri alphanumérique :\n";
print_r($rows_level);
echo "</pre>";
?>
ID | Label | État |
---|---|---|
11 | A | 1 |
12 | Espoir | 1 |
13 | Élite | 1 |
8 | N1 | 1 |
9 | N2 | 1 |
6 | P | 1 |
7 | R | 1 |
3 | U1 | 1 |
4 | U2 | 1 |
5 | U10 | 1 |
10 | Z | 1 |
En utilisant la fonction usort()
et en combinant un tri personnalisé qui prend en compte à la fois les parties alphabétiques et numériques, nous avons pu surmonter les limites d’un tri alphabétique classique, notamment pour des chaînes alphanumériques. Cette approche nous permet d’organiser les données de manière plus intuitive et logique, en triant les éléments comme « U1 », « U2 », et « U10 » dans le bon ordre. Grâce à la flexibilité offerte par usort()
et à une fonction de comparaison bien définie, nous disposons d’un outil puissant pour gérer des tris complexes dans PHP, assurant une présentation des données plus cohérente pour l’utilisateur final.
Conclusion
En plus de l’utilisation de usort()
pour un tri sur mesure, nous avons exploité les expressions régulières pour extraire et manipuler les parties alphabétiques et numériques des chaînes, rendant notre tri plus efficace. Bien que ce genre de tri puisse être effectué avec SQL, PHP permet parfois une plus grande flexibilité et un contrôle précis sur des opérations complexes, notamment lorsqu’on veut aller au-delà des capacités standards d’un simple ORDER BY
.
Pour aller plus loin