Eviter la répétition d'un tirage aléatoire

Petit nouveau ! | 7 Messages

31 mai 2012, 05:48

Je sais afficher avec un ordre aléatoire un texte depuis ma base de données , cependant je n'arrive pas à trouver un moyen d’empêcher la répétition d'un même texte.
A travers un systeme de session, le visiteur clique sur suivant et les textes défilent aléatoirement sans se répéter.
Quelqu'un a t'il une idée? :)
<?php
session_start();
$_SESSION['pseudo'];

try
{
$pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
$bdd = new PDO('mysql:host=mysql51-57.perso;dbname=', '', '', $pdo_options);

$reponse = $bdd->query('SELECT * FROM textesaleatoire ORDER BY rand() LIMIT 1,1');
while ($donnees = $reponse->fetch())
{
<?
	

<table width="100%" border="1" class="arrondiombre">
		<tr>
		<td><?php echo $donnees['textes']; ?></td>
		</tr>
</table>
		

<?php
}  
$reponse->closeCursor();
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}
?>

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

31 mai 2012, 06:48

Salut,

Stock en session les id des choses déjà afficher et utilise le "in" de SQL pour faire une restriction.

Par exemple
select truc from machin where id not in (1,798,78) order by rand limit 1;

La fonction implode te sera utile ;)

@+
Il en faut peu pour être heureux ......

kny
Eléphanteau du PHP | 47 Messages

31 mai 2012, 14:37

Dans un premier temps je stockerais les résultats dans la session pour éviter les accès à la base.
Ensuite, plutôt que d'utiliser un rand sur ce tableau, une solution pourrait être d'utiliser un shuffle pour mélanger le tableau (une seule fois suffit), puis un shift pour enlever cette première pioche des suivantes et l'afficher à l'internaute.

Il y a un sujet similaire concernant le shuffle et le shift ici : php-debutant/plusieurs-images-aleatoires-t253741.html

Petit nouveau ! | 7 Messages

31 mai 2012, 23:40

Merci pour votre aide,
Je vais essayer avec les deux méthodes :)

Comment je pourrais faire pour stocker les id des textes déjà vu en session?

(ps: passer par les sessions plutôt que par la bdd évite le ralentissement du chargement de la page?)

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

01 juin 2012, 08:10

Pour le os : ça dépend si ta requête prend 14s oui si elle n'est faite qu'une fois.

Mais si c'est le cas revoie ton système XD
Pour l'affichage y a peu de chance que tu ai besoin d'un volume de données si important qu'il ralentisse, significativement, le chargement de la page.

Généralement le problème d'image trop lourde, de multiple JS etc

Avec le plugin firebug (Firefox ou chrome) tu peux voir le temps de chargement de la page coté client et donc savoir ce qui prend le plus de temps.

Côté serveur il faut utiliser la fonction time pour calculer le temps que prennent les différentes parties de ton code (en gros faire une "inspection" de ton code). Certain outils propose ce type de chose.


@+
Il en faut peu pour être heureux ......

Petit nouveau ! | 7 Messages

01 juin 2012, 17:53

Pour insérer des info depuis la bdd à une session, je dois m'y prendre de cette manière la?
<?php
session_start();

function open()
{
	global $host,$user,$pass,$db,$connect;	
	$connect = mysql_connect($host, $user, $pass,1);
	$bdd = mysql_select_db($db,$connect);
	return $bdd;
}

	function read ($sid)
{
	$sql = "SELECT id FROM textealeatoire";
	$query = mysql_query($sql,$connect) or die (mysql_error());			
	$donnees = mysql_fetch_array($query);
		
	if(empty($donnees)) return FALSE;
	else return $donnees['id'];//
}
$_SESSION['id'] = $donnees['id'];
?>
Désolé ça dérive un peu du sujet principal.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

02 juin 2012, 12:54

La question la plus importante pour savoir quelle méthode utiliser, c'est combien de textes as-tu en base qui peuvent être affichés (aujourd'hui et d'ici à 5 ans). Car placer 20 textes de ta base en session peut rester raisonnable, en mettre 2000 l'est beaucoup moins et il vaut mieux faire une requête à la demande.

Pour mettre tes id en session, autant profiter de la requête qui affiche ton texte. Lorsque tu affiches le résultat, tu en profites pour stocker l'id dans ta session, cela évite de le faire en double (surtout que là, l'id que tu vas récupérer ne correspondra peut être pas au texte qui sera affiché).

Quant à l'enregistrement en session, il faut utiliser un tableau pour stocker chaque id consulté, sinon tu vas écraser l'id précédent à chaque fois :)

En gros ton code doit :
- chercher un texte au hasard, dont l'id n'est pas dans ceux déjà passés et stockés dans le tableau en session
- afficher le texte retourné
- stocker l'id du texte retourné dans le tableau en session
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Petit nouveau ! | 7 Messages

04 juin 2012, 11:50

Merci pour l'aide apporté, je penses avoir mis toutes les données nécessaire, cependant l'id finit encore par retombé.
Auriez-vous une idée de la marche à suivre pour que ce code réussisse?
Je ne vois pas l'élément qui manque afin de réussir.

Code : Tout sélectionner

