We now move onto the more interesting stuff of what really happens when a request is made and the objects that are responsible for building the response. The top level CiviCRM objects are:
- Form (CRM_Core_Form): CiviCRM forms are based on HTML_QuickForm. For drupal folks, this is more of an object representation of the much loved form api
- Wizard (CRM_Core_Controller): A set of CiviCRM Forms that collectively make up an action (Import Wizard, Mailing Wizard etc). For simplicty sake a one page Form is represented as a Wizard (CRM_Core_Controller_Simple)
- Page (CRM_Core_Page): A Page represents all html pages that are not of the above two types
- Selector (CRM_Core_Selector): A selector is a tabular/grid representation of the database data. Selectors are not high level objects, but are embedded in either a Page or a Form object.
Each of these objects offer a common set of services to all their derived classes. Some of the important functionality includes
- Ability to store/retrieve (set/get) variables in the session in a "namespace" scope. This allows forms/pages to "remember" stuff across requests and we make significant use of this across CiviCRM. This is the cause for a issue raised recently about performing the same operation across multiple tabs in the same browser (the dreaded "Cannot find value for ct") since both the tabs are sharing the same session and hence an operation in one tab messes up the session values for the other tab. We plan on implementing a fix for this in v1.7. Basically the session name now morphs into a combination of the "object name" plus a hidden form variable (similar to the drupal 'token' security fix for 4.7.4)
- Ability to embed itself in other objects. Thus a Form and a Selector can be embedded in a Page and vice versa. This works out quite nicely as we can build fairly complex structures on smaller structures and reuse a fair amount of code across the system.
- Ability to "render" the object in a given format using a template file. Our templating system and rendering engine will be described in detail in another blog post. The base class provides function to allow derived classes to expose "variables" to the template.
The page object is a fairly simple object and we'll conclude by looking at it in more detail. Examples of Pages include
- CiviCRM dashboard: CRM_Contact_Page_View_DashBoard
- The Contact View Page: CRM_Contact_Page_View_Basic
- The Access Control List (ACL) information Page: CRM_Admin_Page_Access
A page could assemble itself using one or more DB queries (using the BAO/DAO objects described in the previous blog post) like the dashboard or the view contact page. Alternatively a Page could be very simple and just be a jumping off point to other pages like the ACL information Page above.
All Pages inherit from CRM_Core_Page. CRM_Core_Page provides a "run" function which basically displays the page onto the screen as specified by the derived class. There is another class called CRM_Core_Page_Basic (which is derived from CRM_Core_Page). This class specializes in managing simple tables (civicrm_tag, civicrm_relationship_type etc) where most of the functionality can and should be automated and centralized (similar to the scaffolding mechanism of Ruby on Rails)
As evident from the above, we rely a lot on classes and objects. We also rely a fair amount on virtual functions and over-riding them when we dont like the default behavior. As always, questions and comments are more than welcome :)