[PHP] moteur de recherche interne, requête insensible aux accents

leica69

Nouveau WRInaute
Bonjour,
je suis en train de faire un moteur de recherche interne pour mon site.

1/ Je récupère le mot cle de recherche transmis par formulaire:
Code:
$recherche = $_POST['rech'];
$recherche = trim($recherche);

2/ je fais une recherche dans ma base de données (PHP5, utf8_unicode_ci)
Je souhaite que les mots recherché soient surlignés dans l'affichage final
Code:
$requete="SELECT * FROM table WHERE champ1 LIKE '%$recherche%' OR champ2 LIKE '%$recherche%''";
$resultat=mysql_query($requete) or die ("Problème lors de la requête. Erreur: ".mysql_error());

while($ligne=mysql_fetch_array($resultat)){
   $valeur1 = $ligne[champ1];
   $valeur2 = $ligne[champ2];
   
   // recherche et remplacement du mot de la recherche pour l'entourer d'un <span> de mise ne forme
   $pattern = '('. quotemeta($recherche) .')';
   $replacement = "<span class='selection'>\\1</span>";
   $valeur1 = eregi_replace($pattern, $replacement, $valeur1);
   $valeur2  = eregi_replace($pattern, $replacement, $valeur2);

   // Affichage du résultat mis en forme
   echo "<p>$valeur1 </p>";
   echo "<p>$valeur2 </p>";
}

Maintenant, je vous explique mon souci:
Quand je fais la recherche SQL, elle est insensible aux accents.
Quand je fais la recherche PHP, elle est insensible à la casse mais sensible aux accents.

Si je fais une recherche sur "pre":
- SQL va me retourner les enregistrements contenant "précision", "âpre", "premier",...
- PHP va uniquement surligner "âpre", "premier"

Mon problème est une incohérence des recherches. Je vais avoir des champs de texte sans rien de surligné, c'est déroutant pour l'utilisateur (et pour moi).

Je souhaite avoir deux recherches cohérentes:
Soit tout insensible aux accents // Soit tout sensible aux accents

J'ai essayé de remplacer tous les caractères accentués par leur équivalent sans accent. Le pb est que je me retrouve avec un texte de résultat sans les accents. Je veux un texte juste (accentué) en retour.

J'espère que qqn pourra m'aider!!

D'avance, merci,
Antonio
 

forty

WRInaute passionné
tu peux utiliser un code comme celui ci pour transformer ta mot recherché en regex. ca transforme par exemple "Eléphant" en "[eèéêë]l[eèéêë]phant" pour alimenter ton partern
Code:
function regexAccents($chaine) {
	$accent = array('a','à','á','â','ã','ä','å','c','ç','e','è','é','ê','ë','i','ì','í','î','ï','o','ð','ò','ó','ô','õ','ö','u','ù','ú','û','ü','y','ý','ý','ÿ');
	$inter = array('%01','%02','%03','%04','%05','%06','%07','%08','%09','%10','%11','%12','%13','%14','%15','%16','%17','%18',
	                    '%19','%20','%21','%22','%23','%24','%25','%26','%27','%28','%29','%30','%31','%32','%33','%34','%35');
	$regex = array('[aàáâãäå]','[aàáâãäå]','[aàáâãäå]','[aàáâãäå]','[aàáâãäå]','[aàáâãäå]','[aàáâãäå]',
	                     '[cç]','[cç]',
	                     '[eèéêë]','[eèéêë]','[eèéêë]','[eèéêë]','[eèéêë]',
	                     '[iìíîï]','[iìíîï]','[iìíîï]','[iìíîï]','[iìíîï]',
	                     '[oðòóôõö]','[oðòóôõö]','[oðòóôõö]','[oðòóôõö]','[oðòóôõö]','[oðòóôõö]','[oðòóôõö]',
	                     '[uùúûü]','[uùúûü]','[uùúûü]','[uùúûü]',
	                     '[yýýÿ]','[yýýÿ]','[yýýÿ]','[yýýÿ]');
	$chaine = str_ireplace($accent, $inter, $chaine);
	$chaine = str_replace($inter, $regex, $chaine);
	return $chaine;
}
 

leica69

Nouveau WRInaute
Merci pour l'astuce. Théoriquement ça devrait fonctionner. Mais malheureusement ce n'est pas le cas pour moi.
J'ai fait une requête simplifiée utilisant ton idée, histoire d'identifier le problème.

ma chaîne de départ
Code:
$desc = "poire très stress tresse tréssaillir(SIC)";

