Tuesday, October 26, 2010

SOAP WebService in Symfony

One interesting topic on web development is webservice development. There are several techniques to implement a webservice out there, and today I’ll talk about one technique that I worked in the recent past that I really like: SOAP. As per wikipedia:

SOAP, originally defined as Simple Object Access Protocol, is a protocol specification for exchanging structured information in the implementation of Web Services in computer networks. It relies on Extensible Markup Language (XML) as its message format and usually relies on other Application Layer protocols, most notably Remote Procedure Call (RPC) and HTTP for message negotiation and transmission. SOAP forms the foundation layer of the web services protocol stack providing a basic messaging framework upon which abstract layers can be built.

The plan for this tutorial is to build a complete set of webservice methods to interact with the citypicker, built in a previous post. For this, I’ll use a great symfony plugin called ckWebService. This plugin enables the developer to expose your actions as a SOAP webservices. Another great functionality is the built-in WSDL generator, that parses module’s doc comment in order to identify which actions should be exposed and it’s input/output parameters.

Let’s start by installing ckWebService plugin in our symfony project. I’ll not install the latest release, instead I’ll checkout from trunk svn, as it contains some nice improvements if compared to latest release:

info@amphee.com [~/symfony/blog]# cd plugins/
barrosws@barros.ws [~/symfony/blog/plugins]# svn co http://svn.symfony-project.com/plugins/ckWebServicePlugin/trunk ckWebServicePlugin


OBSERVATION: Current trunk version has a small bug (actually a wrong variable name) that must be fixed before continuing:

public function getResultProperty()
{
- return $this->resultMember;
+ return $this->resultProperty;
}


Now we need to configure the plugin in order to make it work. The read-me located at plugin page provides a complete guide to configure it. For this project we use a basic configuration:

apps/frontend/config/app.yml:

soap:
enable_soap_parameter: on
ck_web_service_plugin:
wsdl: soap.wsdl
handler: ckSoapHandler
persist: %SOAP_PERSISTENCE_SESSION%
render: off
result_callback: getSoapResult
soap_options:
encoding: utf-8
soap_version: %SOAP_1_2%


apps/frontend/config/filters.yml:

soap_parameter:
class: ckSoapParameterFilter
param:
condition: %APP_ENABLE_SOAP_PARAMETER%


apps/frondend/config/factories.yml:

soap:
controller:
class: ckWebServiceController


Done! That’s all we need to start exposing actions as SOAP webservices. For now on we can expose any of our previously created action by adding a special tag to the doc comment, like this:

/**
* Action description
* @ws-enable
*
* @param string $name
* @return boolean
*/
public function executeSomeAction($request)
{
/* action here */
}


This doc comment will expose the action and instruct the WSDL generator that this action expects a string input parameter, called $name and that it will return a boolean value. An interesting thing about this plugin is that it will place all input parameters in the $request object, so the action can access it as if it was called from a browser, passing name as a query string or a post value:

...
$name = $request->getParameter('name');
...


Also, notice that $request parameter was removed from the doc comment. This is necessary because if we keep it, the WSDL generator will add $request as a parameter to the webservice, what is not the case here.

Let’s start the implementation for this project. We have three actions that will be exposed:

* executeIndex: to list users;
* executeEdit: to insert/edit users;
* executeDel: to delete users.


One might think that we will need to add @ws-enable to these actions doc comment… well, yes, that’s the original idea, but I prefer using a different approach. My approach is to create a new module, called soap (or whatever you want) and create wrappers to actual actions. This will reduce the number changes needed to be done in the actual actions (sometimes it won’t require any change at all) and will make it possible for the developer to code the entire system without even caring about webservice, all adjustments can be easily made only when actually implementing the webservice. This is not the best way to achieve this result. The correct way to do this is to create a custom SoapHandler, but this will kill WSDL generator, so I’ll stick to my way by now (trunk version has all the necessary changes to make this possible – it’s not the case with latest release).

So, let’s create our new module:

info@amphee.com [~/symfony/blog]# symfony generate:module frontend soap
>> dir+ /home/amphee/symfony/blog/apps/frontend/modules/soap/templates
>> file+ /home/amphee/symfony/blog/app...soap/templates/indexSuccess.php
>> dir+ /home/amphee/symfony/blog/apps/frontend/modules/soap/actions
>> file+ /home/amphee/symfony/blog/app.../soap/actions/actions.class.php
>> file+ /home/amphee/symfony/blog/tes...al/frontend/soapActionsTest.php
>> tokens /home/amphee/symfony/blog/tes...al/frontend/soapActionsTest.php
>> tokens /home/amphee/symfony/blog/app...soap/templates/indexSuccess.php
>> tokens /home/amphee/symfony/blog/app.../soap/actions/actions.class.php


