Utiliser Cairngorm dans un projet Flex / AMFPHP
Par Lionel, dans [Dev] Flash / Flex / AIR... # 313Voilà un premier article sur le framework architectural d'Adobe , le bien nommé mais imprononçable Cairngorm MicroArchitecture... cet article est basé sur la série de 6 articles écrits par Steven Webster, un des papa du machin. Cet article s'adresse plutôt aux gens qui ne connaissent pas Cairngorm mais qui ont déja quelques notions de patterns (si vous savez que ça existe, je considère ça comme une notion )
Mise à jour : L'archive des sources contient désormais un petit truc en plus qui permet de spécifier le endpoint depuis un fichier xml. on est plus obligé de recompiler à chaque fois !!!
Dans cet article, nous allons aborder plusieurs points. Pour commencer : un cadrage sur Cairngorm, qu'est ce que c'est, qu'est ce que çà permet... Ensuite nous verrons au travers d'un exemple ( le classique Carnet d'adresse ), comment utiliser Cairngorm dans un projet Flex / AMFPHP. Vous aurez besoin de :
L'exemple de cette article peut être réalisé dans un environnement classique ( Flex Builder / éditeur PHP ) ou dans un eclipse tout intégré (plugin FlexBuilder 3 et PDT ). Nous reviendrons dans un autre article sur l'installation de ce dernier.
Plutôt que de se jeter la tête la première dans Flex et le codage de notre RIA avec Cairngorm, faisons d'abord les présentations.
Cairngorm est un framework architectural
en partie basé sur le pattern Model-Vue-Controller, il va donc fournir aux développeurs un squelette sur lequel bâtir leur application ( en quelque sorte les fondations ). Cela permet par la suite une meilleure articulation du code en fonction des responsabilités de chaque "acteur". Le développement de l'application va suivre certains plans qui nous permettront de mieux appréhender les évolutions futures de l'application (dans le meilleur des cas ),... ou au moins de nous donner un vision plus claire de notre code, et de nous aider à trouver plus rapidement les bugs 
Cairngorm n'est donc pas un pattern, mais une micro architecture
.
On parle de micro architecture lorsque plusieurs modèles de conceptions (les patterns) collaborent, au sein d'une "composition" de patterns (MVC , FrontController ). Alors qu'un pattern est là pour résoudre un problème précis : (comment m'assurer que j'ai bien une instance unique de telle classe dans mon application ? ), la composition de patterns va permettre d'augmenter le niveau d'abstraction et de résoudre donc des problèmes de "plus haut niveau" : (comment lier telle action de l'utilisateur avec un tel traitement).
Cairngorm utilise donc plusieurs patterns et les assemble pour nous permettre de mieux développer nos applications ( tout en nous évitant ainsi les risque de "patternite aiguë", consistant à vouloir coller des patterns partout ). Cairngorm offre une micro architecture qui va permettre de résoudre les problèmes de conceptions récurrents dans le développement d'applications en ligne :
- Récupérer les actions de l'utilisateur
- Encapsuler la logique métier et les interactions avec le serveur
- Gérer et représenter l'état de l'application côté client
Il existe une grande différence entre les applications web classiques
et les RIA : les premières sont "stateless", les secondes "stateful", c'est à dire que l'application conserve "l'état du client" durant les différentes transactions. En gros, quand le HTML "classique" doit recharger entièrement une page après chaque "appel serveur", il est stateless ; alors l'une des caractèristiques des "applications riches" par contre, et de permettre des communications avec le serveur sans avoir à recharger entièrement la page en cours, elles peuvent donc être stateful. On sort en quelque sorte paradigme de la Page web, pour entrer revenir celui d'Application.
Les RIA sont donc largement dépendantes des "modalités" de communication client / serveur, la difficulté résidant dans la capacités à envoyer/recevoir /afficher les bonnes données, au bon moment, et au bon endroit...
Voyons donc maintenant comment Flex et l'AMF ( via AMFPHP dans notre exemple ), accompagnés de Cairngorm, nous permettent de gérer ces communications.
L'AMF ( Action Message Format ) permet à Flex d'accéder à des objets côté serveur. Initialement destinée aux serveurs JAVA, cette "passerelle AMF" est utilisée par AMFPHP, pour accéder à des objets PHP via "services" PHP... mais çà vous le saviez !
Penchons nous sur le rôle joué par Cairngorm : Cairngorm est une sorte de maquette d'application, il fournit un plan de construction et quelques pièces maîtresses. Ce plan est basé sur quelques d'objets ou types d'objets "spécialisés", chacun ayant un rôle, ET ( "en principe" ) UN SEUL.
Ces objets sont : les VO , le ModelLocator, les vues , le FrontController , les commandes, les delegates et le ServiceLocator. Ils vont entièrement articuler le chargement des données côté serveur (PHP) et le traitement / affichage des données côté client ( Flex ).

Bon le générique c'est fait, passons à l'épisode du jour : créer notre "Rich" Carnet d'adresse.
Les VO
Et au départ il y avait la poule ou l'oeuf ?
Dans le cas de notre carnet d'adresse, l'objectif est de récupérer une liste de contacts, les afficher, les modifier, en ajouter ... le 1er type d'objet échangé entre client et serveur sera donc le "(Contact)".
Pour le développement de RIA, on a en effet pas à réfléchir trop longtemps, au départ il y a le VO ou Value Object ( parfois appelé DTO, "Data Transfer Object" ). Le VO est le premier pattern qu'on rencontre dans Cairngorm. Il va nous permettre d'encapsuler dans un objet, toutes les informations correspondant à un enregistrement dans la base de donnée. Un VO n'est rien de plus qu'un objet contenant un ensemble de propriétés (une colonne de notre table dans la BDD correspondant à une propriété de notre objet ).
Un des enjeux majeurs des RIA, puisqu'elles sont "stateful", est d'être synchronisées avec le serveur. En effet à partir du moment où l'information se trouve à deux endroits, il y a un risque qu'un des "acteurs" ne soit pas "à jour". Le VO va nous permettre d'augmenter le niveau d'abstraction en échangeant des objets qui ont un réel sens "sémantique" ( un "(Contact)" par exemple ), et non plus un sens du point de vue "technique" (un tableau de String). On a donc un modèle objet cohérent, et "identique" sur le client et sur le serveur. On s'éloigne de l'implémentation concrète, liée au langage de nos différentes couches (présentation, métiers, ...).
Vous allez vous dire "Mais c'est bien beau d'avoir nos objets d'un coté et de l'autre mais comment on fait pour les échanger.." Et bien tout çà ne serait pas merveilleux si l'on n'avait pas la possibilité de faire du "mapping" entre les objets clients et le objets serveurs. Ce mécanisme permet lorsqu'on envoi un objet "ContactVO" ActionScript, de recevoir un objet du même type mais dans donc notre couche métier (qui peut être en PHP, en Java...). Cela facilite grandement les échanges entre le client et le serveur.
Pour résumer, l'utilisation des VO permet :
- de simplifier l'échange des données entre les différentes couches de l'application
- d'augmenter le niveau d'abstraction en "encapsulant" les données dans des objets "métiers"
Notre 1er VO : ContactVO
package com.lafabrick.Cairngormexemple.vo { [RemoteClass(alias="vo.ContactVO")] class ContactVO { public var id : int; public var nom : String; public var prenom : String; public var mail : String; } }
Le mapping sera assuré grâce à la ligne [RemoteClass(alias=vo.ContactVO)] ça veut dire que cette classe sera mappée côté serveur par la classe qui se trouve dans le package 'vo' et qui s'appelle ContactVO mais on en est pas encore à transférer des objects entre le client et le serveur... on verra plus tard les joies et autres petits plaisirs du mapping.
Très bien notre VO est identifié ! On sait quel type de données nous allons gérer. Voyons comment les intégrer à notre application.
Le ModelLocator Cairngorm étant basé sur un MVC, qui dit "données", dit "Model, et là on va pouvoir parler de "ModelLocator".
Parce que c'est bien beau d'arriver à échanger des objets entre le serveur et le client, mais on en fait quoi après... Cairngorm propose donc d'encapsuler l'état ( des données ) du client dans une structure... je vous le donne en mille Émile... le ModelLocator. Et le ModelLocator reprends donc le concept du model de MVC.
Le ModelLocator doit nous offrir un moyen de centraliser l'état du client plutôt que de le disperser un peu partout dans notre application. Du coup, il doit nous fournir aussi un moyen d'accès globalisé pour pouvoir accéder aux données qu'il contient. Les créateurs de Cairngorm insistent sur le fait que le ModelLocator doit contenir des objets ou collections d'objets qui sont porteur de sens (avec une sémantique associée). Dans un annuaire/ carnet d'adresses, on a des Contacts, des Groupes, des Adresses. Tiens çà vous rappelle rien ? Ben si ces entités seront sûrement les VO de l'application.
CairngormExempleModelLocator
package com.lafabrick.cairngormexemple.model { import com.adobe.cairngorm.CairngormError; import com.adobe.cairngorm.CairngormMessageCodes; import mx.collections.ICollectionView; [Bindable] public class CairngormExempleModelLocator { public var users : ICollectionView; private static var instance : CairngormExempleModelLocator; public function CairngormExempleModelLocator( ) { if (instance == null) { instance = this; } else { throw new CairngormError( CairngormMessageCodes.SINGLETON_EXCEPTION, "CairngormExempleModelLocator" ); } } public static function getInstance() : CairngormExempleModelLocator { if (instance == null) { instance = new CairngormExempleModelLocator(); } return instance; } } }
Pour ce qui concerne la vue, nous allons nous reposer sur un mécanisme de Flex assez pratique, le Binding
. On a vu tout à l'heure que le ModelLocator devait nous fournir un accès globalisé aux données de l'application. Pour cela nous avons implémenté le modèle Singleton
. Les plus perspicaces auront aussi remarqué le mot-clé [Bindable] qui va nous permettre, depuis un fichier mxml, de lier les données de notre Model aux propriétes d'un composant. La mise à jour des données dans l'application cliente sera largement simplifiée, puisqu'à partir du moment ou les données du modèle seront mises à jour, les vues branchées sur ces données bénéficieront des changements et cela automatiquement... pour pas une ligne de code... allons y c'est gratuit...
Pour résumer, le ModelLocator :
* offre un moyen globalisé d'accèder aux données de l'application. * facilite la séparation données / vues grâce aux "Data Binding"
La Vue
Maintenant, on va créer la vue qui sera chargée d'afficher notre liste de contacts. Notre vue sera composée d'un Panel dans lequel sera affichée une Datagrid qui affichera nos objets Contacts. Et on va pas oublier de brancher le dataprovider de la datagrid sur la collection de 'contacts' du model. Pour le moment, on ne va pas se casser la tête, on va directement poser notre composant vue ( le Panel datagridé ) dans notre fichier d'application principal.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:model="com.lafabrick.cairngormexemple.model.*" xmlns:control="com.lafabrick.cairngormexemple.control.*" xmlns:business="com.lafabrick.cairngormexemple.business.*" applicationComplete="loadContacts()" viewSourceURL="srcview/index.html"> <mx:Script> <![CDATA[ import com.adobe.cairngorm.control.CairngormEvent; public function loadContacts() : void { var e : CairngormEvent = new CairngormEvent(CairngormExempleControl.LOAD_CONTACTS_EVENT); e.dispatch(); } ]]> </mx:Script> <model:CairngormExempleModelLocator id="model"/> <control:CairngormExempleControl id="controller" /> <business:Services id="services" /> <mx:Panel id="userpanel" title="Mes contacts" width="400" height="300" horizontalCenter="0" verticalCenter="0"> <mx:DataGrid dataProvider="{model.users}" width="100%" height="100%"> <mx:columns> <mx:DataGridColumn dataField="nom" /> <mx:DataGridColumn dataField="prenom" /> <mx:DataGridColumn dataField="mail" /> </mx:columns> </mx:DataGrid> </mx:Panel> </mx:Application>
Dans cairngorm, les responsabilités de la vue sont :
- Gérer les interactions avec les utilisateurs, et générer les événements associer.
- Récupérer et mettre en forme les données contenues dans le ModelLocator en utilisant les DataBinding.
Sauf que pour l'instant y'a toujours rien à afficher...
Le FrontController
Première chose pour gérer le chargement / envoi des données, on va créer ce qu'on appelle un . Le FrontController est en fait un modèle de conception composé du pattern MVC et du pattern Commande. Ce pattern va nous permettre de bénéficier d'une architecture capable de lier un type d'événement à l'exécution d'un traitement. Avant d'utiliser ce pattern, il faudra d'abord bien identifier les differents évenements qui vont être source de traitement. Pour notre exemple, on a choisi de réaliser le chargement de la liste de contacts depuis un base données au moment où l'application s'ouvrira.
Le but du FrontController est de pouvoir encapsuler chaque traitement dans une classe (c'est l'utilisation de fameux pattern Commande ). Alors que le ModelLocator ne nous fournissait qu'une interface pour la création de notre modèle, cette fois Cairngorm nous fournit une classe presque complète. Nous allons donc créer une classe qui hérite de FrontController, et on va y énumérer les différents types d'évènements que l'application et ses vues peuvent générer. C'est toujours dans cette classe que l'on va associer un type d'évènement à une Commande particulière, une classe chargée du "traitement". Pour notre carnet d'adresse, les différentes événements possible pourront être liés des commandes : d'ajout ou la suppression d'un contact, de chargement de la liste de VO... Pour le moment, nous savons juste qu'à l'ouverture de l'application, un événement CairngormExempleControl.LOAD_CONTACTS_EVENT est envoyé par l'application :
CairngormExempleControl
package com.lafabrick.Cairngormexemple.control { import com.adobe.Cairngorm.control.FrontController; import com.lafabrick.Cairngormexemple.commands.LoadContactsCommand; public class CairngormExempleControl extends FrontController { public function CairngormExempleControl () { addCommand( LOAD_CONTACTS_EVENT, LoadContactsCommand); } public static const LOAD_CONTACTS_EVENT : String = "<load_contacts>"; } }
C'est avec la méthode addCommand(EventType : String, MaCommande : ICommand) que l'on peut associer un type d'événement à un commande, et donc à un traitement. On a donc vu qu'utiliser le FrontController
- nous permet de découper les responsabilité et les déléguées sur plusieurs acteurs
- nous permet d'associer un type d'événement avec une commande
Les Commandes
Il nous faut donc maintenant créer une Commande LoadContactsCommand. Elle aura pour mission de "commander" (le chargement de) la liste de contacts et de gérer sa réception ( ou sa non - réception ). Et comme Cairngorm est jusqu'au bout basé sur le principe de "délégation de responsabilité", les Commandes délèguent l'appel serveur à une autre classe : la bien nommée Delegate. Lorsque qu'elles doivent récupérer des données sur le serveur, chaque commande dispose de sa propre classe Delegate ( dans notre exemple LoadContactsDelegate ). La Command est chargée de récupérer les données à envoyer au serveur ( si des données doivent être envoyées, elles sont stockées dans l'Event émis ), et de les transmettre au Delegate appropriée, qui elle, établira la communication avec le service concerné côté serveur. L'avantage de cette délégation est de garder les Commandes complètement indépendantes des opérations côté serveur. Elles commandent des données, les réceptionnent, les traitent, mais ne savent pas d'où elles viennent. Si il y a le moindre changement du côté du serveur, elles ne sont pas concernées.
LoadContactsCommand :
package com.lafabrick.cairngormexemple.commands { import com.adobe.cairngorm.commands.ICommand; import com.adobe.cairngorm.control.CairngormEvent; import com.lafabrick.cairngormexemple.business.LoadContactsDelegate; import com.lafabrick.cairngormexemple.model.CairngormExempleModelLocator; import mx.collections.ArrayCollection; import mx.rpc.IResponder; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import com.fabrick.cairngormexemple.vo.ContactVO; public class LoadContactsCommand implements ICommand, IResponder { public function execute(event:CairngormEvent):void { trace("execute command LoadContacts"); var delegate : LoadContactsDelegate = new LoadContactsDelegate( this ); delegate.loadAll(); } public function result(obj : Object) : void { var ev : ResultEvent = obj as ResultEvent; var u : ContactVO; CairngormExempleModelLocator.getInstance().users = new ArrayCollection(ev.result as Array); trace("result "+ev.result); } public function fault(obj : Object) : void { var ev : FaultEvent = obj as FaultEvent; trace("error"+ev.fault ); } } }
Notre commande implémente l'interface ICommand qui impose à la classe d'intégrer une méthode execute(event:CairngormEvent):void. Cela permet d'executer n'importe quelle commande puisqu'on aura pas besoin de savoir ce qu'elle fait, du moment qu'elle possède bien cette méthode execute(). Voici donc une partie du Design pattern "(Command)".
L'intérêt d'utiliser les commandes est :
- d'encapsuler un traitement dans une classe
- Dans le cas d'un appel distant, préparer les données nécessaires à la transaction et recupérer le résultat de la transaction
Les Delegates_
Pour charger notre liste d'utilisateur, nous allons utiliser AMFPHP (1.9) et les services de Flex. Pour éviter de mélanger les responsabilités, on utilise donc un objet LoadContactsDelegate qui sera chargé d'encapsuler les appels aux méthodes des services distants. C'est depuis cet objet que l'on établira la connection au service distant puis l'appel à la méthode distante qui nous renverra nos objets ContactVO. Cela nous permet par la même occasion d'avoir différentes commandes qui utilisent la même source de données en ayant chacune une spécificité au niveau du traitement. On appelle cela le Business Delegate.
package com.lafabrick.Cairngormexemple.business { import com.adobe.Cairngorm.business.ServiceLocator; import com.lafabrick.Cairngormexemple.vo.ContactVO; import mx.rpc.AbstractService; import mx.rpc.AsyncToken; import mx.rpc.IResponder; public class LoadContactsDelegate { private var responder : IResponder; private var service : AbstractService; public function LoadContactsDelegate( _responder : IResponder ) { service = ServiceLocator.getInstance().getRemoteObject( "contactService" ); responder = _responder; } public function loadAll( ): void { var token : AsyncToken = service.loadAll(); token.addResponder(responder); } } }
Les objets Delegate vont nous permettre
- la prise en charge des connections aux services distants et les appels aux méthodes distantes
- de cacher les logiques transactionnelles à la commande
Pour localiser notre méthode distante, on fait appel à un "ServiceLocator" qui va nous permettre de nous connecter à un service distant à partir d'un identifiant en utilisant la méthode getRemoteObject(). On verra plus bas comment utiliser le ServiceLocator pour référencer nos services distants. Pour appeller notre méthode distante, il nous suffit de l'appeller depuis notre objet service. Enfin, on utilise l'objet AsynToken pour pouvoir assigner un objet en tant que Responder qui sera alors chargé de gérer le résultat de notre méthode distante, et cet objet c'est notre commande, la boucle est bouclée, on revient à notre Commande. C'est pour cette raison que la Commande que l'on a créé implémente une deuxième interface : IResponder. Cette interface va permettre à notre Commande, lors de l'appel de la méthode distante, de réceptionner les événements result et fault afin de récupérer le resultat de la méthode si tout s'est bien passé ou dans le cas contraire, d'exécuter un gestionnaire d'erreur.
On vient donc de voir la séquence Evènement - Commande - Delegate. Il faut bien comprendre que l'emploi du Délégate est ici justifié par l'appel de méthodes distantes. Il est tout à fait envisageable d'avoir des séquences Evènement - Commande. Gràce à ce genre de découpage, rajouter une fonctionnalité revient à rajouter une commande (ainsi que son type d'événement associé). Ce découp(l)age nous permet aussi de bien séparer les responsabilités de chacun.
Le mélange de FrontController, Command et Event et en fait lui même une "micro architecure" que l'on appelle Service To worker. Grosso modo on dégage la logique métiers des vues, on l'encapsule dans une classe et on utilise des évènements pour déclencher le tout à distance 
ServiceLocator
Nous avons donc besoin de récupérer nos contacts (ContactVO) depuis une méthode distante, et nous allons utiliser pour cela un autre design pattern mis en place dans Cairngorm, le ServiceLocator. Ce dernier va nous permettre de gérer nos différents services distants. Dans Flex, le ServiceLocator différe de son implémentation classique car, il est intégré sous forme de composant mxml (de toute façon il sera compilé en AS ).
<?xml version="1.0" encoding="utf-8"?> <Cairngorm:ServiceLocator xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:Cairngorm="com.adobe.cairngorm.business.*"> <mx:RemoteObject id="contactService" destination="amfphp" source="ContactService" showBusyCursor="true" /> </Cairngorm:ServiceLocator>
Dans notre ServiceLocator, on définit ce qu'on appelle un RemoteObject (qui correspond en faite à la méthode RPC que l'on va appeler ). L'id de chaque RemoteObject permet de gérer et d'appeler plusieurs services au sein du ServiceLocator. Comme son nom l'indique le ServiceLocator va nous permettre de :
- centraliser la définition des différents services, RPC (Appel de méthode distante - Remote Procedure Call )
- Offrir un accès globalisé à ces services
Les plus attentifs ont remarqué, par contre qu'on n'a toujours pas spécifié l'url de notre service. Et bien, qu'ils se rassurent, c'est normal puisque cela se fait dans un fichier de configuration (service-config.xml ... cela nous permettra de mieux gérer le déploiement de plusieurs instances de l'application (oui ca serait plutôt relou de devoir à chaque fois recompiler l'appli parce qu'on a mis une appli sur un nouveau serveur ) ben pour l'instant c'est relou 
Par contre pour le coup, je n'ai pas encore compris toutes les subtilités qu'offrait ce fichier de config...mais dans les grandes lignes ça donne ça... Le services flex transportent les messages, de et vers la destination du service, en utilisant des canaux (message-channel).
Dans notre fichier 'services-config.xml', il faut donc:
- définir un service ainsi que sa destination
- définir un channel qui sera utilisé par la destination du service
Et ça donne...
<?xml version="1.0" encoding="UTF-8"?> <services-config> <services> <service id="amfphp-flashremoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage"> <destination id="amfphp"> <channels> <channel ref="my-amfphp"/> </channels> <properties> <source>*</source> </properties> </destination> </service> </services> <channels> <channel-definition id="my-amfphp" class="mx.messaging.channels.AMFChannel"> <endpoint uri="http://flashservices:80/gateway.php" class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition> </channels> </services-config>
Pour les plus motivés, il reste toujours la doc de flex à ce sujet
Pour le Flex, je crois qu'on a fait le tour... Histoire de tout bien avoir proprement voilà à quoi ca ressemble une fois toutes les classes écrites

