wyszukiwarka google

ContentStream

niedziela, 18 marca 2012

Kurs Flex 3 - biblioteka AMFPHP

Bardzo często zdarza się, że tworzone aplikacje potrzebują pobierać dane z bazy danych, czy wysyłać dane do bazy danych. Sama aplikacja nie ma takiej możliwości, ale z pomocą przychodzi nam biblioteka AMFPHP.
AMFPHP jest open source'owym, binarnym protokołem AMF(Action Message Format), który umożliwia komunikację pomiędzy aplikacją Flex'a, a serwerem opartym na PHP. Aplikacja korzysta z usług udostępnianych przez serwer do pobierania danych z bazy danych, bądź wysyłania danych z aplikacji do bazy danych na serwer. Taka komunikacja jest możliwa dzięki utworzeniu klas i wywoływaniu jej metod  z pozycji aplikacji.

Tutaj można zobaczyć działanie aplikacji komunikującej się z bazą danych MySQL.

Bibliotekę pobieramy ze strony http://sourceforge.net/projects/amfphp/files. Skorzystamy z najnowszej dostępnej wersji 2.0.1. Następnie, użyjemy lokalnego serwera WampServer w wersji 2.2d (może być też wersja starsza np. 2.1) do umieszczenia na nim pobranej biblioteki. W tym celu tworzymy w katalogu C:\wamp\www folder o nazwie amfphp2 i kopiujemy do niego całą zawartość biblioteki.
[kliknij na obrazek aby powiększyć]
 W katalogu Services będziemy umieszczali nasze usługi. Znajduje się w nim przykładowa usługa ExampleServices.php, którą można uruchomić aby zobaczyć jak ona działa.
My napiszemy własną usługę i ją przetestujemy. W tym celu tworzymy klasę Test i tworzymy dwie funkcje: sayHello i myName.
Test.php

<?php
 class Test {
  
  public function sayHello() {
   return "Hello World!";
  }

  public function myName($first_name, $last_name) {
   $result = "Imie: $first_name\nNazwisko: $last_name";

   return $result;
  }
 }
?>

Teraz sprawdzamy, czy usługa działa prawidłowo. Wpisujemy w przeglądarce internetowej adres http://localhost/amfphp2. Po lewej stronie przeglądarki widzimy usługi jakie udostępniliśmy i listę metod, jakie posiadają. Klikamy na metodę sayHello. Obok pojawią się informacje o funkcji i przycisk Call method. Klikając na przycisk, powinien pojawić się napis Hello World!.
[kliknij na obrazek aby powiększyć]

Zobaczmy, jak działa druga metoda. Klikamy na myName. Zauważmy, że tutaj musimy podać dwa parametry i klikamy Call method. Poniżej pojawi się napis z podanymi przez nas parametrami.

[kliknij na obrazek aby powiększyć]

Tak poprawnie utworzoną usługę możemy udostępnić w aplikacji. W tym celu utwórzmy sobie nowy projekt o nazwie AMFPHP_APP. Następnie musimy dodać plik konfiguracyjny, który będzie się łączył z biblioteką na serwerze. Klikamy prawym przyciskiem myszki na folder src i wybieramy New ---> File. W polu File name wpisujemy services-config.xml i klikamy Finish. Do utworzonego pliku wklejamy następujące ustawienia:

<?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="amfphp2"/>
    </channels>
    <properties>
     <source>*</source>
    </properties>
   </destination>
  </service>
 </services>
 <channels>
  <channel-definition id="amfphp2" class="mx.messaging.channels.AMFChannel">
   <endpoint uri="http://localhost/amfphp2/" class="flex.messaging.endpoints.AMFEndpoint"/>
  </channel-definition>
 </channels>
</services-config>

Następnie dołączamy go do kompilatora: Prawym przyciskiem myszki klikamy na AMFPHP_APP ---> Properties ---> Flex Compiler i dołączamy argument -services services-config.xml.
[kliknij na obrazek aby powiększyć]

Skoro mamy już wszystko przygotowane, to możemy przystąpić do udostępnienia usługi w aplikacji. W tym celu użyjemy obiektu zdalnego <mx:RemoteObject>.

<mx:RemoteObject id="test" source="Test" destination="amfphp"
  result="resultHandler(event)" fault="faultHandler(event)">
  <mx:method name="sayHello" />
  <mx:method name="myName" />
 </mx:RemoteObject> 

Do atrybutu source przypisaliśmy nazwę klasy, do destination przypisaliśmy identyfikator z pliku konfiguracyjnego. Przy pomocy znacznika <mx:method> zdefiniowaliśmy metody należące do klasy Test.