The first action will expose is executeIndex, that will return a list of all users registered in the system. This is the simplest one and I’ll use to explain some important points:

apps/frontend/modules/soap/actions/actions.class.php:

/**
* Get users
*
* @ws-enable
*
* @return SoapUser[]
*/
public function executeGetUsers($request)
{
// call actual action
$this->getController()->forward('citypicker','index');

// set result
$actionInstance = $this->getLastActionInstance();
$actionInstance->result = $actionInstance->users;
}


As I said before, we will create wrappers to actual actions. For this action, we don’t have any input parameter, so we don’t need any extra processing. First thing the action does is a forward to actual action. Note that I use the forward method from the controller instead of forward method from sfAction. This is necessary because we need continue our execution flow AFTER actual action returns (sfAction’s forward won’t return control to us). Return value is expected to be located in the deepest action instance, in our case, citypicker/index action, in a property called result (in our case, we store the result of a UserPeer::doSelect() call – made in citypicker/index action and stored in users property). In order to do this we need to get this action’s instance and that’s what getLastActionInstance method do:

apps/frontend/modules/soap/actions/actions.class.php:

/**
* Get last action instance
*
* @return sfActionInstance
*/
private function getLastActionInstance()
{
return $this->getController()->getActionStack()->getLastEntry()->getActionInstance();
}


This method will simply return last actions instance from the action stack, and we will use it in all of our wrappers. If you look at doc comments, you will notice return value is declared as an array of SoapUser objects. SoapUser class is defined as follows:

lib/soap/SoapUser.class.php



Doc comments are REQUIRED here too, because WSDL generator will use it to build the object definition. When sending result back, our result (array of User objects) will be converted into SoapUser objects, making these properties available.

Our first method is complete. In order to start using it, we need to generate the WSDL definition, using the built-in WSDL generator. The generator will also create the frontend dispatcher, in web/ directory:

info@amphee.com [~/symfony/blog]# symfony webservice:generate-wsdl frontend soap http://blog.barros.ws/symfony
>> file- /home/amphee/symfony/blog/web/soap.php
>> file+ /home/amphee/symfony/blog/web/soap.php
>> tokens /home/amphee/symfony/blog/web/soap.php
>> file+ /home/amphee/symfony/blog/web/soap.wsdl


In order to test it we can use a nice piece of software called SoapUI. This software will read soap.wsdl and build the request, all using a nice GUI. I recommend downloading the trial of PRO version, as it is capable of generating forms (web like) where you can input parameters:

executeDel actions is similar to executeIndex:

apps/frontend/modules/soap/actions/actions.class.php:

/**
* Deletes an user
*
* @ws-enable
* @param integer $id
*
* @return boolean
*/
public function executeDelUser($request)
{
// call actual action
$this->getController()->forward('citypicker','del');

// set result
$actionInstance = $this->getLastActionInstance();
$actionInstance->result = true;
}


Now, executeEdit (executeNewUser in our wrapper) is a bit trickier:

apps/frontend/modules/soap/actions/actions.class.php:

/**
* Creates a new user in the system
*
* @ws-enable
* @param SoapUser $user
*
* @return boolean
*/
public function executeNewUser($request)
{
// convert input param from OBJECT to ARRAY
$request->setParameter('user',get_object_vars($request->getParameter('user')));

// call actual action
$this->getController()->forward('citypicker','edit');

// check errors
$actionInstance = $this->getLastActionInstance();
if(!$actionInstance->form->isValid()) $this->throwSoapFormException($actionInstance->form);

$actionInstance->result = true;
}


First difference we can note is the fact this action requires one input parameters. In doc comment we declare that this action expects an SoapUser object as input, but the actual action expects an simple array. The first step then is to convert received object into an array. For this we use get_object_vars and after conversion, we set it back to the $request object. Finally we call actual action, that will act as if the user had submitted the form. Next difference is that we need to check if there was any error processing input data. We do this by checking if form, in actual action instance, is valid, and if not return an error message. In order to throw an exception with detailed errors, I created an small method called throwSoapFormException, that will iterate through all errors in the form and build single string, with one error per line:

apps/frontend/modules/soap/actions/actions.class.php:

/**
* Throw a SoapFault error based on form errors
*
* @param sfForm $form
*/
public function throwSoapFormException($form)
{
foreach($form->getFormFieldSchema()->getError() as $e)
$errors[] = $e;

throw new SoapFault('ERROR',implode("n",$errors));
}

And that’s it, we can now create new users using the new SOAP interface:



Well, actually one small thing is missing to make it really work… Did u notice that I didn’t touch actual actions yet? Sometimes we don’t need to touch it, but that’s not our case. If you look at citypicker post you will notice that both “del” and “edit” actions redirect the user back to index page on success. We can’t do this when running on soap mode, or we will lose control and we won’t be able to send correct result back to the client. To fix this, we just need to make an small change:

if(!$this->isSoapRequest()) return $this->redirect('citypicker/index');

isSoapRequest is a new method added by ckWebservicePlugin and it will return true when executing the actions via SOAP. Adding this check we just perform the redirect when NOT in SOAP mode.

That’s all we need to talk about how to expose your actions via SOAP, but in order to complete our example, we need to create some methods to fetch countries/states/cities informations. For this we create 6 new actions:

/**
* Get countries list
* @ws-enable
*
* @return SoapGeo[]
*/
public function executeGetCountries($request)
{
$this->result = CountryPeer::doSelect(new Criteria());
}

/**
* Get a country
*
* @ws-enable
* @param integer $id
*
* @return SoapGeo
*/
public function executeGetCountry($request)
{
$this->result = CountryPeer::retrieveByPK($request->getParameter('id'));
}

/**
* Get states list
*
* @ws-enable
* @param integer $country_id
*
* @return SoapGeo[]
*/
public function executeGetStates($request)
{
$country = CountryPeer::retrieveByPK($request->getParameter('country_id'));
if(!$country) throw new SoapFault('ERROR','Invalid country');

$this->result = $country->getStates();
}

/**
* Get a state
*
* @ws-enable
* @param integer $id
*
* @return SoapGeo
*/
public function executeGetState($request)
{
$this->result = StatePeer::retrieveByPK($request->getParameter('id'));
}

/**
* Get cities list
*
* @ws-enable
* @param integer $state_id
*
* @return SoapGeo[]
*/
public function executeGetCities($request)
{
$state = StatePeer::retrieveByPK($request->getParameter('state_id'));
if(!$state) throw new SoapFault('ERROR','Invalid state');

$this->result = $state->getCitys();
}

/**
* Get a city
*
* @ws-enable
* @param integer $id
*
* @return SoapGeo
*/
public function executeGetCity($request)
{
$this->result = CityPeer::retrieveByPK($request->getParameter('id'));
}


And to finish, we need to create the SoapGeo class, that will store country name and id:

lib/soap/SoapGeo.class.php:



And we’re done. With this we can now build an external app to create/edit/delete users in the database. I spent several days working with the plugin before coming up with this solution and I hope this will save other developers some time dealing with SOAP implementations.



You can find original post here : http://blog.barros.ws/2008/11/16/soap-webservice-in-symfony/

Tuesday, August 17, 2010

Symfony multiple versions on the same environment

Follow under given steps for best way of running multiple versions of Symfony apps together on the one environment...

Step 1 - Un-install Symfony via PEAR
If you have Symfony installed via PEAR, get rid of it, it will only confuse you with what we are about to do.

express@express-dev:~$ sudo pear uninstall symfony/symfony
uninstall ok: channel://pear.symfony-project.com/symfony-1.1.0

Step 2 - Setup a structure for Symfony
In our case, I still want to install symfony in /usr/share/php/symfony, so lets set that up:

express@express-dev:~$ cd /usr/share/php
express@express-dev:/usr/share/php$ sudo mkdir symfony

Step 3 - Checkout each Symfony version you need
Now lets use SVN checkout to grab each Symfony version we are after, lets put these in a different folder under the base Symfony directory. Note: If you are behind a proxy, change your SVN settings first to go through your proxy. To change your proxy settings:

express@express-dev:/usr/share/php/symfony$ sudo nano /etc/subversion/servers

Now lets checkout each symfony version:
express@express-dev:/usr/share/php$ cd symfony/
express@express-dev:/usr/share/php/symfony$ sudo svn co http://svn.symfony-project.com/branches/1.0 symfony10
...
express@express-dev:/usr/share/php/symfony$ sudo svn co http://svn.symfony-project.com/branches/1.1 symfony11
...
express@express-dev:/usr/share/php/symfony$ sudo svn co http://svn.symfony-project.com/branches/1.2 symfony12
...