Maintenant il reste juste la partie php... juste...
Côté serveur _
Pour commencer, il faut récupérer AMFPHP 1.9 Pour l'instant c'est toujours une version béta... donc à utiliser avec parcimonie. On va modifier un petit paramètre dans le fichier globals.php pour nos besoins de mapping, on va faire pointer le répertoire des VO et celui des services au même endroit.
$servicesPath = "services/"; $voPath = "services/";
Le ContactVO version PHP
Première étape on écrit, non pas la classe ContactService mais la classe ContactVO ( parce qu'il faut bien commencer par quelque part, et puis l'histoire des poules, des oeufs, des VO etc... )
<?php class ContactVO { var $_explicitType = "vo.ContactVO"; var $id; var $nom; var $prenom; var $mail; } ?>
La classe en php est sensiblement identique, on remarque juste une variable bizarre, $_explicitType qui en fait va nous servir à faire du mapping d'objet AS / Php.. la grande classe... Après avoir quelque peu galéré à essayer de mapper les objets... on est arrivé à établir deux règles pour que tout ca fonctionne.
- Il faut que le $_explicitType et l'alias du Remote Class soit identique.
- Il faut que l'identifiant qui va permettre à AMF et Flex de faire le mapping correctement pointe vers la classe de VO en php...
En gros, si notre architecture ressemble à ca... :

