HelloWorld example with Flex and Symfony

15. April 2009 in General

Today I want to show you how to use the sfAmfPlugin to create a simple Hello World application with Symfony, Flex and the plugin. AMF is the most comfortable way to communicate between a backend technology and Flex. AMF allows RMI (Remote Mathod Invocation) from Flex to a backend server that is able to support AMF. The sfAmfPlugin for Symfony adds AMF-support to the PHP-Framework.

What do we want?
Keep it simple! In this small sample project we will create a Flex-Client with a text-field and a button. If the user clicks the button the content of the textfield will be sent to the backend via AMF (we call a service method) and the result from the backend is placed on the screen.

Hello World Flex Client

Symfony Project
First of all we need a Symfony project. We create one with the name “flexdemo” and an application called “frontend”

$> symfony generate:project flextest
$> symfony generate:app frontend

Now it is time to install the plugin. This can be done via the symfony commandline tool:

$> symfony plugin:install sfAmfPlugin
$> symfony cc

Now everything is in place to create our first AMF-Service. Therefore just use a commandline task that comes with the sfAmfPlugin:

$> symfony amf:create-service --package=de.shiftup.flextest HelloWorld
$> symfony cc

This commandline tasks creates a service class in the lib/services folder of your symfony-project. If you used the (optional) –package parameter sfAmfPlugin created a suitable directory-structure. In the case of our simple sample you will find the service-class under lib/services/de/shiftup/flextest/HelloWorldServcie.class.php. Open this file and change the content to the following sourcecode:

<?php
  /**
   * AMF enabled service class HelloWorldService
   *
   * Project: flextest
   *
   * @package   de.shiftup.flextest
   * @author    Timo Haberkern
   *
   * @version SVN: $Id$
   */
  class HelloWorldService extends sfAmfService {

    public function sayHello($who) {
      return "Hello ".$who;
    }
}

The last thing we will need is a AMF-Gateway-Module that we can call via URL. You can create this module as any othe Symfony module:

$> symfony generate:module frontend amfgateway

Open the actions.class.php in apps/modules/amfgateway/actions and change the sourcecode to the following:

<?php
  /**
   * amfgateway actions.
   *
   * @package    flextest
   * @subpackage amfgateway
   * @author     Timo Haberkern <timo.haberkern@shift-up.de>
   * @version    SVN: $Id: actions.class.php 12 2009-04-14 07:12:25Z thaberkern $
   */
  class amfgatewayActions extends sfActions {
    /**
     * Executes index action
     *
     * @param sfRequest $request A request object
     */
   public function executeIndex(sfWebRequest $request) {
      $this->setLayout(false);

      $gateway = new sfAmfGateway();
      $response = sfContext::GetInstance()->getResponse();
      $response->setContent($gateway->service());
      return sfView::NONE;
   }
}

Thats it. This gateway action will check which Service class is called from flex and call the method in the class. Nothing more is needed on the Symfony side of life.

Flex-Client
On the Flex-Side of our small little project we need a project first. Just create a new one. As “Application server type” choose “None”. You can name the Flex project as you want. FlextTestClient will do for us at the moment.

To call AMF-Server-Services you need to configure them to use in a Flex application. This service definition is done in the file services-config.xml. Create this file directly in the src-directory. And add the following XML code to it:

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
  <services>
    <service id="flextest-service"
          class="flex.messaging.services.RemotingService"
          messageTypes="flex.messaging.messages.RemotingMessage">
        <destination id="flextest">
          <channels>
            <channel ref="flextest-channel"/>
          </channels>
          <properties>
            <source>*</source>
          </properties>
        </destination>
    </service>
  </services>
    <channels>
      <channel-definition id="flextest-channel"
            class="mx.messaging.channels.AMFChannel">
        <endpoint uri="http://flextest/frontend_dev.php/amfgateway"
            class="flex.messaging.endpoints.AMFEndpoint"/>
      </channel-definition>
    </channels>
</services-config>

Maybe you will need to change the endpoint-uri to the local URL of your Symfony project.

Now open the Project-Settings of your Flex Project (right click on the project icon->Properties) and select “Flex compiler”. Add the services-config.xml to the compiler options.

