Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox



Webservices PHP MySQL Flex

Compatible Flex Builder 3. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible PHP. Cliquer pour en savoir plus sur les compatibilités.Par sebboon (Sebastien Bordes), le 23 juin 2008

Le but de ce tutoriel est de faire communiquer un application Flex avec un serveur distant disposant de PHP5/MySql

Introduction

Dans ce tutoriel nous allons faire communiquer une application Flex avec un serveur Apache/PHP5 et une base de données MySql par des Web Services.

- La partie Flex nécessite l'utilisation de Flex Builder 3 (vous pouvez télécharger une version d'évaluation sur le site d'Adobe)

- La partie serveur utilise Flampy : un petit framework fait-maison pour la circonstance, il est disponible à cette adresse : http://code.google.com/p/flampy.
Il permet de gérer plusieurs web services en toute simplicité et d'interroger une base de données relationnelle (avec un mapping inspiré d'Hibernate pour les développeurs Java ) afin de fournir les données. Ce tutoriel n'expliquera pas en détail l'utilisation de ce framework car ce n'est pas le sujet (si vous êtes intéressé par l'utilisation de ce petit framework veuillez consulter sa page sur code.google.com ou m'écrire un mail).

Création du fichier de description de votre Web Service

Un service web permet à un client distant d'appeler des méthodes sur un serveur afin de recevoir une réponse.
Les requêtes et les réponses seront transmise via HTTP par le protocole SOAP qui permet de transcoder des objets présent sur le serveur en graphe XML au format SOAP.

Ce fichier a une extension en .wsdl ( Web Service Description Language). La syntaxe d'un tel fichier peut paraître assez compliqué car elle est très verbeuse, et une bonne connaissance de XML (notamment XML Schema) est nécessaire.
Si vous utilisez PDT (une version PHP/Web du célèbre logiciel Eclipse), vous pourrez utilisez un éditeur graphique qui pourra vous aider un peu.
Mais bon avec les explications qui suivent cela devraient être un peu plus facile.

Cet exemple utilise le format RPC/ENCODED (il existe aussi le Document/literal et le Rpc/literal), ce format va nous permettre de transmettre des portions de graphes d'objets plus ou moins complexes.

Du côté du client Flex récupérera ce XML et par la Classe SoapDecoder.as le traduira en graphe d'objet ActionScript.
Voici le fichier de notre exemple:

<?xml version="1.0"?>
<definitions 
xmlns:mywsdl="http://service.emukina.fr/EmulatorService.wsdl" 
xmlns:emukina="http://schema.emukina.fr/Emukina" 
xmlns="http://schemas.xmlsoap.org/wsdl/" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="EmulatorService" 
targetNamespace="http://service.emukina.fr/EmulatorService.wsdl" 
xsi:schemaLocation="http://schema.emukina.fr/Emukina http://schema.emukina.fr/Emukina.xsd">
 
	<wsdl:types>
		<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schema.emukina.fr/Emukina">
			<xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" schemaLocation="http://schemas.xmlsoap.org/soap/encoding/"/>
			<xsd:include schemaLocation="http://schema.emukina.fr/Emukina.xsd"/>
			<xsd:complexType name="EmulatorsArray">
				<xsd:complexContent>
					<xsd:restriction base="soapenc:Array">
						<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="emukina:Emulator[]"/>
					</xsd:restriction>
				</xsd:complexContent>
			</xsd:complexType>	
		</xsd:schema>
	</wsdl:types>
 
 
	<wsdl:message name="listEmulatorRequest">
		<wsdl:part name="emulatorType" type="xsd:string" />  
	</wsdl:message>
	<wsdl:message name="listEmulatorResponse">
		<wsdl:part name="emulators" type="emukina:EmulatorsArray"/>
	</wsdl:message>
 
	<wsdl:message name="saveEmulatorRequest">
		<wsdl:part name="emulator" type="emukina:Emulator"/>
	</wsdl:message>
	<wsdl:message name="saveEmulatorResponse">
		<wsdl:part name="saved" type="xsd:boolean"/>
	</wsdl:message>
 
	<wsdl:message name="deleteEmulatorRequest">
		<wsdl:part name="emulator" type="emukina:Emulator"/>
	</wsdl:message>
	<wsdl:message name="deleteEmulatorResponse">
		<wsdl:part name="deleted" type="xsd:boolean"/>
	</wsdl:message>
 
 
	<wsdl:portType name="EmukinaPorts">
 
		<wsdl:operation name="listEmulator">
			<wsdl:input message="mywsdl:listEmulatorRequest"/>
			<wsdl:output message="mywsdl:listEmulatorResponse"/>
		</wsdl:operation>
 
		<wsdl:operation name="saveEmulator">
			<wsdl:input message="mywsdl:saveEmulatorRequest"/>
			<wsdl:output message="mywsdl:saveEmulatorResponse"/>
		</wsdl:operation>
 
		<wsdl:operation name="deleteEmulator">
			<wsdl:input message="mywsdl:deleteEmulatorRequest"/>
			<wsdl:output message="mywsdl:deleteEmulatorResponse"/>
		</wsdl:operation>
 
	</wsdl:portType>
 
 
	<wsdl:binding name="EmulatorBinding" type="mywsdl:EmukinaPorts">
		<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
 
		<wsdl:operation name="listEmulator">
			<soap:operation soapAction="http://service.emukina.fr/EmulatorService.wsdl"/>
			<wsdl:input>
				<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schema.emukina.fr/Emukina" use="encoded"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schema.emukina.fr/Emukina" use="encoded"/>
			</wsdl:output>
		</wsdl:operation>
 
		<wsdl:operation name="saveEmulator">
			<soap:operation soapAction="http://service.emukina.fr/EmulatorService.wsdl"/>
			<wsdl:input>
				<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schema.emukina.fr/Emukina" use="encoded"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schema.emukina.fr/Emukina" use="encoded"/>
			</wsdl:output>
		</wsdl:operation>
 
		<wsdl:operation name="deleteEmulator">
			<soap:operation soapAction="http://service.emukina.fr/EmulatorService.wsdl"/>
			<wsdl:input>
				<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schema.emukina.fr/Emukina" use="encoded"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schema.emukina.fr/Emukina" use="encoded"/>
			</wsdl:output>
		</wsdl:operation>
 
	</wsdl:binding>
	<service name="EmulatorService">
		<documentation>Emulator Service</documentation>
		<port name="EmukinaPorts" binding="mywsdl:EmulatorBinding">
			<soap:address location="http://service.emukina.fr/?service=Emulator"/>
		</port>
	</service>
