Creating A New Class in Berylium
An outline of how to add a new class of object to Berylium.
Step 1: Add (or include) the new class in code/berylium-classes.php
Here is sample code for Audio objects, to be explained below. (Huh? See
Classes and
Objects from the PHP Maual)
class Audio extends ContentObject {
// audio object
var $duration; // duration in seconds
var $format; // format information (type of wav)
// the following implement ID3v1-style tags
var $album;
var $year;
var $track;
var $genre;
var $comment;
// the following are holders for other metadata stored in the original file (or to be stored in e99o translations)
var $id3v2; // carat-delimited key=value pairs of id3v2 tags
var $ogginfo; // carat-delimited key=value pairs for ogg comment header
// constructor
function Audio () {
$this->objtype= "audio";
$this->columns= "duration=INT^format=varchar(255)^album=varchar(255)^INDEX index_album=(album(64))^year=varchar(255)^track=INT^genre=varchar(255)^INDEX index_genre=(genre(32))^comment=varchar(255)^id3v2=text^ogginfo=text";
$this->publishable= 1;
}
}
This code enables audio objects by extending the generic ContentObject
class. ContentObject is the highest-level generic class in Berylium,
and it defines a set of properties and methods shared by all objects
that store actual content, such as folders, documents, and images.
Take a look at the constructor function-- it declares three very
important properties, the first two of which *must* be declared by all
Berylium objects:
- $this->objtype is,
most importantly, the name of the MySql table that will store instances
of the class.
- $this->columns enables
the BeryliumObject->makeTable() method to create the audio table in
the database, and also tells other methods what properties will be
stored. It is a carat-delimited list of key=value pairs where the key
is the column name and the value is a MySQL Column type-- see MySQL Create
Table Syntax.
There is a special syntax for
specifying column indexes, see the examples for both album and
genre. In this case, the key is "INDEX " and the index name, and the
value is an index_col_name definition enclosed in parentheses.
The method of defining columns dates back to the earliest Berylium
development days and doesn't even begin to consider the capabilities of
a modern MySQL server. If you want to use features that you can't
define here, please contact the developers and we'll work something out.
- $this->publishable is
an optional declaration that instances of this object may be published
to a static website. Comment is an example of a class that is not
publishable by default, because comments are only ever viewed in list
form, not as individual pages.
Step 2: Build Out The Class
Rough out the rest of the class with whatever methods (functions) and
properties you think it will need. If you find that you need to make
changes to the properties that will be stored in the database, be sure
to update $this->columns in the constructor.
In general, the goal is to build properties at runtime where possible,
instead of storing them. The next-best thing is to store properties
serialized within the "properties" property, by prefixing their names
with "p_". Try to only create columns for properties that will need to
be used in conditional SQL Query statements, such as SELECT and ORDER
BY clauses.
If you need to create a custom method (say, a custom upload() method)
that looks like it will duplicate much of the BeryliumObject::upload()
method, see if you can get by with calling that parent method either
before or after your custom code. It will make your methods much easier
to maintain in the long run.
If you need to access other objects in the environment, such as
$session or $site, we've found that it's better (despite being
classically bad programming practice) to import them into the local
scope using a PHP
global declaration than it is to refer to them like
$GLOBALS['site'].
Notice that most methods will announce themselves to the debug log
using a berror() statement near the top. This makes is much easier when
something goes wrong to find out where and why it happened.
Step 3: Add An Upgrade to dbupgrade.php
The dbupgrade.php script is used to perform any necessary upgrades to
the database on installation of a new codebase. The following lines
will check for an existing audio table and create one if not found:
// NEW AUDIO TABLE
$audio= new Audio();
if ($audio->makeTable()) {
print "\nSuccessfully created Audio table.";
}
else print "\nAudio table exists, skipping.";
If existing tables will need to be upgraded with new column
definitions, that can be done via dbupgrade.php, but should be avoided
at almost all costs. If you are implementing radically new
functionality, it will be better to create a new class (as we did with
context2) so as to avoid breaking existing installations.
Run dbupgrade.php as follows:
./dbupgrade.php '<dbadminusername>' '<dbadminpassword>'
Step 4: Add CanSave Functionality To Policies
In the ability for users of a certain role to save objects of a
particular class must be explicity declared in the policy for that
role. This sounds complicated, but it just means that you need to add
the following line to the writer and editor policy files:
$this->canSave['audio']= 1;
As a courtesy to developers who may want more liberal policies in the
future, you should also add the following to the member and anonymous
policies:
$this->canSave['audio']= 0;
Step 5: Create A Minimal Set of Contexts
At the very least, in order to get started, you will need to build
contexts for the "create" and "edit" methods. You can start by
modifying contexts for an existing class, or you can use the
code/contexts/aa-edit-template.be2 template. Generally you want to
restrict object creation and editing to writers.
audio-create-writer-html.be2
audio-edit-writer-html.be2
Step 6: Test
This step is a matter of philosophy-- I like to get a like to get a
little reward for all my hard work up to this point, so I'll usually
login and try creating a new object, just to have some data to play
with when building out additional contexts. In this case, http://example.com/be2/audio-.html?method=create
will get things started.
Step 7: Customise
Generic (class-neutral) contexts exist for view, index, and list
methods, but they generally aren't going to be everything that you want
them to be, so the next step would be rebuilding those so that they are
specific to your new class:
audio-view-anonymous-html.be2
audio-index-anonymous-html.be2
audio-list-anonymous-html.be2
In this case, we're also going to need view contexts that will serve
the attached audio files in wav, mp3, and ogg formats.
audio-view-anonymous-wav.be2
audio-view-anonymous-mp3.be2
audio-view-anonymous-ogg.be2