Best practices

DOs and DONT's of common CakePHP problems.

Config

The easiest approach for the beginning is a local config file that is not under version control per environment/server.
  • app.php (CakePHP core configs)
  • app_custom.php (project overwriting on top)
  • app_local.php (env specific, contains sensitive keys/pwds, in gitignore!)
Then in your bootstrap code:
Configure::load('app', 'default', false);
Configure::load('app_custom');
Configure::load('app_local');

The main idea here is to ease upgrading in minors as the app.php one is the one from cakephp/app repo always and it makes it easy to diff and know what new config keys are added with each minor. If you overwrite it directly, this will get very messy otherwise.

Check this applications's config folder on how it is done.

Note: You can also leave out the app_local file, and instead use env() wrapper to read from environment variables.
People often use the env() approach on the server, and the local file on the development machine locally.

Routing

Best to use the DashedRoute class (see conventions) as default one:
use Cake\Routing\Route\DashedRoute;

Router::defaultRouteClass(DashedRoute::class);
This way your URLs are `my-prefix/my-plugin/controller-name/action-name` whereas your URL contains the CamelCase variants:
'prefix' => 'my-prefix // unmodified
'plugin' => 'MyPlugin, // camelCased
'controller' => 'ControllerName', // camelCased
'action' => 'actionName' // camelBacked
The idea of CakePHP 3.x is to inflect internally as less as possible.

URLs

Use array URLs wherever possible, this saves you a lot of trouble once you actually want to customize the routing:
// URL /my-controller/my-action
echo $this->Html->link($title, ['controller' => 'MyController', 'action' => 'myAction']);
You can then alter the URLs via Routing and all those URLs change cleanly. The speed issue can be neglected compared to the advantages of the flexibility. You can also use "named routes", of course (`['_name' => 'admin:account:password']`). This centralizes the arrays into the routes config.

Don't sanitize the heck out your data

Use Sanitization wisely, and not blindly.
Not without reason the Sanitize class has been kicked out of the core files.

Sanitization is useful and necessary, when working with HTML content, that needs to be stripped of invalid/dangerous markup. But here it is best to use plugins specifically written for this job.
For most normal use cases, by using save(), SQL injections are already prevented. No need to modify the data upon save. Only use h() in the view to secure (stringish) output by escaping potentially dangerous chars:
echo h($entity->name);

Logging

By default the log streams catch all, even scoped logs that should only go to those scoped listeners. As a result they are duplicated.
So I would change the scopes to false here for all default listeners:
// in your app.php config
'Log' => [
	'debug' => [
		'scopes' => false,
	],
	'error' => [
		'scopes' => false,
	],
	...
],

Another very useful addition is to log 404s separately from actual (internal) errors happening using Tools.ErrorHandler.

Use AJAX wisely

Don't over-ajaxify your views. It can easily create complications and become error-prone. Use it wisely where it makes sense.
Also always try to provide a non-JS fallback solution in case the JS breaks or cannot work properly in some browsers. It might also be a good idea for search engines to properly pick up your site content (e.g. when using pagination and AJAX).

So the smart approach is: First code the non-JS functionality. And then you can add JS-functionality for it on top. In case the JS breaks, the non-js part can take over without users being unable to proceed.

Deployment

You should automate your deployment process, e.g. via basic deploy.sh file. Then you can just execute `./deploy.sh`.

Always use your `www-data` user for this and never root, otherwise you will have some serious permissions problems afterwards.

The following file is an example sh script for a very very basic single server update process:

#!/bin/bash
bin/cake Setup.MaintenanceMode activate

echo "### CODE ###";
git pull

php composer.phar install --prefer-dist --no-dev --optimize-autoloader --no-interaction

mkdir -p ./tmp
mkdir -p ./logs
mkdir -p ./webroot/js/cjs/
mkdir -p ./webroot/css/ccss/

echo "### DB MIGRATION ###";
bin/cake Migrations migrate -p Geo // and all other plugin migrations
bin/cake Migrations migrate

echo "### ASSETS ###";
bower install // or npm -i etc

mkdir -p ./webroot/css/fonts
cp -R ./webroot/assets/bootstrap/dist/fonts/* ./webroot/css/fonts/

bin/cake AssetCompress.AssetCompress build

echo "### CLEANUP ###";
bin/cake clear cache

echo "### CACHE WARMING ###;
bin/cake orm_cache build

echo "### DONE ###";
bin/cake Setup.MaintenanceMode deactivate

It should at least contain:

  • composer install (from lock file!)
  • DB Migration
  • Asset update
  • Cache invalidation

Wrapping it with maintenance mode can help to avoid side effects for users currently on the website.

Note that the above script is after initial deployment. For a first checkout you will need to clone the repository and probably set up a few more things like your app_local.php.

Send your feedback or bugreport!