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>

More examples


Send your feedback or bugreport!