</definitions>

Il faut noter que chaque ligne de document a une grande importance, notamment les déclarations des namespaces.
Pour cet exemple je dispose de plusieurs sous-domaines
→ schema.emukina.fr ⇒ où sont stockés tous les schéma XML de Flampy et du modèle métier de l'application.
→ service.emukina.fr ⇒ où sont stockés les fichiers WSDL et le code peremttant d'appeler Flampy pour retourner le bon résultat.
→ flex.emukina.fr ⇒ L'application Flex appelant les Web Services.

Parcourons un peu ce fichier

L'élément types permet de définir le modèle métier correspondant à nos objets. Certains outils de modélisation UML permettent à partir d'un diagramme de classe de générer un fichier XML Schema (par exemple IBM Rational Software Modeler).

Ici nous importons un document XML Schema contenant la description de notre petit graphe d'objet.

<xsd:include schemaLocation="http://schema.emukina.fr/Emukina.xsd"/>

Puis nous déclarons un tableau d'émulateurs en utilisant le fameux schema SOAP-Encoding, cette syntaxe fonctionne avec le bon encodingStyle que nous verrons plus loin.

<xsd:complexType name="EmulatorsArray"> 
   <xsd:complexContent> 
       <xsd:restriction base="soapenc:Array"> 
             <xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="emukina:Emulator[]"/> 
        </xsd:restriction> 
    </xsd:complexContent> 
 </xsd:complexType>
<wsdl:message name="listEmulatorRequest">
		<wsdl:part name="emulatorType" type="xsd:string" />  
	</wsdl:message>
 
	<wsdl:message name="listEmulatorResponse">
		<wsdl:part name="emulators" type="emukina:EmulatorsArray"/>
	</wsdl:message>
	....

Ensuite nous déclarons les éléments messages qui correspondent aux éléments d'entrée et de sortie de chacune de nos méthodes de notre service web. (ici la méthode listEmulator)
Ces message sont composés d'éléments part qui font référence à des types simples (le plus souvent le type String), ou des types complexes (par exemple emukina:EmulatorsArray qui est un graphe d'objet)

	<wsdl:operation name="listEmulator">
		<wsdl:input message="mywsdl:listEmulatorRequest"/>
		<wsdl:output message="mywsdl:listEmulatorResponse"/>
	</wsdl:operation>

Puis nous déclarons l'élément port Type composé d'opérations qui sont en fait nos méthodes de notre service web.
Les opérations ont des éléments input et output qui font référence aux éléments messages précédemment déclarés.

La partir Binding permet de définir le type de notre service web, nous allons donc utiliser le format Rpc/Encoded, en faisant attention à bien déclarer l'encoding style pour pouvoir gérer correctement les tableaux Soap.

Et enfin pour finir l'élément service permet de définir l'url à laquelle votre service web va répondre. L'url fournit en exemple utilise le framework Flampy.

Voici le code du fichier index.php à déposer dans le même répertoire que vos fichiers Wsdl.

<?php
 
include '../Emukina/bootstrap.php';
 
//Check the availability of the service parameter
if(isset($_REQUEST['service'])){
 
	//Set the local path where wsdl files are stored
	ServicesManager::getInstance()->setWSDLPath(SERVICE_PATH);
	//Execute the service
	ServicesManager::getInstance()->execute($_REQUEST['service']);
 
}else{
 
	// There isn't any Service to process
	ServicesManager::getInstance()->displayAPI();
 
}
 
