ATiM's developer introduction
This guides assumes you have knowledge of web development and PHP.
ATiM is based on CakePHP. CakePHP is a framework for PHP working under the MVC (Model/View/Controller) concept. Should you need it, CakePHP documentation is available here.
Contents |
MVC
It's important for you to grasp the basics of MVC. You can start by reading its wiki page.
Apache's Mod rewrite
CakePHP works with apache's mod rewrite. Basically, it means that your URLs will not point directly to a file. If we take an ATiM link as example,
- http://[...]/atim/clinicalannotation/participants/profile/1
- This link is not trying to access 1.php nor 1.html under the profile directory. Actually, it's accessing the profile function under /atim/app/plugins/clinicalannotation/controllers/participants_controller.php. How does it work? In some ATiM's directory, you'll notice some .htaccess files. These files change the targeted URL based on some rules. It allows the application to use clean URLs.
The links construction is usually path_to_atim/plugin/controller/function/[param 1[/param 2[/param n...]]].
CakePHP Conventions
CakePHP MVC works with convention so that you need less configuration. The convention requires you to name your files, controllers, models and database tables following certain patterns to have them working out of the box. You can learn more on these conventions here.
The music band tutorial
This tutorial will show you how to create a module to save participants favourite bands into ATiM. It's assumed that you have at least navigated a bit into ATiM. If you don't know already, participants are the core element of the clinical annotation module.
Create the database table
The first thing you need to do is to create the database table that will store that information. The only thing we'll want to record here is the band's name. Of course, we'll also need to save the participant_id to link the record to him. So, we have the following creation script <source lang="mysql"> CREATE TABLE participant_bands(
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `band_name` VARCHAR(50) NOT NULL DEFAULT , `participant_id` INT NOT NULL, `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `created_by` int(10) unsigned NOT NULL, `modified` datetime DEFAULT NULL, `modified_by` int(10) unsigned NOT NULL, `deleted` tinyint(3) unsigned NOT NULL DEFAULT '0', `deleted_date` datetime DEFAULT NULL, FOREIGN KEY (`participant_id`) REFERENCES `participants`(`id`)
)Engine=InnoDb;
CREATE TABLE participant_bands_revs(
`id` INT UNSIGNED NOT NULL, `band_name` VARCHAR(50) NOT NULL DEFAULT , `participant_id` INT NOT NULL, `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `created_by` int(10) unsigned NOT NULL, `modified` datetime DEFAULT NULL, `modified_by` int(10) unsigned NOT NULL, `deleted` tinyint(3) unsigned NOT NULL DEFAULT '0', `deleted_date` datetime DEFAULT NULL, `version_id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `version_created` datetime NOT NULL
)Engine=InnoDb; </source>
- Why so many fields?
- Through CakePHP, ATiM tracks data changes (audit trails). Thus, the created, created_by, modified, modified_by, deleted and deleted_by fields should be present in every table that record user data. They are automatically put to use by the framework.
- Why two tables?
- Then again it's for audit trails. The framework will save every version of a record into the _revs table. It's important name the second table with the same name as the first and to add "_revs" as a suffix. You'll also notice that the primary key and auto increment have changed and that version_id and version_created fields appeared. They are all required. Note that if you fail to meet the audit trail requirement, the worse case scenario is that audit trailing doesn't work for your model.
Note: For now, assume the id field is mandatory.
We need to create 4 files: The controller, the model, the add and index view. First, the controller will be located at atim/app/plugins/clinicalannotation/controllers/participant_bands_controller.php. The template for that file is <source lang=php> class ParticipantBandsController extends ClinicalannotationAppController { function index($participant_id){
}
function add($participant_id){
} } </source>
Then the model will be at /atim/app/plugins/clinicalannotation/models/participant_band.php <source lang=php> class ParticipantBand extends ClinicalannotationAppModel {
} </source>
The different views will be at /atim/app/plugins/clinicalannotation/views/participant_bands/{add.ctp|detail.ctp|index.ctp} You can leave those files empty for now. Notice the extension: .ctp rather than .php.
Now we need to create a menu entry to gain access to our module. <source lang=sql> INSERT INTO `atim_new`.`menus` (`id` ,`parent_id` ,`is_root` ,`display_order` ,`language_title` ,`language_description` ,`use_link` ,`use_params` ,`use_summary` ,`flag_active` ,`created` ,`created_by` ,`modified` ,`modified_by`) VALUES ('clin_CAN_1_14', 'clin_CAN_1', '0', '0', 'music bands', NULL , '/clinicalannotation/participant_bands/index/%%Participant.id%%', , , '1', '0000-00-00 00:00:00', , '0000-00-00 00:00:00', ); </source> For more info about menus, see the Menu page.
Now, reach any participant profile and put the mouse on the first menu tab. You shouls see your new menu at the top. However, it's preceded by a yellow warning sign. That icons means you do not have permission to access that module. Why? Because it's a brand new module and the permission manager doesn't know about it. To resolve the situation you must logout of ATiM and truncate the acos table. Then, refresh the page. Permissions will be rebuilt (that may take a short while). Log back and head over a participant and your menu option should now be available. Note that if debug is on, it might be surrounded by a span tag. That's because your menu name hasn't been successfully translated. This is normal at this point.
Create the structure
So your menu option directs you to the index function. Currently, it's a blank page. ATiM displays almost everything with structures. So what we need to do now is create that structure. If you haven't downloaded it yet, checkout atimTools from SVN into your web space. (https://ctrnet.svn.cvsdude.com/tools) Once done, open common/config.php and enter your configuration. Then through the browser access atimTools/index.html and select "Form Builder".
Form Builder
- At the top, select your database schema. (If some valid schema is already loaded, the schema selection box might be at the bottom.)
- In the center view, select the "Tables" tab and then select your table (participant_bands).
- In the top panel, the "Auto build" tab should already be selected. If not, select it.
- Type an alias for your structure. That can be anything. For our example, we'll use "participant_band_struct".
- In the right pane of the center view, click on "band_name". That should automatically fill some fields of the top view.
- You need to specify the plugin and model. Plugin = Clinicalannotation and model = ParticipantBand
- You need to specify the structure value domain, that is the possibles values to use. This is usefull for dropdowns and checkboxes, thus not for us. Type NULL.
- You need to specify the display column and order. Put 1 and 1.
- You need to specify in which form you want your field to appear. Click on the checkboxes bellow add and index.
- The line is now complete. Click on "Add row".
- The structure is now complete. Click on "Generate SQL".
- The SQL you need to run to build that structure is printed in the bottom view.
- Run the generated SQL.
Congratulations! You've just built a (very simple) structure! Note that most of the time you'll want to save the SQL into a script file so that you can easily rebuild your structure at some later point.
Create the index page
Our index will list all bands of our participant. Index can vary depending on the plugin. Most of the time it will either be a list or a search.
ParticipantBandController index
Here is a code sample <source lang=php> function index($participant_id){ $this->set( 'atim_menu_variables', array('Participant.id'=>$participant_id) ); $this->data = $this->ParticipantBand->find('all', array('conditions' => array('ParticipantBand.participant_id' => $participant_id))); $this->Structures->set("participant_band_struct"); } </source>
- $this->set(name, value)
- This is made to assign a value to a variable that we will use in the view. Here we assign an array with a key value pair to the variable "atim_menu_variable".
- $this->data
- This is the data that we want to display. In pages with more than one structure, $this->set can be used and the data to use for each structure must be specified.
- $this->Structures->set(structure_name[, variable_name])
- This is the structure to use for our view. If the variable_name is not defined, atim_structure will be used by default. Here, our structure name is "participant_band_struct", just as we did in the form builder.
ParticipantBand view index
Here is a code sample <source lang=php> $links = array("bottom" => array("add" => "/clinicalannotation/participant_bands/add/".$atim_menu_variables['Participant.id'])); $structures->build($atim_structure, array("type" => "index", "settings" => array("pagination" => false), "links" => $links)); </source> $structures->build is the central command of ATiM's views. It builds structures! It takes a lot of parameters into account. Here, we have defined our structure type as "index", we've disabled pagination and given a link array with an "add" button as the only link. You may notice the use of $atim_menu_variables that was previously defined in the controller.
For more informations on $structures->buid, see what are the settings/options available when building a form structure? and forms builder
Create the add page
The add page will simply allow users to provide a textual name for a band.
ParticipantBandController add
Here is a code sample <source lang=php> $this->set( 'atim_menu_variables', array('Participant.id'=>$participant_id) ); $this->Structures->set("participant_band_struct"); if(!empty($this->data)){ $this->data['ParticipantBand']['participant_id'] = $participant_id; if($this->ParticipantBand->save($this->data)){ $this->atimFlash("your data has been saved", "/clinicalannotation/participant_bands/index/".$participant_id); } } </source> You should already be familiar with the two first lines. Then comes the if clause. We enter that block if data was submitted. The first thing we do then is add the participant id to our data. Remember that participant_id field in our database table? The user is certainly not specifying it by himself so prior to saving we need to add it to the data. Then we call save on our current model with our data. That's in a if clause. Save validates the data so should validation fails false would be returned and we would not proceed to $this->atimFlash. Validations errors are automatically displayed at the top of the form. Since our structure had no validation, this example should always return true and proceed to atimFlash. atimFlash is a function that will proceed to a page and display a message at the top. If you are in debug mode, you will get an intermediary page where you can see all SQL that was executed.
ParticipantBand view add
Here is a code sample <source lang=php> $links = array( "top" => "/clinicalannotation/participant_bands/add/".$atim_menu_variables['Participant.id'], "bottom" => array("cancel" => "/clinicalannotation/participant_bands/index/".$atim_menu_variables['Participant.id'])); $structures->build($atim_structure, array("type" => "add", "links" => $links)); </source> We still have the links array. There is now a "top" key. This determines where the submit button will send the data. Note that without it, even if you specify "add" a a structure type, the structure builder will ignore it and render it as a detail type.
Try it out!
Everything should work now! Try to add some bands. Then go to your database and look at the result. Verify that both participant_bands and participant_bands_revs have records. If not, make sure you have the necessary fields.
Expanding on the example
What can you do now?
- Adding data is great, but you'll probably want a way to edit it. Try doing it by yourself! Here are some quick tips
- If you create a new controller function, you'll need to rebuild the permissions
- Your structure type will be edit
- You will need the ParticipantBand id to fetch your data
- Add more fields. Try to add some select fields (maybe for music genre?) to get used to structure_value_domains. It will also let you try a bit more the form builder.
- You might also want to be able to delete some entries. Try building a working delete button.
Remember that ATiM is full of working functions that can be taken as examples. Feel free to browse them. Participant's contacts is a relatively easy to understand.
Suggested readings
Know that you know the basis of ATiM, you'll probably want to do more!
- Validation
- We want to store valid data. Learn more on how to validate it! See How to set up validation on form fields. Also, keep in mind that some basics validations are made on date and number fields.
- Master/detail concept
- The master detail concept allows to group some similar items (aliquots all have a barcode) under the same table while allowing their differences (tubes have a volume, tissues blocks have a type), to be recorded into various details tables. See Developer_Documentation/inventory/#General_use_of_Master.2C_Detail_and_Control_Table. Reading the entire page will also give you a good knowledge of how the inventory system works in ATiM. (Sample, derivative and aliquots)
- Summaries
- In the top right corner of forms, there is a "Summary" label. To have it working, see How to set up summary information for any menu item.
- Hooks
- Streamlined pages, like forms, need to make some calls to hooks to allow banks to easily customize their code. See How to develop hooks compliant code.
Of course, you can also always refer to the developers' guides index.