PDA

View Full Version : Ext.Direct router for the symfony framework



dancablam
4 May 2009, 7:26 AM
Hello Ext Community,

I'm happy to announce the release of the dsExtDirectPlugin for symfony. The plugin provides a router and an API generator for use in symfony 1.1 and 1.2.

Features include:

Automatic API generation - simply add @extdirect-enable to your actions' doc comments and the JS and YAML is generated for you via the generate-api task
Works with your existing code with minimal changes required.
Fully integrates with symfony's request methods ( $this->getRequestParameter('name') )
Extensible so you can easily add custom functionality to meet your specific needs.


The Plugin: http://www.symfony-project.org/plugins/dsExtDirectPlugin

I will be maintaining this plugin actively so please comment on this thread or email me (danhstevens [at] gmail [dot] com) if you find any bugs or would like to request any enhancements. If you would like to contribute to the plugin, that would be welcomed too.

Thanks to the Ext team for all your hard work bringing us Ext.Direct and all the additional great features of 3.0.

Cheers,
Dan

aconran
4 May 2009, 10:15 AM
Thanks for the contribution Dan! We will soon release a blog entry that details many of the features of Ext.Direct.

We will also be providing links to popular implementations of the Ext.Direct server-side stack.

christocracy
4 May 2009, 10:58 AM
Nice!

dancablam
6 May 2009, 8:59 AM
Thanks guys. The blog entry will be a huge help to ensure I haven't left anything out - and hopefully it will clarify some things for me (like the purpose of 'Around Calls'). I'm looking forward to that.

Thanks again.

Cheers,
Dan

dancablam
20 May 2009, 1:03 PM
Version 1.0.1 of the plugin has just been released. It contains enhanced error handling, the ability to set custom names for your actions and methods via the doc comments, and several other tweaks to make the plugin operate more smoothly. If you use this plugin please be sure to upgrade to take advantage of these latest enhancements.

Special thanks to Jesse Dhillon (jesse.dhillon) for joining the plugin dev team and for his contributions.

More enhancements to come.

Cheers,
Dan

dancablam
14 Jun 2009, 4:08 PM
Version 1.0.2 has been released. If you're using 1.0.1 or lower it's highly recommended you upgrade to take advantage of the lastest features in Ext JS RC2.

Cheers,
Dan

woleium
2 Jul 2009, 11:37 AM
I'm still learning symfony, and am keen to use this plugin. I cant seem to get it to work though. Could you tell me where i went wrong here?

Follwing the instructions in the readme, and using the Jobeet sample code.

The frontend module was gnenerated with the command

symfony propel:generate-module --with-show --non-verbose-templates frontend job JobeetJobI then edited the app.yml and factories.yml with the changes as per the readme.

Next I added your example function executeMultiply to the class JobeetJob (/lib/model/JobeetJob.php)

then i ran

./s extdirect:generate-api frontend -dbut the extdirect_api.yml file is empty (well not empty, it looks like this :)

# DO NOT MODIFY
# Generated by extdirect:generate-api task
{ }Next I added

<?php use_javascript('extdirect_api') ?>
<?php use_javascript('test') ?> to my layout.php file, and then tried to call the multiply function from the test.js file like so:


Ext.Direct.addProvider(Ext.app.REMOTING_API);
console.log(JobeetJob.Multiply);
But it doesn't work.

I suspect that I am using the wrong class, as the generated yml file is empty, but I dont know.

Any pointers would be hugely appreciated...

woleium
3 Jul 2009, 9:55 AM
I'm such a wally.

the plugin parses the actions files, not the model files.

puting the commneted function in the actions.class.php file fixed everything.

Starfall
18 Jul 2009, 4:30 AM
I'm using your plugin. Thanks for sharing your work!

bornsilly
10 Aug 2009, 12:07 PM
It might be because i am new to Symfony, but reading the installation instruction i run into a problem straight away.

When i do:


symfony plugin:install dsExtDirectPlugin

then the following errors are returned:


>> plugin installing plugin "dsExtDirectPlugin"
>> sfPearFrontendPlugin Could not download from
>> sfPearFrontendPlugin "http://plugins.symfony-project.org/get/dsExtDirectPlugin/dsExtDirectPlugin-1.0.2.tar"
>> sfPearFrontendPlugin (File
>> sfPearFrontendPlugin http://plugins.symfony-project.org:80/get/dsExtDirectPlugin/dsExtDirectPlugin-1.0.2.tar
>> sfPearFrontendPlugin not valid (received: HTTP/1.1 404 Not Found
>> sfPearFrontendPlugin ))
>> sfPearFrontendPlugin Invalid or missing remote package file


I seems the package file is incorrect?

dancablam
12 Aug 2009, 10:52 AM
Hi bornsilly,

I can't recreate your problem. What version of symfony are you using? You may try prepending php to your command: php symfony plugin:install dsExtDirectPlugin

You may also want to try just checking out via SVN: svn co http://svn.symfony-project.com/plugins/dsExtDirectPlugin/trunk plugins

Cheers,
Dan

joserprieto
18 Aug 2009, 9:02 AM
Hi, Daniel:

First, I want to thank you four your plugin.

I installed the plugin from commandline:
joserprieto@joserprieto-vaio:$ symfony plugin:install dsExtDirectPlugin
>> plugin installing plugin "dsExtDirectPlugin"
>> sfPearFrontendPlugin downloading dsExtDirectPlugin-1.0.2.tgz ...
>> sfPearFrontendPlugin Starting to download dsExtDirectPlugin-1.0.2.tgz (10,528
>> sfPearFrontendPlugin bytes)
>> sfPearFrontendPlugin .
>> sfPearFrontendPlugin .
>> sfPearFrontendPlugin ...done: 10,528 bytes
>> sfSymfonyPluginManager Installation successful for plugin "dsExtDirectPlugin"And in my project I have installed the plugins:
joserprieto@joserprieto-vaio:$ symfony plugin:list
Installed plugins:
symfony 1.2.8-stable # pear.symfony-project.com (symfony)
dsExtDirectPlugin 1.0.2-stable # plugins.symfony-project.org (symfony-plugins)
sfDoctrineGuardPlugin 3.0.0-stable # plugins.symfony-project.org (symfony-plugins)I changed the app.yml and the factories.yml files of my application, and enabled the plugin in settings.yml; I enabled an action in one module with the comment * @extdirect-enable, and when I executed the command line extdirect:generate-api task, i got this error:


joserprieto@joserprieto-vaio:$ symfony extdirect:generate-api systemp -e extdirect -d
>> file- /home/joserprieto/projects/SysT...pment.systemp/web/extdirect.php
>> file+ /home/joserprieto/projects/SysT...pment.systemp/web/extdirect.php
>> tokens /home/joserprieto/projects/SysT...pment.systemp/web/extdirect.php

Fatal error: Class 'autosfGuardUserActions' not found in /home/joserprieto/projects/SysTemp/trunk/development.systemp/plugins/sfDoctrineGuardPlugin/modules/sfGuardUser/lib/BasesfGuardUserActions.class.php on line 18

Call Stack:
0.0005 65172 1. {main}() /usr/bin/symfony:0
0.0098 318912 2. include('/home/joserprieto/projects/SysTemp/trunk/development.systemp/lib/vendor/symfony/lib/command/cli.php') /usr/bin/symfony:37
0.3909 5291840 3. sfSymfonyCommandApplication->run() /home/joserprieto/projects/SysTemp/trunk/development.systemp/lib/vendor/symfony/lib/command/cli.php:20
0.4095 5459728 4. sfTask->runFromCLI() /home/joserprieto/projects/SysTemp/trunk/development.systemp/lib/vendor/symfony/lib/command/sfSymfonyCommandApplication.class.php:76
0.4098 5459728 5. sfBaseTask->doRun() /home/joserprieto/projects/SysTemp/trunk/development.systemp/lib/vendor/symfony/lib/task/sfTask.class.php:77
0.4635 5995620 6. dsExtDirectGenerateApiTask->execute() /home/joserprieto/projects/SysTemp/trunk/development.systemp/lib/vendor/symfony/lib/task/sfBaseTask.class.php:63
0.5198 6390788 7. dsExtDirectGenerateApiTask->loadModuleClassFile() /home/joserprieto/projects/SysTemp/trunk/development.systemp/plugins/dsExtDirectPlugin/lib/task/dsExtDirectGenerateApiTask.class.php:92
0.5202 6395688 8. require_once('/home/joserprieto/projects/SysTemp/trunk/development.systemp/plugins/sfDoctrineGuardPlugin/modules/sfGuardUser/actions/actions.class.php') /home/joserprieto/projects/SysTemp/trunk/development.systemp/plugins/dsExtDirectPlugin/lib/task/dsExtDirectGenerateApiTask.class.php:228
0.5204 6397980 9. require_once('/home/joserprieto/projects/SysTemp/trunk/development.systemp/plugins/sfDoctrineGuardPlugin/modules/sfGuardUser/lib/BasesfGuardUserActions.class.php') /home/joserprieto/projects/SysTemp/trunk/development.systemp/plugins/sfDoctrineGuardPlugin/modules/sfGuardUser/actions/actions.class.php:3
Any idea?

Thanks a lot :)

r3ap3r
27 Sep 2009, 9:50 PM
I'm getting the same error as joserprieto is. Actually I'm getting it for a different module (not sfGuardUser) but it's the same error.

Has anyone figured out how to fix this?

dancablam
27 Sep 2009, 10:03 PM
Are you using the latest version from SVN or the pear package? If you installed using symfony plugin:install dsExtDirectPlugin please try checking out from SVN described in my last post. If the error still occurs please let me know and I'll get a fix out asap.

Thanks for the report,
Dan

r3ap3r
27 Sep 2009, 10:33 PM
Are you using the latest version from SVN or the pear package? If you installed using symfony plugin:install dsExtDirectPlugin please try checking out from SVN described in my last post. If the error still occurs please let me know and I'll get a fix out asap.

