Google Analytics as a Page Object

The complexity of the underlying HTML templates is largely a matter of style. For someone who prefers only the document information and a series of component identifiers, there is a GoogleAnalytics object in the Istarel Workshop Application Framework.

Listing: page/html/public.html

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
	"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">

{head}
{body}
{google}

This approach requires two things: a constant defining the Google account number and a method that return a GoogleAnalytics object.

Partial Listing: rsrc/ApplicationDelegate.php

<?php

class ApplicationDelegate
{
    const GOOGLE_ACCOUNT_NUMBER = 'UA-7285492-1';
    
    ...
}

?>

The constant can instead be defined as a global constant called GOOGLE_ACCOUNT_NUMBER. (I personally prefer class-based constants.) Each Page object has the ability to replace component identifiers with objects or text, using a method name constructed from the name of the component identifier.

Partial Listing: rsrc/page/PublicPage.php

<?php

class PublicPage extends XHTML11Page
{
    ...
    
    function expectedComponents()
    {
        return array('head', 'body', 'google');
    }
    
    function replaceGoogle()
    {
        return GoogleAnalytics::googleCode();
    }
}

?>

Deploy an Application to the Remote Server

With Git prepared on both the local workstation and on the remote server, I can now deploy my application.

Defining a Remote Repository

In order to deploy an application from my development workstation to istarelworkshop.com, I need to configure the application repository on my workstation such that is can communicate with the repository on the production server.

Here is the current configuration for the Istarel Workshop application:

markf$ cd ~/Sites/iw
markf$ cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true

Now I configure this repository with user and path information on the remote Git repository. (Note that the 1158 in the remote declaration is the port, for cases where ssh is not listening to its standard port 22.)

markf$ git remote add istarelworkshop 
       ssh://git@www.istarelworkshop.com:1158/var/git/iw
markf$ cat .git/config

If I look at the configuration for the local repository now, I see the definition for "istarelworkshop".

markf$ cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
[remote "istarelworkshop"]
	url = ssh://git@www.istarelworkshop.com:1158/var/git/iw
	fetch = +refs/heads/*:refs/remotes/istarelworkshop/*

Cloning a Bare Repository

With the description of the remote repository done, I can now deploy the application.

markf$ git push istarelworkshop master

I am not done yet, though. The repositorities I set up on istarelworkshop.com are bare repositories; that is, they contain all the branches and history but are not a working repository. I need to clone that bare repository to the appropriate location on istarelworkshop.com. In an earlier article, I showed how to set up Apache2 virtual hosts. The first such host has its base directory at /var/www/istarelworkshop.com. I need to take the bare repository and clone it to that location.

markf$ ssh markf@www.istarelworkshop.com
iwuser$ cd /var/www
iwuser$ sudo git clone /var/git/iw istarelworkshop.com
iwuser$ sudo chown -R www-data:www-data istarelworkshop.com

Configure the Remote Application

Back when I set up the local repository, I made sure that certain key configuration files were not part of the repository. This is important because every server is different, and the key paths and identities used on my local workstation are quite different from the Ubuntu VPS I deployed. So, I need to create those configuration files on istarelworkshop.com.

iwuser$ sudo su - www-data
$ bash
www-data$ cd /var/www/istarelworkshop.com
www-data$ mkdir conf && cd conf
www-data$ vi ApplicationConstants.php

Listing: /var/www/istarelworkshop.com/conf/ApplicationConstants.php

<?php

# Fundamental Application Mode
define('IN_PRODUCTION', 1);             # 0 = Development, 1 = Production

# Application Directories
define('APPL_ROOT_DIR', '/');
define('APPL_RSRC_DIR', '/var/www/istarelworkshop.com/rsrc/');

# Istarel Workshop Frameworks Directory
if (! defined('FRAMEWORK_DIR'))
   define('FRAMEWORK_DIR', '/var/www/istarelworkshop.com/fw/');

# Supplemental Directories Required by Application Frameworks Classes
define('APPL_IMG_DIR', APPL_ROOT_DIR . 'img/');
define('APPL_LIB_DIR', APPL_ROOT_DIR . 'lib/');

# Define the Application Database attributes
define('DB_TYPE',       'pgsql');
define('DB_HOST',       'localhost');
define('DB_NAME',       'iw');
define('DB_USERNAME',   'iwdb');
define('DB_PASSWORD',   'secret');

?>

The application framework uses Apache's mod_rewrite system to power the Front Controller. I need to create an .htaccess file at the root of the repository to ensure that proper routing happens.

Listing: /var/www/istarelworkshop.com/.htaccess

RewriteEngine On
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

Fluent Interfaces

A term first introduced by Martin Fowler and Eric Evans, a fluent interface is one in which a class is self-referencial wherever possible, resulting in cleaner code. Wherever possible, the Istarel Workshop Application Framework utilizes fluent interfaces.

Making your own code fluent can be astonishingly easy: simply return the current object in any method where you are modifying the state or behavior of the object.

Partial Listing: /bnr/rsrc/model/default/DefaultStudent.php

<?php

class DefaultStudent extends ORMObject
{
    ...

    function setFirstName($first_name)
    {
        $this->setValueForProperty('first_name', $first_name);
        return $this;
    }

    ...
}


?>

Using that technique, I can now write very elegant code. Notice that in the example below, I made a design choice to visually separate the save() method.

Partial Listing: /path/to/imaginary/script

<?php

$student = new Student;
$student->setFirstName('Mark')
        ->setLastName('Fenoglio')
        ->setCompany('Istarel Workshop');
$student->save();

?>