So, I thought for the demo, I'd actually build something that will show results. So, I'll make a quick app that uses ASRA to make a simple API. Essentially, a basic ASRA workflow involves:
1) config.ini to define connection to databases
2) creating or having a database
3) creating a controller class that defines methods which query the database
4) choosing via the url how you want to export the data
So, essentially, you set up your queries in a controller and then you have the data as xml, json, serialized arrays, var=value pairs or yaml (it's as easy as switching a string in the url).
To start, you'll use Subversion grab the code from my subversion repository. I'm going to use export because I want this to be versioned under the Typeoneerror repository as well.
#open up terminal and "cd" to my webroot (sites is just an alias to my apache root) sites ; cd Typeoneerror #export asra into "api" folder svn export http://svn.typeoneerrordev.com/asra/trunk api #open up new folder with textmate mate api
Step 1 : Configuration
First things first. Let's get our api ready to connect to our database. I'm gonna open up /app/config.ini and change the [default] settings to match my website's mysql info.
#obviously the user/pass will be different, I just want to show it here [default] database.adapter = mysqli database.params.host = localhost database.params.dbname = asra_test database.params.username = root database.params.password = root #define a different setting for a different domain #but inherit from default [default : typeoneerror.com] database.params.dbname = typeoneerror_live
Default gets used if no other database info is present. You can set database settings by domain. Meaning [typeoneerror.com] would get used if the app was on that server. You can also extend the default ini to override it by using a colon between the two [default : typeoneerror.com]. This just means that default will be used but any settings in this one will override the default (works similar to Zend's Zend_Config_Ini).
Note: Currently, the ini file doesn't use the same data for "www.typeoneerror.com" if you defined ["typeoneerror.com"] because the subdomains aren't wildcarded yet
Now that the database is ready to rock (hopefully), I'll point my browser over to the api folder in my webroot at /api. You should see some XML saying the app's name and version. If your database params are wrong, it doesn't matter quite yet as it won't connect unless you actively query.
Step 2 : The Database
Maybe you have a database already. In this example, we're going to do portfolio pieces with associated images. So, we've got two tables. You can create a database and run this (already has some simple associations):
CREATE TABLE `images` ( `id` int(10) unsigned NOT NULL auto_increment, `portfolio_id` int(10) unsigned NOT NULL, `source` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) TYPE=MyISAM AUTO_INCREMENT=4 ; INSERT INTO `images` VALUES (1, 1, 'image1a.jpg'); INSERT INTO `images` VALUES (2, 1, 'image1b.jpg'); INSERT INTO `images` VALUES (3, 2, 'image2a.jpg'); CREATE TABLE `portfolio` ( `id` int(10) unsigned NOT NULL auto_increment, `title` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) TYPE=MyISAM AUTO_INCREMENT=3 ; INSERT INTO `portfolio` VALUES (1, 'Piece 1'); INSERT INTO `portfolio` VALUES (2, 'Piece 2');
Step 3 : Creating a Controller Class
So we've got our database created and config'ed. Now we want to create a controller to view some data. First we want to list our portfolio pieces so lets create a controller called /app/controllers/portfolio.php - controllers always go in /app/controllers and they should reflect the name of the database table or "model" you are querying (that's not a rule, just a good idea :))
A basic Controller extends the ASRA Controller class so start with this:
/** /app/controllers/portfolio.php **/
class Portfolio extends Controller {
}
No need to worry about includes - they are already loaded. You can see that the name of the class is a camelized, underscore-less, uppercase version of our file name (e.g. a file called portfolio_images.php will have a class defined in it called PortfolioImages and would be accessed at the URL /api/portfolio_images).
Next up, define an index function and use the find method of the controller to run a simple query. We're also going to override the protected variable uses (without parameters, the find method selects all from whatever table you set uses to). This variable is there in case the table names are wacky and you want to still call the class "Portfolio" even though the table name is `pflio` or something.
class Portfolio extends Controller {
protected $uses = 'portfolio';
public function index() {
$this->find();
// -- I could also use $this->query("SELECT * FROM portfolio");
}
}
Now I'm going to point my browser to /api/portfolio. You can see that I use the same name as the file for my controller name ("portfolio"). If your database connection is set properly, you should see this:
<rsp>
<item>
<id>1</id>
<title>Piece 1</title>
</item>
<item>
<id>2</id>
<title>Piece 2</title>
</item>
</rsp>
index is a default function called when no method call is present (if we defined a method in the class called featured it would be accessed at /api/portfolio/featured). What we just created above is the same as /api/portfolio/index.
Step 4 : Views
So, you've just created an XML export of your portfolio data. Want it in JSON instead? Prepend the url with "json" - now your URL is /api/json/portfolio:
[{"id":"1","title":"Piece 1"},{"id":"2","title":"Piece 2"}]
Ah, crap, my flash AS1 guy hasn't learned AS3 yet...can I get that as var/value pairs? Sure ... /api/vars/portfolio:
count=2&results0_id=1&results0_title=Piece 1&results1_id=2&results1_title=Piece 2
So, now you can see where ASRA can be useful in creating a list export in up to 5 formats in 6 lines of code :) I use it a lot in flash applications.
How about something a bit more complex? Maybe I want to get those associated images into my results. Let's continue...
class Portfolio extends Controller {
protected $uses = 'portfolio';
public function index() {
$this->find(array(
'subquery' => array (
'query' => "SELECT * FROM images WHERE portfolio_id = %s",
'key' => 'images'
)
));
}
}
Now we've added four more lines of code. Hit the url at /api/xml/portfolio to see what the results are. You can see that the associated images for each portfolio item have been selected into a sub-node called images. Again, you can use the URL to switch between xml, json, vars, serialized arrays and yaml (experimental).
All we are doing above is passing an array to the find method. One of the accepted params is "subquery" which defines keys of "query" (the subquery to run on the find() query results) and "key" (what to select the subquery results into). Basically the id of each portfolio gets passed in to the subquery['query'] string and replaces the "%s".
A Single Piece
Perhaps now I want another export that just gives me data about one single portfolio piece. Create a new method:
public function id($id) {
$this->single($id);
}
Navigating to /api/portfolio/id/1 would export the data for the portfolio piece with an id of 1. single is a special method that takes an integer and basically just selects by id. There are other methods like this in the ASRA package.
More
I'll be putting up the reference and API online soon, but you can see the API in progress in /trunk/help/documentation.txt (also some more examples there in the reference). There's also a TextMate bundle in /trunk/help/ASRA.tmbundle. As always, feel free to contact me with any comments/questions/feedback!