Thanks for the report,
Dan

Dan,

I first tried with the pear version, when that didn't work I tried with the svn version, but I still get the same error.

Thanks,
-Chris

dancablam
28 Sep 2009, 1:25 PM
Ok r3ap3r & joserprieto - thanks pointing out this issue. I believe I have fixed the problem and the changes have been posted to SVN. Please verify it works for you now and I'll roll it into the new package.

What I had to do is add a new config parameter called include_plugins that allows you to list the plugins you wish to have scanned when generating the API. Before, it would scan all plugins some of which (like sfDoctrineGuard) won't work because they include auto generated action classes. Note that from now on if you wish to include plugins in the API generation they must be listed in the include_plugins parameter. I realize this breaks backwards compatibility slightly but this is the only long term solution I could think of in order to prevent frequent headaches.

Thanks again and please let me know the results of the update.

Cheers,
Dan

r3ap3r
28 Sep 2009, 4:40 PM
Dan,
That seems to have worked. I say seems to because it no longer throws the error and the task exits out cleanly but my extdirect_api.yml file is coming up simply as:


# DO NOT MODIFY
# Generated by extdirect:generate-api task
{ }

So now I need to track down whether it's actually reading through my plugin or not. If it is, then I must not be properly configuring the action.

As a side note: I like the option of specifying which plugins you want to use. I was going to suggest that you add that option but I figured you wouldn't like it. :D By having that option you can avoid any number of future problems with rogue plugins. It breaks backward compatibility, but how hard is it really to list the handful of plugins that the developer may have written to use ExtJS. Of course you may need to field some questions from current users the first time they update, but long term it should work out nice.

Off topic: I understand the error that your plugin was throwing. It was trying to include a class file that extended an auto*Action class, but since the auto*Action class hadn't already been loaded the require_once() call would fail. I can't seem to find where that auto generated class (the one that was causing the error) is located on my system. If I grep for it I can't find it. Either Symfony is doing some kind of weird voodoo class instantiating or I'm totally missing something. If anyone knows what is happening behind the scenes to load this class, and where it is located I'd love for you to explain it to me.

Thanks,
-Chris

r3ap3r
28 Sep 2009, 5:04 PM
Dan,
OK. I figured out what happened to cause my empty extdirect_api.yml file. I didn't notice that the app configuration had changed in your readme. In the original readme (at least the one on the Symfony plugin page) for app.yml you had:


extdirect:
ds_ext_direct_plugin:
# Optional. Only use this parameter if you intend to create a mod_rewrite our route to a different URL for your extdirect front controller.
#router_url: /extdirect.php

# Optional. Set the javascript variable name for the js api spec (defaults to 'Ext.app.{YOUR_API_NAME}_API)
#js_var: Ext.app.EXTDIRECT_API

# Optional. Set a provider_type (defaults to remoting)
#provider_type: remoting


But your new readme list:


all:
ds_ext_direct_plugin:
# Optional. Only use this parameter if you intend to create a mod_rewrite our route to a different URL for your extdirect front controller.
#router_url: /extdirect.php

# Optional. Namespace for direct actions (ex: myNs.myAction.myMethod();)
#action_namespace: myNs

# Optional. Set the javascript variable name for the js api spec (defaults to 'Ext.app.{YOUR_API_NAME}_API)
#js_var: Ext.app.EXTDIRECT_API

# Optional. Set a provider_type (defaults to remoting)
#provider_type: remoting

# Optional. List plugins who's action classes you want included in API generation (defaults to null)
# ex: include_plugins: [sfGuardPlugin]
#include_plugins: ~


A subtle change that I'm posting here for anyone else who is having problems and didn't catch it.
The environment has been changed from 'extdirect' to 'all'.
(Obviously I knew about the include_plugins change, and had already applied that one.)

So to recap: Your fix seems to work great for me, now I need to finish my ExtJS code so that I can actually test your plugin :D

Thanks for the quick response and fix.
-Chris

r3ap3r
28 Sep 2009, 7:07 PM
Dan,
OK, I found a potential bug in your code.

I have a plugin that I have made where I put all of the code in the Base[module name]Actions class leaving the actual [module name]Actions class empty for the end users of the plugin to modify as necessary. I believe this to be the correct way of creating a plugin, although I could be wrong.

Anyway, when I put the @extdirect-action MYACTION directive in the class comments (of the base class) they don't get pulled in by your script. It only works if I put them in the actual derivative [module name]Actions class comments.

I would suggest adding something like the following to fix this (inside dsExtDirectGenerateApiTask.class.php right after the call to $class->getDocComment():


$parent_class_name = 'Base'.$module.'Actions';
if (class_exists($parent_class_name)) {
$parent_class = new ReflectionClass($parent_class_name);
$class_comments .= $parent_class->getDocComment();
}


Obviously this may not be the best way of dealing with it since I'm not positive if the majority of action classes have a base class starting with 'Base' (I assume they do, at least for plugins). I don't know much about what the ReflectionClass can do, so there is probably a better way of finding the parent class. This way the @extdirect-action directive can remain in the same file as the actual method that is being used.

By appending the parent class comments your code will still grab the child class directive even if it has been declared in the parent and overridden in the child.

As a side note: I would modify your readme to be a little bit more descriptive of the @extdirect-action directive. At first I thought that this directive was supposed to be placed in the method comments along with the @extdirect-enable directive and I couldn't figure out why it wasn't working until I looked through your code to see what it was doing.

Looking back I realize what you meant by "Action-level doc comment options include:" in the readme, but it wasn't clear enough for me to begin with to know that these comments went with the class, not the method. Being a bit more descriptive of exactly where to put the directive may eliminate support requests from some of the slow people like me. :(

Thanks again,
-Chris

dancablam
30 Sep 2009, 12:56 PM
Hi Chris,

Glad the update seems to have worked for you.

Regarding the auto*Action classes - you're pretty much right about it being voodoo - It's my understanding the auto classes don't exist until runtime - there's no actual source code generated that references those auto classes.

I did sneak that little environment change in there from 'extdirect' to 'all' a few commits back since when you generate the API for the first time the extdirect env actually doesn't exist yet so it causes issues. :)

I posted clarification in the README regarding action and method level comment options. Thanks for the tip.

Regarding Base Action classes - I believe prepending Base to an action class is somewhat common practice although I'm not sure exactly how prolific it is. I hadn't given any thought to the possibility of plugins being written that wish to take advantage of dsExtDirect. My concerns are: 1) Are Base Action classes really commonplace enough to justify searching through with Reflection? 2) Will searching through Base Action classes cause problems similar to what we've already encountered with freely meddling around in plugin classes? 3) Do plugin users really want extdirect actions defined for them by default? What if they want to override or disable them?

Perhaps you're just creating the plugin for yourself but if you are creating a plugin for the public, those type of questions have me concerned. I'm thinking that type of functionality should be employed by writing a custom task that extends the dsExtDirectPlugin task. I'm not sure of the best approach - further thoughts from you would be welcomed. I just want to avoid adding a feature that others will rebuke later because of unforeseen issues it creates.

Thanks for all of your thoughts and suggestions!

Cheers,
Dan

dancablam
30 Sep 2009, 2:30 PM
Version 1.0.4 has been packaged and is available for download. It's highly recommended you upgrade to the latest version to take advantage of new features and bug fixes:



Added _raw request parameter for access to all request data
Removed automatic scanning of all plugins in favor of manually specified plugins (to prevent failed API generation for some plugins using auto classes)
Removed error handler - caused too many issues with external libraries
Added action namespace support
Fixed bug where app.yml params were not being pulled because of environment


Cheers,
Dan

r3ap3r
30 Sep 2009, 6:01 PM
Regarding Base Action classes - I believe prepending Base to an action class is somewhat common practice although I'm not sure exactly how prolific it is. I hadn't given any thought to the possibility of plugins being written that wish to take advantage of dsExtDirect. My concerns are: 1) Are Base Action classes really commonplace enough to justify searching through with Reflection? 2) Will searching through Base Action classes cause problems similar to what we've already encountered with freely meddling around in plugin classes? 3) Do plugin users really want extdirect actions defined for them by default? What if they want to override or disable them?


Dan,
With the addition of your include_plugins directive I don't think it is a problem to search the base classes for the listed plugins. The fact that you have specified wanting the plugin should mean that only plugins designed to work with dsExtDirect are actually being searched. Performance isn't an issue since running the task is really only a one time event done when you add/remove allowed methods, etc.

Most of the plugins that I looked at before writing my own plugin place all the functionality in the base class leaving the actual [module name]Action class empty. In the code I posted I append the parent class docs to the child class docs before your code searches for the directive. The way your code is currently written it grabs the first entry that it finds (in this case the child entry). This means that if the end user of the plugin extends the plugin by modifying the child class and adds a different directive for the class, that new directive is used, not the one listed in the parent (base) class.

As a side note: I've got my ExtJS code finished and your plugin is working great (with the addition of my code in your task anyway :D ).

Thanks again,
-Chris

jpipas
13 Oct 2009, 6:38 AM
Hey guys,

I've got the plugin working - however, I'm working with a grid (and pager) - and I'm looking to add a "total_count" property to the response thats being created by the server. I'm wondering if in order to do such a thing, I need to extend the resultAdapter?

Any direction (or sample code) would be greatly appreciated. Thanks

dancablam
13 Oct 2009, 7:46 AM
Hi jpipas,

Here's all you have to do:

1. Add totalProperty to your directStore. This is what I have:

totalProperty : 'data.total_items'

2. Now simply add total_items to your result. Ex:

$this->result['data']['total_items'] = $total;

3. Finally, make sure your PagingToolbar lists your store:

bbar :new Ext.PagingToolbar( {
pageSize: 25,
store: YOUR_DIRECT_STORE_HERE,
displayInfo: true,
plugins: [new Ext.ux.PageSizePlugin()]
})

