Before we dive any further into the mechanics of querying the database, a few words about how to format the data once you get it out. One of the most powerful features of Fakoli is the format() method that is supplied by the DataItem base class. This method allows you to output object values based on a formatting template string. Since the method is available on all the DataItems that make up your data model it provides the backbone for flexible templating with Fakoli's user interface helper classes (such as DataListView, or PagedList).
The format specifier syntax is highly flexible, and supports simple field substitution with formatting hints and relationship traversal, as well as local and static method calls when more complicated or conditional formatting is required.
The key syntax for the format() method is the format specification point. These are indicated by a pair of parentheses containing the substitution specifier. The simplest form of specifier is a field name:
echo $employee->format("Full Employee Name: {title} {first_name} {last_name}");
To provide more precise control over the formatted output, the format method provides a hinting syntax. The general form is "{field:hint}". Formatting hints are specific to different datatypes.
Hint Syntax | Example | Description |
---|---|---|
upper | {first_name:upper} - ANDREW | Converts the field to upper case |
lower | {first_name:lower} - andrew | Converts the field to lower case |
ucwords | {first_name:ucwords} - Andrew | Converts the first letter of each word to upper case |
prettify | {identifier:prettify} | Converts an underscored or camel case string to a human readable label |
codify | {name:codify} - andrew_green | Converts the string to a valid 'code' format (all lower case, underscores for spaces, punctuation removed |
stripHTML | {description:stripHTML} | Strips all HTML markup from the string |
html | {multiple_paragraphs:html} | Formats plain text into HTML paragraphs with line breaks |
xml | {some_data:xml} | Escapes the text to make it valid XML data. HTML tags are escaped, HTML entities are converted to numeric XML entities. |
phone | {phone_number:phone} (703) 123 4567 | Renders a string containing a phone number in a standard US format |
jsSafe | {some_string:jsSafe} | Escapes the string to make it a valid Javascript-safe string, with quotes and carriage returns escaped |
Numeric value | {first_name:5} - Andre... | Cuts the output at the specified length and appends an ellipsis (if necessary) |
Hint Syntax | Example | Description |
---|---|---|
Numeric Value | {price:2} - 99.99 | The numeric value specifies the number of decimal points. Default is no decimal points |
Date and time formatting hints support the full set of format specifiers provided by PHP's built-in date() function. Some examples are given below:
Hint Syntax | Example | Description |
---|---|---|
m/d/Y | {end_date:m/d/Y} - 03/01/2013 | Simple month/day/year |
c | {end_date:c} - 2013-03-01T15:19:21+00:00 | ISO 8601 date format |
r | {end_date:c} - Fri, 1 Mar 2013 16:01:07 +0200 | RFC 2822 date format |
Hint Syntax | Example | Description |
---|---|---|
True / False | {is_checked:Yes/No} - Yes for true, No for false | Substitutes the appropriate value from the format hint based on the value of the boolean field |
The alternation specifier allows you to provide an alternate output string that will be emitted when the specified field is empty. Often this is useful for providing default text when, say, a title field is empty. The format is {field|Text} so {title|None Specified} would output the text "None Specified" whenever the title field is empty.
Sometimes you hit a situation where your formatting logic is more complex than simply substituting fields. In these cases you can make use of a couple of different syntaxes provided by format() that allow you to call out to PHP methods to provide formatting for all or part of your output.
You define these methods on your DataItem object. Examples might be getFullName() to format a user's full name neatly including prefixes and suffixes or rank as necessary, or getFullAddress() to format a postal address with the appropriate line breaks. These methods should take no parameters. The syntax for calling the method from within a format template is simply {methodname()}.
For example, we might define an Employee record as follows:
class Employee extends DataItem { var $table = "employee"; var $primary_key = "employee_id"; var $fields = array("employee_id" => Number, "first_name" => String, "last_name" => String, "position" => String); function getFullName() { if ($this->position) { return "{$this->first_name} {$this->last_name}, {$this->position}"; } else { return "{$this->first_name} {$this->last_name}"; } } function Office() { return $this->getRelated(Office); } }
We would then be able to output the full name using the helper method like this:
$employee = new Employee(1); // Load Employee record from database $employee->format("{getFullName()}");
Obviously in this case we might just as well call the method directly, but the neat thing is that the helper function format specifier can be used as part of a longer format template, or the format string could be passed to an object handling the output that knows nothing about our Employee class (see Chapter 8–DataListViews).
The second formulation for helper methods is to put them as static methods on an external class. This can help keep our DataModel object uncluttered while helping us build an internal "API" for working with our data model objects. This is quite a common pattern of development in Fakoli applications, especially when developing components for applications using the Fakoli CMS.
In this pattern, we might create a Manager class to handle operations related to Employee objects. This class would not be limited to simple formatting operations–in fact it would most probably contain most of the application's business logic related to Employees (i.e. the code that manipulates the data model according to business rules). Our super simple EmployeeManager class might look like this:
class EmployeeManager { static getFullName($employee) { if ($employee->position) { return "{$employee->first_name} {$employee->last_name}, {$employee->position}"; } else { return "{$employee->first_name} {$employee->last_name}"; } } }
Note that the code is almost identical to the local helper method, with the exception that the Employee object is being passed in as a parameter, rather than as a $this context. To use the static helper within a format template, the code would look like this:
$employee = new Employee(1); // Load Employee record from database $employee->format("{EmployeeManager::getFullName}");
One of the most powerful features of the format() method is the ability to traverse relationships within format specifiers. This allows you to output information held not just in the object on which you initially called format(), but also any related objects, including those accessed through one-to-many relations. The syntax is simple. Suppose we had an Employee class and an Office class, defined as such:
class Employee extends DataItem { var $table = "employee"; var $primary_key = "employee_id"; var $fields = array("employee_id" => Number, "first_name" => String, "last_name" => String, "position" => String, "office_id" => Number); function getFullName() { if ($this->position) { return "{$this->first_name} {$this->last_name}, {$this->position}"; } else { return "{$this->first_name} {$this->last_name}"; } } function Office() { return $this->getRelated(Office); } } class Office extends DataItem { var $table = "office"; var $primary_key = "office_id"; var $fields = array("office_id" => Number, "address" => String, "phone" => PhoneNumber); function Employees($constraint = "") { return $this->getRelatedList(Employee, "", $constraint); } }
Then we could output information about the office where employee #1 works like this:
$employee = new Employee(1); // Load the Employee from the database $employee->format("{first_name} {last_name} works at {Office.address} {Office.phone}");
But that's not all. We can also traverse the one-to-many relation back again to concisely specify a format that lists all the employees at a given office:
$office = new Office(1); // Load the Office from the database $office->format("Employees at {address} {phone}:<br>{Employees.getFullName()}");
This would output a comma-separated list of the full names of every employee at office #1. We can change the list separator easily using the second parameter of the format() method, like so:
$office = new Office(1); // Load the Office from the database $office->format("Employees at {address} {phone}:<br>{Employees.getFullName()}", "<br>");
The last format function is more of a DataItem class feature than a feature of the format() method itself. Any DataItem object can optionally provide a default format in its class definition that is used if no format template is passed to the format() method. For instance, if we added
... var $default_format = "{getFullName()}";
to our Employee class, then a call to $employee->format() would use this format template by default.
As I hope you can see, the DataItem::format() method provides an exceedingly powerful set of tools to help you format output from your data. Whereas you can use the method directly, its real power and benefit will be felt once you start using classes such as DataListView to present your data to your users. For that you will have to wait for (or skip ahead to) Chapter 8.