RSS
 

Archive for December, 2016

CakePHP Tips Winter 2016

31 Dec

This is literally my last blog post for the year 2016 🙂

Use the right DateTime typehint

If you are passing DateTime objects around in CakePHP, they are usually Chronos objects,
and either \Cake\I18n\Time or \Cake\I18n\FrozenTime then.
Both those are not suited as typehint usually, as both are fine for most plugin solutions and also
limit the user in what kind of class he uses. At least FrozenTime would be a bit better then Time.

But what would be the correct typehint? Use DateTimeInterface from PHP.

public function addRow(DateTimeInterface $date, $content, $options = []) {
    $day = $date->format('d');
    ...
}

It is fairly simple to typehint that we need a DateTime object without unnecessarily restricting the plugin to a specific subset.

Note: This is only valid if you are not modifying the object, otherwise use the concrete one. But you really should try to code "immutable" and rather clone and then modify without altering the original value.

Nullable database fields

Nullable fields in the DB should usually either be null or have a concrete value. For foreign keys as well as most integer, float and even string fields that means, that an empty string as input coming in should be transformed to this null value then for data integrity.
Unfortunately, the CakePHP ORM doesn’t do that out of the box for some fields.

Why is it an issue? Well, imagine you want to query for all with an empty value for a field.
Instead of a simple 'field IS' => null you then need to do a more complex ['OR' => ['field IS' => null, 'field' => '']] all the time.
And if you forget it in a single query they results will be inconsistent. That’s kind of bad.

The Shim.Nullable behavior can help here.
Once attached, it will take care of those values when saving and make sure you don’t end up with different null values in your DB.

Leverage Composer scripts

The new cakephp/app skeleton contains some new interesting gotchas regarding composer scripts:

    "scripts": {
        ...
        "check": [
            "@test",
            "@cs-check"
        ],
        "test": "phpunit --colors=always",
        "cs-check": "phpcs --colors -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests",
        "cs-fix": "phpcbf --colors --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests"

That basically standardizes and simplifies the way the test and sniff calls are executed.
Before it was php phpunit and a long vendor/bin/phpcs ... call, now it is always just composer test or composer cs-fix etc.
Even doing together now is easy with composer check.

My apps and plugins use the PSR2R sniffer version, but from the outside the commands are completely identical, which is nice.

Note: The vendor/bin/ prefix part (as you might have expected) is not necessary as Composer will still find the scripts just fine.

Testing different databases

Locally, testing the CakePHP core for example defaults to SQLite.
The other databases, as they would need to have a valid connection, are skipped.

But if you are working on a specific feature that would need those to test if it is working, you would want to use that one instead.
Thanks to getenv() usage we can easily exchange the connection via db_dsn.

In your console:

export db_dsn="mysql://root:secret@127.0.0.1/cake_test"

This expects a "cake_test" database set up, and an access with the username root and the password secret (vagrant default for CakeBox here).

For Postgres it would probably be something like postgres://postgres@127.0.0.1/cakephp_test.
CakeBox also ships with that out of the box.

When you now run php phpunit.phar from now on, it will use that connection until you switch it again (for that session).

 
2 Comments

Posted in CakePHP