Let me know if you have any more questions.

Cheers,
Dan

jpipas
13 Oct 2009, 8:02 AM
Color me stupid. That works - I didn't even think about extending the result array sent to the controller. Its been a long day already - appreciate the quick response!

Flukey
25 Oct 2009, 12:34 PM
Good work :-) I shall be using this in the very near future.

Flukey
28 Oct 2009, 2:09 PM
A quick question.

In an action where you want to use in extdirect the '$this->result' variable is always used for the response output from the rpc call. Is that right?

If yes, what about if my action retrieves a dataset. I don't wish to do a load of template logic in the action and assign it the result variable. Is there not a way to return the template output as the result? Any help would be hugely appreciated :-)

dancablam
28 Oct 2009, 2:41 PM
A quick question.

In an action where you want to use in extdirect the '$this->result' variable is always used for the response output from the rpc call. Is that right?

If yes, what about if my action retrieves a dataset. I don't wish to do a load of template logic in the action and assign it the result variable. Is there not a way to return the template output as the result? Any help would be hugely appreciated :-)

Hi Flukey,

I'm not 100% confident I understand your questions but tell me if this helps:

Yes - $this->result is the variable always used for the result data that's passed in the extdirect response. You can put pretty much any kind of data in $this->result including complex arrays, stdObjects, etc. Best practice for extdirect data is that you return raw data (instead of HTML) and let ext manage that data via XTemplates or via another ext component.

You can return HTML if you want, however (and sometimes it can be your only good option). If you want to return the HTML from a template you could create a partial and use the following code to get that partial data as a string:


$this->result = $this->getPartial('partialName', array('data'=>$vals));

Let me know if that clears anything up for you.

Cheers,
Dan

Flukey
28 Oct 2009, 2:48 PM
Hi Flukey,

I'm not 100% confident I understand your questions but tell me if this helps:

Yes - $this->result is the variable always used for the result data that's passed in the extdirect response. You can put pretty much any kind of data in $this->result including complex arrays, stdObjects, etc. Best practice for extdirect data is that you return raw data (instead of HTML) and let ext manage that data via XTemplates or via another ext component.

You can return HTML if you want, however (and sometimes it can be your only good option). If you want to return the HTML from a template you could create a partial and use the following code to get that partial data as a string:


$this->result = $this->getPartial('partialName', array('data'=>$vals));

Let me know if that clears anything up for you.

Cheers,
Dan

Hi Dan,

Thank you for your prompt response. It is very much appreciate.

Ok, regarding my question. What I mean is, for example i have an action called 'executeGetUsers()' and in my template 'getUsersSuccess.php' I have some simple template logic - I would like to return this template content, rather than a variable.

For me personally, to keep everything clean, I would like to return the json code from a template, rather than buld json strings in the action. Hope that makes sense :-)

dancablam
28 Oct 2009, 3:08 PM
Hi Flukey,

Gotchya. There might be a way to retreive the content of yourTemplateSucces as a string - I'll leave the research up to you. But I do know there is a way to retreive a partial template as a string using the example I gave previously. You could always just make your yourTemplateSuccess include a partial and also pull that same partial for extdirect requests.

Hope that helps.

Cheers,
Dan

Flukey
28 Oct 2009, 4:06 PM
Dan,

Whilst I think that the plugin you have created is superb. It does it's job very well indeed, however, I would like to stress a point - and I feel passionately strong about it.

I think removing the template layer of the framework really destroys the doctrine of the framework. Returning a variable and not a template is not good practice. I would like to keep the presentation layer to format the data and then return that as an extdirect response, otherwise, all I will have is actions and no templates and of course that takes away the whole consistency of the framework and renders (no pun intended) sfPHPView useless and obselete. I don't like that the plugin is treating an action as two layers instead of one.

And using partials, although it's a quick solution is not very good in the long run as it takes away alot of the functionality that the symfony framework has dedicated to ensuring a clean MVC design pattern. And to be honest, i'm a bit of a stickler for clean hierarchiral architecture design.


Would you object to me adding an extra doc comment parameter of '@usetemplate' (or something similar) and then extending the result adapter to render the template content as the result instead of the 'result' variable?

Of course, It is your plugin and I would very much appreciate your opinions, criticisms, objections etc. to what i've proposed.


I thank you for the time you've put into the project.

Jamie

dancablam
28 Oct 2009, 11:08 PM
Hi Jamie,

Thank you for your comments.

I want to start out with your claim that the current approach renders sfPhpView useless - you're correct (for Ext.Direct requests). But it's important to understand that in a web service application the "View Layer" is shifted to the client-side instead of relying on the server side. Thus, in the conext of Ext.Direct your understanding of an MVC framework has to change slightly. To be 100% accurate, there's still "technically" a server-side view layer, it's just that it's JSON-RPC instead of HTML - so including sfPhpView in dsExtDirectPlugin would actually require an MVVC framework. :)

Here's my explaination on why the plugin works the way it does: The Ext.Direct spec is a JSON-RPC implementation which is designed to return data to Ext components like the grid, tree, XTemplate, etc. This data is always going to be in array/hash/scalar format. That said, returning raw template data is an exception to the rule, at best - and in general not good practice. Moreover, getting the symfony templating layer involved in Ext.Direct requests would come at a performance cost that would negate the spirit of high-performance AJAX apps users have come to expect. And if dsExtDirect was modified to return full symfony templates, it would include the entire <html></html> document which would in turn cause unexpected behavior in Ext. That said, your request for full template support is not as clear-cut as it might seem. It's still perfectly acceptable to return HTML as a scalar in your response, but relying 100% on a templated view in an RPC context negates the point of using RPC in the first place and you should instead rely on a regular HTTP request.

I hope that clears some things up for you. There's still plenty of room to be an MVC purist - it just works a little different for web services. :). I'd be more than happy to hear more about what kind of application you're writing and offer some advice on the best way to go about it. I've written several large apps using Ext.Direct and I haven't come across an instance where getting symfony's template layer involved seemed like the best way to go. And I've never been tempted to mix view logic in the action layer - since the JSON rendering logic is handled by the plugin there's still good separation and the action layer does not have to serve a dual role.

I appreciate you reading my perspectives and I welcome your thoughts. I'm committed to continually improving the plugin so your comments are very welcome.

Cheers,
Dan

jpipas
3 Nov 2009, 7:30 AM
How do we populate the "message" and "where" return results when a symfony action has errors or fails due to application logic, etc.? The return result looks something like {"type":"exception",...,"message":null,"where":null} - I can add my own json parameters, however, I'm wondering if there was a way to do this "natively". Thanks for the help!

dancablam
3 Nov 2009, 10:40 AM
How do we populate the "message" and "where" return results when a symfony action has errors or fails due to application logic, etc.? The return result looks something like {"type":"exception",...,"message":null,"where":null} - I can add my own json parameters, however, I'm wondering if there was a way to do this "natively". Thanks for the help!

In order to see exception data you need to have the debug flag (-d) on when generating the API. That will populate the message and the where information so you can debug your code.

To generated errors you wish the client to see (ex: form validation errors, authentication errors, etc) I use the following response format:


{
"type":"rpc",
"tid":2,
"action":"myAction",
"method":"myMethod",
"result":{
"success":false,
"message":"Operation failed.",
"data":[

]
}
}

I then look on each request to see if success is true. My way is by no means the only way - it just works for me. Don't rely on throwing exceptions for client errors, however, as you'll soon find them both inefficient and insecure.

Hope that helps.

Cheers,
Dan

jpipas
3 Nov 2009, 11:09 AM
Thats what I ended up doing anyways - was simply returning my own set of "results" and having the client handle them on the basis of "success" or not. Just wondering if there was some other more "native" way to do things, but as it seems, I was doing the same thing you're doing all along.

Working good so far. This + doctrine has shaved a lot of dev time off of things. I'm beginning to pump out components left and right.

woleium
9 Dec 2009, 2:30 PM
Thanks for this Dan, It's great!

I'm staring a new project, and wondered if anyone has this working with 1.3/4, or should I stick to 1.2?

I cant see anything in the source that would stop it working with 1.3 (which i have to use for compatibility), but then I'm no expert...

W

dancablam
9 Dec 2009, 3:13 PM
I'm staring a new project, and wondered if anyone has this working with 1.3/4, or should I stick to 1.2?

Hey woleium,

I agree that to my knowledge the plugin should work in 1.3/4 but I haven't validated that myself. However, if you're willing to be brave and give it a shot, then I'll be happy to quickly fix any snags you come across.

If you do give it a whirl, let me know how it works out!

Cheers,
Dan

woleium
9 Dec 2009, 3:42 PM
If you do give it a whirl, let me know how it works out!


Cool. I'll have a play and let you know.

W :-)

Flukey
13 Dec 2009, 7:57 AM
Hi Dan,

Sorry for my slow reply.

In regards to your comment about Symfonys view layer being slow. I disagree. If you're only using a template which is returning json and you disable returning any layout etc. then it's very quick. Plus I can keep my json presentation in a template rather than an action.

Furthermore, if I wish to cache any response from the server then I would need to have a template for an action. No?

Anyway, I do have a little problem which I hope you may be able to help me with :$



public function executePerformLogin(sfWebRequest $request)
{
$user = $this->getUser()->retrieveByUsername($request->getParameter("loginUsername"));
// check user exists and the password entered is correct
if($user && $user->checkPassword($request->getParameter("loginPassword"))){
// sign in the user
$this->getUser()->signIn($user);
$this->result = array("success"=>true);
} else {
// can't be authenticated
$this->result = array("success"=>false, "errors"=>array("loginUsername"=>"Invalid Credentials"));
}
}

Simple enough code, but apparently on return from the Ext.Direct request 'result is undefined'.


$this->result = true

