![]() |
Xataface
2.0alpha2
Xataface Application Framework
|
So far we have created a module that has included:
None of these examples included any module-associated data. In this section we will extend the "hello_world" module to include a blog component. This will require us to introduce some database tables that are associated with the module.
The data structure for this blog will be very basic: a single table. We'll call this table: xf_blog_posts
It's definition is:
Create this table in your application's database.
In order for users to access the blog we need them to add a link to the xf_blog_posts table in their application. There are two ways to achieve this:
We're going to take the second approach here. We want our module to appear along with the links to all of the tables at the top of the page.
g2 module). This is because that module allows us to add links to the top left by way of the actions.ini files. The 1.0 look doesn't allow this for that part of the UI.
date_posted, last_modified, and posted_by fields get populated automatically.In order to configure our table, we need to define a fields.ini file. The normal location of a fields.ini file for a table would be at APPLICATION_PATH%/tables/xf_blog_posts/fields.ini but we want this configuration to be packaged with our module, so instead we'll place it at:
Our basic fields.ini file will contain:
hello_world.php): xf_blog_posts table is our hello world module so it will look for the "tables" directory with the fields.ini file etc.. inside our module's directory - at least when it is looking for config for the xf_blog_posts table.Now the new record form for the xf_blog_posts table should look like:
Notice that the date_posted, last_modified, and posted_by fields are now hidden. We've specified the date_posted and last_modified fields to be automatically populated with the appropriate timestamps (using the timestamp directive in the fields.ini file), but we still need to add some logic to populate the posted_by field. We will do this by implementing the DelegateClass::beforeInsert() method.
Our table delegate class will be located at:
The contents will start with:
Now if you insert a new post then look at the post in the database you should see the username of the currently logged in user reflected accurately.
So far we have completely ignored permissions. I.e. How do we decide which users can post blog posts or which blog posts they can view, edit, or delete. This can be a somewhat complex problem because when developing a module you often don't have any knowlege of how the user accounts and permissions will be managed in the application in which the module will be eventually installed.
If we were developing a blog application we would implement permissions simply by implementing a DelegateClass::getPermissions() or DelegateClass::getRoles() method where we would grant permissions based on factors such as the user's role, or who owns the post. In our case we have no knowledge of how the end application may define its roles so we can't reference them directly from a DelegateClass::getPermissions() method inside our module.
There are many strategies that we can employ for infusing our blog module with permissions. Some of these include:
posted_by field).For this tutorial we are going to implement a hybrid of option #1 and option #2 above. We will implement #1 as the default functionality, and we will allow the application developer to define its own permissions method (option #2) if they want a more complex structure.
For our default permissions system, we'll just have 3 levels of users, and we'll create corresponding roles for them in the module's permissions.ini file.
posted_by field.xf_blog_posts delegate class we add the following DelegateClass::getRoles() implementation: Now if you try out your application with different user accounts, logged in or no, you should notice that:
The default permissions that we set up seem logical, but one inescapable fact of life is that there is no such thing as one-size-fits-all. In order for this module to be truly useful to application developers, they need to be able to override the permissions to fit their own needs. We will allow them to do this by leveraging the DelegateClass::getDelegate() method to return an alternate delegate class at our discretion.
Our DelegateClass::getDelegate() method will check the application's tables/xf_blog_posts directory to see if they have defined their own delegate class. If so we'll return an instance of that object. Otherwise it just returns null to indicate that our delegate will remain in the authority.
xb_blog_posts_override. Make sure that you include this in your documentation so that application developers know how to extend your module.An example overriding delegate class might look like:
Before moving onto the final steps of packaging our module, there is one nagging thing that we should really polish up: The label for our table. The "New Record" button says "New xf_blog_post". This is really ugly. Let's change it to "Blog Posts" using the global label directive of the fields.ini file:
Ahh.. much better.
If we were to distribute our module at this point, we would need to include directions on how to build the xf_blog_posts MySQL table manually. What if we improve our module later on and want to distribute the changes? We would need to again direct our users to modify their xf_blog_posts table to correspond with our changes. This is inconvenient and error-prone. Thankfully, Xataface offers a better way with its packaging and versioning features.
version.txt that marks the version number of the module. This file follows the same format as the Xataface version.txt file: x.y.z is the human-readable version number (e.g. 1.3.1) and w is the ever-incrementing build number (e.g. 1256). Every time you release a new version you should increment this number so that Xataface can tell that the file system version of your module is newer than the version currently in the database.We'll start our module at version 0.1 (human-readable) and with a build number 1. So our version.txt file will look like:
Now that we have a means to marking the file system version of our module, let's create the installer.php file to tell Xataface what it has to do to bring the database inline with the current file system version. Our initial installer.php file will be located in the root of the module's directory (i.e. APPLICATION_PATH%/modules/hello_world/installer.php, and it will contain the following:
Things to notice:
modules_modulename_installer.update_1() will be executed by Xataface in the event that the database version of the module is lower than 1 and the file system version is greater than or equal to 1. This conveniently serves as an install script for your module since the first time your module is activated, the database version is effectively 0, so the update_1() method would be called to create the xf_blog_posts table.dataface__modules. If you need to clear the database version number or play with it for development purposes, you can modify the record corresponding to your module in this table.dataface__modules table. Then load the application in your browser again.If everything went as planned you should see no difference in your application. If you check the database, your xf_blog_posts table has been recreated and the dataface__modules table now has an entry for your module marking it with version 1:
Now that the module has a self-contained installer you should be able to package it up and distribute it. Installing the module in a different application is as simple as adding and entry to the [_modules] section of the conf.ini file. If you do distribute your module, make sure to include documentation so that people know how to use it. Also be sure to let the rest of us know about your module on the Xataface forum.
You can download the source for this module here.
1.8.1.2