CatalystX::Controller::ExtJS::REST - RESTful interface to dbic objects
package MyApp::Controller::User; use base qw(CatalystX::Controller::ExtJS::REST); __PACKAGE__->config({ ... }); 1; # set the Accept header to 'application/json' globally Ext.Ajax.defaultHeaders = { 'Accept': 'application/json' };
This controller will make CRUD operations with ExtJS dead simple. Using REST you can update, create, remove, read and list objects which are retrieved via DBIx::Class.
Local configuration:
__PACKAGE__->config({ ... });
Global configuration for all controllers which use CatalystX::Controller::ExtJS::REST:
MyApp->config( { CatalystX::Controller::ExtJS::REST => { key => value } } );
The method to call on the resultset to get an existing row object. This can be set to the name of a custom function function which is defined with the (custom) resultset class. It needs to take the primary key as first parameter.
Defaults to 'find'.
This resultset method is called on every request. This is useful if you want to restrict the resultset, e. g. only find objects which are associated to the current user.
Nothing is called if the specified method does not exist.
This defaults to extjs_rest_[controller namespace].
extjs_rest_[controller namespace]
A controller MyApp::Controller::User expects a resultset method extjs_rest_user.
MyApp::Controller::User
extjs_rest_user
To allow your form validation packages, etc, access to the catalyst context, a weakened reference of the context is copied into the form's stash.
$form->stash->{context};
This setting allows you to change the key name used in the form stash.
Default value: context
context
Defaults to root/forms
root/forms
Defaults to root/lists
root/lists
If set to a true value there will be no meta data send with lists.
Defaults to undef. That means the metaData hash will be send by default.
Defaults to DBIC
DBIC
Defaults to "default_resultset"
Defaults to "namespace" in Catalyst::Controller
Defaults to the plural form of "namespace". If this is the same as "namespace" list_ is prepended.
list_
This module is limited to HTML::FormFu as form processing engine, DBIx::Class as ORM and Catalyst as web application framework.
Considering you create controller like this:
package MyApp::Controller::User; use base 'CatalystX::Controller::ExtJS::REST'; 1;
Then you will want to create the following files:
root/ forms/ user.yml user_get.yml user_post.yml user_put.yml lists/ user.yml
Only root/forms/user.yml is required. All other files are optional. If ExtJS issues a GET request, this controller will first try to find the file root/forms/user_get.yml. If this file does not exist, it will fall back to the so called base file root/forms/user.yml.
root/forms/user.yml
root/forms/user_get.yml
This controller tries to guess the correct model and resultset. The model defaults to DBIC and the resultset is derived from the name of the controller. In this example we look for the resultset $c->model('DBIC::User').
$c->model('DBIC::User')
You can override these values in the form config files:
# root/forms/user.yml --- elements: - name: username - name: password - name: name - name: forename # root/forms/user.yml (exactly the same as the above) --- model_config: resultset: User schema: DBIC elements: - name: username - name: password - name: name - name: forename # root/forms/user_get.yml and friends --- load_config_file: root/forms/user.yml
Now you can fire up your Catalyst app and you should see two new chained actions:
Loaded Chained actions: ... | /users/... | /user/list | /user/... | /user/object
You can access http://localhost:3000/users to get a list of users, which can be used to feed an ExtJS grid. If you access this URL with your browser you'll get a HTML representation of all users. If you access using a XMLHttpRequest using ExtJS the returned value will be a valid JSON string. Listing objects is very flexible and can easily extended. Any more attributes you add to the url will result in a call to the corresponding resultset.
# http://localhost:3000/users/active/ $c->model('DBIC::Users')->active($c)->all;
As you can see, the Catalyst context object is passed as first parameter. You can even supply arguments to that method using a komma separated list:
# http://localhost:3000/users/active,arg1,arg2/ $c->model('DBIC::Users')->active($c, 'arg1', 'arg2')->all;
You can chain those method calls to any length. You cannot access resultset method which are inherited from DBIx::Class::ResultSet, except all. This is a security restriction because an attacker could call http://localhost:3000/users/delete which will lead to $c->model('DBIC::Users')->delete. This would remove all rows from DBIC::Users!
all
http://localhost:3000/users/delete
$c->model('DBIC::Users')->delete
DBIC::Users
To define a default resultset method which gets called every time the controller hits the result table, set:
__PACKAGE__->config({default_rs_method => 'restrict'});
This will lead to the following chain:
# http://localhost:3000/users/active,arg1,arg2/ $c->model('DBIC::Users')->restrict($c)->active($c, 'arg1', 'arg2')->all; # and even with GET, POST and PUT # http://localhost:3000/user/1234 $c->model('DBIC::Users')->restrict($c)->find(1234);
The default_rs_method defaults to the value of "default_rs_method". If it is not set by the configuration, this controller tries to call extjs_rest_$class (i.e. extjs_rest_user).
default_rs_method
extjs_rest_$class
To create, delete and modify user objects, simply POST, DELETE or PUT to the url /user. POST and DELETE require that you add the id to that url, e. g. /user/1234.
user
POST
DELETE
PUT
/user
/user/1234
This module handles your uploads. If there is an upload and the name of that field exists in you form config, the column is set to an IO::File object. You need to handle this on the model side because storing a filehandle will most likely fail.
There a modules out there which can help you with that. Have a look at DBIx::Class::InflateColumn::FS. DBIx::Class:InflateColumn::File will not work as this module expects a hash with the file handler and the file name set. But you can still overwrite "handle_uploads" to your needs.
As an upload field is a regular field it gets set twice. First the filename is set and $row->update is called. This is entirely handled by HTML::FormFu::Model::DBIC. After that "handle_uploads" is called which sets the value of a upload field to the corresponding IO::File object. Make sure you test for that, if you plan to inflate such a column.
$row->update
If you want to handle uploads yourself, overwrite "handle_uploads"
sub handle_uploads { my ($self, $c, $row) = @_; if(my $file = c->req->uploads->{upload}) { $file->copy_to('yourdestination/'.$filename); $row->upload($file->filename); } }
But this should to be part of the model actually.
Since you cannot upload files with an XMLHttpRequest ExtJS creates an iframe and issues a POST request in there. If you need to make a PUT request you have to tunnel the desired method using a hidden field, by using the params config option of Ext.form.Action.Submit or extraParams in Ext.Ajax.request. The name of that parameter has to be x-tunneled-method.
XMLHttpRequest
params
Ext.form.Action.Submit
extraParams
Ext.Ajax.request
x-tunneled-method
Make sure you do not include a file field in your GET form definition. It will cause a security error in your browser because it is not allowed set the value of a file field.
GET
Returns a new HTML::FormFu::ExtJS class, sets the model config options and the request type to Catalyst.
Catalyst
List Action which returns the data for a ExtJS grid.
Handles uploaded files by assigning the filehandle to the column accessor of the DBIC row object.
Returns true if the current request looks like a request from ExtJS and has multipart form data, so usually an upload. This requires that you add a x-requested-by parameter to your form which has the value ExtJS. This can be done either by adding a hidden form field, by using the params config option of ExtJS Ext.form.Action.Submit or extraParams in Ext.Ajax.request.
x-requested-by
ExtJS
Determines the default name of the resultset class from the Model / View or Controller class.
Returns the path in which form config files for grids will be searched.
Returns the path to the specific form config file for grids or the default form config file if the specfic one can not be found.
REST Action which returns works with single model entites.
REST Action to update a single model entity with a PUT request.
REST Action to create a single model entity with a POST request.
Internal method for REST Actions to handle the update of single model entity with PUT or POST requests.
This method is called before the form is being processed. To add or remove form elements dynamically, this would be the right place.
REST Action to get the data of a single model entity with a GET request.
REST Action to delete a single model entity with a DELETE request.
Returns the path to the specific form config file or the default form config file if the specfic one can not be found.
Returns the path in which form config files will be searched.
Returns the path to the default form config file.
These methods are private. Please don't overwrite those unless you know what you are doing.
Run this code before any action in this controller. It sets the ActionClass to CatalystX::Action::ExtJS::Deserialize. This ActionClass makes sure that no deserialization happens if the body's content is a file upload.
ActionClass
If the request contains a file upload field, extjs expects the json response to be serialized and returned in a document with the Content-type set to text/html.
Content-type
text/html
This accessor contains the configuration options for this controller. It is created by merging __PACKAGE__->config with the default values.
__PACKAGE__->config
Moritz Onken Mario Minati
This software is Copyright (c) 2009 by Moritz Onken.
This is free software, licensed under:
The (three-clause) BSD License
To install CatalystX::ExtJS, copy and paste the appropriate command in to your terminal.
cpanm
cpanm CatalystX::ExtJS
CPAN shell
perl -MCPAN -e shell install CatalystX::ExtJS
For more information on module installation, please visit the detailed CPAN module installation guide.