~kolibri-dev/kolibri/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
***************
 ABOUT KOLIBRI
***************

Kolibri is a lightweight, flexible web application framework written in PHP 5.
Kolibri's focus is on providing you with the tools to customize the framework to
your liking -- from its custom URI mapper and plug-in-based core architecture,
to the view technology of your choice. Kolibri flies either way.

Kolibri is under active development and is not feature complete, nor is the API
stable. It is however fully functional and is already used in several production
applications.


**************
 INSTALLATION
**************

Requirements:
	* Apache with mod_rewrite support PHP 5.2 with: (PHP 5.1 should work but is
	* currently untested)
		- XSL extension
		- PostgreSQL or SQLite extensions for database applications

Check out our bazaar branch if not already done:

	$ bzr branch lp:kolibri

The src/ directory within the checked out directory contains the framework
itself, and can be installed where ever you want where Apache has read access
(it doesn't need to be in your Apache document root). Copy kolibri.php and
.htaccess (which resides in the src/ directory as well) into an Apache document
root, and edit kolibri.php to set the install path and application directories.


*****************
 GETTING STARTED
*****************

As documentation is yet a major TODO, this is a [somewhat] small guide to help
you get started. A sample application you should look at when following this
guide is provided in the examples/wishlist directory.

1. Setup and directory structure
--------------------------------

Copy the contents of examples/wishlist to an Apache document root along with
kolibri.php and .htaccess as mentioned in the general installation instructions
above. Modify the permissions on the db/ directory (which will hold the SQLite
database required for this application) to give Apache write access (i.e. "chmod
777 db/" in a shell). As with Kolibri itself, the application files don't have
to reside in the document root, but for simplicity's sake we'll keep it there
for this example.

With the exception of the db/ directory, the other directories are default
Kolibri application directories (although again, most can be changed). Their
names should be pretty self-evident.

To complete the installation of the example application, go to
http://localhost/setup (or whatever hostname you're using) to create the
database tables.

2. Configuration (or lack thereof)
----------------------------------

You don't have to edit any configuration if you followed the defaults above, but
let's take a quick look. As mentioned above, kolibri.php defines Kolibri's and
the application's installation directories, while conf/config.php defines
application-specific configuration. You can change the web root, URI of static
resources (.htaccess may also have to be updated if this is changed), database
settings among others.

More interesting is the sections related to action mappers and interceptor
mappings. Action mappers are PHP classes that defines how URIs (and/or other
request data) are mapped to actions, while the interceptor mappings specifies
which interceptors (you could also call this "plug-ins" or "filters") are
invoked. We discuss both of these in more detail below.

3. Actions and action mappers
-----------------------------

In Kolibri, when using the default action mapper (appropriately named
DefaultActionMapper, as can be seen in config.php), an URI maps to exactly one
action class and vice versa. This mapper tries to find matching action classes
by mapping the URI to directory and file names within the actions directory,
with a fallback to an Index.php action if no exact match is found. What this
means is that a request to the web root will be mapped to Index.php directly
within the actions/ directory, while /items maps to items/Index.php if an items/
directory exists, else Items.php directly within the actions/ directory.

After adding some wishlist items in the example application, take a look at the
URI for the links to delete entries. It links to /items/del/<itemname>, which
corresponds to the actions/items/Del.php file. As you can see, no action maps to
the item name. Instead, if the last URI-part is not matched to an action, it is
put into a request parameter named "id".

As for the action classes themselves, their names must exactly match their file
names. They implement either one, or both, of doGet() and doPost() methods,
which will be executed on HTTP GET or POST calls respectively. Action classes
can be plain old objects and need not extend ActionSupport as our example
actions does, but more often than not they do simply because it provides some
functionality commonly needed by most actions (specifically access to the
session and status message facility).

How you write your own action mappers are beyond the scope of this introduction,
but generally you simply extend DefaultActionMapper, override mapAction(),
mapMethod() and/or mapParams() and put it in a directory named mappers/ in your
application. Writing your own mappers gives you complete freedom to lay out the
actions however you want (i.e. you can have action methods instead of action
classes).

4. Interceptors and the wings of Kolibri
----------------------------------------

Kolibri makes use of interceptors for much of it's core functionality.
Interceptors are like filters or plug-ins, but we like their names because they,
well, intercept requests and can change the outcome of the execution. Everything
from session support through authentication to validation is implemented through
interceptors. This makes it easy to customize the framework however you like, by
disabling interceptors you don't need or writing your own.

Interceptors are enabled per URI similar to action mappers. See the
documentation in config.php for information how they are setup, and
conf/interceptors.php in Kolibri for a list of the currently supported
interceptors.

The most interesting use of interceptors in our example application can be seen
in the Add action. It implements ModelAware and ValidationAware and defines a
couple of fields that are used by the ModelInterceptor and ValidatorInterceptor
respectively. When the ModelInterceptor is invoked (which it is, as it's part of
the defaultStack interceptor stack we have configured), it checks to see if the
target action is ModelAware. If it is (as is the case with the Add action), a
model instance of the class named in the $model property of the action (in this
case Item) is created. Request parameters are traversed and the model instance
is populated with their values before being put back into the action.

For the ValidatorInterceptor to have any meaning it must be invoked after the
ModelInterceptor, which it is as dictated by their order in the defaultStack. If
the target action is ValidationAware, it grabs the model object from the action
(which was prepared by the ModelInterceptor) and validates the model according
to its own rules. Any errors are put into the $errors field in the action, which
the action can look at to determine what to do during its execution.

While the interceptors just described are among the most complex, more simple
functionality such as session support and status messages are also implemented
by interceptors. The session for instance can be access in SessionAware actions
through $this->session['key'], while the messages we set by
$this->msg->setMessage(...) is a facility provided to MessageAware actions by
the MessageInterceptor.

5. Models and data access objects
---------------------------------

Models in Kolibri applications can, and will most often be, plain objects. You
don't have to extend any specific class, in fact, there is no provided
"super-model" class to extend. Instead Kolibri makes use of the object-oriented
proxy pattern to transparently wrap model objects in a proxy, which among other
things provides access to the model's corresponding data access object. Contrary
to many other frameworks, Kolibri doesn't include an Object-Relational Mapper
(ORM). Instead we prefer to write more complex SQL queries ourselves (as ORMs
rarely manage), and provide a SQL-to-Object-Framework (acronyms are fun, let's
call it SOFA) which automatically maps the result of SQL to a nested structure
of model objects according to their relationships (if joins were involved).

Clean separation of PHP logic and SQL queries are of course important, which is
why the SQL should be put in a data access object (DAO). They must be put in a
dao/ directory within the models/ directory, and each model (which cares about
the database) should have it's own DAO. As an example, our Item model has a
corresponding ItemDao class for its database access.

Taking a step back, let's look at a model class and how it defines its
validation rules which is used by the validation framework provided by the
ValidatorInterceptor. Our Item model implements Validateable and a rules()
method which defines its validation criteria. See the Item class to see how it's
defined, and conf/validation.php in Kolibri for a list of all available
validators.

If the validation is successful, you might want to save the model during the
action execution. This is done by simply calling save() on the model. The model
proxy will try to figure out if the model actually need saving (i.e. do we know
if it was changed since it was retrieved from the database?), and if so calls
the appropriate method in the DAO (either insert() or update()). Similarly you
call delete() to, well, delete. Direct access to the DAO is provided through the
objects property, as demonstrated in the sample actions.

6. Results and views
--------------------

One of the cornerstones of Kolibri is the fact that it is not tied to any
specific view technology. While we prefer XSL for our views, we realize that not
all do (although we urge you to take a look, not least due to the simplified
form handling in XSL). This is why Kolibri does not tie actions to views
directly. Instead actions return a Result object, of which there could be many
kinds. Kolibri already has XsltResult for XSL views, SmartyResult for Smarty
templates, PhpResult for regular PHP files, RedirectResult for HTTP redirects,
JsonResult for JSON, and more. This makes it easy to support other view
technologies and response data simply by providing other Result implementations.

As XSL is the view technology with most widespread use in current Kolibri
applications, it has got some extra love to offer you. Most importantly it
provides simplified syntax to define HTML forms, which will automatically output
validation errors and model data. Take a look at the form in views/index.xsl
which targets the Add action (which, again, is ModelAware and ValidationAware).
If validation fails, XSL and thus the form receives and presents the errors and
model data from the action without any extra code on your part.

X. The end
----------

While this guide has only scratched the surface, it has hopefully got you going.
Any questions, feedback and last but not least contributions are most welcome:

	https://launchpad.net/kolibri