How to Build an aFramework Module
Published Friday 16th of April 2010
As a follow up to "How aFramework Works" I thought I'd explain how to build your own modules for aFramework.
It's actually very simple. Of course you'll need to know the languages you'll be writing but I assume you do. The simplest modules consist of nothing but HTML.
A module in aFramework is just a specific part of the page, contained within its own div with a unique id.
That div is the sandbox for your module. In it you can do whatever you want. It's also possible to communicate with other modules but we'll get to that in a later article.
A module is located in your site's Modules/ directory within its own folder. The folder name defines your module's name.
The folder has to consist of at least either a template file (view) or PHP class (controller). It can consist of several views, a controller as well as CSS, JS and images.
In this example I'll build a simple Flickr module that displays some random images. We'll need 2 views, a controller, a CSS and a JS-file.
First, I create all the files we need.
- MySite/Modules/Flickr/
- MySite/Modules/Flickr/Flickr.tpl.php (the view)
- MySite/Modules/Flickr/Error.tpl.php (the 2nd view (in case there is an error))
- MySite/Modules/Flickr/FlickrModule.php (the controller)
- MySite/Modules/Flickr/Flickr.css
- MySite/Modules/Flickr/Flickr.js
I like to start with the view. That way I know what variables I'll need to assign from the controller.
The View(s)
The views in our case are nothing but HTML and some PHP loops. We'll need one view for displaying the images and one view to display in case there are any errors (like if Flickr's down).
aFramework automagically wraps our HTML inside a div with an id the same name as our module so you don't need to add a wrapper-div yourself.
The Images View
<h2>Random Flickr Images</h2>
<ul>
<?php foreach ($images as $image) { ?>
<li>
<a href="<?php echo $image['url']; ?>">
<img src="<?php echo $image['src']; ?>" alt="<?php echo escHTML($image['description']); ?>"/>
</a>
</li>
<?php } ?>
</ul>
The Error View
<h2>Error</h2>
<p>There was an error retrieving images from Flickr. Please try again later.</p>
That's it. Pretty straight forward huh? If you put this on a page now you'll get an empty list, our module controller needs to assign the variable $images to the view.
The Module Controller
After writing the view code we know we need an array called $images with fields called 'src', 'url' and 'description'. We'll put all our module code in a class named MySite_MyModuleModule.
The Module Skeleton
Here's the bare minimum a module class needs to consist of.
<?php
class MySite_FlickrModule {
public static $tplVars = array(); # Variables assigned to the view
public static $tplFile = true; # Which view to display (true = same as module name, false = no view, String (ie 'Error') = defined view)
public static function run () {
# Code here will run when the page loads
}
}
?>
We have $tplVars and $tplFile which we use to tell aFramework which template variables we want to assign to our template file and also which template file should be used. We also need the mandatory run()-method.
If your module is missing one (or more) of the three required pieces aFramework will complain.
Now let's fill the run()-method with the goodies we need to get our hands on those images.
I'll pretend aFramework has a Flickr Model so we'll use that to communicate with Flickr. You could probably find one in under a minute if you Google 'flickr api php class' or something.
<?php
class MySite_FlickrModule {
public static $tplVars = array();
public static $tplFile = true;
public static function run () {
if (!(self::$tplVars['images'] = Flickr::getRandomImages(8))) {
self::$tplFile = 'Error';
}
}
}
?>
See what I did there? It looks a bit messy like that so let's break it up.
if (!(self::$tplVars['images'] = Flickr::getRandomImages(8))) {
self::$tplFile = 'Error';
}
# Could (and maybe should:) be written as
$images = Flickr::getRandomImages(8);
if (!$images) {
self::$tplFile = 'Error';
}
else {
self::$tplVars['images'] = $images;
}
I try to call the Flickr-class' getRandomImages()-method and assign the return-value to self::$tplVars['images']. Remember, assigning something to the module's $tplVars-array will make it available in your view. Thus, assigning self::$tplVars['images'] = $arrayOfImages; will make $arrayOfImages available in your view under the name $images.
If Flickr::getRandomImages() returns false the if-statement will become true and I tell aFramework to use my module's "Error"-view instead. If you don't tell aFramework anything it will default to the view with the same name as your module. If you don't want to display any view, set self::$tplFile to false.
So that's it, we try to get some random images, if that fails we use the error-view instead.
That should be it for the basics. If you put that on a page now you should get 8 random flickr images. But let's style and script it a bit for fun :)
The CSS
The CSS you put in the module directory will be included in all styles so unless you know this is the look you want forever (well, it can of course be overwritten by a style) try not to be too specific in your styling.
/* Remember, aFramework automatically wraps your module in a containing div */
#flickr {
}
/* The heading (Random Flickr.. or Error) */
#flickr h2 {
}
/* The list of images
aFramework comes with support for CSS constants and every CSS file
you create has access to all of aframework's base constants. One of them
is "one-9th-boxes" which is a list of "boxes" (white border + shadow)
that are one ninth of 960px in width.
*/
#flickr ul = $one-9th-boxes;
/* Error message */
#flickr p {
}
/* This will be added with JS later on */
#flickr p a {
}
That's enough to make it look alright at least. If the module is much wider than 300px on your page you might wanna try $one-6th-boxes instead.
The Javascript / jQuery + Ajax
If you add a JS-file to your module's directory aFramework will always include it on the page as long as the module is used on any page on the site.
aFramework has a Javascript object (called "aFramework") with some functionality. The aFramework object also contains an array called "modules" where all the module JS code should be.
This way we namespace our code so that it doesn't collide with other module's code.
aFramework.modules.Flickr = {
run: function () {
// This code will run "onload"
}
};
Not entirely unfamiliar if you remember the PHP class structure from the module controller.
What I thought we'd do with the FlickrModule is provide a link which you can click to get some new images. Without JS you'd have to reload the window to do the same.
aFramework.modules.Flickr = {
run: function () {
this.reloadLink();
},
reloadLink: function () {
// Add a paragraph with a link and when you click it
$('<p><a href="#">Get some new images</a></p>').appendTo('#flickr').find('a').click(function () {
// Load the FlickrModule into the #flickr-div
$('#flickr').load(Router.urlForModule('Flickr'), function () {
// And then re-run the flickr-module (as it has been replaced)
aFramework.modules.Flickr.run();
});
return false;
});
}
};
Let's have a look at the reloadLink() function in detail:
$('<p><a href="#">Get some new images</a></p>').appendTo('#flickr').find('a')
First we create the HTML needed for the link and append it to the #flickr-element (our module div). We need to .find('a') because the $()-function would in this case return the <p>-element, not the <a>.
$('<p><a href="#">Get some new images</a></p>').appendTo('#flickr').find('a').click(function () {
return false;
});
We assign a click-event to the link that we return false from. This prevents the browser from following the link's normal behaviour and actually browse to the link's destination. You could (and maybe should:) use e.preventDefault() instead;
$('<p><a href="#">Get some new images</a></p>').appendTo('#flickr').find('a').click(function () {
// Load the FlickrModule into the #flickr-div
$('#flickr').load(Router.urlForModule('Flickr'), function () {
// And then re-run the flickr-module (as it has been replaced)
aFramework.modules.Flickr.run();
});
return false;
});
If you're not familiar with jQuery's load() function it basically loads the HTML found on the URL passed in as the first argument into the element it is run on.
Here we run it on our #flickr-div and pass in the URL to our module as the first argument. The Router-class is aFramework specific and you should try to use it as much as possible in order to make URL-changes as smooth as possible.
This is one reason why aFramework automatically wraps modules with divs. If the view contained a div we'd get two nested #flickr-elements on the page now.
After the new HTML has been loaded into the #flickr-div we run our own module code again. If we didn't the new #flickr-div wouldn't contain any "Get some new images"-link. You can try commenting the run()-bit out if you're curious.
Finally
As you can see an aFramework module is really nothing but HTML, CSS, JS and PHP for a certain part of the page collected under the same roof.
Try out the demo to see aFramework in action or head over to the bugtracker to find out what's in store for aFramework. Want to try it out locally? Go to the official site and download the zip.

Bookmark this Article