InterJinn

Development Framework For PHP

Components

All components extend the JinnBaseComponent class or some sub class of it. This provides the framework for the component to be bound to a module, access services, and access other components bound to the same module. Continuing with our news example from the introductory section we might have the following:

Example:
class ProjectNewsFeedRetrieve extends JinnBaseComponent
{
    //                       
    // Set the default property path.
    //                               
    var $propertyPath = 'Project/newsFeed/retrieve';
function ProjectNewsFeed() { // // Constructor stuff. // }
function execute() { // // Code to be run after components have been bound to // a module. // }
// // Any methods pertinent to the component. // }

From the above example there are a couple of things to consider. The first is the instance variable, $propertyPath. This provides a convenient and powerful feature for components whereby object properties can be externally defined for a component via the Property System. Properties are very important and just like components can be interchanged for modules, the property path can be switched for a component which provides a great deal of flexibility and separation.

The other thing to consider is the execute() method. Generally speaking most initialization code is performed within the constructor. This also is true of components provided that no attempts are made to access other components in the module. The reason for this is that the creation of the component occurs before the binding of the component to the module. However, once the component has been bound, then its execute() method is called. At this stage it is safe to make requests for other components and to use their methods and member variables.

The render component

Given that our example component above has real code in it to perform the real job of retrieving the news content from some feed. We don't currently do anything with it. The example above would be an example of a data or logic component. There is one other type of component -- the render component. The render component has one major difference from the other components. The render component may have it's render() method called from within the page by calling the render() method of the module. This will occur after the module has been declared and allows for all module initializations to occur at the top of the document. Given this arrangement all output occurs after any initialization, data retrieval, or logic has been performed. This generally works out great for error handling since errors will be detected before any content is output, and can thus be handled gracefully. An example of a render component follows:

Example:
class ProjectNewsFeedDisplay extends JinnBaseComponent
{
    //                       
    // Set the default property path.
    //                               
    var $propertyPath = 'Project/newsFeed/display';
function ProjectNewsFeed() { // // Constructor stuff. // }
function execute() { // // Code to be run after components have been bound to // a module. // }
function render( $selector=null ) { // // Code to run to generate output to the browser. // }
// // Any methods pertinent to the component. // }

So far so simple, but what's that $selector parameter to the render() method? The purpose of the parameter is to provide a means for accessing parts of the rendered output. Perhaps the news feed has a title, a copyright or some other information that can be partitioned. When accessing the component from your page you can optionally provide a selector to the render method which it can use to select pieces of the output.

Creating a module

Now that we have two components it is a good time to demonstrate where the module aspect fits in. As stated earlier, a module is really a description of how components are bound together. The following example illustrates our two components being bound together in a module declaration:

Example:
<script language="php">
$newsFeedModule = $dl->do->load( array
(
    'logic' => array
    (
        array
        (
            'name' => 'newsFeed',
            'source' => '//Project/newsFeed/retrieve.inc',
            'properties' => array
            (
                //
                // Overriden properties.
                //
                array
                (
                    'group' => 'urls',
                    'name'  => 'sourceUrl',
                    'value' => 'http://www.somegreatnews.cmx',
                ),
            ),
        ),
    ),
    'render' => array
    (
        array
        (
            'source' => '//Project/newsFeed/display.inc',
        ),
    ),
) );
</script>

The above example binds an instance of the ProjectNewsFeedRetrieve class together with an instance of the ProjectNewsFeedDisplay class. From the declaration we see that a module is described by a series of arrays with each array denoting a component of the desired module. This looks somewhat heavy to work with and to some degree it is; however, if you are using InterJinn in conjunction with the built in TemplateJinn system then you would be able to use the following to describe the module:

Example:
<jinn:module name="newsFeed" noRender="true">
    <jinn:component                        
        type="logic"
        name="newsFeed"
        source="//Project/newsFeed/retrieve.inc"
    />                            
    <jinn:component
        type="render"
        source="//Project/newsFeed/display.inc"
    />                
</jinn:module>

You will find that field names (the keys) of the PHP code declaration style are identical to the attributes defined for the <jinn:module/> tag when using the TemplateJinn system.

The first thing probably on your mind is "What the heck is $dl?". This is one of the few core objects that the InterJinn engine loads by default. $dl is short for Dynamic Loader. As you may guess from the name it is responsible for dynamically loading any modules, components. or services on an as needed basis. This has the advantage of providing simple access to powerful libraries but only having their code loaded when the code is actually needed. Thus if a certain conditional is never met, the overhead of loading the library will never be encountered.

If you have not yet read about it, then the use of ->do may have you wondering about its purpose. The ->do resolution is necessary because by default all objects and services when retrieved are returned with a wrapper layer. This is to bypass an issue in PHP where functions and methods return copies of objects instead of a reference. This is dangerous when singleton objects are being manipulated that internally keep track of such things as session data or other such single instance data. At any rate, this technique may very well be deprecated (although compatability will be retained) with the release of PHP 5 which according to documentation will always return object references.

Finally we can get to describing the actual structure of the declaration. The declaration is fulfilled by a single parameter to the load method of the dynamic loader. This parameter is an array of component type declarations where the key of the component type declaration is one of data, logic, or render. Within each type declaration is an array declaring all of the constituent components of that type (it is not unreasonable to have multiple data retrieval or processor components). The following illustrates the generic structure:

Example:
Load:
Data: Data Component 1 Data Component 2 ...
Logic: Logic Component 1 Logic Component 2 ...
Render: Render Component 1 Render Component 2 ...

There are several fields available for describing a component. Only the source field is mandatory and the rest are optional (though some restrictions may apply). A description of each field follows.

Attribute - name

Set this to the identifier that you would like to use in components to retrieve other components that have been bound to the module. For instance in a render component it is common to retrieve the data or logic component for retrieving and processing any data before it is output.

Example:
//
// Obtain the newsFeed data object.
//
$loader = $this->getObject( 'newsFeed' );
// // Request the news entries from the data loader component. // $newsEntries = $loader->do->retrieveNews();

The above example also illustrates the fact that if you are going to create multiple interchangeable components then they must conform to some kind of interface. This is not unlike what is done for the Java programming language with one major difference, the interface is implicit and not defined explicitly except (hopefully) by documentation.

Attribute - source

Set the value of this attribute to the location of the source file containing the component class definition. If the fileName path begins with /// then the path will be considered relative to the coreRoot path that was set in the project configuration. If instead it begins with // then the path will be considered relative to the codeRoot path that was set in the project configuration. If the path is preceded by / then the component file will be retrieved relative to the root of the filesystem. Last, if none of the former cases are true, then the path will be treated as though it had been prefixed with //. You should make sure that read permission is set for the appropriate owners at this location.

Attribute - class

Set this to the name of the class for which a component object should be instantiated. For instance in our example we would use ProjectNewsFeedDisplay for the render component. This parameter is optional IF the source path follows a specific convention. The convention requires that the name of the class be the camel notation markup of the concatenation of the path parts. For instance the path //project/newsFeed/display.inc would result in the class name of ProjectNewsFeedDisplay which is exactly what we used for the example and subsequently why we can omit the class attribute. Note that if you use an absolute path then you lose out on a lot of flexibility and portability of your code since the class name would need to be the same as the ALL the parts of the absolute path, regardless of whether it resolves to a sub-path of the codeRoot.

Attribute - propertyPath

Whether you are using the file-based or database property system, the value of this attribute overrides the default propertyPath that may have been defined for the component. The advantage of doing this is to create exceptional instances of a component that inherits all of the normal property values and for which some have been set differently. For an in depth discussion of the property system please view the Property System Documentation.

Attribute - properties

As well as the ability to assign a property set to a component via the propertyPath attribute, you may also declare individual properties and their values using the properties attribute. Individually defined properties are added to the component after the propertyPath is evaluated, and so this also serves as a mechanism for overriding properties values in the component's declaration.

Example:
'properties' => array
(
    array
    (
        'group' => 'urls',
        'name'  => 'sourceUrl',
        'value' => 'http://www.somegreatnews.cmx/feed.xml',
    ),                    
),
In the above example a property with group urls and name sourceUrl has been assigned the given value. This property might be accessed in the component as follows:
Example:
$xmlFeed = file( $this->urls['sourceUrl'] );
Note that the group entry is optional and if it had been omitted then we would have the following to retrieve the XML feed instead:
Example:
$xmlFeed = file( $this->sourceUrl );

What next?

We have only clipped the tip of the iceberg so far. As powerful as modules are, they represent only a small part of the InterJinn framework. Soon you will learn how to create services, transparently connect to user profile information, session information, forms, and how to work with multiple databases in a clear and concise fashion.