Aby możliwe było wyświetlenie danych pobranych z serwera implementujemy w ActionScript funkcję resultHandler przypisaną do atrybutu result. W razie niepowodzenia wyświetlimy kod i nazwę błędu przy pomocy funkcji faultHandler, którą przypisaliśmy do atrybutu fault.

<mx:Script>
 <![CDATA[
  import mx.rpc.events.FaultEvent;
  import mx.rpc.events.ResultEvent;
  
  private function resultHandler(event:ResultEvent):void {
   wynik.text = event.result.toString();
  }

  private function faultHandler(event:FaultEvent):void{
   wynik.text = "### Error ###\nCode: " + event.fault.faultCode + "\nMessage: " + event.fault.faultString;
  }
 ]]>
 </mx:Script>

Zwracane dane są przypisane do pola tekstowego, a za pomocą przycisków wywołamy metody z klasy Test.

<mx:VBox top="100" left="100">
  <mx:HBox>
   <mx:Button id="btn1" label="sayHello" click="test.sayHello()" />
   <mx:Button id="btn2" label="myName" click="test.myName('Jan','Kowalski')" />
   <mx:Button id="btn3" label="error" click="test.myName()" />   
  </mx:HBox>
    
  <mx:TextArea id="wynik" width="260" height="120" />
 </mx:VBox>

Zauważmy, że do ostatniego przycisku dodaliśmy funkcję myName bez parametrów. Sprawdzimy w ten sposób działanie funkcji faultHandler.

Poniżej widzimy działanie aplikacji komunikującej się z serwerem PHP.

[kliknij na obrazek aby powiększyć]




Przykład komunikacji AMFPHP z bazą danych MySQL

Na początek stwórzmy sobie przykładową bazę danych AMFPHP_FLEX i tabelę users następująco:

CREATE TABLE users( 
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(1000) NOT NULL ,
surname VARCHAR(1000) NOT NULL ,
email VARCHAR(1000) NULL,
age INT NULL)

Ważne jest aby przy tworzeniu bazy, tabeli i kolumn (typ np. varchar) Metoda porównywania napisów była ustwiona na utf8_general_ci. Wtedy nie będą pojawiać się "krzaczki" podczas pobierania z bazy i przesyłania do bazy danych napisów z polskimi znakami.

Teraz musimy napisać usługę, która będzie się komunikowała z bazą danych.

Database.php

<?php
class Database 
{
 private $AUTHOR          = "poradnikkomp@o2.pl";
 private $URL            = "http://poradnikkomp.blogspot.com";
 private $VERSION         = "2.0.0";
 
 public function Database()
 {
 }
 
    /*
     * Execute a SQL statement to database. READ
     * @return the result array.
     *      
     */
 public function query($strQuery, $strUser, $strPass, $strHost, $strDB)
 {
  $rsConnectionID = $this->open($strUser, $strPass, $strHost, $strDB);

  $rsResult = mysql_query($strQuery, $rsConnectionID);
  $result = array();
  
   while ($data = mysql_fetch_object($rsResult)) {
            $result[] = $data;
            
  }  
  
  if(!$rsResult)
     return mysql_error($rsConnectionID);
  
  if(($lastInsertID = mysql_insert_id($rsConnectionID)) != 0)
   return $lastInsertID;
 
  $this->close($rsConnectionID);
  return $result;
 }
 
    /*
     * Execute a SQL statement to database. CREATE, UPDATE, DELETE
     * @return null.
     *      
     */
 public function queryOperation($strQuery, $strUser, $strPass, $strHost, $strDB)
 {
  $rsConnectionID = $this->open($strUser, $strPass, $strHost, $strDB);

  $rsResult = mysql_query($strQuery, $rsConnectionID);  
  
  if(!$rsResult)
     return mysql_error($rsConnectionID);
  
  if(($lastInsertID = mysql_insert_id($rsConnectionID)) != 0)
   return $lastInsertID;
 
  $this->close($rsConnectionID);
  return null;
 } 
 
    /*
     * Open the connection to the database.
     * @return the connection id.
     *      
     */
 private function open($strUser, $strPass, $strHost, $strDB) 
 {      
     $rsConnectionID = @mysql_connect($strHost, $strUser, $strPass);
  mysql_select_db($strDB, $rsConnectionID); 
      
  mysql_query("SET NAMES 'utf8';");
                mysql_query('SET CHARACTER SET utf8;');
    
     return $rsConnectionID; 
   }
 
