Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

AMFPHP + RecordSet

Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.Compatible PHP. Cliquer pour en savoir plus sur les compatibilités.

Le mot RecordSet n'est pas nouveau, ça existait déjà dans VB6. :-)

RecordSet, c'est une classe permettant de gérer très facilement des résultats issus d'une base de donnée SQL. L'implémentation du RecordSet façon Macromedia laisse à désirer (comme pas mal de ses composants et classes malheureusement) mais ce sont des souçis de puristes. L'important ici est le net avantage que procure l'utilisation de RecordSet.

MISE EN OEUVRE SERVER-SIDE

Crééz une table avec cette requête :

-- 
-- Table structure for table `news`
-- 
 
CREATE TABLE `news` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `headline` tinytext character SET utf8 NOT NULL,
  `news` text character SET utf8 NOT NULL,
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=8 ;
 
-- 
-- Dumping data for table `news`
-- 
 
INSERT INTO `news` VALUES (1, '1e news, mise en service !', 'Contenu texte de la 1e news', '2005-11-01 17:10:24');
INSERT INTO `news` VALUES (2, '2e news', 'Contenu texte de la 2e news', '2005-11-02 09:44:28');
INSERT INTO `news` VALUES (3, '3e news, rien de neuf !', 'Contenu texte de la dernière news', '2005-11-14 09:36:07');

Ensuite allez dans le répertoire services d'AMFPHP et créez 2 fichiers 'Sql.php' et 'News.php' :

<?php
class Sql
{ 
	var $dbhost = "localhost";
	var $dbname = "test";
	var $dbuser = "user";
	var $dbpass = "pass";
 
 	// Constructor
	function Sql ()
	{
		$conn = mysql_pconnect($this->dbhost, $this->dbuser, $this->dbpass) 
		or NetDebug::trace(mysql_error());
		mysql_select_db($this->dbname, $conn) 
		or NetDebug::trace(mysql_error());	
	}
 
	// Executes a sql query with NetDebug support
	function query ($sql)
    { 
		$result = mysql_query($sql) 
		or NetDebug::trace(mysql_error());
		return $result;
    }
}
?>
<?php
include_once("Sql.php");
include_once("../util/MethodTable.php");
 
class News
{
	var $db;
	function News()
	{
		$this->methodTable = MethodTable::create("News");
		$this->db = new Sql();
	}
 
	/**
	 * @desc Retrieves the news from the database
	 * @access remote
	 */
	function getNews()
	{
		$sql = "SELECT id, headline, date, news from news";
   		$result = $this->db->query($sql);
		return $result;
	}
}
?>

Attardons-nous quelque peu sur l'en-tête de getNews() :

  • Pourquoi n'y a-t-il pas de type de variable de retour spécifié ?

Tout simplement parce qu'AMFPHP gère automatiquement la plupart des types de variables retournés à Flash. Dans le cas présent, AMFPHP va automatiquement retourner $result en tant que RecordSet et c'est exactement ce que nous voulons.

Voici le tableau des types de variables et la manière dont AMFPHP les gère :

AS type PHP type Notes Automatic
null null Yes
boolean boolean Yes
String string Yes
Date float UNIX timestamp, set return type to date to send to AS. See also DateWrapper class No
Array array Yes
Object associative array * Yes
XML string Set return type to xml to send to AS No
Recordset Resource PHP → actionscript only Yes

Ce tableau provient directement du wiki d'amfphp : http://www.amfphp.org/wiki/doku.php

MISE EN OEUVRE DANS FLASH

Ouvrez maintenant Flash. Le code ci-dessous est de l'AS2. N'oubliez pas d'inclure dans votre librairie les composants RemotingClasses et RemotingDebugClasses.

