ZXAF Development notes

ZXAF Development notes admin 23 September, 2009 - 21:57

ZXAF is really the culmination of a lot of individual techniques that I've used in many different developments.

So, I decided to pull of these together into a project, release it as opensource, and continue developing it with a view to doing everything the right way. It is a development without a timescale or a budget, and its nice to be able to spend time on pure development.

Basic Class Structure

Basic Class Structure admin 22 November, 2009 - 13:48

ZXAF is built from a few key components (classes/objects) and largely follows
convention over configuration and DRY.

The system core classes all resides in the exec directory. The modules are as
follows:

auditlog.php Provides audit log messages together with a recipeint that will log
all of these to the database
config.php Responsible for loading the correct site based config
dbentity.php Core of the database access, provides DbEntity and DbIterator
classes
emesary.php The interobject messaging classes. Understanding this fully is
crucial to working with ZXAF
refmon.php Security and access control following the
reference
monitor
paradigm
sysident.php System identifications for use by the messaging
system.php System glue classes - provides most of the biasic objects and
functions, connects to the database, provides session management etc.
user.php User entity - provides layer between the database table and the
higher level CBF user object.

In terms of the classes the first to understand is those related to the
database, namely DbEntity and DbIterator.

DbEntity

The DbEntity usually maps an object to a database table, providing INSERT,
UPDATE, DELETE and SELECT functions. Within the constructor the keyfield must be
defined, after which point the load(id) method can be used to
get a record from the table. If a table has multiple keys then the method of
loading is subtly different, in that you must set the fields using the
set_
methods and then call load_from_fields(), at
which point if successful true will be returned and the entity will contain the
contents of the first record that matches the fields specified. NOTE
this really works best when the fields are part of a unique key.

Example DbEntity

The following is a complete implementation of a database table.

In the constructor, it is necessary to call the parent constructor to define
the table name and the keyfield. If the table_autoid is set
then the keyfield will be assumed to be an auto_increment.

If required the set_auto_date_fields can be used to
automatically set the two defined fields to automatically contain the creation
and last update date/time for each record.

In the sample below the contstructor takes an optional key
parameter, which if present will request that the record is loaded. If this
fails then an error will be raised.

class DbTransaction extends DbEntity
{
    function __construct($key=null)
    {
        parent::__construct("transaction","id");
        $this->table_autoid = true;

        $this->set_auto_date_fields('created','modified');

        if (!$this->init_done)
            $this->init();
        if ($key !== null)
        {
            if(!$this->load($key))
                cbf_error("Failed to load Transaction id: $key ");
        }
    }

    function create()
    {
        if(!parent::create())
            return false;

        return true;
    }

    function get_id()             { return $this->get_field('id'); } 
    function set_id($v)           { return $this->set_field('id',$v); }
    function get_created()        { return $this->get_field('created'); } 
    function set_created($v)      { return $this->set_field('created',$v); }
    function get_modified()       { return $this->get_field('modified'); } 
    function set_modified($v)     { return $this->set_field('modified',$v); }
    function get_user()           { return $this->get_field('user'); } 
    function set_user($v)         { return $this->set_field('user',$v); }
    function get_item()           { return $this->get_field('item'); } 
    function set_item($v)         { return $this->set_field('item',$v); }
    function get_quantity()       { return $this->get_field('quantity'); } 
    function set_quantity($v)     { return $this->set_field('quantity',$v); }
    function get_amount()         { return $this->get_field('amount'); } 
    function set_amount($v)       { return $this->set_field('amount',$v); }
    function get_type()           { return $this->get_field('type'); } 
    function set_type($v)         { return $this->set_field('type',$v); }
    function get_paid_by()        { return $this->get_field('paid_by'); } 
    function set_paid_by($v)      { return $this->set_field('paid_by',$v); }
    function get_reference()      { return $this->get_field('reference'); } 
    function set_reference($v)    { return $this->set_field('reference',$v); }
    function get_reference_email() { return $this->get_field('reference_email'); } 
    function set_reference_email($v) { return $this->set_field('reference_email',$v); }

    static function begin($where="")
    {
        return new DbTransactionIterator("SELECT * from `transaction` $where");
    }

};//class Transaction

CBF modules

Typically the DB modules as indicated above are
autogenerated by a tool, and it is by convention in the cbf/
directory that customisations to the basic database entities are performed to
provide the model.

require_once("db/transaction.php");

