Friday, April 27, 2007

Mapping VO's from Flex to PHP using AMFPHP

I sometimes wonder why it is that I never seem to blog till shortly after the midnight hour... oh well... life just gets too crazy when the sun is up. So as it happens, one of my minions spent the better part of today putting together php classes, using a nifty code generator found called DAO Generator by the guys over at Titantic Linux. It seems to do a pretty decent job of generating the sql necessary to create the db table, and then builds three additional files, the VO, a datasource file, and then finally a DAO file that has a ton of functions in it. (and I mean a ton!)

Going to take a moment here to give credit where credit is due... Michael Ramirez's tutorial on Using Amfphp 1.9 with the Adobe Flex 2 SDK is what really got us started in the right direction, it's pretty detailed and yet easy to follow. We quickly were able to get our AMFPHP up and running with the tutorial files as outlined by Michael. Tickled pink by the code generator we figured we could easily start building our own php gateway. But enough background... on to the thick of things.

Passing VO's from PHP seems to be very well covered in numerous articles on the web, and it involves mainly adding a var $_explicitType="tutorials.Person" to the php VO, with a value that corresponds to the [RemoteClass(alias="tutorials.Person")] in the Flex VO. Pretty straightforward and foolproof. But no one seems to spend any time talking about going back the other way.

Generally when a VO is passed from Flex to AMFPHP, it is treated as an Associative Array. In Michael's examples, he treats the ValueObject that is passed back to Save function as just that $valueObject["lastName"]. But we were pretty keen on using the generated code, which actually uses the syntax $valueObject->getLastName(). And this of course, failed, because the $valueObject being passed to PHP from Flex was not in fact being mapped back to the right VO, or any php VO for that matter.

Turns out, after much googling, that only under certain conditions will AMFPHP successfully map an incoming Flex VO to the corresponding PHP VO. In the globals.php, found at the root of the AMFPHP folder structure, you'll find the following line:

$voPath = "services/vo/";

Meanwhile we had been placing our VO in the same folder as our Datasource and DAO files, something like com/crazedCoders/testapp, and this was off the services/ folder, not the services/vo folder. In fact we didn't even have a /vo folder. I found this little tidbit of information: "As for incoming class mapping, remember that if amfphp doesn't find the class to be mapped in the services/vo folder, it will simply deserialize the object as though it were untyped, that is, as an associative array. " in this blog.

It took a little bit of playing around but ultimately, without making any changes to the core AMFPHP files, I was able to get the reverse mapping working successfully. This is what that looked like:

//file: src/tutorials/Person.as
package tutorials{
[RemoteClass(alias="tutorials.Person")]
[Bindable]
public class Person {
public var firstName:String;
public var lastName:String;
public var phone:String;
public var email:String;
}}
and then in php:
<?php
//file: services/vo/tutorials/Person.php
class Person {
var $firstName;
var $lastName;
var $phone;
var $email;
// explicit actionscript package
var $_explicitType = "tutorials.Person";}
function formatLastName(){
$lastName = strtolower($lastName); }
?>
To test the mapping I added the following function to a PersonService.php file:
//file: services/tutorials/PersonService.php
function modify($person){
//this will call a function on person and return it
$person->formatLastName();
return $person;
}
Only if the valueobject was mapped correctly would it be able to call the function on the PHP VO. In Flex I just created a Person object, passed it in to the remoteobject call, and observed the formatting of the last name on the Person object that was returned.

Again, I hope this will save others the time that it took me to figure this out. Enjoy!

15 comments:

Anonymous said...

With php5 you can also do this:

function modify(Person $person)
{
...
}

you need a Person class in the php class that mimics the one in the flex side.
Something that can also be interesting is the [Transient] metadata tag if you have some properties in the flex VO that you don't want to be sent to server side.
Just my two cents.
cheers!

Gurufaction said...

Thanks for the credit. It took me over three weeks to write that tutorial because I had very little spare time. I plan to write another tutorial that uses Fluorine.

Unknown said...

what's wrong for me. Whenever I added a vo directory under service directory, and run the flex, got Channels Disconnect error.

Vic Rubba said...

Do yourself a favor and grab a copy of
Service Capture
by Kevin Langdon, and see what the actual error message is you're getting in your response. This tool is invaluable for troubleshooting amf calls from flex or flash.

Anonymous said...

Thanks for the tutorial... ServiceCapture looks alot like Fiddler (free from Microsoft) IMHO a good HTTP Sniffer and using something as simple as TAIL to watch flashlogs.txt are invaluable.

Dimitri said...

Great post ! thanks a lot. :)

Richard C Haven said...
This comment has been removed by the author.
Richard C Haven said...

Ahhhh! I was SO wrong!

The [RemoteClass(alias="vo/Fred")] in the AS3 class

and the $_explicitType = "vo/Fred"; in /vo/Fred.php

DO INDEED all have to be the same. The Remote classes in Flex will remember the alias and reverse-lookup the correct AS3 class using its alias.

PHP, on the other hand, uses the "alias" to find the php class that it will use to map outgoing data.

For incoming classes, see above.

Cheers

Unknown said...

Cool, sometimes it's just that easy :-) Google, read great blog post, fixed.

Thank You!

Unknown said...

Sometimes it's just that easy :-) Google, read great blog post, fixed!

Thank You!

DecoX said...

AMFPHP is amazingly simple! Its a shame that Adobe has selected Zend implemtation ;-(

Anonymous said...

Thank you. Your post continues to save lives even 2 years after the original article!

Anonymous said...

Hi - great post.

Just wanna help others by mnetioning that in order for AMFPHP to work so that PHP can receive VO from FLEX, you must also remember to put a port number in the URI declaration in services-config.xml in your flex project! PHP can send VO the other way without a port number... Took me prtty long time to troubleshoot :-)

Unknown said...

Thank you, great Post.

Unknown said...

I know that this is long overdue, but I think that that the following code
function formatLastName(){
$lastName = strtolower($lastName);
}
should be replaced with:
function formatLastName(){
$this->lastName = strtolower($this->lastName);
}