If i return the above then it works all fine and dandy.

But if I return
$this->result['success']= true

It doesn't like it. Any ideas?

Thanks very much :-)

Flukey
13 Dec 2009, 12:02 PM
Ah, please ignore my last question. I had tunnel vision....I forgot when I was working on a different machine that I had changed the prefixes to the tables. When I did an svn update on my other machine, I forgot to do a doctrine:build-all-reload. I feel like a bit of an now :$

Anyhows, I would like to know what you think about my sfVIEW opinion. :-)

Thanks.

woleium
15 Dec 2009, 1:54 PM
I agree that to my knowledge the plugin should work in 1.3/4 but I haven't validated that myself. However, if you're willing to be brave and give it a shot, then I'll be happy to quickly fix any snags you come across.

Hi Dan,

I'm assuming that if i set 'action_namespace', I should then call the direct functions using the namespace? Specifically, if I use your multiply example as an action in an a module called main (and an app called recorder), with the following settings:

app.yml

all:
ds_ext_direct_plugin:
action_namespace: myNs
js_var: Ext.app.RECORDER_API
include_plugins: [sfDoctrineGuardPlugin]factories.yml

extdirect:
controller:
class: dsExtDirectControllerI should be calling the function (after a 'symfony cc' and a 'symfony extdirect:generate-api recorder') like so:

Ext.Direct.addProvider(Ext.app.RECORDER_API);
Ext.QuickTips.init();
myNs.main.multiply({"number":4}, function(result){
console.log(result);
});However this errors saying that myNs is not defined. Omitting the myNs however, works:

main.multiply({"number":4}, function(result){...}I still consider much of your module to be voodoo, so I'm not sure if this is the intended behavior, or if I've found a bug in 1.3 compatibility....


W.

dancablam
15 Dec 2009, 3:23 PM
Hi Dan,
In regards to your comment about Symfonys view layer being slow. I disagree. If you're only using a template which is returning json and you disable returning any layout etc. then it's very quick. Plus I can keep my json presentation in a template rather than an action.

Furthermore, if I wish to cache any response from the server then I would need to have a template for an action. No?


Hi Flukey,

I'm still going to be a bit stubborn :). In addition to my prior arguments, and regardless of speed, there isn't much presentation logic that goes into making a JSON Ext Direct response. It's basically a structure of a couple arrays that can be generated by a single json_encode() call. To me that seems much simpler than nesting <?php foreach():.. ?> ... <?php more_code?> ... <?php endforeach(); ?>.

I do agree with you that separation is still important - and that's why I created a separate 'result' class in my applications to manage and generate the result structure. My result class allows me to setData(), addMessage(), addError() etc - calling those methods is no different then defining variables to be accessible in the view via $this->myVar. In other words - instead of $this->myVar = $myVar, I do $result->setData('myVar', $myVar). Then I simply return $this->getResult() which compiles the entire result structure.

Voila! Code separation that rivals that of having a view layer - all without having to actually create and manage a view layer for each action. If you're interested, I can PM you the result class I use (it'll require some refactoring since I use custom exception handlers, etc that won't be in your app). Or you can create your own - mine is maybe 100 lines of well-documented code.

So there's my latest rant :) I view the JSON response similarly to any other API response such as SOAP, XMLRPC, etc - they're part of a rigid protocol versus HTML which is a fuster-cluck of nested data.

Cheers,
Dan

dancablam
15 Dec 2009, 3:27 PM
Hi wolium,

The myNs seems to be working for me with symfony 1.2. I can't imagine why it wouldn't work on symfony 1.4 since it's purely a JS/JSON thing.

Take a look at the API JS source and make sure it's including "namespace":"myNs". Also make sure your browser hasn't cached the API JS. If that doesn't work, which version of Ext JS are you using?

Cheers,
Dan

Flukey
16 Dec 2009, 2:14 AM
Hi Flukey,

I'm still going to be a bit stubborn :). In addition to my prior arguments, and regardless of speed, there isn't much presentation logic that goes into making a JSON Ext Direct response. It's basically a structure of a couple arrays that can be generated by a single json_encode() call. To me that seems much simpler than nesting <?php foreach():.. ?> ... <?php more_code?> ... <?php endforeach(); ?>.

I do agree with you that separation is still important - and that's why I created a separate 'result' class in my applications to manage and generate the result structure. My result class allows me to setData(), addMessage(), addError() etc - calling those methods is no different then defining variables to be accessible in the view via $this->myVar. In other words - instead of $this->myVar = $myVar, I do $result->setData('myVar', $myVar). Then I simply return $this->getResult() which compiles the entire result structure.

Voila! Code separation that rivals that of having a view layer - all without having to actually create and manage a view layer for each action. If you're interested, I can PM you the result class I use (it'll require some refactoring since I use custom exception handlers, etc that won't be in your app). Or you can create your own - mine is maybe 100 lines of well-documented code.

So there's my latest rant :) I view the JSON response similarly to any other API response such as SOAP, XMLRPC, etc - they're part of a rigid protocol versus HTML which is a fuster-cluck of nested data.

Cheers,
Dan

Hi Dan,

Thanks for the response - I don't regard it as a rant, more of a well-structured argument. :-)

I can see your points however, if i wanted to return JSON data in a structure like below, I would not be happy doing it in an action.

This isn't exactly the best example I could think of: :P


{"ResultSet":{
"count":"<?php echo count($possible_sessions)?>",
"Result":[
<?php foreach($possible_sessions as $session): ?>
{"id": "<?php echo $session->getId()?>",
"name": "<?php echo $session->getName()?>",
"start_date": "<?=date("l jS \of F Y h:i:s A", strtotime($session->getStartDate()))?>",
"location": "<?php echo $session->getLocation()?>"
}
<?php endforeach;?>
]}
}


Granted, most results can be returned as simple arrays which have been json_encode'd - I have no problem with that. I only have a problem when i'm returning large complicated json responses.

However, I would like to see your Result class. It may even change my view ;-) (no pun intended!)

Thanks,

Flukey

woleium
16 Dec 2009, 4:10 PM
Hi wolium,
Take a look at the API JS source and make sure it's including "namespace":"myNs". Also make sure your browser hasn't cached the API JS. If that doesn't work, which version of Ext JS are you using?


Was using EXTjs 3.0. Have upgraded to 3.0.3 and it's still not playing ball (i.e. "namespace":"myNs" is not being appended to the extdirect_api.js file).

I can confirm that the same files work when I throw them into a 1.2 install.

I got curious, and replaced the /lib/vendor folder with the 1.2 version, and ran the generate. It didn't work, but I guess that's 'cos of what's copied about when you initialize the project.

This is not a showstopper for me at present, and I should really create another install of 1.3/4 just to confirm that it's not something silly that I've done. I'll have a go at that later in the week.

W

dancablam
23 Dec 2009, 4:07 PM
Hi woleium,

I've tested the plugin on 1.3/1.4 and it appears to work without issue including namespace support. There must be something with your configuration that's not quite right.

Cheers,
Dan

Czapla
28 Jan 2010, 11:25 AM
Greetings form Poland

I have problem in plugin configuration. I'm using Symfony 1.4.1
I installed plugin via PEAR channel.
Then i setted up my files like documentation says.

My app.yml

all:
ds_ext_direct_plugin:

My factories.yml


prod:
logger:
class: sfNoLogger
param:
level: err
loggers: ~

test:
storage:
class: sfSessionTestStorage
param:
session_path: %SF_TEST_CACHE_DIR%/sessions

response:
class: sfWebResponse
param:
send_http_headers: false

mailer:
param:
delivery_strategy: none

dev:
mailer:
param:
delivery_strategy: none

all:
routing:
class: sfPatternRouting
param:
generate_shortest_url: true
extra_parameters_as_query_string: true

view_cache_manager:
class: sfViewCacheManager
param:
cache_key_use_vary_headers: true
cache_key_use_host_name: true

extdirect:
controller:
class: dsExtDirectController

Next i created a new app and called it "main".
Next i generate forms via doctrine:create-module main media media
I setted up doc comment as following:

My action.class.php

<?php

/**
* media actions.
* @package extplugin
* @extdirect-enable
* @extdirect-len 1
* @subpackage media
* @author Your name here
* @version SVN: $Id: actions.class.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $
*/
class mediaActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$this->medias = Doctrine::getTable('Media')
->createQuery('a')
->execute();
}

public function executeNew(sfWebRequest $request)
{
$this->form = new MediaForm();
}

public function executeCreate(sfWebRequest $request)
{
$this->forward404Unless($request->isMethod(sfRequest::POST));

$this->form = new MediaForm();

$this->processForm($request, $this->form);

$this->setTemplate('new');
}

public function executeEdit(sfWebRequest $request)
{
$this->forward404Unless($media = Doctrine::getTable('Media')->find(array($request->getParameter('id'))), sprintf('Object media does not exist (%s).', $request->getParameter('id')));
$this->form = new MediaForm($media);
}

public function executeUpdate(sfWebRequest $request)
{
$this->forward404Unless($request->isMethod(sfRequest::POST) || $request->isMethod(sfRequest::PUT));
$this->forward404Unless($media = Doctrine::getTable('Media')->find(array($request->getParameter('id'))), sprintf('Object media does not exist (%s).', $request->getParameter('id')));
$this->form = new MediaForm($media);

$this->processForm($request, $this->form);

$this->setTemplate('edit');
}

public function executeDelete(sfWebRequest $request)
{
$request->checkCSRFProtection();

$this->forward404Unless($media = Doctrine::getTable('Media')->find(array($request->getParameter('id'))), sprintf('Object media does not exist (%s).', $request->getParameter('id')));
$media->delete();

$this->redirect('media/index');
}

public function executeMultiply() {
$num = $this->getRequestParameter('number');
if(is_numeric($num)) {
$this->result = $num * 8;
}
else {
$this->result = false;
}
return sfView::SUCCESS;
}