We now have created three installations of Symfony.

Step 4 - Create symbolic links for each version
The next step is for us to create symlinks for each version of Symfony. Lets place these in the standard bin directory:

sudo ln -s /usr/share/php/symfony/symfony10/data/bin/symfony /usr/bin/symfony10
sudo ln -s /usr/share/php/symfony/symfony11/data/bin/symfony /usr/bin/symfony11
sudo ln -s /usr/share/php/symfony/symfony12/data/bin/symfony /usr/bin/symfony12

Now lets test the sym links:

express@express-dev:~$ symfony10 -V
symfony version 1.0.19-PRE
express@express-dev:~$ symfony11 -V
symfony version 1.1.5-DEV (/usr/share/php/symfony/symfony11/lib)
express@express-dev:~$ symfony12 -V
symfony version 1.2.0-DEV (/usr/share/php/symfony/symfony12/lib)

What next? - Creating a new project
So to create a new project, you will need to use the relevant Symfony command. For example, to create a Symfony 1.0 project:

sudo symfony10 init-project test1

or to create a Symfony 1.1 or Symfony 1.2 Project:

sudo symfony11 generate:project test11
sudo symfony12 generate:project test12

Once you create a new project, check in the project Config to ensure its picked up the right version. For Symfony 1.0:

express@express-dev:/usr/local/express/projects/$ sudo symfony10 init-project test10
express@express-dev:/usr/local/express/projects/$ cat config/config.php

// symfony directories
$sf_symfony_lib_dir = '/usr/share/php/symfony/symfony10/lib';
$sf_symfony_data_dir = '/usr/share/php/symfony/symfony10/data';

for Symfony 1.1:
express@express-dev:/usr/local/express/projects/test$ cat config/ProjectConfiguration.class.php

require_once '/usr/share/php/symfony/symfony11/lib/autoload/sfCoreAutoload.class.php';
sfCoreAutoload::register();
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
}
}

and for Symfony 1.2, its the same, just make sure its including the right 1.2 files. Thats it! Hope this helps!

Friday, July 16, 2010

HTML5 Samples




Step one


You can use standard object testing to determine if the browser supports GeoLocation.
<script>
/**
* This function is the callback which is passed the result from the .getCurrentPosition()
* function. The pos argument can contain more information than just the latitude/longitude,
* such as altitude, accuracy and speed information.
*
* @param object pos The result from the getCurrentPosition() call
*/
function myCallback(pos)
{
var myLatitude = pos.latitude;
var myLongitude = pos.longitude;
}

/**
* Test for GeoLocation support and make the call
*/
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(myCallback);
} else {
alert("Sorry, your browser doesn't appear to support GeoLocation");
}

</script>

Step two


Once you have tested for it, we can then retrieve the position using the getCurrentPosition() method. You pass this method a callback function which you define. This callback function is given an object (if successful), with various properties:

  • latitude

  • longitude

  • altitude (optional)

  • accuracy

  • altitudeAccuracy (optional)

  • heading (optional)

  • speed (optional)

  • timestamp




more  detail go to


Monday, June 21, 2010

Interesting symfony plugins: sfSyncContentPlugin

With the amount of plugins published in the symfony site, many great plugins get lost in the maze. With this series of posts, we would like to bring some attention to plugins we use every day or that we think are essential for any symfony developer.

sfSyncContentPlugin


Deploying symfony applications is always a key part of developing and maintaining websites that run on symfony. It is always a recommended practice to do development on a local environment or dedicated development server. It is also recommended to have a QA/staging server that is as close as possible to your production server. Using this well proven method you can spot problems and bugs before everybody else sees or experiences them, you know, those bugs that “only” happen in production, don’t tell me that it never happened to you, I won’t believe you.

Anyway, making changes in a live site is not only not recommended, it should never be done!

When developing and testing symfony applications, a lot of times you need to have a copy of the live data. Or you may have a staging server where you make changes before pushing them to a live site in a production server. symfony already provides a way to deploy code changes to a remote server, but what about uploaded and data files? And database content?

Since we discovered and started using it, we can’t live without thesfSyncContentPlugin plugin by Tom Boutell and Alex Gilbert, also developers ofApostrophe CMS. This plugin helps with all the tasks and needs described above. Using it is quite simple. All you need to do is define your servers in config/properties.ini like this:
[qa]
host=qa.example.com
port=22
user=user
dir=/var/www/mysite

