Version 6 supported

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',
    ];
}

Most DBField subclasses will be validated using a FieldValidator subclass which is call as part of the DataObject::validate() method. This means that when a value is set on a DBField subclass, it will be validated against the constraints of that field. If the value is invalid then a ValidationException will be thrown.

Available types

FieldAPI linkDescriptionValidation
BigIntDBBigIntAn 8-byte signed integer fieldMust be an int between -9223372036854775808 and 9223372036854775807
BooleanDBBooleanA boolean field stored as a tinyintMust be a boolean
CurrencyDBCurrencyA number with 2 decimal points of precision, designed to store currency valuesMust be a decimal
DateDBDateA date fieldMust be a valid date in Y-m-d format
DatetimeDBDatetimeA date/time fieldMust be a valid datetime in Y-m-d H:i:s format
ClassNameDBClassNameA special enumeration for storing class namesMust be a valid FQCN of a DataObject subclass
ClassNameVarcharDBClassNameVarcharA special enumeration for storing class names in a Varchar fieldMust be a valid FQCN of a DataObject subclass
DecimalDBDecimalA decimal numberMust be a decimal.
DoubleDBDoubleA floating point number with double precisionMust be numeric
EmailDBEmailAn email fieldMust be a valid email address
EnumDBEnumAn enumeration of a set of strings that can store a single valueMust be one of the defined values
FloatDBFloatA floating point numberMust be numeric
ForeignKeyDBForeignKeyA special Int field used for foreign keys in has_one relationshipsMust be an int
HTMLFragmentDBHTMLTextA variable-length string of up to 2MB, designed to store HTML. Doesn't process shortcodesMust be a string
HTMLTextDBHTMLTextA variable-length string of up to 2MB, designed to store HTML. Processes shortcodesMust be a string
HTMLVarcharDBHTMLVarcharA variable-length string of up to 255 characters, designed to store HTML. Can process shortcodes with additional configurationString must not be longer than specified length
IntDBIntA 32-bit signed integer fieldMust be an int between -2147483648 and 2147483647
IPDBIpAn IP fieldMust be a valid IP address, either IPv4 or IPv6
LocaleDBLocaleA field for storing localesMust be a valid locale
MoneyDBMoneyA localised money field with a 3 character currency component, and an amount component.Currency string must not be greater than 3 characters, amount must be a decimal
MutliEnumDBMultiEnumAn enumeration set of strings that can store multiple valuesMust be one of the allowable values
PercentageDBPercentageA decimal number between 0 and 1 that represents a percentageMust be a decimal between 0 and 1
PolymorphicForeignKeyDBPolymorphicForeignKeyA special ForeignKey class that handles relations with arbitrary class typesMust be an int
PrimaryKeyDBPrimaryKeyA special type Int field used for primary keysMust be an int
TextDBTextA variable-length string of up to 2MB, designed to store raw textMust be a string
TimeDBTimeA time fieldMust be a valid time in H:i:s format
URLDBUrlA URL fieldMust be a valid URL
VarcharDBVarcharA variable-length string of up to 255 characters, designed to store raw textString must not be longer than specified length
YearDBYearRepresents a single year fieldMust be a valid year between 1901 and 2155

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 ModelData, 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 ModelData 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()).

API documentation