Flex Project Settings

Now open the main applictaion file FlexTestClient.mxml. We need to add MXML markup for the Panel with the button and the text input field.

<mx:Panel x="120" y="42" width="281" height="194"
      layout="absolute" title="SayHello">
  <mx:TextInput id="username" x="90" y="10"/>
  <mx:Label x="10" y="12" text="Your Name:" fontWeight="bold"/>
  <mx:Button x="176" y="40" label="SayHello" click="sayHello()"/>
  <mx:Text x="10" y="83" id="result" text="Result"
      width="241" height="61" fontWeight="bold"
      color="#FF0000"/>
</mx:Panel>

Nothing special here. As you can see we call the function sayHello on the click event of the button, so we will need a Script-Block with such a function:

<mx:Script>
  <![CDATA[
    private function sayHello():void {

    }
  ]]>
</mx:Script>

Now we will do what we wanted to do just from the beginning: Callin our serverside Symfony service class and method. Therefore we will use the service definition we have done in the services-conf.xml. Add the following lines to the sayHello function:

var remote:RemoteObject = new RemoteObject("helloworld");
remote.source = "de.shiftup.flextest.HelloWorldService";
remote.sayHello(username.text);

What’s going on here? We initialize a new RemoteObject (given the ID of the service from the service-conf.xml) and define the Symfony-Service-class. Please keep in mind that you need write the full packagename of the Servcieclass (in our case de.shiftup.flextest.HelloWorldService). Now we can call the sayHello-Method of this Serverclass. But what to do with the return value of the Server-Service? AMF Services are assynchronous, so you can not do something like this:

result = remote.sayHello(username.text);

We need to define result event listeners to deal with the different result states (Success and Fault)

private function sayHello():void {
  var remote:RemoteObject = new RemoteObject("helloworld");
  remote.source = "HelloWorldService";

  remote.addEventListener("result", function (event:ResultEvent):void {
    result.text = event.result.toString();
  });
  remote.addEventListener("fault", function(event:FaultEvent):void {
    Alert.show(event.fault.toString(), "Error");
  });

  remote.sayHello(username.text);
}

Your MXML should now look like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.rpc.remoting.RemoteObject;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private function sayHello():void {
				var remote:RemoteObject = new RemoteObject("helloworld");
				remote.source = "HelloWorldService";

                remote.addEventListener("result", function (event:ResultEvent):void {
                 	result.text = event.result.toString();
                 });
                remote.addEventListener("fault", function(event:FaultEvent):void {
                 	Alert.show(event.fault.toString(), "Error");
                 });

                remote.sayHello(username.text);
			}
		]]>
	</mx:Script>
	<mx:Panel x="120" y="42" width="281" height="194" layout="absolute" title="SayHello">
		<mx:TextInput id="username" x="90" y="10"/>
		<mx:Label x="10" y="12" text="Your Name:" fontWeight="bold"/>
		<mx:Button x="176" y="40" label="SayHello" click="sayHello()"/>
		<mx:Text x="10" y="83" id="result" text="Result"
				width="241" height="61" fontWeight="bold"
				color="#FF0000"/>
	</mx:Panel>
</mx:Application>

Start your Flex-Application and have fun with your small Hello World application :-)

More service classes
In a real-life project you will have more than one Service-class. But keep in mind: Regardless the amount of service-classes you will always need only one gateway module (amfgateway). This gateway can handle all Service-Classes.