la chaîne à mettre en gras:
Code:
$pattern = "(tr[eèé])"; // qui devrait correspondre à tre, trè, tré

La substitution pour mettre en gras
Code:
$replacement = "<b>\\1</b>";

le remplacement
Code:
$test3 = eregi_replace($pattern, $replacement, $desc);
echo "$test3";

et j'obtiens
dans le code source: poire <b>tr�</b>�s s<b>tre</b>ss <b>tre</b>sse <b>tr�</b>�ssaillir(SIC)
à l'affichage: poire tr��s stress tresse tr��ssaillir(SIC)

(ce qui est étonnant c'est que le caractère soit doublé. Le gras fonctionne mais n'est pas affiché dans ce dernier exemple)
les caractères d'erreur sont des losanges avec des ? à l'intérieur.

Je suis en UTF-8 dans tout le processus et mon navigateur (firefox) est correctement réglé.

Uni idée de solution??
 

forty

WRInaute passionné
c'est sûrement a cause de l'utf-8. comme les lettres accentuées sont sur deux caractères les [] matchent toutes les lettres.
essaye de remplacer [eèé] par (e|è|é)
 

leica69

Nouveau WRInaute
Ca ne fonctionne toujours pas.

Je joue un peu avec l'encodage/décodage UTF-8 / ISO-8859-1 pour essayer de contourner le problème mais je bloque encore.
Code:
$desc = "poire très stress tresse tréssaillir(SIC)";
$desc = htmlentities($desc,ENT_NOQUOTES, "UTF-8");
//desc: poire tr&egrave;s stress tresse tr&eacute;ssaillir(SIC)

$pattern = "(tr[è|e|é])";
$pattern = htmlentities($pattern,ENT_NOQUOTES, "UTF-8");
//pattern: (tr[&egrave;|e|&eacute;])

$replacement = "<b>\\1</b>";

$test3 = eregi_replace($pattern, $replacement, $desc);

j'obtiens ça dans le code source de la page

Code:
poire <b>tr&</b>egrave;s s<b>tre</b>ss <b>tre</b>sse <b>tr&</b>eacute;ssaillir(SIC)
Ce qui me surprend c'est que seul le premier "caractère" (&) du caractère accentué soit dans la balise <b>
 

forty

WRInaute passionné
tu ne peux pas mettre [&egrave;|e|&eacute;] car les crochets autorisent les caractères & e g r a v c u t et |
pour indiquer des chaînes il faut mettre entre parenthèse comme indiqué dans le précédent message : (&egrave;|e|&eacute;)
 

leica69

Nouveau WRInaute
Je n'avais pas vu les parenthèses!!!!!
Ca fonctionne! J'ai quand même du jongler avec les UTF-8 et ISO

Voici le code, si ça peut aider qqn un jour:

Code:
function regexAccents($chaine) {
	$accent = array('a','à','á','â','ã','ä','å','c','ç','e','è','é','ê','ë','i','ì','í','î','ï','o','ð','ò','ó','ô','õ','ö','u','ù','ú','û','ü','y','ý','ý','ÿ');
	$inter = array('%01','%02','%03','%04','%05','%06','%07','%08','%09','%10','%11','%12','%13','%14','%15','%16','%17','%18','%19','%20','%21','%22','%23','%24','%25','%26','%27','%28','%29','%30','%31','%32','%33','%34','%35');
	$regex = array('(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)',
'(c|ç)','(c|ç)',
'(è|e|é|ê|ë)','(è|e|é|ê|ë)','(è|e|é|ê|ë)','(è|e|é|ê|ë)','(è|e|é|ê|ë)',
'(i|ì|í|î|ï)','(i|ì|í|î|ï)','(i|ì|í|î|ï)','(i|ì|í|î|ï)','(i|ì|í|î|ï)',	'(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)',			'(u|ù|ú|û|ü)','(u|ù|ú|û|ü)','(u|ù|ú|û|ü)','(u|ù|ú|û|ü)',
'(y|ý|ý|ÿ)','(y|ý|ý|ÿ)','(y|ý|ý|ÿ)','(y|ý|ý|ÿ)');
	$chaine = str_ireplace($accent, $inter, $chaine);
	$chaine = str_replace($inter, $regex, $chaine);		
	return $chaine;
}

$recherche = $_POST['rech'];
$recherche = trim($recherche);
		
$pattern = '('. $recherche .')';
$pattern = regexAccents($pattern);
$pattern = htmlentities($pattern,ENT_NOQUOTES, "ISO-8859-1");
	
$replacement = "<span class='selection'>\\1</span>";

