Enums
With CakePHP 5 we can now use more native (backed) enums in our apps. They map to a string or int type usually in your DB.
Let's use the `UserStatus` backed enum (int values and string labels) to test it. It can be found in source code for details. We use a tinyint(2) column in the database for the `status` field.
<?php
namespace Sandbox\Model\Enum;
use Cake\Database\Type\EnumLabelInterface;
use Cake\Utility\Inflector;
use Tools\Model\Enum\EnumOptionsTrait;
enum UserStatus: int implements EnumLabelInterface
{
use EnumOptionsTrait;
case Inactive = 0;
case Active = 1;
case Deleted = 2;
/**
* @return string
*/
public function label(): string {
return Inflector::humanize(Inflector::underscore($this->name));
}
}
Now lets use `::cases()` to list all possible enum cases:
- Inactive (`0`)
- Active (`1`)
- Deleted (`2`)
In DB and Forms
When used in forms, CakePHP form helper will automatically extract those to display as dropdown.
For it to work, we need to assign the database table field to enum type:
// SandboxUsersTable
public function initialize(array $config): void {
$this->getSchema()->setColumnType('status', EnumType::from(UserStatus::class));
}
Let's load a demo record with the enum included now:
Foo
Sandbox\Model\Entity\SandboxUser Object
(
[id] => 1
[created] => Cake\I18n\DateTime Object
(
[date] => 2015-02-11 13:16:47.000000
[timezone_type] => 3
[timezone] => UTC
)
[modified] => Cake\I18n\DateTime Object
(
[date] => 2015-02-11 13:16:47.000000
[timezone_type] => 3
[timezone] => UTC
)
[username] => Foo
[slug] => Foo
[password] =>
[email] =>
[role_id] => 0
[status] => Sandbox\Model\Enum\UserStatus Enum:int
(
[name] => Inactive
[value] => 0
)
[[new]] =>
[[accessible]] => Array
(
[*] => 1
[id] =>
)
[[dirty]] => Array
(
)
[[original]] => Array
(
)
[[originalFields]] => Array
(
[0] => id
[1] => created
[2] => modified
[3] => username
[4] => slug
[5] => password
[6] => email
[7] => role_id
[8] => status
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => Sandbox.SandboxUsers
)
Unfortunately, enums cannot implement Stringable. So here we need to always manually call the respective string output.
$user->status->label()
Status: Inactive
Submit a form
Manuel form (without entity passed in)
Note the order customization, as well.
Serialization
When serializing, the actual (DB) value (in this case int) is used:
json_encode() of the entity containing the enum
{
"id": 1,
"created": "2015-02-11T13:16:47+00:00",
"modified": "2015-02-11T13:16:47+00:00",
"username": "Foo",
"slug": "Foo",
"email": "",
"role_id": 0,
"status": 0
}
If you also want the human-readable string form, you can add a virtual field `status_string` etc that would include this in the dataset.
Unserialize
json_decode() + patching an entity
Sandbox\Model\Entity\SandboxUser Object
(
[created] => Cake\I18n\DateTime Object
(
[date] => 2015-02-11 13:16:47.000000
[timezone_type] => 3
[timezone] => UTC
)
[modified] => Cake\I18n\DateTime Object
(
[date] => 2015-02-11 13:16:47.000000
[timezone_type] => 3
[timezone] => UTC
)
[username] => Foo
[slug] => Foo
[role_id] => 0
[status] => Sandbox\Model\Enum\UserStatus Enum:int
(
[name] => Inactive
[value] => 0
)
[[new]] => 1
[[accessible]] => Array
(
[*] => 1
[id] =>
)
[[dirty]] => Array
(
[created] => 1
[modified] => 1
[username] => 1
[slug] => 1
[role_id] => 1
[status] => 1
)
[[original]] => Array
(
)
[[originalFields]] => Array
(
[0] => id
[1] => created
[2] => modified
[3] => username
[4] => slug
[5] => role_id
[6] => status
)
[[virtual]] => Array
(
)
[[hasErrors]] => 1
[[errors]] => Array
(
[email] => Array
(
[email] => Email invalid
)
)
[[invalid]] => Array
(
[email] =>
)
[[repository]] => Sandbox.SandboxUsers
)
Here you can see that it is now a backed enum object again.
XML
Since CakePHP 5.1.2
$result = Xml::fromArray(['user' => $user], ['pretty' => true])->saveXML();
<?xml version="1.0" encoding="UTF-8"?>
<user>
<id>1</id>
<created>2015-02-11 13:16:47</created>
<modified>2015-02-11 13:16:47</modified>
<username>Foo</username>
<slug>Foo</slug>
<email/>
<role_id>0</role_id>
<status>0</status>
</user>