protected function processForm(sfWebRequest $request, sfForm $form)
{
$form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
if ($form->isValid())
{
$media = $form->save();

$this->redirect('media/edit?id='.$media->getId());
}
}

}

After that i called extdirectory:generate-api
And the blank extdirect_api.yml was created [not exacly blank]


# DO NOT MODIFY
# Generated by extdirect:generate-api task
{ }


What am i doing wrong? Please solve my problem.

Cheers

dancablam
28 Jan 2010, 12:51 PM
Hi Czapla,

You must enable each method, not each class. So you'll need to move @extdirect-enable and @extdirect-len to just above each method in the classes you wish to enable.

Please let me know if you have any more questions.

Cheers,
Dan

Czapla
28 Jan 2010, 11:47 PM
Hello Dan! thank you for your help.
I added followed param to all methods in my class [just in case].


<?php

/**
* media actions.
*
* @package extplugin
* @extdirect-enable
* @extdirect-len 1
* @subpackage media
* @author Your name here
* @version SVN: $Id: actions.class.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $
*/
class mediaActions extends sfActions
{
/**
* @extdirect-enable
* @extdirect-len 1
*/
public function executeIndex(sfWebRequest $request)
{
$this->medias = Doctrine::getTable('media')
->createQuery('a')
->execute();
}
/**
* @extdirect-enable
* @extdirect-len 1
*/
public function executeNew(sfWebRequest $request)
{
$this->form = new mediaForm();
}
/**
* @extdirect-enable
* @extdirect-len 1
*/
public function executeCreate(sfWebRequest $request)
{
$this->forward404Unless($request->isMethod(sfRequest::POST));

$this->form = new mediaForm();

$this->processForm($request, $this->form);

$this->setTemplate('new');
}
/**
* @extdirect-enable
* @extdirect-len 1
*/
public function executeEdit(sfWebRequest $request)
{
$this->forward404Unless($media = Doctrine::getTable('media')->find(array($request->getParameter('id'))), sprintf('Object media does not exist (%s).', $request->getParameter('id')));
$this->form = new mediaForm($media);
}
/**
* @extdirect-enable
* @extdirect-len 1
*/
public function executeUpdate(sfWebRequest $request)
{
$this->forward404Unless($request->isMethod(sfRequest::POST) || $request->isMethod(sfRequest::PUT));
$this->forward404Unless($media = Doctrine::getTable('media')->find(array($request->getParameter('id'))), sprintf('Object media does not exist (%s).', $request->getParameter('id')));
$this->form = new mediaForm($media);

$this->processForm($request, $this->form);

$this->setTemplate('edit');
}
/**
* @extdirect-enable
* @extdirect-len 1
*/
public function executeDelete(sfWebRequest $request)
{
$request->checkCSRFProtection();

$this->forward404Unless($media = Doctrine::getTable('media')->find(array($request->getParameter('id'))), sprintf('Object media does not exist (%s).', $request->getParameter('id')));
$media->delete();

$this->redirect('media/index');
}
/**
* @extdirect-enable
* @extdirect-len 1
*/
public function executeMultiply()
{
$num = $this->getRequestParameter('number');
if(is_numeric($num))
{
$this->result = $num * 8;
}
else
{
$this->result = false;
}

return sfView::SUCCESS;
}
/**
* @extdirect-enable
* @extdirect-len 1
*/
protected function processForm(sfWebRequest $request, sfForm $form)
{
$form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
if ($form->isValid())
{
$media = $form->save();

$this->redirect('media/edit?id='.$media->getId());
}
}
}

my new extdirect_api looks like that


media:
action: media
methods: { index: { len: '1', formHandler: false }, new: { len: '1', formHandler: false }, create: { len: '1', formHandler: false }, edit: { len: '1', formHandler: false }, update: { len: '1', formHandler: false }, delete: { len: '1', formHandler: false }, multiply: { len: '1', formHandler: false } }
method_map: { index: index, new: new, create: create, edit: edit, update: update, delete: delete, multiply: multiply }

I though plugin will generate a js files for my forms and will handle my CRUD [pagination, sorting] , but i still see forms, like after create-module command [Nothing was change]. I realize that i need start js coding all my ".success" files.
Could you provide me a example or some tutorials, how to handel request from extjs to yours plugin.

Sorry for my Bad Grammar
Cheers

nicolx
8 Feb 2010, 10:51 AM
Hi Dan!

I'm trying to use your plug-in but without success. I show you every steps that i did...
I'm using symfony 1.4

1. install with pear.. no problem!
2. I generated a new app "extjstest" and a new module "test"
3. in my app.yml i didn't change nothing (all configuration are optional)
4. factories.yml is set to:



extdirect:
controller:
class: dsExtDirectController
5. Right! I insered your test action on my new test module:



/**
* Multiplies a number by 8
*
* @extdirect-enable
* @extdirect-len 1
*
*/
public function executeMultiply()
{
$num = $this->getRequestParameter('number');
if(is_numeric($num))
{
$this->result = $num * 8;
}
else
{
$this->result = false;
}

return sfView::SUCCESS;
}
6. finaly i used



php symfony extdirect:generate-api extjstest
the result is ok...

7. i created some ui to try your functions!



/*!
* Ext JS Library 3.1.0
* Copyright(c) 2006-2009 Ext JS, LLC
* licensing@extjs.com
* http://www.extjs.com/license
*/

Ext.onReady(function(){
var callback = function(response, e) {

var status = '<b>Success</b>'
var text = '';

// success handling - e.status is success flag:
// true is success, false is failure
if(true === e.status) {
// response argument is same as e.result
text = response;
}

// failure handling
else {
status = '<b><i>Failure</i></b>'

// in the case of an exception, we don't have response but message
text = e.message;
}

// grab the center body
var body = win.items.itemAt(1).body;

// display the response
body.createChild({
tag:'div'
,cls:'response'
,html:status + ': ' + text
});

// scroll down
body.scrollTo('top', 100000, true);
};


// create Ext.Direct test window
var win = new Ext.Window({
title:'Mastering Ext.Direct by Saki'
,width:600
,height:400
,closable:false
,layout:'border'
,border:false
,items:[{
// west region with buttons
region:'west'
,width:160
,minSize:160
,split:true
,defaults:{minWidth:120}
,layout:'table'
,bodyStyle:'padding:20px'
,layoutConfig:{columns:1, tableAttrs:{style:{width:'100%'}}}
,items:[{
xtype:'button'
,text:'multiply'
,handler:function(){test.multiply({"number":2}, callback)}
}]
},{
// responses are displayed here
region:'center'
,autoScroll:true
,tbar:['->', {
text:'Clear'
,handler:function(){win.items.itemAt(1).body.update('')}
}]
}]
});
win.show();

});
i included on my view.yml these js

ext/adapter/ext/ext-base.js, ext/ext-all-debug.js, extdirect_api.js

and css

ext-all.css

Ok. On estjstest start page ('test/index) i insered:

<?php use_javascript('example.js') ?>

now i see a button to invoke your action and some space to view result...

i tried it but i founded this result :



test is not defined
[Break on this error] ,handler:function(){test.multiply({"number":2}, callback)}
example.js (line 65)
help me!:s
i hope that you can find my error!:(

dancablam
8 Feb 2010, 11:51 AM
Hi nicolx,

First, I didn't see where you added the direct provider - perhaps you did so in another js file. If not - you'll need to make sure you're calling: Ext.Direct.addProvider(Ext.app.RPC_API); (Where Ext.app.RPC_API is make sure it matches the value in your extdirect_api.js if it's different).

Aside from that - your 'action' should always start with a capital letter. In other words test.multiply should be Test.multiply

Tweak those two things and let me know how that worked.

Cheers,
Dan

nicolx
9 Feb 2010, 1:38 AM
ok! i'm approaching to the solution...

i added this call in the final js (example.js) script called from the templates "test/indexSuccess"

Ext.Direct.addProvider(Ext.app.EXTDIRECT_API);

if i don't wrong "Ext.app.EXTDIRECT_API" is the variable defined in the js script generated by your task right?!

so on my view.yml i included in the order:

[ext/adapter/ext/ext-base.js, ext/ext-all.js, extdirect_api.js]

but i get another error! :-/

to give you a complete view of the scripts status i post generated file extdirect_api.js



Ext.app.EXTDIRECT_API = {"url":"\/extjsTestApp_dev.php","type":"rpc","actions":{"test":[{"name":"multiply","len":1}]}};




Ext.Direct.PROVIDERS[e.type] is not a constructor
Ext.DomHelper=function(){var s=null,j=/^...b.stopEvent();this.completeEdit()}}}});
ext-all.js (line 7)


any ideas!?

dancablam
9 Feb 2010, 11:30 AM
Hi nicolx,

I'd use FireBug to see exactly what the server is returning (in the console tab you should see a request made to your server after executing Test.multiply. Look and see what the response was.

You may also want to change extjsTestApp_dev.php to extjsTestApp.php since the _dev may be generating the toolbar that symfony has in development mode and that will definitely mess things up. Instead use the debug flag (-d) when generating the API so that you can still see debug data but without symfony generating the toolbar.

~Dan

nicolx
10 Feb 2010, 5:57 AM
wow!
it's running!!

i'm going to learn something about extjs for create a complete symfony app!
thank you for your contribution!

nicolx
11 Feb 2010, 10:30 AM
i dan!

please don't hate me :D

i have another problem!
I want to use sfGuardPlugin...
I included it:



all:
ds_ext_direct_plugin:
include_plugins: [sfGuardPlugin]


when i try to do

extdirect:generate-api frontend
i get this error


Fatal error: Class 'BaseSfGuardUserGeneratorConfiguration' not found in /home/poldotz/workspace/em/trunk/plugins/sfGuardPlugin/modules/sfGuardUser/lib/sfGuardUserGeneratorConfiguration.class.php on line 11