class CbfTransaction extends DbTransaction
{
    function write()
    {
        $this->mark_as_unsaved('total');
        return parent::write();
    }

    function initialise_validation()
    {
        $this->set_validate_field('amount',new ValidateEntityPositiveInteger());
    }

    function make_load_query($record_id)
    {
        $q = "select *,transaction.quantity*transaction.amount as total from ".$this->table." where ".$this->id_field."='$record_id'";
        $q .= $this->extra_sql_load;
        return $q;
    }
}

DbIterator

The DbIterator is used to locate one or more matching
fields. Generally it is constructed with a SELECT statement.

An example of usage is below. The $dbi variable is the
iterator, which is a container that allows moving forwards through a result set
(any other movement is not supported). if the begin method
returns true the container is valid and contains elements.

To access the DbEntity that is contained within the
DbIterator
use.the current() method, which will return
an object of the type expected (as defined in the constructor of the
DbIterator
.

 $dbi = new DbTransactionIterator("SELECT * FROM transaction");
if ($dbi->begin())
{
	do
	{
		$re = $dbi->current();
		$output .= $re->get_item()." - ".$re->get_amount();
	}while($dbi->next());
}

Summary so far...

With the two classes DbEntity and DbIterator
it

Breaking rule of thumb no.4

Breaking rule of thumb no.4 admin 22 November, 2009 - 13:42

I've just realised that effectively I'm breaking my own rule of thumb No.4 "You don't need to write an application framework", and here I am writing one.

So I need to clarify this, partly for my own sanity, and investigate why I'm doing this. To start with the rule of thumb No. 4, looking back what I meant was simply "when faced with building something that is new and confusing, it is a big mistake to start off by writing a framework because that's the only way to begin". In this sense, and fortunately, ZXAF falls outside of the rule because I'm pulling together various bits of code that have been around, in production use, for many years.

What I'm doing with ZXAF is taking all the various bits of good code and putting them together in a coherent manner to get rid of what I call the middle-aged code spread. This is something that I've observed happening often. You start off with a nice tight set of classes and a good design, and as real world pressures (i.e. delivery) creep in there simply isn't time to rework the design to make it able to handle a new requirement, so it's more expedient to add something that is similar but different to something that was already there.

This is exactly how I ended up with my ZXAF Views and Forms problem whereby the view and the form should have been one thing but actually the Form came first and then the View came along. Neither were right, the Form was good a presenting a Form (i.e. two column form with prompt and value of a single entity), and the View was excellent at displaying a row per entity based on an iterator. To further confuse things the View had started out life as a Form and then been heavily modified to fit a specific project requirement. The View introduced the concept of Items and Transformations, but didn't really support editting.

The net result was that both items had to be reworked because neither of the two classes individually were good enough. The end result is the View class that is now present in ZXAF.

The resulting design (the View) is the results of a code evolution of stable code being reworked (or refactored for the buzzword conscious).

This single case really illustrates what ZXAF is all about; reducing and producing something that is more consistent and coherent. There are still areas of general ickkiness within ZXAF as it is an evolving entity rather than being a rush to publish.

In conclusion I don't consider that I've broken my own rule, (MRDA applies).

Building a page with ZXAF.

Building a page with ZXAF. admin 9 November, 2009 - 15:11

This is a mini tutorial of how to build a page, consisting of the required entities and views to produce a fully functional page that shows the built in dynamic updating and demonstrates the ease of presenting different views.

Anything within ZXAF is built from the following core components.

The other thing that ZXAF guides you to is seperation of the layers of the logic.

  • Database
  • Business Logic (CBF)
  • UI

Step 1 - creating the entity

Most things that we create need some some of model or storage behind them. This is where we use the ZXAF DBEntity which provides both model and storage. Usually the DB entity will be extended to provide a CBF version of the entity with extra features, although this isn't required and DBEntities work just fine for most things. The reason that I usually extend the DB entity to provide a CBF version is the knowledge that probably, at some point in the future the DBEntity will need to have extra facilities. The class division convention within ZXAF dictates that the DB Entity must be a simple representation of a DB record, mainly because DB Entities are automatically created and may be replaced at any time.

The following is the SQL that will create the table in the database. This is stored in the file db/db-create-user.sql which is where the user database schem should be stored.

CREATE TABLE `help` (
    `id` int(11)  NOT NULL auto_increment,
    `category` varchar(100) default '' ,
    `section`  varchar(100) default '' ,
    `topic` varchar(100) default '' ,
    `help` text  NOT NULL,
    PRIMARY KEY  (`id`)
);

Taking this SQL file and running it through the creating tool via awk -f exec/mkdbe.awk db/db-create-user.sql will produce the db/help.php. This file provides the database entity, together with the iterators that will be used to access it

The DB entity

The following will be created by the above script, as you can see it is provides an Iterator and a Db Entity that you is used to access the DB

table_autoid = true;

        if (!$this->init_done)
            $this->init();
        if ($key !== null)
        {
            if(!$this->load($key))
                cbf_error("Failed to load Help id: $key ");
        }
    }

    function create()
    {
        if(!parent::create())
            return false;

        return true;
    }
    function get_id()             { return $this->get_field('id'); } 
    function set_id($v)           { return $this->set_field('id',$v); }
    function get_category()       { return $this->get_field('category'); } 
    function set_category($v)     { return $this->set_field('category',$v); }
    function get_section()        { return $this->get_field('section'); } 
    function set_section($v)      { return $this->set_field('section',$v); }
    function get_topic()          { return $this->get_field('topic'); } 
    function set_topic($v)        { return $this->set_field('topic',$v); }
    function get_help()           { return $this->get_field('help'); } 
    function set_help($v)         { return $this->set_field('help',$v); }
    static function begin($where="")
    {
        return new DbHelpIterator("SELECT * from `help` $where");
    }

};