on va spécifier comme chemin "vo.ContactVO"
Par contre, si on avait eu ce genre d'architecture

nous aurions dû spécifier comme chemin "com.lafabrick.caringormexemple.vo.ContactVO" et dans le fichier globals.php on aurait eu :
$servicesPath = "../"; $voPath = "../";
Il aurait fallu déplacer le fichier DiscoveryService.php initialement dans le répertoire services/AMFPHP dans le répertoire racine de AMFPHP, et dans le fichier ServiceLocator, la source aurait été :
<?xml version="1.0" encoding="utf-8"?> <Cairngorm:ServiceLocator xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:Cairngorm="com.adobe.cairngorm.business.*"> <mx:RemoteObject id="contactService" destination="amfphp" source="com.lafabrick.cairngormexemple.ContactService" showBusyCursor="true" /> </Cairngorm:ServiceLocator>
Le ContactService
Comme quoi les histoires de mapping... TOUT est lié !!! Maintenant passons à lécriture d'un service simple histoire de tester l'appel de méthde distante ainsi que le mapping objet...
<?php include_once "vo/ContactVO.php"; class ContactService { function loadAll() { $ar = array(); $yo = new ContactVO(); $yo->id = 1 ; $yo->nom = "Breduillieard"; $yo->prenom = "Lionel"; $yo->mail = "lionel@Breduillieard.com"; $ar[] = $yo; $erick = new ContactVO(); $erick->id = 2 ; $erick->nom = "Ghaumez"; $erick->prenom = "Erick"; $erick->mail = "erick@ghaumez.com"; $ar[] = $erick; $fab = new ContactVO(); $fab->id = 3 ; $fab->nom = "Bizot"; $fab->prenom = "fabien"; $fab->mail = "fabien@bizot.com"; $ar[] = $fab; return $ar; } } ?>
Il est temps de compiler et de déployer...
Et là normalement vous devriez obtenir ça 