any ideas!?

thanks in advance!!

buildero
16 Feb 2010, 6:09 PM
Hi nicolx.

I have followed your example but I have the same problem. When I change the type to "rpc" gives me the same problem that you presented. This is my initial configuration:

This is my configuration of App.yml



all:
ds_ext_direct_plugin:
# Optional. Only use this parameter if you intend to create a mod_rewrite our route to a different URL for your extdirect front controller.
router_url: /extdirect.php

# Optional. Namespace for direct actions (ex: myNs.myAction.myMethod();)
#action_namespace: myNs

# Optional. Set the javascript variable name for the js api spec (defaults to 'Ext.app.{YOUR_API_NAME}_API)
js_var: Ext.app.EXTDIRECT_API

# Optional. Set a provider_type (defaults to remoting)
provider_type: remoting

# Optional. List plugins who's action classes you want included in API generation (defaults to null)
# ex: include_plugins: [sfGuardPlugin]
#include_plugins: ~
This is the dispatch:


JSON




action
"Test"
data
[Object { number=10}]
method
"multiply"
tid
2
type
"rpc"Source
{"action":"Test","method":"multiply","data":[{"number":10}],"type":"rpc","tid":2}
The type is 'rpc' but the deafult configuration is 'remoting'. This is the file code exdirect_api.js



Ext.app.EXTDIRECT_API = {"url":"\/extdirect.php","type":"remoting","actions":{"country":[{"name":"update","len":1}],"Test":[{"name":"multiply","len":1}]}};
Note that the type is "remoting". The example will not send any error but also no satisfactory result.

If I change the type in app.yml to "rpc" and execute the task, "exdirect:generate api" sends me the following error:



Ext.Direct.PROVIDERS[e.type] is not a constructor
Ext.DomHelper=function(){var t=null,k=...lclick",this.onNodeDblClick,this)}});
ext-all.js (line 7)
Can you guide on how to resolve the problem?

Cheers,
Buildero

nicolx
17 Feb 2010, 1:21 AM
for me i founded a solution.. i removed sfGuardPlugin inclusion and if i want to use a sfGuardPlugin function i'm extracting them (for example execute signin) and attach extDirect comment in the header. this procedure is running.

Unfortunatly i don't have full control of extjs/extdirect and plugin, so i can't help you to the specific problem :(

i hope that Dan can help us ;)

oaugustus
19 Feb 2010, 3:37 PM
Thank you for this nice plugin. It's very useful for me. But I have an important question, the admin generator generate files in cache, I have created a custom theme for it, and this theme generate a automatic @extdirect-enable in my action methods, but extdirect-generateapi don't work because it was generated in cache folder. How to solve this problem?

corpse_br
28 Feb 2010, 5:27 PM
Hi,

here's an api generated by the plugin:

Ext.app.EXTDIRECT_API = {"url":"\/extdirect.php\/dsExtDirect\/router","type":"remoting","actions":{"media":[{"name":"multiply","len":1}]}};

factories.yml:

all:
extdirect:
controller:
class: dsExtDirectController

app.yml:

all:
ds_ext_direct_plugin:
js_var: Ext.app.EXTDIRECT_API


use:


Ext.Direct.addProvider(Ext.app.EXTDIRECT_API);
Ext.onReady(function() {
media.multiply({"number":1}, function(result, e){
debugger;
});
});


but Symfony is not correctly routing the URL.. I get a:
POST http://127.0.0.1:9001/extdirect.php/dsExtDirect/router 404 Not Found
{"action":"media","method":"multiply","data":[{"number":1}],"type":"rpc","tid":2}

any ideas? (sf 1.4.3; ext 3.1.1)

dancablam
1 Mar 2010, 9:40 AM
Hi nicolx,

Not sure why I haven't been getting emails that there have been more posts to this thread - sorry for appearing to have dropped off the map for the past couple weeks. Can you search your project for the class 'BaseSfGuardUserGeneratorConfiguration' and tell me if it exists and if so, what directory the file is in. Some of these plugins have magical classes that only materialize at runtime (a lot of these can be found in the admin generator) so it's impossible to load them which causes a problem. But if the file does in fact exist I might be able to come up with a means to locate and load it.

Cheers,
Dan

dancablam
1 Mar 2010, 9:46 AM
Hi oaugustus,

Implementing the ext direct plugin into admin generator actions has proven to be tricky for others as well for similar reasons. What I may do, which may help other users such as nicolx as well, is add additional parameters in app.yml that let you manually specify actions for plugins and admin generator actions where reading the actions from the comments is not easy/possible.

I'll experiment with this in the coming days and keep you posted.

Thanks,
Dan

dancablam
1 Mar 2010, 9:53 AM
Hi corpse_br,

Not sure why you're seeing that error. Have you cleared the symfony cache? You might also using the debug flag (-d) when generating the API. Let me know if none of these suggestions helped and I'll see what else I can dig up for you.

Cheers,
Dan

corpse_br
1 Mar 2010, 2:53 PM
Thx Dan, problem solved!

in factories.yml, I was doing:


all:
extdirect:
controller:
class: dsExtDirectController
:((
instead of pasting it in the root level of the file:


extdirect:
controller:
class: dsExtDirectController
all:
...

it verks!!!

alexro
17 Apr 2010, 6:59 AM
Hi Dan,

is it possible to have your plugin parse not only the action-classes but the model classes (lib/model/...) as well? It would be nice, if you could make extdirect calls to the model classes. This would skip the controller, of course ...

dancablam
19 Apr 2010, 3:01 PM
Hi alexro,

I'm hesitant to bypass the action layer as this would skirt all of symfony's security capabilities as well as numerous object instantiations like sfWebRequest, sfWebResponse, and other objects that both the plugin and symfony rely on to function. To do what you're wanting I would suggest using the straight PHP router on a non-symfony project and just download a standalone instance of Doctrine or Propel to use as your ORM.

Cheers,
Dan

RyanG
5 Jun 2010, 10:27 AM
Hi Dan,

Firstly, thanks a lot for a great plugin, works really well and has saved me a lot of time and effort with my application development so far.

One thing I've noticed though, is that if I populate $this->result in my action with an already created JSON string (for example, i'm using atolExt3WidgetPlugin to build formpanel JSON (derived from a Symfony form) and return it via Ext.Direct), by the time the router assembles the response it json_encodes a JSON string and puts it into the result hash.

resulting in:



"result":"{xtype:\"form\",url:null,monitorValid:true,standardSubmit:false,method:\"POST\",autoScroll:true,labelWidth:150,bodyStyle:{padding:\"10px\"},buttons:[{xtype:\"directsubmitbutton\",cls:\"sf_validate_button\",text:\"Submit\"}]


desired instead:



"result": { xtype: "form", url: null ... etc ... }


Of course I can run Ext.decode(data.result) to convert the escaped string back into a JS hash again, but it would be nice if it was in the correct format when served out in the first place?

I'm wondering if it might be worth adding an action comment tag like @rawJsonOutput to ensure that the result is inserted without attempting to json encode a string which is already in JSON format.

Does anyone have a view on this please, or am I simply missing something obvious?

Thanks,

Ryan

dancablam
7 Jun 2010, 10:01 AM
Hi Ryan,

Yeah the controller will definitely attempt to encode anything you get it into JSON as you've seen. My recommendation is to simply do:

$this->result = json_decode($myData, true);

The overall performance hit would be on the order of a few microseconds so this should work just fine unless you have an extremely high performance requirement. Let me know if that works for you.

Cheers,
Dan

RyanG
7 Jun 2010, 12:34 PM
Hi Dan,

Thanks for the tip, but unfortunately as the JSON string I am trying to put into $this->result sometimes contains references to Ext.Direct methods (eg. in a DirectStore ComboBox directFn definition), it returns a null string as I assume json_decode considers the string illegal.

If it were possible to use a raw attribute comment tag for the action as I mentioned before, perhaps you could temporarily replace $this->result with "%REPLACE%" and then after the direct router json_encodes it, simply replace that string with the raw JSON output again and serve it all up?

Not the most elegant solution, but I'm not sure one exists for this particular case? Do you have any other ideas?

Thanks,

Ryan


Hi Ryan,

Yeah the controller will definitely attempt to encode anything you get it into JSON as you've seen. My recommendation is to simply do:

$this->result = json_decode($myData, true);The overall performance hit would be on the order of a few microseconds so this should work just fine unless you have an extremely high performance requirement. Let me know if that works for you.

Cheers,
Dan

dancablam
7 Jun 2010, 2:58 PM
Hi Ryan,

Can you post an example of some JSON that returns a NULL string? If it's valid JSON you should be able to decode regarless of what references it contains.

Dan

RyanG
8 Jun 2010, 1:36 AM
Hi Dan,

Ok, so heres a small excerpt of my test JSON which gives NULL when doing json_decode:



{
xtype:"form",
url:null,
monitorValid:true,
standardSubmit:false,
method:"POST",
autoScroll:true,
labelWidth:150,
bodyStyle:{
padding:"10px"
},
buttons:[
{
xtype:"submitbutton",
cls:"sf_validate_button",
text:"Submit"
}
],
api:{
submit:Extdirect.project.update
},
paramsAsHash:true,
border:false
}


There are 2 problems with it: a) The names need to be in double quotes, b) and so do the values ... so using direct JS function references like "submit: Extdirect.dceProject.update" will also return NULL

The real issue here is that while the above JSON string is valid Javascript, the built in PHP JSON parser cannot handle it and gives up.

I am quite impressed with atolExt3WidgetPlugin's JSON builder (which is where the above string came from), but when it comes to reversing that JSON string in PHP your options are pretty limited.

I'm still thinking my original idea from earlier post could work, but of course its up to the developer to ensure the JSON string in $this->result is completely valid.