29 responses to HelloWorld example with Flex and Symfony

  1. Hi,
    I am sorry to tell you that,after doing what you say above,when I run my project,I received an error message:
    RFC Fault faultString=”send failed” faultCode=”client.error.MessageSend” faultDetail=”Channel.Security.Error error Error”.

    Can you help me to fill the problem?
    I have followed the steps above.

  2. The newest error is:
    [ RFC Fault faultString="Channel disconnected"
    faultCode="Client.Error.DeliveryInDoubt"
    faultDetail="Channel disconnected before an acknowledgement was received"]

    Does anyone have the same problems?

  3. I attempt to run this example and get a Client.Error.DeliveryInDoubt error. Any thoughts?

    Thanks!

  4. Aha sneaky.

    Sophia, I don’t know if you had the same problem I had, but when I went to the apache logs I found the following error:

    PHP Strict Standards: Only variables should be passed by reference in [Path_To_Symfony]\\sfAmfGateway.class.php on line 89

    When I looked this up, I found the offending line had an array_pop command that was referencing an explode directly. Apparently, in php 5 this is now an error (http://the-stickman.com/web-development/php/php-505-fatal-error-only-variables-can-be-passed-by-reference/). I changed line 89 to the following 2 lines of code:

    $tmpvariable=explode(“.”, $service_name);
    $class_name = array_pop($tmpvariable);

    And it worked for me. You may want to check your server logs to see if this is the error you get.

  5. @sophia: Please try to install the new plugin version 1.2.4 and take a look if this is fixing your problem. Justins fix is added to this version

    Timo

  6. If it helps anyone, I think in the code for the sayHello function, this line:

    var remote:RemoteObject = new RemoteObject(“helloworld”);

    should be changed to:

    var remote:RemoteObject = new RemoteObject(“flextest-service”);

    as that is the id of the service as defined in the services-config.xml file. This may be why a lot of the commenters are having trouble with the connection.

    – Eric

  7. Eric said on 4. May 2009

    Okay, I fought my way through this and here’s what I had to do to get this working:

    1.) Copy the HelloWorldService.class.php file from lib/services/de/shiftup/flextest to lib/services.

    2.) In the Flex mxml, change the line:
    var remote:RemoteObject = new RemoteObject(“helloworld”);
    To:
    var remote:RemoteObject = new RemoteObject(“flextest”);
    Because that’s the id of the destination in the services-config:

    It works now after some major fiddling with the Flex files and the Apache setup.

  8. Riza said on 4. May 2009

    It works for me too…
    What about an example about using doctrine to load data from database to dataGrid?

    Thx

  9. Hi, i can receive simple string, array and other simples types, but I think that classService dont autoload all classes of Syfmonfy, because I dont declare my modules or modules of plugin in classService’s methods, and receive this message of error:
    [RPC Fault faultString="Channel disconnected" faultCode="Client.Error.DeliveryInDoubt" faultDetail="Channel disconnected before an acknowledgement was received"]
    I do my method return only this code:
    return 1/0;
    And receive identical error.
    Why find this error?

  10. Hi, I recomend you use the code in sfAmfService class file:

    require_once sfConfig::get(‘sf_lib_dir’) . ‘/symfony/autoload/sfCoreAutoload.class.php’;
    sfCoreAutoload::register();

    With this all class autoinclude in your service class.

    I’m trying applicate an exception treatment for be possible treat exception in service classes, because today inst possible.

  11. sorry my friends, im wrong, the autoload code inst necessary.
    But I saw that except tratement inst possible, so aplicate this code to sfAmfService class:

    On top sfAmfService file I insert:
    [php]
    set_error_handler(array(‘sfAmfService’, ‘errorHandler’));
    [/php]

    In sfAmfService class I insert static function:
    [php]
    public static function errorHandler($errno, $errstr, $errfile, $errline) {
    throw new Exception($errstr, $errno);
    }
    [/php]

    Now, the wargin, notice and fatal erros be trater how Exception, whit this you dont receive “[RPC Fault faultString=”Channel disconnected” …” message. And now you can return message when you have an exception.

    P.S.
    Timo Haberkern, I think it’s good for you plugin.

  12. Hi raphox

    need to think about that. Sounds good but I will need to check if there are any problems with symfony if doing so

    Timo

  13. Hi

    When I run the project I also encountered the error:

    RFC Fault faultString=”send failed” faultCode=”client.error.MessageSend” faultDetail=”Channel.Security.Error error Error”.

    I placed the crossdomain.xml configuration on the flextest/web/ and this solves the problem.

    crossdomain link:
    http://livedocs.adobe.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001621.html

  14. Hi, I get this Error:

    [RPC Fault faultString="[MessagingError message='Destination 'getLibros' either does not exist or the destination has no channels defined (and the application does not define any default channels.)']” faultCode=”InvokeFailed” faultDetail=”Couldn’t establish a connection to ‘getLibros’”]

    I have crossdomain.xml configured, and i follow the tutorial for the rest.

    What can be the problem?

  15. Hi,

    I was getting the following error.
    “Channel disconnected before an acknowledgement was received”

    I increased the max execution time value and the error is gone.

    I’m interested in knowing the root cause of the issue. I would like to know in which all conditions above issue may appear.
    Thank you

  16. Hi, I get the next error when trying to connect flex to symfony:

    [RPC Fault faultString="Send failed" faultCode="Client.Error.MessageSend" faultDetail="Channel.Connect.Failed error NetConnection.Call.Failed: HTTP: Failed: url: 'http://flextest.bi/frontend_dev.php/amfgateway'"]

    How can I solve it?, I need help urgent.

    Thanks!!

  17. I got the following error:

    RPC Fault faultString=”Send failed” faultCode=”Client.Error.MessageSend” faultDetail=”Channel.Connect.Failed error NetConnection.Call.BadVersion:

    any idea ?

    These are the following test I did

    -opening the gateway using a browser and I received “no valid AMF request received” on the error.log.

    -tried to put the wrong <endpoint uri=”http://flextest/frontend_dev.php/amfgatewaylollolol” address and instead of BadVersion error I received a HTTP 404 error, so I am assuming the gateway is responding to my request.

    So I am assuming the error happened in the PHP side and flash choked while interpreting this error and throws a BadVersion error. Is that correct ?

  18. everthing work fine if u using sfAMFPlugin 1.4.2
    and make a little change :

    1.) Copy the HelloWorldService.class.php file from lib/services/de/shiftup/flextest to lib/services.
    2.) In the Flex mxml, change the line:
    var remote:RemoteObject = new RemoteObject(”helloworld”);
    To:
    var remote:RemoteObject = new RemoteObject(”flextest”);
    Because that’s the id of the destination in the services-config

    (thanks to @eric who post this issue)

    have a try..

  19. For those getting errors, you need to know that there are some security in FLEX which are not allowing you to make service links outside of your domain name.

    So if your flex appl is accessible from http://myflexappclient/abc.html , and your symfony project from http://mysymf/ you’ll have an error.

    Way to get it work is either copy your swf file in your symfony project and run it from there.
    Or, use a crossdomain.xml (google it)

  20. If you use FlashDevelop for Flex development, you have to include the path to the servies_config.xml:

    -services src\services_config.xml

    Works like a charm..

  21. I have this error:
    [RPC Fault faultString="Send failed" faultCode="Client.Error.MessageSend" faultDetail="Channel.Security.Error error Error #2048 url: 'http://127.0.0.1/flextest/frontend_dev.php/amfgateway'"]
    can anyone help me
    Thanks

  22. Friends.

    all you have to do to make it work with sf 1.4 is to change the gateway module name “sfAmfPlugin” just create it with other name like “test”. its private because is the same name than the plug in module..

    regards
    god.. 4 hours to do that.

  23. hello friends,

    Did you find an article about how to work with objects Or Arrays trasfer between symfony and flex?

  24. HelloWorld example for thanx.

  25. I had some problems to disalbe the security filter in SF 1.4. Each time i call the amf route SF gave me an error message that said: login required. So after a while i found a solution. The amfplugin overrides the security value in its own yaml file.

    change it from
    default:
    security: off

    to
    default:
    security: false

    seems to be a bug or maybe i am wrong. Them please correct me.

  26. Lem: Where should I save my swf, because I put it in the web folder and I still got this message “cross-domain-policy”, when I put this address in the browser:
    http://127.0.0.1:8080/frontend_dev.php/amfgateway

    thanks a lot!

  27. i am a symfony newbie and spent my 5 hours for the NetConnection.Call.BadVersion error.

    but i found the solution.
    the trick is to change the
    default:
    is_secure: false

    to

    all:
    is_secure: false

    in the security.yml

  28. Hello,
    I created my symfony project in a payment server(production server), but testing of the services the server gives me the error File does not exist: / home/xxx/public_html/500.shtml, referer: ?method=de.shiftup.flextest.HelloWorldService%3A%3AsayHello. Somebody wants to happen?

  29. Hi, very nice post. Very usefull

Leave a reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>