Data types and casting
Each model in a Silverstripe CMS DataObject
will handle data at some point. This includes database columns such as
the ones defined in a $db
array and methods that return data for for use in templates.
A data type is represented in Silverstripe CMS by a DBField
subclass. The class is responsible for telling the ORM
how to store its data in the database and how to format the information coming out of the database, i.e. on a template.
In the Player
example, we have four database columns each with a different data type (Int
, Varchar
, etc).
// app/src/Player.php
namespace App\Model;
use SilverStripe\ORM\DataObject;
class Player extends DataObject
{
private static $db = [
'PlayerNumber' => 'Int',
'FirstName' => 'Varchar(255)',
'LastName' => 'Text',
'Birthday' => 'Date',
];
}
Available types
'BigInt'
: An 8-byte signed integer field (see: DBBigInt).'Boolean'
: A boolean field (see: DBBoolean).'Currency'
: A number with 2 decimal points of precision, designed to store currency values. Only supports single currencies (see: DBCurrency).'Date'
: A date field (see: DBDate).'Datetime'
: A date/time field (see: DBDatetime).'DBClassName'
: A special enumeration for storing class names (see: DBClassName).'Decimal'
: A decimal number (see: DBDecimal).'Double'
: A floating point number with double precision (see: DBDouble).'Enum'
: An enumeration of a set of strings that can store a single value (see: DBEnum).'Float'
: A floating point number (see: DBFloat).'Foreignkey'
: A specialInt
field used for foreign keys inhas_one
relationships (see: DBForeignKey).'HTMLFragment'
: A variable-length string of up to 2MB, designed to store HTML. Doesn't process shortcodes. (see: DBHTMLText).'HTMLText'
: A variable-length string of up to 2MB, designed to store HTML. Processes shortcodes. (see: DBHTMLText).'HTMLVarchar'
: A variable-length string of up to 255 characters, designed to store HTML. Can process shortcodes with additional configuration. (see: DBHTMLVarchar).'Int'
: A 32-bit signed integer field (see: DBInt).'Locale'
: A field for storing locales (see: DBLocale).'Money'
: Similar to Currency, but with localisation support (see: DBMoney).'MultiEnum'
: An enumeration set of strings that can store multiple values (see: DBMultiEnum).'Percentage'
: A decimal number between 0 and 1 that represents a percentage (see: DBPercentage).'PolymorphicForeignKey'
: A special ForeignKey class that handles relations with arbitrary class types (see: DBPolymorphicForeignKey).'PrimaryKey'
: A special type Int field used for primary keys. (see: DBPrimaryKey).'Text'
: A variable-length string of up to 2MB, designed to store raw text (see: DBText).'Time'
: A time field (see: DBTime).'Varchar'
: A variable-length string of up to 255 characters, designed to store raw text (see: DBVarchar).'Year'
: Represents a single year field (see: DBYear).
See the API documentation for a full list of available data types. You can define your own DBField
instances if required as well.
Default values for new database columns
One way to define default values for new records is to use the $defaults
configuration property, which is described in default in Dynamic Default Values.
That will only affect new records, however. If you are adding a new field to an existing DataObject
model, you may want to apply a default value for that field to existing records as well.
When adding a new $db
field to a DataObject
, you can specify a default value
to be applied to all existing and new records when the column is added in the database
for the first time. You do this by passing an argument for the default value in your
$db
items.
For integer values, the default is the first parameter in the field specification. For string values, you will need to declare this default using the options array. For enum values, it's the second parameter.
For example:
namespace App\Model;
use SilverStripe\ORM\DataObject;
class Car extends DataObject
{
private static $db = [
'Wheels' => 'Int(4)',
'Condition' => 'Enum("New,Fair,Junk", "Fair")',
'Make' => 'Varchar(["default" => "Honda"])',
];
}
Enum
fields will use the first defined value as the default if you don't explicitly declare one. In the example above, the default value would be "New" if it hadn't been declared.
Formatting output
The data type does more than set up the correct database schema. They can also define methods and formatting helpers for output. You can manually create instances of a data type and pass it through to the template.
In this case, we'll create a new method for our Player
that returns the full name. By wrapping this in a DBVarchar
object we can control the formatting and it allows us to call methods defined from Varchar
as LimitCharacters
.
// app/src/Model/Player.php
namespace App\Model;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBField;
class Player extends DataObject
{
public function getName()
{
return DBField::create_field('Varchar', $this->FirstName . ' ' . $this->LastName);
}
}
Then we can refer to a new Name
column on our Player
instances. In templates we don't need to use the get
prefix.
use App\Model\Player;
$player = Player::get()->byId(1);
// returns the `DBVarChar` instance, which has the value "Sam Minnée"
$name = $player->Name;
// returns the `DBVarChar` instance, which has the value "Sam Minnée"
$name = $player->getName();
// returns "Sa…"
$name = $player->getName()->LimitCharacters(2);
For DBField
types that represent strings, you can just treat the instance like a string.
$player = Player::get()->byId(1);
// returns the string "Name: Sam Minnée"
$string = 'Name: ' . $player->Name;
For other types, we need to make sure we get the value from the DBField
instance first:
$player = Player::get()->byId(1);
// where `getAge()` returns a `DBInt` field:
// this will throw a "TypeError: Unsupported operand types: SilverStripe\ORM\FieldType\DBInt + int"
$player->Age + 5;
// returns the correct int value as a result
$player->Age->value + 5;
That doesn't apply to templates, where we can just treat all DBField
instances as though they are primitives or call methods on them:
<% with $player %>
<%-- prints out the name, e.g. Sam Minnée --%>
$Name
<%-- prints out the name in all caps, e.g. SAM MINNÉE --%>
$Name.UpperCase
<% end_with %>
On the most basic level, the DBField
classes can be used for simple conversions from one value to another, e.g. to round a number.
use SilverStripe\ORM\FieldType\DBField;
// returns 1.23
DBField::create_field('Double', 1.23456)->Round(2);
Of course that's much more verbose than using the equivalent built-in PHP round()
function. The power of DBField comes with its more sophisticated helpers, like showing the time difference to the current date:
use SilverStripe\ORM\FieldType\DBField;
// returns "30 years ago"
DBField::create_field('Date', '1982-01-01')->TimeDiff();
Casting
Most objects in Silverstripe CMS extend from ViewableData, which means they know how to present themselves in a view
context. Rather than manually returning objects from your custom functions. You can use the $casting
configuration property. This casting only happens when you get the values in a template, so calling the method in your PHP code will always return the raw value.
While these examples are using DataObject
subclasses, you can use the $casting
configuration property on any ViewableData
subclass.
namespace App\Model;
use SilverStripe\ORM\DataObject;
class Player extends DataObject
{
private static $casting = [
'Name' => 'Varchar',
];
public function getName()
{
return $this->FirstName . ' ' . $this->LastName;
}
}
Using this configuration, properties, fields, and methods on any Silverstripe CMS model can be type casted automatically in templates, by transforming its scalar value into an
instance of the DBField
class, providing additional helpers. For example, a string can be cast as a DBText
type, which has a FirstSentence()
method to retrieve the first sentence in a longer piece of text.
As mentioned above, this leaves you free to use the raw values in your PHP code while giving you all of the helper methods of the DBField
instances in your templates.
$player = Player::get()->byId(1);
// returns the string "Sam Minnée"
$name = $player->Name;
// returns the string "Sam Minnée"
$name = $player->getName();
// throws an exception, since `getName()` returns a string, not a `DBVarchar` instance
$name = $player->getName()->LimitCharacters(2);
<% with $player %>
<%-- prints out the name, e.g. Sam Minnée --%>
$Name
<%-- prints out the name in all caps, e.g. SAM MINNÉE --%>
$Name.UpperCase
<% end_with %>
You can get the casted DBField
instance of these properties by calling the obj()
method:
$player = Player::get()->byId(1);
// returns string
$player->getName();
// returns string
$player->Name;
// returns DBVarchar instance
$player->obj('Name');
// returns string
$player->obj('Name')->LimitCharacters(2);
The database field types DBHTMLVarchar
/DBHTMLText
and DBVarchar
/DBText
are exactly the same in
the database. However, the template engine knows to escape the non-HTML variants automatically in templates,
to prevent them from rendering HTML interpreted by browsers. This escaping prevents attacks like CSRF or XSS (see
security), which is important if these fields store user-provided data.
See the Template casting section for controlling casting in your templates.
Overriding
"Getter" and "setter" methods help save fields to our DataObject
instances. By default, the
methods getField()
and setField()
are used to set column data. They save to the protected array, $obj->record
.
We can override the default behavior by making a method called "get<fieldname>()
" or "set<fieldname>()
".
The following example will use the result of getCost()
instead of the Cost
database column. We can refer to the
database column using getField()
.
namespace App\Model;
use SilverStripe\ORM\DataObject;
/**
* @property float $Cost
*/
class Product extends DataObject
{
private static $db = [
'Title' => 'Varchar(255)',
//cost in pennies/cents
'Cost' => 'Int',
];
public function getCost()
{
return $this->getField('Cost') / 100;
}
public function setCost($value)
{
return $this->setField('Cost', $value * 100);
}
}
Note that in the example above we've used a PHPDoc comment to indicate that the $Cost
property is a float
, even though the database field type is Int
. This is because the getCost()
getter method will automatically be used when trying to access Cost
as a property (i.e. $product->Cost
will return the result of $product->getCost()
).