if($_POST['rech'] != ""){
	if (strlen($_POST['rech']) >= 3){
		echo "<p>Résultat de la recherche pour <strong>$recherche</strong></p>";
					
		$requete="SELECT * FROM table WHERE champ1 LIKE '%$recherche%' OR champ2 LIKE '%$recherche%' ";
		$resultat=mysql_query($requete) or die ("Problème lors de la requête. Erreur: ".mysql_error());
				
		if (mysql_num_rows($resultat) > 0){
			echo "\n<h2>Huiles essentielles</h2>\n";
		}
						
		while($ligne=mysql_fetch_array($resultat)){
			$val1 = $ligne[champ1];
			$val2 = $ligne[champ2];
						
			$val1 = html_entity_decode(eregi_replace($pattern, $replacement, htmlentities($val1,ENT_NOQUOTES, "ISO-8859-1")));
			$val2 = html_entity_decode(eregi_replace($pattern, $replacement, htmlentities($val2,ENT_NOQUOTES, "ISO-8859-1")));
						
			if(preg_match("/".$pattern."/i",$val1 ) > 0 OR preg_match("/".$recherche."/i",$val1 ) > 0){
				echo "\n$val1 ";
			}

			if(preg_match("/".$pattern."/i",$val2 ) > 0 OR preg_match("/".$recherche."/i",$val2 ) > 0){
				echo "\n$val2 ";
			}
					
		} else {
			echo "\n<p>Votre recherche doit comporter au moins trois caractères</p>\n";
		}
				
	} else {
		echo "\n<p>Veuillez saisir un mot pour effectuer votre recherche.</p>\n";
	}
}
 

blman

WRInaute accro
Réponse hors sujet, mais tu gagnerais en performance en faisant ta requête SQL ainsi :

Code:
$requete="SELECT * FROM table WHERE CONCAT(champ1," ",champ2) LIKE '%$recherche%'";

Les OR sont vraiment gourmand en ressources. En concaténant tes champs, tu gagne en performance.
 

leica69

Nouveau WRInaute
Cooool! Merci

C'est d'autant plus pertinent que je fais ma recherche sur 5 champs. Ca me permet aussi de faire un seul LIKE.
Encore Merci
 

leica69

Nouveau WRInaute
Je me rends compte d'un souci supplémentaire.

Une partie du texte de ma base contient du formatage HTML <strong>Nom</strong>, par exemple.
Comment faire pour que ma recherche ne prenne pas en compte mes balises?

Pour rappel, j'ai ceci:
Code:
$search = '('. $search  .')';
$search = regexAccents($search); // voir posts précédents, c'est pour être insensible aux accents
$search = htmlentities($search,ENT_NOQUOTES, "ISO-8859-1");

$valeur = html_entity_decode(eregi_replace($search, $replacement, htmlentities($valeur,ENT_NOQUOTES, "ISO-8859-1")));

Je dois modifier mon critère de recherche la bonne syntaxe. Pour l'instant, j'en suis là:
Code:
$search = '([^<][\S]*)('. $search .')([\S]*[^>])';
Dans ce cas précis, si je recherche "tron", c'est la catastrophe!!!
 

forty

WRInaute passionné
il y a peut-être une piste avec la fonction preg_replace_callback pour extraire tout ce qui n'est pas balise et faire ensuite ton replace mais je ne suis pas spécialiste
 

blman

WRInaute accro
Leica69,

personnellement, j'ajouterais un champs à ma table qui contient une chaine avec tous tes champs déjà concaténés et formaté (sans balise HTML, sans accent, tout en minuscule et sans les mots qui peuvent être des stop words). Tu indexera ton contenu dans ce champ à chaque fois que tu met à jour la ligne concernée dans ta table.

Tu y gagnera énormément en ressource et en simplicité au niveau de la recherche. Tu pourrais par exemple l'indexer en FULLTEXT pour gagner en pertinence ou plus facilement le charger en mémoire si tu veux faire de la correction orthographique.

Parce que là, en voyant la manière dont tu fais la recherche et comment tu la traite en sortie, à mon avis, tu es mal parti. Essaye de traiter tes données au maximum en entrée (indexation de tes keywords)

PS : sinon tu a la fonction strip_tags() en PHP pour retirer les balises HTML : http://fr2.php.net/strip-tags
 

leica69

Nouveau WRInaute
Salut,
Je m'en suis sorti avec strip_tags()

C'est vrai que pour la suite, si je complexifie le traitement de la base, je devrais passer par une colonne qui regroupera mes entrées.

En tout cas, merci pour ta solution.
 

Discussions similaires

Haut