Creating the CBF object

Create cbf/help.php containin the following. As explained earlier this is really just to get started. In the future extra logic related to the model may be added in here. For now there is none so a skeleton will suffice.

require_once("db/help.php");

class CbfHelp extends DbHelp
{
}

Using the CBF object

Basically you can easily create, load and delete records

// create a new record. ID is automatically assigned.
   $helpr = new CbfHelp();
   $helpr->create();
   $helpr->set_category('CAT');
   $helpr->write();

// the $helpr now contains a valid record which includes the created id, so 
// load a copy into another variable.

   $help2 = new CbfHelp($helpr->get_id());

//
// Now use an interator to dump the entire table.

   $helpit = new CbfHelpIterator("SELECT * FROM `help`"); // illustrative normally SELECTs belong in the Cbf/Db
   // or $helpt = CbfHelp::begin();
   if ($helpit->begin())
   {
      do
      {
          $record = $helpit->current();
          echo $record->get_id()." : CAT= ".$record->get_category();
      } while ($helpit->next());
   }

// now get rid off the record.

   $helpr->delete(); 

Creating the View

View creation is relatively simple. As documented elsewhere a View in ZXAF is built from a ViewItemList which is merely a set of ViewItems. Each ViewItem relates to one field from the entity. Firstly though we will need to include the pre-requesites:

require_once "views/view-main.php";
require_once "cbf/help.php";

$entity = new CbfHelp();

Now we can create a simple view by doing the following:

$vil = new ViewItemList();
$col = &$vil->add(new ViewItem("category","Category"));

$col = &$vil->add(new ViewItem("section","Section"));
$col = &$vil->add(new ViewItem("topic","Topic"));
$col = &$vil->add(new ViewItem("help","Help"));
$col = &$vil->add(new ViewItem("type","Type"));
$col->set_output_transform(new ViewItemTransformParams());
$col = &$vil->add(new ViewItem("visible","Visible"));
$col->set_output_transform(new ViewItemTransformCheckbox());

$col = &$vil->add(new ViewItem("id",""));
$col->set_output_transform(new ViewItemTransformFieldAction("helpsite","form1"));

That's the view created. Each ViewItem constructor takes two arguments the first is the field name, the second the field description (for use in headers/titles/prompts etc). The Type element has an associated ViewItemTransformParams which will take care of the translation between the numeric ID and the related record in the params table. This concept of the ViewItem providing an transformation routine to manage the output and input of individual elements is documented elsewhere and is a core part of the flexibility of the framework.

The ViewItemTransformFieldAction provides the Edit and delete buttons. Notice that it takes the ID (which is the entity ID), so that it knows which record to pull up for view or delete

The Visible element is presented via a checkbox transform. If we remove this the raw value of 1/0 will be output and may still be editted, but this is a good indication of what the ViewItemTransform concept is capable of.