// - Main
import mx.remoting.Service;
import mx.remoting.PendingCall;
import mx.remoting.NetServices;
// - Listeners
import mx.rpc.RelayResponder;
// - Data
import mx.remoting.RecordSet;
import mx.utils.Iterator;
// - Interfaces
import mx.rpc.FaultEvent;
import mx.rpc.ResultEvent;
// - Debug
import mx.remoting.debug.NetDebug;
NetDebug.initialize();
 
var serviceListener:Object = new Object();
 
// Evénements d'AMFPHP
serviceListener.onResult = function(res:ResultEvent):Void
{
	// Lorsqu'on manipule un RecordSet, c'est toujours avec un Iterator ;)
	var iter:Iterator = res.result.getIterator();		
 
	// Tant que le curseur du résultat peut sauter à la ligne suivante...
	while(iter.hasNext()) 
	{
		var currentData:Object = iter.next();
 
		/*
		Habituez-vous à utiliser le for..in pour débugger et
		voir comment est constitué votre résultat (sous-objets, ...)
		*/
		for (var i:String in currentData)
		{
			trace(i + " -> " + currentData[i]);
		}
 
		/* 
		Voyez comme il est simple d'accéder à un champ précis
		du RecordSet !
		*/
		trace(currentData.headline);
	}		
}
serviceListener.onFault = function (res:FaultEvent):Void
{
	trace ("Erreur : " + res.fault.faultstring);
}
 
service = new Service("http://localhost/flashservices/gateway.php", null,"News",null,null);
 
// Appelle les news maintenant
var pendingCall:PendingCall = service.getNews();
pendingCall.responder = new RelayResponder(serviceListener, "onResult", "onFault");

ALTERNATIVE

Notez encore une chose : chaque instance de PendingCall est déjà un listener de service. Ainsi, vous n'êtes pas obligé de créer un new RelayResponder pour écouter le service. Cette solution est un peu moins élégante que la précédante car on y déclare les événements après l'appel à AMFPHP. ;-)

// - Main
import mx.remoting.Service;
import mx.remoting.PendingCall;
import mx.remoting.NetServices;
// - Listeners
import mx.rpc.RelayResponder;
// - Data
import mx.remoting.RecordSet;
import mx.utils.Iterator;
// - Interfaces
import mx.rpc.FaultEvent;
import mx.rpc.ResultEvent;
// - Debug
import mx.remoting.debug.NetDebug;
NetDebug.initialize();
 
service = new Service("http://localhost/flashservices/gateway.php", null, "News", null, null);
 
// Appelle les news maintenant
var pendingCall:PendingCall = service.getNews();
 
// Evénements d'AMFPHP
pendingCall.onResult = function(res:ResultEvent):Void
{
	/*
	----
	ATTENTION, LE RESULTAT RETOURNE PAR AMFPHP N'EST PAS DISPONIBLE
	DANS res.result MAIS DANS res !
	----	
	*/
	// Lorsqu'on manipule un RecordSet, c'est toujours avec un Iterator ;)
	var iter:Iterator = res.getIterator();		
 
	// Tant que le curseur du résultat peut sauter à la ligne suivante...
	while(iter.hasNext()) 
	{
		var currentData:Object = iter.next();
 
		/*
		Habituez-vous à utiliser le for..in pour débugger et
		voir comment est constitué votre résultat (sous-objets, ...)
		*/
		for (var i:String in currentData)
		{
			trace(i + " -> " + currentData[i]);
		}
 
		/* 
		Voyez comme il est simple d'accéder à un champ précis
		du RecordSet !
		*/
		trace(currentData.headline);
	}		
}
/*
CE N'EST PLUS onFault MAIS onStatus QUI ECOUTE LES ERREURS
*/
pendingCall.onStatus = function (res:FaultEvent):Void
{
	trace ("Erreur : " + res.description);
}

Voilà ! Au final vous avez, en peu de lignes de code, extrait et manipulé des données de votre database SQL avec une simplicité impressionnante. :-)

Le tutorial suivant traitera de l'autentification et de la sécurité avec AMFPHP. ;-)

- Anty