<?php session_start(); try { $pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; $bdd = new PDO('mysql:host=mysql51-57.perso;dbname=', '', '', $pdo_options); $reponse = $bdd->query('SELECT id FROM textealeatoire WHERE id != "'.$_SESSION['id'].'" ORDER BY rand() LIMIT 1,1'); if (isset($_SESSION['id'])) { $_SESSION['id'] = array(); } while ($donnees = $reponse->fetch()) { $_SESSION['id'][] = $donnees['id']; echo $donnees['id']; } $reponse->closeCursor(); } catch(Exception $e) { die('Erreur : '.$e->getMessage()); } ?>

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

04 juin 2012, 14:42

Ta variable $_SESSION['id'] est un tableau qui va contenir chacun des id tirés au hasard. Pour pouvoir l'utiliser dans ta requête SQL il faut faire appel à la fonction implode() qui va transformer ce tableau en chaine d'id séparés par des virgules. Tu pourras alors l'utiliser avec l'opérateur IN de ta requête SQL :
... WHERE id NOT IN (" . implode(',' , $_SESSION['id']) . ") ...
Ensuite, tu as une petite erreur au niveau de ton test isSet() : en effet, tu tests si le tableau d'id est défini et si c'est bien le cas, tu le déclares comme étant un nouveau tableau. En fait, c'est le contraire qu'il te faut, tu ne dois le déclarer comme nouveau tableau que s'il n'est pas défini :)

Et logiquement il te retournera tes id un par un de façon aléatoire jusqu'à ce qu'il n'y ai plus de résultat en base (il faudra alors peut être prévoir de remettre à zéro ton tableau pour recommencer, ou mettre un message pour dire que ça y est, l'utilisateur a tout vu :))
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Petit nouveau ! | 7 Messages

04 juin 2012, 23:24

J'ai ajouté l'implode et modifier le isset.
Cependant les id affichés reviennent
<?php
session_start();
 
try
{
    $pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
 $bdd = new PDO('mysql:host=mysql51-57.perso;dbname=', '', '', $pdo_options);
 $reponse = $bdd->query('SELECT id FROM gage WHERE id NOT IN ("' . implode(',', $_SESSION['id']) . '") ORDER BY rand() LIMIT 1,1');

 
if (!isset($_SESSION['id'])) {
    $_SESSION['id'] = array();
    }
    while ($donnees = $reponse->fetch()) {
 $_SESSION['id'][] = $donnees['id'];
 echo $donnees['id'];
    }
    $reponse->closeCursor();
 }
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}
 ?>

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

05 juin 2012, 13:33

Ta requête sql n'est pas bonne, tu as des apostrophes en trop dans le IN. La syntaxe a obtenir est : " id NOT IN (2,7,5) " alors que actuellement tu génères " id NOT IN ('2,7,5') ". Du coup mysql vérifie que ton id n'a pas la valeur de cette chaine plutôt que chacune des valeurs qui la constitue :)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Invité
Invité n'ayant pas de compte PHPfrance

07 juin 2012, 16:12

Lorsque j'enlève les guillemets ou apostrophes il y a une erreur de syntaxe qui apparait.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

07 juin 2012, 23:52

En fait, il y a deux choses à gérer... il faut effectivement enlever les guillemets qui sont en trop dans ta requête, mais également traiter le cas où tu n'as pas encore d'id en session pour éviter d'avoir un IN () qui sera incorrect. Il faut donc construire ta requête ainsi, avant de l'exécuter :
$sql = 'SELECT id FROM gage ';
if (isset($_SESSION['id']))
  $sql.= ' WHERE id NOT IN (' . implode(',', $_SESSION['id']) . ')';
$sql.= ' ORDER BY rand() LIMIT 1,1';
$reponse = $bdd->query($sql);
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

ViPHP
ViPHP | 2577 Messages

08 juin 2012, 09:49

Après un certain nombre d'affichage (10,50 ou plus) il faudrait prévoir d'autoriser les répétitions et donc de supprimer le premier élément (voir fonction array_shift). Ca te permettra de limiter ta requete SQL à pas trop d'id.
<?PHP
$_SESSION['id'][] = $donnees['id'];  
if (count($_SESSION['id'] > 50) { $_SESSION['id'] = array_shift($_SESSION['id']); }
?>

Petit nouveau ! | 7 Messages

08 juin 2012, 16:59

Plus aucune erreur ne s'affiche merci!
L'id se stock bien mais n’empêche pas la répétition.
(ps: array_shift supprimerai à partir du premier id stocker?)
<?php
session_start();
 
try
{
    $pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
	$bdd = new PDO('mysql:host=mysql51-57.perso;dbname=', '', '', $pdo_options);
 
if 		(!isset($_SESSION['id']))	{
		$_SESSION['id'] = array();
		$reponse = $bdd->query('SELECT id FROM base ORDER BY rand() LIMIT 1,1');
		while ($donnees = $reponse->fetch()) 	{
		$_SESSION['id'][] = $donnees['id'];
		echo $donnees['id'];
												}
									} 
else 	{
		$reponse = $bdd->query('SELECT id FROM base WHERE id NOT IN ("' . implode(',', $_SESSION['id']) . '") ORDER BY rand() LIMIT 1,1');
		}
		while ($donnees = $reponse->fetch()) 	{
		$_SESSION['id'][] = $donnees['id'];
		echo $donnees['id'];
												}
    $reponse->closeCursor();
}
catch(Exception $e)
{
die('Erreur : '.$e->getMessage());
}
 ?>