The view is responsible for providing the output, which could be a input form or a list which will be presented in the form of a table. There are three main Views available by default

  • TableView - readonly tabular output of all elements as provided by the DBiterator
  • TableViewEdit - allows modification. There is added flexibility here as the edit wrapping is provided by either a click to edit version (ViewElementProviderInPlace), or by an element provider that will output either static or editable fields depending on the view (ViewElementProviderModeBased). There is also ViewElementProviderOnChange that allows elements to cause updates to other views when changed.

Now we need to create the views. All of these three views are simply a different presentation of the contents defined in the ViewItemList. Built into all of the views is the automatic update (via Ajax) when any value is changed. One the example there is a fourth representation of this entity which is the menu on the left hand side. Notice that this is also automatically updated when any changes are made.

$tv = new TableViewEdit($entity, $vil, 'formx');
$v2 = new TableView($entity, $vil, 'form2');
$v3 = new FormView($entity, $vil, 'form3');

Creating the page

All that is required now to create a complete working page (within the template provided by the standard webpage object) is to create an instance of WebPage and request that each view outputs its content.

$page = new WebPage("Help");
$page->begin();
  $page->add_body($tv->get_output(CbfHelp::begin()));
  $page->add_body($v2->get_output(CbfHelp::begin()));
  $page->add_body($v3->get_output(CbfHelp::begin()));
$page->end();

Update is performed automatically, as is save. There are clever things that can be done with the View, the ViewItemTransform and the ViewContainer to get different results, such as using Jquery to add scrolling to a table, or providing a postback based table paging

Directory structure and inclusion of third party libraries into projects

Directory structure and inclusion of third party libraries into projects admin 26 September, 2009 - 17:55

I'm now using zxaf for a project, it's tried and testing code just repackaged slightly so it makes a lot of sense. What doesn't make sense though is the directory structure that the current version has. The reason being because it really doesn't lend itself to being part of a larger system, or more importantly part of a composite system.

During the initial design it looked as though it wouldn't be a problem. I thought that I'd probably use ZXAF in it's entirety for any given project, so rather than spend time trying to figure out something super scalable and elegant I went with a simple approach. The original idea was that zxaf would be integrated by exporting from svn (or extracting from tar) into the root of the project. Sounds plausible, and actually it probably would work well. However what it doesn't allow me to do is to work on zxaf and the project at the same time and have the whole lot controlled by different SVN repositories.

Inclusion of third party libraries into any software project or package or system has always been, for me at least, a dilemma, because in one way I don't want the source tree polluted by a whole raft of unrelated source code, but at the same point I want to be able to checkout the project from source control and be able to build and run with it, reliably and consistently. Although this is really more of a problem with compiled and linked projects (because there all we need are the libs), it still applies to PHP, but I suspect that the solution is different.

What the solution will finally be is still something that I'm thinking about, but it looks likely that it will be a case of putting ZXAF into a sub-directory, or maybe just moving around the areas that are changed for a project (the DB, CBF, and VIEWS), such that there is a system version of these, and a project version will be sufficient.

Event Messaging in ZXAF

Event Messaging in ZXAF admin 9 November, 2009 - 15:30

The hardest thing to grasp completely with ZXAF is probably the way that the inter object communications can be used.

The easiest way of showing this is to refer to views/view-main.php, search for GlobalEventBus::notify_all and you will see many occurrences. Each one of these messages sent is really related to the standard stuff that you need to be in the page container. The beauty of the event notifications is that the view module does not concern itself with how the message is handled, all it knows is that someone, somewhere will receive the message and do the right thing with it.

This is important because it decouples view-main from the WebPage container, it frees view-main from specific chunks of code related to templates.

The other benefit is that the recipient can manage the messages as they come in, for example when using the following to request the inclusion of a javascript module:

GlobalEventBus::notify_all(new MessageRegisterJavascript(MessageRegisterJavascript::Type_Script, "javascripts/jquery.timeentry.js"));

It is the recipient that can decide to only do this once, so each object within view-main doesn't need to worry if that script has already been included it can simply state that it needs the module included, and be confident that it will be included

Another example is the callback processing during an Ajax update. The objects in view-main don't need to be concerned at how the updated HTML will be fed to back to the browser, all they need to do is to create the HTML together with the identifying key (id) and send a message as follows:

GlobalEventBus::notify_all(new MessageAjaxUpdate($key, $html));

.

Upon receipt the WebPage can store this, and later on during the processing it will convert this into JSON, output it and exit, because the WebPage knows that this is the right action, whereas the objects in view-main do not need to have this logic wired in.