    /*
     * Close the connection to the database.
     * @return a resource.
     *      
     */
   private function close($rsConnectionID) 
 {
     $res = @mysql_close($rsConnectionID);
     return $res;
   }
 
    /*
     * @return a string Author.
     *      
     */
 public function getAuthor()
 {
  return $this->AUTHOR;
 }
 
    /*
     * @return a string Url.
     *      
     */
 public function getUrl()
 {
  return $this->URL;
 }
 
    /*
     * @return a string Version.
     *      
     */
 public function getVersion()
 {
  return $this->VERSION;
 }
}
?>


Dołączamy naszą usługę jak uprzednio.

 <mx:RemoteObject id="connection" source="mysql.database" destination="amfphp" 
  showBusyCursor="true" result="resultHandler(event)" fault="faultHandler(event)">
  <mx:method name="query" />
  <mx:method name="queryOperation" />
 </mx:RemoteObject>

Następnie ustawiamy parametry funkcji query i queryOperation:


  private static const USER:String = "root";        // login do MySQL'a
  private static const PASSWORD:String = "";        // hasło do MySQL'a
  private static const HOST:String = "localhost";  // nazwa hosta
  private static const DB:String = "AMFPHP_FLEX";  // nazwa bazy danych

Teraz napiszemy funkcje, które będą wykonywały operację CRUD (create, read, update, delete).

 private var execute:String; 
 // CREATE
 private function createUser():void {
   execute = "INSERT INTO users (name, surname, email, age) VALUES ('" + textUSER.text + "', '"
    + textSURNAME.text + "', '" + textEMAIL.text + "', '" + textAGE.text + "')";
   connection.queryOperation(execute, USER, PASSWORD, HOST, DB);        
 }
 // READ
 private function getUsers():void{
   execute = "SELECT * FROM users";
   connection.query(execute, USER, PASSWORD, HOST, DB);    
 }
 // UPDATE
 private function updateUser():void {
   execute = "UPDATE users SET name='" + textUSER.text + "', surname='" + textSURNAME.text 
   + "', email='" + textEMAIL.text + "', age='" + textAGE.text 
   + "' WHERE id='" + dataMysql.selectedItem.id + "'";
   connection.queryOperation(execute, USER, PASSWORD, HOST, DB);
 }
 // DELETE
 private function deleteUser():void{    
    execute = "DELETE FROM users WHERE id=" + dataMysql.selectedItem.id;
    connection.queryOperation(execute, USER, PASSWORD, HOST, DB);
 }
  

Funkcje te podpinamy do zdarzenia click znacznika <mx:Button>.
Potrzebujemy jeszcze kontrolki, która będzie wyświetlała naszych użytkowników. Użyjemy do tego DataGrid.

   <mx:DataGrid id="dataMysql" dataProvider="{users}">
    <mx:columns>
     <mx:DataGridColumn dataField="id" headerText="Number" />
     <mx:DataGridColumn dataField="name" headerText="First name" />
     <mx:DataGridColumn dataField="surname" headerText="Last name" />
     <mx:DataGridColumn dataField="age" headerText="Age" />
     <mx:DataGridColumn dataField="email" headerText="Email" width="200" />
    </mx:columns>
   </mx:DataGrid>

Do atrybutów dataField przypisaliśmy nazwy kolumn z tabeli MySQL'a.

I na koniec zmodyfikujmy funkcję resultHandler:

  [Bindable]
  public var users:ArrayCollection;
  private var resultObject:Object;

  private function resultHandler(event:ResultEvent):void {
   resultObject = event.result;
   users = new ArrayCollection(resultObject as Array);
  }








W kolejnych częściach kursu:

5 komentarzy:

  1. Mam ptytannie odnośnie "Na początek stwórzmy sobie przykładową bazę danych AMFPHP_FLEX i tabelę users następująco:"
    Gdzie mam utworzyć tą baze?

    OdpowiedzUsuń
    Odpowiedzi
    1. Generalnie, to jak zainstalujesz sobie wampa, to tam masz phpAdmina i tam sobie utwórz bazę danych.

      Usuń
  2. Dodatkowo nie mogę dojść do tego gdzie wrzucić te funkcje :(

    OdpowiedzUsuń
    Odpowiedzi
    1. Funkcje w jednym pliku Database.php umieszczasz w amfphp2/services. Zrób sobie dokładnie pierwszą część opisu na tej stronie. Ten ogólny. Tam jest opisane gdzie umieszczać usługi.

      Usuń
  3. Niestety, teraz nie mam chwili żeby przysiąść i napisać bardzo szczegółowo tego. Może za jakieś 2 tygodnie mogę postarać się napisać.

    OdpowiedzUsuń