One other client-side option might be to intercept the JSON receipt in a Direct call before the requesting code has a chance to do anything with it, and run a quick Ext.decode over it first, but again I think I'd prefer it was in the desired format to begin with.

Hope this helps!

Thanks,

Ryan

dancablam
12 Jun 2010, 9:44 AM
Hi Ryan,

Sorry for the delay. Yeah, like you illuded to the problem isn't with PHP's JSON parser, it's that what you're passing it is not valid JSON. In fact it's not JSON at all - it's JavaScript.

You may just want to try echoing the javascript out followed by a die; command to skip the controller. return $this->renderText(...); may do what you need too. I've used a similar approach for Ext.Direct requests that I wanted to return a CSV file and it worked very well.

I'm hesitant to change things within the plugin itself since it would stray from the API specifications defined by the ExtJS team.

Let me know if any of the suggestions work out for you.

Cheers,
Dan

.ben
16 Jun 2010, 6:37 AM
Hi,

Is it possible to use dsExtDirectPlugin in an AIR/ExtJS application ? My AIR application can't find my DirectFn variable.

Here is my GroupingStore object (the same i used for a xHTML application which works fine) :



store = new Ext.data.GroupingStore
(
{
proxy: new Ext.data.DirectProxy
(
{
paramsAsHash: true,
directFn: president.list,
api: {
create: president.create,
read: president.list,
update: president.update,
destroy: president.destroy
}
}
),
autoLoad: true,
writer: writer,
autoSave: true,
groupField:'partyname',
sortInfo: { field: 'partyname', direction: 'ASC' },
remoteSort:false,
groupOnSort: true,
remoteGroup: false,
reader: new Ext.data.JsonReader
(
{
idProperty: 'idpresident',
root: 'data',
fields: [{
name: 'idpresident',
type: 'int'
},
'firstname',
'lastname',
{
name: 'idparty',
type: 'int'
},
{
name: 'partyname',
type: 'string',
mapping: 'Party.name'
},
{
name: 'tookoffice',
type: 'date',
dateFormat: 'Y-m-d'
},
{
name: 'leftoffice',
type: 'date',
dateFormat: 'Y-m-d'
},
{
name: 'income',
type: 'float',
sortable: true
}]
}
)
}
);
Has anybody ever tried?
Thanks

dancablam
16 Jun 2010, 1:19 PM
Is it possible to use dsExtDirectPlugin in an AIR/ExtJS application?


Hi Ben,

If Air can work with Ext.Direct then it should be able to work just fine with dsExtDirectPlugin since the implementation is a compliant version of the Ext.Direct API Spec. I've never worked with Air but if you have success it'd be great to hear about it.

Dan

.ben
17 Jun 2010, 7:32 AM
OK thanks Dan.

But my problem is more important than I thought (non sandbox AIR application, cross domain, ScriptTagProxy, ...)
Anyway, if I find how use your GREAT plugin with an AIR application connected to a Symfony application hosted in a different domain I'll let you know.

Ben

user_z
30 Jun 2010, 1:33 AM
Hi.

What should be result of function for auto commit or cancel moving tree node?
like this
tree.on('beforemovenode', function(tree, node , oldParent, newParent, index) {
Ext.Ajax.request({
url: '/my-treenode-move',
params: {
nodeid: node.id,
newparentid: newParent.id,
oldparentid: oldParent.id,
dropindex: index
}
});
});



tree.on('beforemovenode', function(tree, node, oldParent, newParent, index ) {
treeapi.move({ nodeid: node.id, newparentid: newParent.id, oldparentid: oldParent.id, dropindex: index},
function(result, e){
...
});
});

in actions.class.php
...
/**
* move tree node
*
* @extdirect-enable
* @extdirect-len 1
*/
public function executeMove(sfWebRequest $request) {
$id = $this->getRequestParameter('nodeid');
$from = $this->getRequestParameter('oldparentid');
$to = $this->getRequestParameter('newparentid');
$dropindex = $this->getRequestParameter('dropindex');

...
//$this->result['success'] = true;
$this->result['success'] = false;
return sfView::SUCCESS;
}

dancablam
1 Jul 2010, 7:46 AM
Hi user_z,

Use $this->getRequestParameter('_raw'); to take a look at the raw data - using _raw is best for use with Trees anyways since Ext JS's trees work very differently from the rest of their components with regards to requests/responses. If you var_dump() or print_r() the _raw data you can get a really good idea of what data is being passed.

Dan

heidyz
19 Jul 2010, 5:53 AM
Hello,
Thanks for this plugin.
I meet some difficulties with a TreeLoader when adapting the extjs example to symfony 1.3.6.

If i comment my action like this (1 parameter):