Et vous devriez obtenir ça dans la zone de trace
[code] execute command LoadUsersCommand result [object ContactVO], [object ContactVO] ,[object ContactVO]
Merci d'avoir lu cet article.
Et pour finir quelques liens utiles :
- Un site de ressources sur Cairngorm
- Le tutoriel en 6 parties de steven webster sur Cairngorm : part 1 - part 2 - part 3 - part 4 - part 5 - part 6
- Configuring data service
- Les patterns J2EE
Commentaires
1. Le mercredi 3 octobre 2007 à 18:42, par erick
2. Le mercredi 3 octobre 2007 à 19:21, par Nikozy
3. Le jeudi 4 octobre 2007 à 11:04, par Hervé
4. Le lundi 8 octobre 2007 à 18:36, par dehats
5. Le lundi 8 octobre 2007 à 21:42, par Lionel
6. Le samedi 20 octobre 2007 à 15:49, par pinfred
7. Le dimanche 21 octobre 2007 à 01:31, par Erick
8. Le dimanche 21 octobre 2007 à 19:39, par yoy
9. Le mardi 23 octobre 2007 à 17:24, par kouri
10. Le mardi 23 octobre 2007 à 20:31, par Lionel
11. Le lundi 5 novembre 2007 à 19:10, par Ju
12. Le mardi 6 novembre 2007 à 10:57, par yoy
13. Le vendredi 9 novembre 2007 à 11:29, par Loïc
14. Le dimanche 18 novembre 2007 à 23:04, par mäzèrté
15. Le samedi 15 décembre 2007 à 12:16, par lionel
16. Le samedi 19 janvier 2008 à 18:48, par Gallo_Teo
17. Le vendredi 8 février 2008 à 12:38, par mmm
18. Le mardi 12 février 2008 à 14:38, par ARobert
19. Le vendredi 15 février 2008 à 14:37, par Hervé
20. Le vendredi 15 février 2008 à 16:40, par ARobert
21. Le vendredi 15 février 2008 à 16:56, par Lionel
22. Le samedi 12 avril 2008 à 07:24, par Raginis
23. Le samedi 7 juin 2008 à 15:59, par Leonardo
24. Le samedi 7 juin 2008 à 19:15, par Leonardo