As said at the beginning this is a hard concept to really grasp, and these worked examples on start to demonstrate the power that can be harnessed from this technique

PHP Implementation of a reference monitor to provide record level access

PHP Implementation of a reference monitor to provide record level access admin 7 October, 2012 - 14:53
If you refer to the classic "design of a reference monitor":http://chateau-logic.com/content/object-model-design-reference-monitor To add record level protection to certain tables firstly we're going to use the "interface record segmentation" method, which allows us to provide the authorisation database within the protected subject - it is the subject that becomes responsible for providing ownership, group and protection information. This works well with tables in a database implemented at a low level within the active record implementation, however it doesn't protect the database or entities against direct access via SQL (because that's not what we're trying to achieve, and to do that would require a traditional reference monitor wired in a the driver level). With this approach protected objects need no extra code to support * prevent unauthorised access by providing an ID * filter lists (via DbIterator) to only contain accessible objects * protection against modification of records * protection against deletion of records * granting of access to a record to a user, group or everyhone h3. Description of elements of the ZXAF reference monitor |_<. Element|_<. Description| |Subjects|DbEntity derived items (tables/records)| |Objects|SystemUser derived entity| |Audit trail|Not yet implemented, but will eventually provide a record of all security-relevant events, such as access attempts, successful or not| h2. Implementation overview Added interfaces, namely IControlledObject for a DBentity and ISubject for User To store the data that is required (on record on all protected items) is: * IControlledObject.Protection INT * IControlledObject.Owner INT * IControlledObject.Group INT * ISubject.Group INT * ISubject.Privileges varchar(255) - comma seperated list of permission names h2. Reference implementation * "refmon.php in ZXAF":http://zxaf.svn.sourceforge.net/viewvc/zxaf/trunk/exec/refmon.php * "DB Entity.PHP in ZXAF":http://zxaf.svn.sourceforge.net/viewvc/zxaf/trunk/exec/dbentity.php

Record interface segmentation - Object Mapping

Record interface segmentation - Object Mapping admin 7 October, 2012 - 07:45
To add object protection to ZXAF the Reference monitor requires protectable objects to provide interfaces, namely IControlledObject for a DBentity. To store the data that is required (on record on all protected items) is: * Protection INT * Owner INT * Group INT To implement this we will add fields to each record corresponding to the above, when these records are present (detected by DbEntity) it will effectively provide low-level object protection via the reference monitor. This I'm calling record interface segmentation - where a record in the DB has extra fields that relate to the interface that the implementing object implements, and the presence of these fields means that the interface is implemented The ISubject requires a UserID, GroupID and list of permissions, which is implemented by adding the the following fields * Group INT * Privileges varchar(255) - comma seperated list of permission names bq. The net result was a quick and easy way to add interfaces in an extensible way to an existing object - and the existence of the fields can be used to determine if the interface is applicable. Which in this case is used to determine if a DB table (record) is protected and needs to have permission checking via the reference monitor performed h2. References * "refmon.php in ZXAF":http://zxaf.svn.sourceforge.net/viewvc/zxaf/trunk/exec/refmon.php * "DB Entity.PHP in ZXAF":http://zxaf.svn.sourceforge.net/viewvc/zxaf/trunk/exec/dbentity.php

View update complexities

View update complexities admin 23 September, 2009 - 22:01

On a page where there are lots of views but all from the same entity there is an interesting challenge when an individual item is updated within a view. Normally this is catered for during a full postback simply because the entire page is recreated dynamically, so it is enough that the post is processed during the construct phase.

However this isn't true when we are using an ajax update to modify an item.

So what we need to happen is when any item within a view is updated all views that reference the item should also be updated. There was an immediate and elegant solution using one of my current favourite techniques which is to effectively label elements within the DOM by adding a class to them. The all that had to be done was to return a simply text value representing the new value of the item.

This all worked fine, nice and easy with a single javascript call

(".entity_item_view").html(new_value)

Except that it completely broke most of the rules of good development, and whilst it worked for a simple case it didn't work for anything more complex.

So, what is needed is something that will allow all entity items to have their inner html updated using the same objects that originally generated them.

This is further complicated because, of course, there isn't a coherent list of the views that are currently used, and no cross reference between them, and furthermore I really don't want to add this as each view should be as independent as possible. Within the PHP I can send out a message via the global post office which could be received by the views, except that doesn't necessarily help to get the data onto the web page, and global messages aren't really the right solution in this case (because of the way that the objects are constructed and updated processed in the constructor).

