Last updated on 21 Jul 2007.
View is an object that can be serialized to string as piece of HTML code.
To display a View all you need to do is to return it from controller method.
class MainCtrl extends Ctrl {
public function show() {
$label = new Label();
$label->text = 'Hello World';
return $label;
}
}
This displays a text message.
You can have composite views easily using containers. Containers are Views that can contain other Views. To build a composite view using container you need to add one or more Views to it.
class MainCtrl extends Ctrl {
public function show() {
$label1 = new Label();
$label1->text = 'Hello ';
$label2 = new Label();
$label2->text = 'World';
$container = new View('compView');
$container->add($label1);
$container->add($label2);
return $container;
}
}
You can use one of bundled containers such as HBox, VBox, Menu, Tree, etc. or write your own using template file.
Templates are written in Smarty template language. Use $self variable to refer to current view in your template file.
<div id="{$id}">
{foreach from=$self item=child}
{$child}
<hr/>
{/foreach}
</div>
Save it in views/MyCompositeView.tpl and add the following line before return in show() method from previous chapter:
$container->template = 'MyCompositeView.tpl';
Each View added to your container will be displayed and followed by horizontal line. Views implement Iterator interface, therefore can be handled directly using {foreach} Smarty instruction. Views implement magical PHP 5 __toString() method, therefore can be echoed straight away.
Another thing you will notice here is {$id} variable. It is View's unique identifier. It is not mandatory for many Views. If a View is anonymous it cannot be replaced or updated later, unless a non-anonymous container is replaced or updated.
You can add template variables just by assigning values to respective property. For example:
$composite->title = 'This is embedded view!';
or equivalent:
$l = new Label(); $l->text = 'This is embedded view!'; $composite->title = $l;
Can be used in template file:
<div id="{$id}">
<h1>{$title}</h1>
{foreach from=$self item=child}
{$child}
<hr/>
{/foreach}
</div>
When building an application you need to take care of identifier uniqueness for yourself. This is because there cannot be duplicate identifiers in HTML document. It may be difficult to remember all the identifiers you have used before in your program. So, you are advised to prefix identifiers with container identifier as a convention. This may make identifiers very long and not particular practical for use in templates. In such case you may use Views aliases.
class MainCtrl extends Ctrl {
public function show() {
$label1 = new Label('MainModule_RootContainer_Label1');
$label2 = new Label('MainModule_RootContainer_Label2');
$container = new View('MainModule_RootContainer');
$container->add($label1, 'label1');
$container->add($label2, 'label2');
return $container;
}
}
In the example above Labels will be registered as label1 and label2 for use in template.
<div id="{$id}">
{$label1}
{$label2}
</div>
You will learn more about aliases while working with Forms.
If you use a helper method to provide composite view and you need to access sub-views you can use property accessor to reach it. Refer to sub-view using its alias (or identifier when there is no alias given).
class MainCtrl extends Ctrl {
private function getPreparedView() {
$label1 = new Label('MainModule_RootContainer_Label1');
$label2 = new Label('MainModule_RootContainer_Label2');
$container = new View('MainModule_RootContainer');
$container->add($label1, 'label1');
$container->add($label2, 'label2');
return $container;
}
public function show() {
$container = $this->getPreparedView();
$container->label1->text = 'Composite ';
$container->label2->text = 'View!';
return $container;
}
}
This way you are accessing sub-views using property access operator. This method works only for views nested directly in composite view. When you need to access view in composite view which is nested in another composite view you need to use View getChildById() method supplying identifier.
$label1 = $container->getChildById('label1');
This method works good for simple one level composition too.
Use Views' update() method to update many of them at the same time.
$label1 = new Label('myLabel1');
$label1->text = time();
$label2 = new Label('myLabel2');
$label2->text = rand(0, 1000);
$label1->update();
$label2->update();
Action string is a string that identifies certain Controller and its method. It can also contain path to Controller class file. The most basic way Action string looks like is
MainCtrl/show
It contains Controller class name, which is MainCtrl and its method name, show.
It also tells that the Controller class is placed directly in ctrl/ directory.
LoginService/LoginForm/LoginCtrl/authenticate
This Action string points to method authenticate of Controller class LoginCtrl,
which is paced in ctrl/LoginService/LoginForm directory.
Action is a request from Controller method execution. Define an Action passing Action string for desired Controller method to be invoked. You can optionaly provide second parameter, which is a value or array of values passed to that Controller method. See examples:
$action1 = new Action('MainCtrl/show');
$action2 = new Action('MainCtrl/displayDocument', 42);
$action2 = new Action('LoginService/LoginForm/LoginCtrl/authenticate', array($login, $password));
In the first case MainCtrl Controller will be instantiated and method show
called. In the second case method displayDocument will receive one argument.
In third example two arguments will be passed to method authenticate of
LoginCtrl Controller saved in ctrl/LoginService/LoginForm/ directory.
Use method run() of Action to execute it immediately.
ActionService is a class that has one static method execAction.
Calling that method staticaly let you execute an Action just without instantiating Action object.
Pass the same arguments as in Action constructor.
ActionService::execAction('LoginService/LoginForm/LoginCtrl/authenticate', array($login, $password));
Both Action::run() and ActionService::execAction
return the result of Controller method.
Listener is an observer that triggers defined action when event occurs. Use listeners to react on user clicks, key press, mouseovers and other well known Javascript events. Define Listener in conjunction with Action or Behaviour.
class MainCtrl extends Ctrl {
public function show() {
$button = new Button('InteractiveWidget');
$button->text = 'Click me!';
$button->addListener(
new ClickListener(
new Action('MainCtrl/click')
)
);
return $button;
}
public function click() {
$button = new Button('InteractiveWidget');
$button->text = 'You clicked me!';
return $button;
}
}
Build a Form using template and sub-views. To pass form data you need to define Listener
that performs Action with a parameter. That parameter is a special value reader and id provided
by Form via valueReader() method.
class MainCtrl extends Ctrl {
public function show() {
$f = new Form();
$f->add(new Input('login'));
$f->add(new Input('password'));
$ok = new Button('ok');
$ok->addListener(
new ClickListener(
new Action('MainCtrl/login', $f->valueReader())
)
);
$f->add($ok);
return $f;
}
public function login() {
}
}
Value reader is a piece of Javascript that reads form fields values. Those values will be sent to Controller method then.
Values gathered with Form value reader are passed as FormContext object to Controller.
See what should login() method from example above look like:
public function login(FormContext $cx) {
$cx->login; // value of login input
$cx->password; // value of password input
}
Values are passed in FormContext object. Access values using form fields identifiers. If you used alias to add form field to Form view, use alias to access the value respectively.
THIS SECTION NEEDS TO BE UPDATED
Setting and clearing error messages for form elements can be done using errorMessage()
and clearError() methods respectively. It is convenient to obtain prepared Form object.
class MainCtrl extends Ctrl {
public function show() {
$f = new Form();
$f->add(new Input('login'));
$f->add(new Input('password'));
$ok = new Button('ok');
$ok->addListener(
new ClickListener(
new Action('MainCtrl/login', $f->valueReader())
)
);
$f->add($ok);
return $f;
}
public function login(FormContext $cx) {
$f = $this->show();
$f->clearError(); // clear all previous errors first on all form fields
if (!$cx->login) {
$f->login->errorMessage('Enter your username'); // set new error message
return;
}
if (!$cx->password) {
$f->password->errorMessage('Enter your password'); // set new error message
return;
}
// more processing
}
}
Using clearError() on Form will clear errors on all form fields.
There are many database abstraction layers out there.
Tigermouse is flexible enough to support most of them. It is done through
In the same manner there are many database engines out there. Each of them is more or less compatible to SQL language standard. To handle the differences Tigermouse incorporates SQLDialect objects.
To connect to database you need these two: adapter and dialect objects. Here is an example for connecting to PostgreSQL database using PDO abstraction layer.
$dsn = 'pgsql:dbname=yourdbname;user=yourloginname'; $pdo = new PDO($dsn); $dialect = new SQL92Dialect(); $adapter = new PDOAdapter($pdo, $dialect);
DSN (Data Source Name) is a way of telling PDO which database to connect. SQL92Dialect is the dialect class that is appropriate for PostgreSQL engine.
It is a good idea to have factory class that produces database adapter. Such class is provided
with Tigermouse in model/ directory. Alter its buildAdapter()
method to produce a desired DBAdapter.
protected static function buildAdapter() {
try {
$pdo = new PDO('sqlite:var/sqlite/phonebook.sqlite');
} catch (Exception $e) {
die($e->getMessage());
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return new PDOAdapter($pdo, new SQL92Dialect());
}
The example above shows how to configure PDO backend with SQLite database.
Make sure that var/sqlite/ is writable for your web server. Otherwise
SQLite will complain it could not create temporary transaction file.
Use DBFactory::getAdapter() method when you need to obtain
an instance of configured DBAdapter.
Active Record is a design pattern closely related to database. It represents single database table record and provides interface for reading, writing and removing to/from that table.
Create the simplest Active Record by providing information
about table name and primary key name. If you skip primary key name
it will be set to 'id'.
class Person extends ActiveRecord {
protected $tableName = 'people';
protected $primaryKey = 'id';
}
Load a record into Active Record object using primary key value (42 in this example):
$p = new Person(); $p->loadById(42);
Note that database adapter is required for Active Record to work. DBAdapter will be obtained automaticaly
from DBFactory::getAdapter() method, unless you overwrite
ActiveRecord::buildDBAdapter method.
If you need multiple selection using non primary key value, do it this way:
$p = new Person();
$females = $p->loadByFieldValue('gender', 'female');
The example above shows how to fetch a collection of Active Records. Array $females
is filled with all Active Records of class Person matching given condition.
In addition the first object of that array is the current object.
In other words, varriable $p is the first item in returned array.
Modifying Active Record properties will result in insertion or update on database table after saving.
$p->name = 'Random J. Hacker'; $p->gender = 'male'; $p->save();
One-to-many relations are the most often used ones in most database schemes. Tigermouse Active Record implementation makes using such relations really easy. Consider the following database scheme:
CREATE TABLE people ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, gender TEXT ); CREATE TABLE emails ( id SERIAL PRIMARY KEY email TEXT NOT NULL, person_id INT REFERENCES people(id) );
Define Active Record classes reflecting the scheme.
class Person extends ActiveRecord {
protected $tableName = 'people';
protected $hasMany = array(
'emails' => array('Email', 'person_id')
);
}
class Email exteds ActiveRecord {
protected $tableName = 'emails';
protected $belongsTo = array(
'person' => array('Person', 'person_id')
);
}
Having it defined this way you can access related objects directly
just by calling $p->emails[$i] (where $p is instance of Person
and $i is valid array index) or $e->person (where $e
is instance of Email.
$e = new Email(); $e->email = 'rjhacker@gmail.com'; $p = new Person(); $p->name = 'Random J. Hacker'; $p->gender = 'male'; $p->email->add($e); $p->save(); $e->save();
To remove Active Record from collection use:
$p->email->remove($e);
Many-to-many relations are often avoided because of their complexity level. From relational point of view extra database table is needed to link entities from two tables. Managing such connections may be troublesome.
Define many-to-many relation between users and groups. Use external table to link each other.
CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT NOT NULL ); CREATE TABLE groups ( id SERIAL PRIMARY KEY name TEXT NOT NULL, ); CREATE TABLE users_link_groups ( user_id INT REFERENCES users(id), group_id INT REFERENCES groups(id) );
Create Active Record definitions reflecting the relation.
class User exteds ActiveRecord {
protected $tableName = 'users';
protected $manyToMany = array(
'groups' => array('Group', 'users_link_groups', 'user_id', 'group_id')
);
}
class Group exteds ActiveRecord {
protected $tableName = 'group';
protected $manyToMany = array(
'users' => array('User', 'users_link_groups', 'group_id', 'user_id')
);
}
Accessing collection of linked Active Records is done the same way as in one-to-many relation.
Use add() and remove() methods to manage objects.
$user = new User(); $group = new Group(); $user->groups->add($group); $group->users->remove($users);
Tigermouse provides internationalization framework with flexible translations storage backends.
Setting up i18n subsystem requires you to define default language and translation service backend.
The best place to do it is index.php file.
i18n::setDefaultLang('en_EN');
i18n::setBackend(new i18nFileTranslationService());
Later on language can be changed:
i18n::setLang('pl_PL');
i18nFileTranslationService is the basic translation service backend.
It requires no additional configuration. It uses translation files from i18n/
directory. File names must correspond to language codes. Use string=translation
format for translation files. Leading and trailing white spaces will be ignored.
Once you setup i18n sybsystem __() function will return translations for
string given as argument.
echo __('Click here to resume');
When translation for given string cannot be found, original string will be returned.
Note that __() returns a translation object instead of old-fashion string.
The object is automaticaly converted to string when used in string context.
> 6. Advanced topics > 6.1. Class autoloader > 6.2. > 6.2. Errors and pop-up windows > 6.3. Requests log > 6.4. Debugging Javascript > 6.5. Deploying application on production server