Maintenir l'état
L’un des principaux inconvénients de l’exemple précédent est qu’il récupère l’ensemble complet des résultats en une seule fois, ce qui n’est pas pratique lorsque les jeux de résultats sont volumineux. Il est plus pratique d’afficher l’ensemble des résultats sur plusieurs pages et de permettre à l’utilisateur de naviguer d’une page à l’autre.
C’est simple dans une application conventionnelle, où la connexion au serveur distinct est maintenue jusqu’à ce que l’utilisateur quitte l’application. Cependant, dans une implémentation web, cette exigence apparemment simple implique un niveau de complexité nettement plus élevé en raison de la nature sans état des pages web. L’une de ces complexités est que, chaque fois qu’une nouvelle page de résultats est affichée, la recherche initiale des enregistrements doit être relancée. Cela est contraignant pour le développeur web et potentiellement lent pour l’utilisateur.
Le serveur IMu fournit une solution à ce problème. Lorsqu’un objet handler est créé, un objet correspondant est créé sur le serveur afin de traiter la requête du handler : cet objet côté serveur se voit attribuer un identifiant unique par le serveur IMu. Lors d’une requête visant à obtenir davantage d’informations, l’identifiant unique peut être utilisé pour connecter un nouveau handler au même objet côté serveur, tout en conservant son état.
L’exemple suivant illustre la connexion d’un deuxième objet IMuModule créé indépendamment au même objet côté serveur :
// Créer un objet module comme d'habitude $first = new IMuModule('eparties', $session); // Lancer une recherche - ceci créera un objet côté serveur $keys = array(1, 2, 3, 4, 5, 42); $first->findKeys($keys); // Obtenir un jeu de résultats $result1 = $first->fetch('start', 0, 2, 'SummaryData'); // Créer un second objet module $second = new IMuModule('eparties', $session); /* * L'attacher au même objet côté serveur que le premier module. * C'est l'étape la plus importante. */ $second->id = $first->id ; // Obtenir un deuxième ensemble de résultats à partir de la même recherche $result 2 = $second->fetch('current', 1, 2, 'SummaryData') ;
Bien que deux objets IMuModule totalement distincts aient été créés, ils sont tous deux liés au même objet côté serveur, puisqu’ils partagent la même propriété id. Cela signifie que le deuxième appel fetch accédera au même jeu de résultats que le premier appel fetch. Notez qu’un indicateur current a été transmis au deuxième appel. L’état actuel est conservé dans l’objet côté serveur ; ainsi, dans ce cas, le deuxième appel à Fetch renverra les troisième et quatrième enregistrements du jeu de résultats.
Bien que cet exemple illustre l’utilisation de la propriété id, il n’est pas particulièrement réaliste, car il est peu probable que deux objets distincts faisant référence au même objet côté serveur soient nécessaires dans le même extrait de code. Il est bien plus probable qu’il faille se reconnecter au même objet côté serveur lors de la génération d’une autre page de résultats. Cette situation implique la création d’un objet IMuModule côté serveur (pour effectuer une recherche dans le module et renvoyer le premier jeu de résultats) lors d’une première requête, puis la reconnexion à ce même objet côté serveur (pour récupérer un deuxième jeu de résultats) lors d’une deuxième requête. Comme précédemment, cela s’obtient en attribuant le même identifiant à la propriété id de l’objet de la deuxième page, mais deux autres éléments doivent être pris en compte.
Par défaut, le serveur IMu détruit tous les objets côté serveur à la fin d’une session. Cela signifie que, sauf instruction contraire explicite donnée au serveur, l’objet côté serveur peut être détruit lorsque la connexion depuis la première page est fermée. Pour indiquer au serveur de conserver l’objet côté serveur, il suffit de définir la propriété destroy de cet objet sur false avant d’appeler l’une de ses méthodes. Dans l’exemple ci-dessus, le serveur reçoit l’instruction de ne pas détruire l’objet comme suit :
$module = new IMuModule('eparties', $session); $module->setDestroy(false); $keys = array(1, 2, 3, 4, 5, 42); $module->findKeys($keys);
Le deuxième point est assez subtil. Lorsqu’une connexion est établie à un serveur, il est nécessaire de spécifier le port auquel se connecter. Selon la façon dont le serveur a été configuré, il peut y avoir plus d’un processus serveur à l’écoute des connexions sur ce port. Votre programme n’a aucun contrôle sur le choix du processus qui acceptera effectivement la connexion et traitera les requêtes. En temps normal, cela n’a aucune incidence, mais cela pose problème lorsqu’on tente de maintenir l’état en se reconnectant à un objet côté serveur préexistant.
Par exemple, supposons qu’il existe trois processus serveur distincts à l’écoute des connexions. Lorsque la première requête est exécutée, elle se connecte, de manière pratiquement aléatoire, au premier processus. Ce processus traite la requête, crée un objet côté serveur, effectue une recherche dans le module Personnes / Organisations à l’aide des termes fournis et renvoie le premier jeu de résultats. Le serveur reçoit l’instruction de ne pas détruire l’objet et transmet l’identifiant côté serveur à une autre page, qui récupère le jeu suivant de résultats issus de la même recherche.
Le problème survient lorsque la page suivante se reconnecte au serveur. Une fois la connexion établie, n'importe lequel des trois processus serveur peut l'accepter. Cependant, seul le premier processus maintient l'objet côté serveur correspondant. Si le deuxième ou le troisième processus accepte la connexion, l’objet ne sera pas trouvé.
La solution à ce problème est relativement simple. Avant que la première requête ne ferme la connexion à son serveur, elle doit signaler au serveur que les requêtes suivantes devront se connecter explicitement à ce processus. Pour ce faire, il suffit de définir la propriété suspend de l'objet IMuSession sur true avant de soumettre toute requête au serveur :
$session = new IMuSession('server.com', 12345); $module = new IMuModule('eparties', $session); $session->setSuspend(true); $module->findKeys($keys);
Le serveur traite une demande de suspension d’une connexion en se mettant à l’écoute des connexions sur un deuxième port. Contrairement au port principal, ce port est réservé à ce processus serveur spécifique. Cela signifie qu'une page suivante peut se reconnecter à un serveur sur ce deuxième port et avoir la garantie de se connecter au même processus serveur. Cela signifie que tout objet conservé côté serveur sera accessible via son identifiant. Une fois la requête terminée (dans cet exemple, il s’agissait d’un appel à findKeys), la propriété port de l’objet IMuSession contient le numéro de port auquel se reconnecter :
$session->setSuspend(true); $module->findKeys($keys); $reconnect = $session->port;
Exemple
Pour illustrer cela, nous allons modifier la page de résultats très simple de la section précédente afin d’afficher la liste des noms trouvés par groupes de cinq enregistrements par page. Nous fournirons des liens simples Suivant et Précédent pour permettre à l’utilisateur de se déplacer dans les résultats, et nous utiliserons d’autres paramètres GET pour transmettre le port auquel nous voulons nous reconnecter, l’identifiant de l’objet côté serveur et le rownum du premier enregistrement à afficher.
Construisez d’abord la page de recherche, qui est un formulaire HTML simple :
<br> <title>Recherche de Personnes / Organisations</title> </head> <body> <form action="example.php"><p>Entrez un nom de famille à rechercher (par exemple S*):</p> <input type="text" name="name"/> <input type="submit" value="Recherche"/> </form> </body>
Ensuite, construisez la page de résultats
- Créez un objet
IMuSession. Ensuite, la propriétéportest définie sur une valeur standard, sauf si un paramètreporta été passé dans l’URL./*
* Créer un nouvel objet de session.
*/
$session = new IMuSession();
$session->setHost('imu.mel.kesoftware.com');
/*
* Déterminer à quel port se connecter.
*/
$port = 40136;
if (isset($_GET['port']))
$port = $_GET['port'];
$session->setPort($port);
- Connectez-vous au serveur et définissez immédiatement la propriété
suspendsurtruepour indiquer au serveur que nous pouvons vouloir nous reconnecter :/*
* Établir la connexion et indiquer au serveur que nous pouvons vouloir nous reconnecter.
*/
$session->connect() ;
$session->setSuspend(1) ;
Cela garantit que le serveur écoute sur un nouveau port unique.
- Créez l’objet côté client
IMuModuleet définissez sa propriétédestroysurfalse:/*
* Créer un objet module et indiquer au serveur de ne pas le détruire.
*/
$module = new IMuModule('eparties', $session) ;
$module->setDestroy(false) ;
Cela garantit que le serveur ne détruira pas l’objet côté serveur correspondant à la fin de la session
- Si l’URL comprend un paramètre
name, nous devons effectuer une nouvelle recherche. Sinon, si elle comprend un paramètreid, nous devons nous connecter à un objet côté serveur existant :/* Si le nom est fourni, faire une nouvelle recherche. Le terme de recherche est transmis à partir de
** search.html à l’aide de GET
*/
if (isset($_GET['name']))
{
$terms = new IMuTerms();
$terms->add('NamLast', $_GET['name']);
$module->findTerms($terms);
}
/*
* Sinon, si id est fourni, se rattacher à l’objet côté serveur existant.
*/
else if (isset($_GET['id']))
{
$module->id = $_GET['id'];
}
/*
* Sinon, impossible de traiter la demande.
*/
else
{
header('HTTP/1.1 400 Bad Request');
print("missing 'name' or 'id' parameter\r\n");
exit(1);
}
- Construire une liste de colonnes à récupérer :
$columns = array
(
'NamFirst',
'NamLast'
);
- Si l’URL comprend un paramètre
rownum, récupérer les enregistrements à partir de celui-ci. Sinon, commencer à partir du numéro d’enregistrement1:/*
* Déterminer quel bloc d’enregistrements récupérer
*/
$rownum = 1;
if (isset($_GET['rownum']))
$rownum = $_GET['rownum'];
- Construire la page principale :
/*
* Récupérer les cinq enregistrements suivants
*/
$results = $module->fetch('start', $rownum - 1, 5, $columns);
$hits = $results->hits;
/*
* Construire la page de résultats
*/
?>
<!DOCTYPE html>
<html>
<head>
<title>IMu PHP API - Maintenir l'état</title>
</head>
<body>
<p>Nombre de correspondances : <?php echo $hits ?></p>
<table>
<?php
// Afficher chaque correspondance dans une ligne distincte d'un tableau
foreach ($results- rows as $row)
{
?>
<tr>
<td><?php echo $row['rownum'] ?></td>
<td><?php echo $row['NamFirst'], ' ', $row['NamLast'] ?></td>
</tr>
<?php
}
?>
</table>
<?php
- Enfin, ajoutez les liens Précédent et Suivant pour permettre à l’utilisateur de parcourir les résultats en avant et en arrière. C’est la partie la plus compliquée ! Tout d’abord, pour vous assurer qu’une connexion est établie avec le même serveur et le même objet côté serveur, ajoutez les paramètres appropriés
portetidà l’URL du lien :/*
* Ajouter les liens Précédent et Suivant
*/
$url = $_SERVER['PHP_SELF'];
$url .= '?port=' . $session->port;
$url .= '&id=' . $module->id;
- Si le premier enregistrement ne s’affiche pas, ajoutez un lien Précédent pour permettre à l’utilisateur de revenir d’une page dans le jeu de résultats. De même, si le dernier enregistrement ne s’affiche pas, ajoutez un lien Suivant pour permettre à l’utilisateur d’avancer d’une page :
$first = $results->rows[0] ;
if ($first['rownum'] > 1)
{
$prev = $first['rownum'] - 5;
if ($prev < 1)
$prev = 1;
$prev = $url . '&rownum=' . $prev;
?>
<a href="<?php echo $prev ?>">Prev</a>
<?php
}
$last = $results->rows[count($results->rows) - 1];
if ($last['rownum'] < $results->hits)
{
$next = $last['rownum'] + 1;
$next = $url . '&rownum=' . $next;
?>
<a href="<?php echo $next ?>">Next</a>
<?php
}
?>
</body>
</html>