I don't want to change the way that the objects are constructed; there's still a large post-it on top of the monitor that reads ZXAF - NO COMPROMISES.

There are two distinct parts to the solution, the first being obvious in that the ajax should return JSON containing all of the identified DOM elements that need to have the HTML replaced by that provided.

The second part of the solution was very difficult to get right, and I hope it's right. Initial impressions look good, but it's new and hasn't been onto the battlefield yet (whereas most of the original code came complete with years of battle scars).

The solution to the PHP update problem was a large rework of the way that DOM elements were named to make them consistent and identifiable. Then to change the naming of the form elements to be based on their entity item name. This was the difficult bit to figure out, and I think it's right. The logic is as follows:

Where a form references an entity any updates to any part of that entity are relevant to all forms that reference the same entity, so any form that uses the same entity is equally capable of updating the entity. As the update logic in entities is smart, such that only changes are written to the backing store.

What that means is basically that it doesn't matter which view prompted the update, any view can process it, and will process it, because by processing it we can get the HTML for the update that is required, and build this into a JSON update message.

The actual building of the JSON update is performed by the WebPage object, via messages.

So, changes made, and with a few small faults that were easily fixed, it all works fine.

I can have an entity on a view, update it from that view, and all the items on the web page that reference it will be updated.

At the same time I also implemented the ability to cause this update mechanism to support linked entity items, such as an href, where the text and the target are usually two fields. It looks promising that the design is good because this change only took about 5 lines of code, and I was expecting a lot more.

ZXAF Views and Forms problems [resolved]

ZXAF Views and Forms problems [resolved] admin 24 September, 2009 - 11:29

This has now been resolved with the new views that arrived in r11; however I'm keeping the page because it is a good indicated of middle aged code spread

Currently we have the view-main module; however we have developed into a situation where there are two different methods for presenting form and or table view (for display and/or edit).

So, we have ViewItems and FormFields, and they are needlessly different as their work and function is very similar; the major difference being that FormFields are currently much more closely tied to the DB entities, and as such can directly update values; whereas ViewItems merely have an ID and expect not to be able to modify their data (in this sense a View is originally Readonly, whereas a Form was always intended to allow modification of elements).
This stems from the way that the view module has been built by refactoring existing working code and we really need to fix this sensibly.

A form is created as follows:

$fields = array();
$validator = new ValidateFieldRequired($edit_buttons);

$ffp = new FormFieldProvider($edit_buttons);
$fields = array();
$form = new Form("form1", $fields,"Details","HelpSite Details","admin/helpsite");

$form->add_field ( new FormField($si, "Category", "category", $validator, $ffp));
$form->add_field ( new FormField($si, "Section", "section", $validator, $ffp));
$form->add_field ( new FormField($si, "Topic", "topic", $validator, $ffp));
$form->add_field ( new FormField($si, "Help", "help", $validator, new HtmlEditorFormFieldProvider ()));

It is processed as follows:

$entity = new CbfEntity();
$form = make_form($entity);

if ($form->ajax_process($entity))
    return;

This is fairly clean and quick, although the Form constructor needs rationalizing to remove the quantity of fields it is tolerable.
Each form field must be constructed with a FormFieldProvider derived object. This object is used to define the way that the form field operates.

For displaying lots of data (from an iterator) we use a TableView, which is more developed than the Form, however it's not as good at editting: typical use of a TableView is as follows:

$tv = new TableView('id');
$col = &$tv->add_column("user","UserID");
$col = &$tv->add_column("type","Type");
$col = &$tv->add_column("date","date");
$col = &$tv->add_column("table","Item");
$col = &$tv->add_column("actioncode","Action");
$col->set_output_transform(new ViewItemTransformHtmlSpecialChars());
$col = &$tv->add_column("ipaddress","IP");
$col = &$tv->add_column("additional","Details");
$col->set_output_transform(new ViewItemTransformDetails());
echo $tv->get_output(new CbfDbIterator());

Internally the view works closely with the iterator and the db_entity. By default the value of the field specified in add column (param1) is merely output, however we have the concept of an output transformation, which is very useful as it is an object (derived from ViewItemTransform) that will be responsible for preparing the value for output (the output is still done by the class).
The output transformation can be used in a simple case to format decimals, however it can also lookup values from other tables, and substitute names for ids etc.
Also output transformation objects may be chained by passing an output transformation object to the constructor.