/**
* @extdirect-enable
* @extdirect-len 1
* @extdirect-method getTree
*/
function executeGetTree($request) {
$raw = $request->getParameter('_raw');
....
I have this exception: Invalid argument supplied for foreach() in dsExtDirectController.class.php on line 147.
Obviously, it's because parameters are not like "key/values".

If I comment my action with @extdirect-len 0. There is no exception but no parameter indicates which node (id) is concerned.

So what is the solution according to you ?


I have temporarily modified dsExtDirectController.class.php in a quick & dirty way:

//Parse object literals into key/val pairs
try {
foreach ($cdata->data[0] as $key => $val) {
$this->context->getRequest()->setParameter($key, $val);
}
} catch (Exception $ex) {
// nothing
}
Thanks
Heidy

dancablam
22 Jul 2010, 9:24 AM
Hi Heidy,

I would be interested in seeing the request the tree is generating that is causing that error. I've worked with trees using the plugin without a problem. Regardless, I've made the following changes which should fix your issue:


//Parse object literals into key/val pairs
if(!empty($cdata->data[0]))
{
foreach ($cdata->data[0] as $key => $val)
{
$this->context->getRequest()->setParameter($key, $val);
}
}

I've posted the change to the SVN repository so feel free to let me know if it's resolved your issue. Thanks for bringing this up.

Cheers,
Dan

heidyz
23 Jul 2010, 2:32 AM
Hello dancablam,
Thanks but it does not work because $cdata->data[0] is not empty: it's just not an associative array.

Here is my code for the treeloader:


var tree = new Ext.tree.TreePanel({
width: '50%'
, height: '80%'
, useArrows: true
, autoScroll: true
, region: 'center'
, lines: true // ne fonctionne pas !
, animate: true
, mask: true, maskConfig: { msg: "Chargement ..." }
, root: {
id: 'root',
text: 'Root',
expanded: true
}
, rootVisible: false
, loader: new Ext.tree.TreeLoader({
directFn: CentreVisite.getTree
,listeners: {
beforeload: function(){tree.body.mask('Chargement', 'x-mask-loading');}
, load: function(){tree.body.unmask();}
}
})
...
and here is the post request that it produces (firebug):


21612

I'm in vacation for 5 weeks so ...

Thanks a lot for your help.
Heidy

dancablam
27 Jul 2010, 3:50 PM
Hi Heidy,

Thanks for the additional information. I changed the if(!empty(...)) code to:

if(!empty($cdata->data[0]) && !is_scalar($cdata->data[0]))...

Let me know if that works for you. (Changes posted to SVN).

Cheers,
Dan

athome
23 Aug 2010, 10:52 AM
Hello everyone,

Dan thank you for taking the time to bring this wonderful invention that is Ext.Direct to Symfony.

I wrote you an email this morning before discovering this post, apologies, sorry for the spam.

I have progressed since then, but I still have some misunderstandings.

Here's firebug log I get with the request to extdirect.php :


{"Action": "event", "method": "multiply", "data": [2], "type": "rpc", "tid": 2}Here is the answer (in French ?!?!?!?)


Échec du chargement de la source pour: http://www.extdirect_symfony.lab/extdirect.php( eq : Failed to load source for: http://www.extdirect_symfony.lab/extdirect.php )

If I summarize:

- App.yml


all:
ds_ext_direct_plugin:
action_namespace: RPCTEST
-Factories.yml


extdirect:
controller:
class: dsExtDirectController
I left the code unchange for the function "multiply", on my event module.

And the client call :



...
<link rel="stylesheet" type="text/css" href="/direct/ext/resources/css/ext-all.css">
<script type="text/javascript" src="/direct/ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="/direct/ext/ext-all-debug.js"></script>
<script type="text/javascript" src="/js/extdirect_api.js"></script>
<script type="text/javascript">

Ext.Direct.addProvider( Ext.app.EXTDIRECT_API );
RPCTEST.event.multiply( 2, function(response, e ){ alert(response) });

</script>
</head>
=> alert reponse is undefined

Here is the result of extdirect-generate :

- Extdirect_api.js :


Ext.app.EXTDIRECT_API = {"url":"\/extdirect.php","type":"remoting","actions":{"event":[{"name":"multiply","len":1}]},"namespace":"RPCTEST"};
- extdirect.php controler is created, as extdirect_api.yml

So, everything looks fine... Maybe I forgot something. I read the seventh page post...

- What is the value of extdirect_api.json ?

- If I understand, the purpose of the statement in the factories.yml file is to declare a controller, and thus, an environment. Why create a special envirronnement? Since I do not understand, I tried to add the statement in ENV or ALL, but it break it all ...
Should I forgot something towards the creation of a special envirronnement ? Is File factories enough ?

athome
24 Aug 2010, 4:11 AM
Everything is fine, now.

The correct call in the template is :



RPCTEST.event.multiply( {"number":2}, function(response, e ){ console.debug(response); console.debug(e); });


The first param is an object, not a scalar value. That is probably why my code alert undefined.

athome
25 Aug 2010, 2:51 AM
Hello Dan, Hi Ext tribe,

I continue my Direct exploration ...

What solution are you using to match the form data to those of a Doctrine object ?

Example, with an event table : date_start, date_end, title, ...

Here is a working code, but I find it extremely naive :



$form = new EventForm();
$form->disableCSRFProtection();

$tempEvent = array(
"start" => array(
'month' => $start_tempDate[1],
'day' => $start_tempDate[2],
'year' => $start_tempDate[0],
'hour' => $start_tempTime[0],
'minute' =>$start_tempTime[1]
),
"end" => array(
'month' => $end_tempDate[1],
'day' => $end_tempDate[2],
'year' => $end_tempDate[0],
'hour' => $end_tempTime[0],
'minute' =>$end_tempTime[1]
),
"eventName" => $this->getRequestParameter('title'),
"allDay" => $this->getRequestParameter('isAllday'),
"eventDesc" => $this->getRequestParameter('description'),
$form->getCSRFFieldName() => $form->getCSRFToken("dc8a160b3caa48277addacfaa2e573f935bcaf76")
);

$form->bind( $tempEvent );

if( $form->isValid() )
{
$event = $form->save();

$this->result = $event->getPrimaryKey();
}
else
{
$this->result = $form->getGlobalErrors();
}

return sfView::SUCCESS;


Some comments:

- With a normal ajax request, there is no problem, we have the same POST data, but there, we must completely rebuild the bidding process. How did you do?
- Can not disable the security token. How do you stand out?
- What about @extdirect-formhandler? Is not this a case of possible use ? How to implement ?

I tryed :



$event = new Event();
$event->start = $this->getRequestParameter('start');
$event->end = $this->getRequestParameter('end');
$event->eventName = $this->getRequestParameter('title');
$event->allDay = $this->getRequestParameter('isAllday');
$event->eventDesc = $this->getRequestParameter('description');

$form->bind( $event->toArray() );
I find this way more elegant, but I can not make it work. If I manually add the security token, the code throws an error because "Event" has no "CRSF" field , but if I add not, I get a Securityerror ...

Any comments, shared experience, is welcome. ;)

dancablam
26 Aug 2010, 1:41 PM
Hi athome,

The purpose of the declarations in the factories.yml is to define a separate controller that's used only by Ext.Direct requests. Symfony's default controller accepts standard web requests and populates things like POST data in the sfWebRequest object, enables for page redirection, etc. Since dsExtDirect uses JSON-RPC a standard web request won't do, so we need a separate controller that can make sense of the data posted by Ext.Direct and populate sfWebRequest, etc.

To match the data submitted by an Ext.Direct direct request, in my experience, takes a bit of extra creativity. I typically don't create/use symfony forms with working with Ext.Direct data. I do often, however, use the validators that are generated by the form to make sure the data conforms to my requirements. There are many ways to do this - I recommend browsing the form API to see what your options are. However, the way you do it above I think is perhaps pretty close - and a good way to do it. I've had trouble disabling CSRF too before. One work-around is to find a way to pass the CSRF data and make it a hidden form element in your Ext form. Aside from that I'd get on the symfony boards and ask why your attempts to disable CSRF aren't working - I'd like to know what you find out too because that's always confused me.

@extdirect-formhandler is really only for use in Ext forms that have one or more file-upload field. If your form doesn't have file-upload @extdirect-formhandler should be excluded.

I hope this helps some.

Please let me know if I can answer any more questions for you.

Cheers,
Dan

athome
28 Aug 2010, 4:34 AM
Hello Dan,

I published a few days ago my account on ExtDirect - Symfony use, thanks to your plugin.

It is there:

Ext.Direct & Symfony, DsExtDirectPlugin: testing implementation (http://www.sylvainartois.fr.nf/PHP/extdirect-and-symfony-dsextdirectplugin-testing-implementation)

Here is the code I used to map ExtDirect data to a Doctrine object:



$form = new EventForm();
$form->disableCSRFProtection();

$event = new Event();
$event->start = $this->getRequestParameter('start');
$event->end = $this->getRequestParameter('end');
$event->eventName = $this->getRequestParameter('title');
$event->allDay = $this->getRequestParameter('isAllday');
$event->eventDesc = $this->getRequestParameter('description');

$aEvent = $event->toArray();
$aEvent[ $form->getCSRFFieldName() ] = $form->getCSRFToken("my key here");

$form->bind( $aEvent );
The idea of sending the key to the client is a good idea that I'll probably use to correct my tutorial. I am a bit disappointed by the fact that you also met this difficulty. This means that the error comes from symfony, this is not common.

About "@extdirect-formhandler", this means that dsExtDirectPlugin can handle file uploads? Do you have a short example to put me on the way?

thank you for everything

fanch50
6 Dec 2010, 8:47 AM
Hi,

Not original but big thanks for these great tools ! ExtJs & dsExtDirectPlugin combination is a really efficient way to create a WebApp !

I have write a WebApp using dsExtDirectPlugin but now I want to secure it.

Before I dive into, have you any recommandations to manage this ?

I have see that we can include some plugins like sfGuardPlugin in app.yml but I have no idea how to use this option

# Optional. List plugins who's action classes you want included in API generation (defaults to null)
# ex: include_plugins: [sfGuardPlugin]
#include_plugins: ~

Could you help me ?

Thanks !

dancablam
6 Dec 2010, 9:42 PM
Hi fanch50,

I personally have never used the sfGuardPlugin plugin as I normally write my own authentication and security methods. However, the dsExtDirectPlugin, as you pointed out, does support plugins like sfGuardPlugin. It's really as simple as adding sfGuardPlugin to the include_plugins directive (ex: include_plugins: [sfGuardPlugin]) and then in the action classes that you create that extend from sfGuardPlugin you can add @extdirect-enable to the comments and this will enable those actions in the ext.direct API. I hope that makes at least some sense. Please don't hesitate to let me know if I can clarify anything further for you!

Cheers,
Dan

turbod
4 Feb 2011, 3:30 AM
Hi! How can i write functional test or unit test from Ext.Direct?
I use symfony 1.4 and dsExtDirectPlugin 1.0.5

jorgelive
21 Feb 2011, 9:05 AM
Hi Dan:

Are you planing on making some ext direct bundle for ext direct and symfony2?

spaff
12 Apr 2011, 9:37 PM
Hi!

First of all, thank you for your Plugin, it works great!

I just wondered if there is a way to keep the possibility to switch between a development and a production environment? There are a lot of settings we have to switch between dev and prod and it wouldn't be possible to switch the settings by hand.

lduchesne
5 Jun 2011, 1:40 AM
Hello guys,

I am new to ExtJS and I have issues to handle the server response after form submit.
My website is based on Symfony 1.4.9 with dsExtDirectPlugin 1.0.5

When I click Save Changes data is correctly submitted and saved in the database however when it comes to reply back to the client I get the error missing ) in parenthetical and the messageBox is not displayed.

Below is the code I use :


in actions.class.php


public function executePersonalInfo(sfWebRequest $request)
{
$this->forward404Unless(
$this->user = Doctrine_Core::getTable('sfGuardUser')->find(array('id' => $this->getUser()->getGuardUser()->getID()))
);

$this->form = new sfGuardPersonalInfoForm($this->user);

if ($request->isMethod('post'))
{
$values['first_name'] = $this->getRequestParameter('first_name');
$values['last_name'] = $this->getRequestParameter('last_name');
$values['email_address'] = $this->getRequestParameter('email_address');
[...]
$values['id'] = $this->user->get('id');
$values['_csrf_token'] = $this->getRequestParameter('_csrf_token');

$this->form->bind($values);

if ($this->form->isValid())
{
$phpPersonalInfo = $this->form->save();
$this->result = array('success' => true);
return sfView::SUCCESS;
}
}
}



in personalInfoSuccess.php


<?php use_javascript('extdirect_api.js') ?>
<?php include_javascripts() ?>
<?php use_helper('I18N') ?>

<script type="text/javascript">
Ext.onReady(function() {

Ext.QuickTips.init();

var formData = new Ext.FormPanel({
url: 'PersonalInfo',
renderTo: Ext.getBody(),
title: 'Personal Information',
width: 500,
height: 400,
padding: 10,
id: 'personalInfo',
items: [{
xtype: 'textfield',
fieldLabel: 'Firstname',
anchor: '100%',
name: 'first_name',
id: 'first_name',
allowBlank: false
},{
xtype: 'textfield',
fieldLabel: 'Lastname',
anchor: '100%',
name: 'last_name',
id: 'last_name',
allowBlank: false
},{
xtype: 'textfield',
fieldLabel: 'eMail',
anchor: '100%',
name: 'email_address',
id: 'email_address',
allowBlank: false,
vtype: 'email'
},
[...]
{
xtype: 'textfield',
fieldLabel: 'CSRF',
anchor: '100%',
name: '_csrf_token',
id: '_csrf_token',
hidden: true
}
],
buttons: [{
text: 'Save Changes',
handler: function(){
formData.getForm().submit({
success: function(form, action){ Ext.Msg.alert('Success', 'It worked'); },
failure: function(form, action){
if (action.failureType == Ext.form.Action.CLIENT_INVALID) {
Ext.Msg.alert('Form Error', 'Some fields are invalid');
} else if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
Ext.Msg.alert('Failure', 'Server communication failure: '+ action.response.status+' '+action.response.statusText);
} else if (action.failureType === Ext.form.Action.SERVER_INVALID) {
Ext.Msg.alert('Warning', action.result.errormsg);
}
}
})
}
}]
});
Ext.getCmp('personalInfo').getForm().setValues({
first_name:'<?php echo $user->getFirstName()?>',
last_name:'<?php echo $user->getLastName()?>',
email_address:'<?php echo $user->getEmailAddress()?>',
_csrf_token:'<?php echo $form->getCSRFToken()?>'
});
});
</script>

Any help is very much appreciated
Cheers

Tchinkatchuk
14 Feb 2013, 3:23 AM
Hi all,

I don't know if it is the best place for this question but I can't find a way.
How could I launch an event (notification I have in my app) when extdirect symfony plugin receive an exception ...

Does anyone has a clue ?

Thanks.

Sylvain