[prod]
host=www.example.com
port=22
user=user
dir=/var/www/mysite

[staging]
host=staging.example.com
port=22
user=user
dir=/var/www/mysite

Make sure to use SSH keys to authenticate to your remote servers, so you don’t get asked again and again for passwords. Then just run the following symfony tasks:

# Migrate files and DB from development to qa
./symfony project:sync-content frontend dev to qa@qa

# Migrate files and DB to production (always make a backup of production before doing this!)
./symfony project:sync-content frontend dev to prod@prod

# Migrate files and DB from QA into development
./symfony project:sync-content frontend dev from prod@prod


Files and DB content are copied accordingly, almost magically. It saves so much time, but please make sure you understand and check the order that you apply in the symfony task. With the power this plugin provides, is very easy, by mistake, to overwrite production data, so again, always make a backup!

Thursday, December 31, 2009

CMS With XML Based Data Storage

OPEN SOURCE :)
GetSimple is The Simplest Content Management System EVER.
GetSimple has everything your client needs, and nothing a CMS doesn't

GetSimple is an open-source project licensed under the GNU GENERAL PUBLIC LICENSE.

GetSimple don't use mySQL to store information, but instead depend the simplicity of XML. By utilizing XML, able stay away from introducing an extra layer of slowness and complexity associated with connecting to a mySQL database. Because GetSimple was built specifically for the small-site market, feel this is the absolutely best option for data storage.

GetSimple CMS Home Page
About GetSimple CMS
Demo
Documentation

REQUIREMENTS

  • UNIX/Linux host

  • PHP 5.1.3+

  • Apache

  • No Database

  • 6 minutes to spare


FEATURE LIST

  • XML based data storage

  • Best-in-Class User Interface

  • 'Undo' protection & backups

  • Easy to theme

  • Great documentation

  • Growing community

Saturday, October 31, 2009

Once Again SENSIO LABS comes with great things for developers [STAND ALONE COMPONENTS FOR PHP]

Symfony Components are stand alone php libraries that can be handy in the development of php. Those are earlier coupled with the symfony and now available separately from the symfony package.

Just have look at those
YAML : PHP library that speaks yaml. YAML framework is the php library which take yaml as string and convert in to php array. All in all it is great product if you want to use it in any of your php project.
it is open source and available at YAML Component of Symfony

Templating : Which is templating engine, that you can use in any of your php project. It is the templating tool which you can use in any kind of templating system. Available at Templating System

And some other components available like EVENT DISPATCHER, DEPENDENCY INJECTION

And Request Handler The flexible micro-kernel for fast frameworks is coming sooner. have a look at
Symfony Components

All in all if you are now interested in symfony's some of the features to implement only. THIS IS THE BEST OF COMPONENTS YOU CAN TRUST. AND YET NOT REQUIRED TO USE THE SYMFONY. It shows the robustness of symfony projects.

Sunday, October 25, 2009

Have a look at the new Symfony 1.3 Alpha && its features

Here are some of the advantages of SYMFONY Framework..

* Compatible with as many environments as possible
* Easy to install and configure
* Simple to learn
* Enterprise ready
* Convention rather than configuration, supporting fallback calls
* Simple in most cases, but still flexible enough to adapt to complex cases
* Most common web features included
* Compliant with most of the web "bests pratices" and with web "design patterns"
* Very readable code with easy maintenance
* Open-Source

Check it out the new Alpha Symfony 1.3 with new features... SYMFONY 1.3

  • WHAT'S NEW IN SYMFONY 1.3


This alpha version will be the next stable release, is not suitable for production use. You can follow its development use by having a look at the Whats new in Symfony 1.3 even if you are willing to explore the .pdf then please click here.

  • Upgrading projects from Symfony 1.2 to Symfony 1.3


To upgrade your 1.2 project to a newer version of Symfony 1.3, you will find all the steps and other information through this.. please   Click here This document describes the changes made in symfony 1.3 and what need to be done to upgrade your symfony 1.2 projects. And If you want more detailed information on what has been changed/added in symfony 1.3, you can read the What's new? tutorial.

  • Deprecations and removals in 1.3


Here are some of the deprecations and removals in newer version of symfony 1.3.To find the lists of all settings, classes, methods, functions, and tasks that have been deprecated or removed in symfony 1.3 please Click here

You can find the installation for the symfony 1.3 here Installation

And for the first time user don't bother here you can have basic installation Getting Started

Here are some of the important links through which you can direct download the source for Symfony 1.3