include '../Emukina/close.php';
 
?>

et le fichier .htaccess pour rediriger joliment tout ce petit monde

	AddType x-mapp-php5 .php                        #Active Php5 chez 1&1 
	Options +Indexes +FollowSymlinks
	RewriteEngine On
	RewriteBase /
	RewriteRule !((crossdomain.xml)|\.(wsdl))$ index.php [L]

Voici la structure complète de l'application. (le contenu est disponible sur le site de Flampy)

Pour l'application Emukina, je ne vais pas détailler le mapping avec la base de données relationnelles. C'est du PHP

Voici seulement les fichiers relatifs aux service web pour que ca puisse fonctionner.

Le fichier de paramétrage : services.xml

<?xml version="1.0" encoding="UTF-8"?>
<services xmlns="http://schema.emukina.fr/Services" xsi:schemaLocation="http://schema.emukina.fr/Services http://schema.emukina.fr/Services.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 
	<service>
		<name>Emulator</name>
		<className>EmulatorService</className>
	</service>
 
	<service>
		<name>EmulatorType</name>
		<className>EmulatorTypeService</className>
	</service>
 
	<!-- Seulement si vos objet PHP n'ont pas le même nom que vos éléments XML Schema-->
	<classMap>
		<entry className="Emulator" schemaName="Emulator" />
	</classMap>
 
</services>

La Classe appelée par le service web EmulatorService

<?php
 
/**
 * The Emulator Service.
 */
class EmulatorService {
 
	/**
	 * @param emutype
	 * @return The list of emulators
	 */
	function listEmulator($emulatorType) {
 
		if(isset($emutype)){
			$criterion = new Criterion(new ComparisonFilter('type.name', ComparisonFilter::EQUAL, $emutype));
		}else{
			$criterion = new Criterion();
		}
		$emuType = new EmulatorType();
		$emuType->setLabel($emulatorType);
 
		$criterion->addOrder(new OrderCriterion('name', true));
 
		$res = BeanFactory::getManager('Emulator')->load($criterion);
 
		foreach ($res as $emu){
			$emu->setType($emuType);
		}
 
		return $res;
	}
 
	function saveEmulator($emulator) {
		if(isset($emulator)){
			return BeanFactory::getManager('Emulator')->save($emulator);
		}
	}
 
	function deleteEmulator($emulator) {
		if(isset($emulator)){
			return BeanFactory::getManager('Emulator')->delete($emulator);
		}
	}
}

C'est tout !!, ce n'est pas énorme, à noter que vous pouvez appeler directement les focntions mysql pour vous retrouver les objets à transmettre.
Flampy utilise un fichier ServiceManager.php pour appeler les méthodes PHP SOAP de manière transparente.
Flampy est en phase de développement active icon_razz.gif (un petit peu chaque semaine selon mes disponibiltés)

Import du Web Service dans Flex Builder 3

Dans Flex Builder 3, cliquez sur le menu Data → Import Web Services

Vous n'avez plus qu'à suivre le guide …

FB3_import_web_service_1.png

FB3_import_web_service_2.png

FB3_import_web_service_3.png

Flex Builder va maintenant générer toutes les classes qui nous seront utiles pour charger les données dans notre application Flex.

Plutôt simple et rapide comme étape !!

Appel à une méthode du service web Emulator

L'exemple fonctionnel est disponible à cette adresse http://flex.emukina.fr, il utilise le framework PureMVC (peut-être un prochain tutoriel en perspective).

Nous allons étudier le code nécessaire pour l'appel de la méthode listEmulator.

public var emuService:EmulatorService;
public var data:ArrayCollection;
 
public function init():void
	emuService = new EmulatorService();
	emuService.addEmulatorServiceFaultEventListener(manageFaultEvent);
	emuService.addlistEmulatorEventListener(manageListEmulatorEvent);
}	
 
// La méthode a appeler
public function listEmulator(emuType:String):void
{
	emuService.listEmulator(emuType);
}
 
public function manageFaultEvent(event:Event):void{
	event.toString();
}
 
public function manageListEmulatorEvent(event:ListEmulatorResultEvent):void{
 
	var tab:EmulatorsArray = event.result as EmulatorsArray;
 
	data = new ArrayCollection();
	for(var i:int = 0 ; i < tab.length ; i++){
		data.addItem(tab[i]);
	}
 
}

Il faut donc appeler la méthode init avec par exemple un tag onCreationComplete=“init()” dans l'élément application de votre fichier mxml.

Puis selon vos besoins appeler la méthode listEmulator et de manière asynchrone, votre variable data recevra un tableaux d'objets Emulator, à vous de le binder comme bon vous semble.

Si vous utilisez le framework PureMVC, vous devez envoyer une notification afin de prévenir un eventuel composant graphique qu'il doit se rafraichir avec ce code par exemple

//Système de notification de PureMVC
sendNotification(ApplicationFacade.EMULATOR_LIST_CHANGED);