cakephp-utils plugin for CakePHP
This plugin is full of handly stuff, (*1)
Installation
You can install this plugin into your CakePHP application using composer., (*2)
The recommended way to install composer packages is:, (*3)
composer require 3xw/cakephp-utils
In src/Application.php, (*4)
use Cake\Http\BaseApplication;
...
class Application extends BaseApplication {
...
public function bootstrap()
{
parent::bootstrap();
this->addPlugin('Trois/Utils', ['routes' => true]);
}
..
}
ORM
Behaviors
- Sluggable Behavior
- Translate Behavior
Rules
Associations
MinMaxAssocationTrait
namespace App\Model\Table
use Trois\Utils\ORM\Traits\MinMaxAssocationTrait;
class SubscriptionsTable extends Table
{
use MinMaxAssocationTrait;
public function initialize(array $config)
{
...
// custom Association
$this->belongsToMinMax('MinPeriodes', [
'type' => 'MIN',
'field' => 'date',
'className' => 'Periodes',
'foreignKey' => 'subscription_id',
'targetForeignKey' => 'periode_id',
'joinTable' => 'subscriptions_periodes',
'joinType' => 'LEFT'
'joinAlias' => 'SP', // extra stuff: define yourself join alias
'joinExtraConditions' => ['SP.was_present' => true] // extra stuff: add conditions on join clause
]);
}
}
// OR
namespace App\Model\Table
use Trois\Utils\ORM\Traits\MinMaxAssocationTrait;
class LessonsTable extends Table
{
use MinMaxAssocationTrait;
public function initialize(array $config)
{
...
// custom Association
$this->hasOneMinMax('MinPeriode', [
'type' => 'MIN',
'field' => 'date',
'className' => 'Periodes',
'foreignKey' => 'lesson_id',
'joinType' => 'LEFT'
]);
}
}
Shell
View
Security
GUARD Component
This component allows you to check constructed request object and clean it if needed..., (*6)
in your src/Controller/AppController.php add following:, (*7)
public function initialize()
{
parent::initialize();
...
// Auth
$this->loadComponent('Auth', [...]);
// Guard
$this->loadComponent('Trois/Utils.Guard',[
'autoload_configs' => [
'Guard.requestBody' => 'guard_request_body'
]
]);
...
}
in config/guard_request_body.php, (*8)
<?php
use Cake\Http\ServerRequest;
return [
'Guard.requestBody' => [
[
'role' => '*',
'prefix' => 'Book',
'controller' => ['Users','Subscriptions'],
'action' => ['register','registerAndBook'],
'method' => ['POST','PUT'],
'rule' => function($user, $role, ServerRequest $request)
{
// magic here... manipulate request here
}
],
]];
CORS Middleware
in your src/Application.php add following:, (*9)
use Trois\Utils\Middleware\CorsMiddleware;
...
public function middleware($middlewareQueue)
{
$middlewareQueue
->add(new CorsMiddleware::class)
/* -- OR -- */
->add(new CorsMiddleware([
// thoses are default options
'all' => [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Expose-Headers' => 'X-Token',
'Access-Control-Max-Age' => '86400'
],
'options' => [
'methods' => 'GET, POST, OPTIONS, PUT, DELETE'
]
]))
...
}
Config example:, (*10)
config/bootsrap.php, (*11)
Configure::load('auth', 'default');
src/Controller/Appcontroller.php, (*12)
$this->loadComponent('Auth', Configure::read('Auth.V2'));
config/auth.php, (*13)
<?php
use Cake\Core\Configure;
return [
'Auth.V2' => [
'loginAction' => false,
'unauthorizedRedirect' => false,
'checkAuthIn' => 'Controller.initialize' ,
// Authenticate
'authenticate' => [
// map role
'all' => ['finder' => 'Auth'],
// Legacy X-API-TOKEN header token
'Trois/Utils.LegacyToken' => [
'key' => Configure::read('Legacy.key'),
'salt' => Configure::read('Legacy.salt')
],
// with Bearer JWT token
'Trois\Utils\Auth\JwtBearerAuthenticate' => [
'duration' => 3600
],
// Basic username + pass
'Trois\Utils\Auth\BasicToJwtBearerAuthenticate' => [
'fields' => ['username' => 'username'],
'passwordHasher' => 'Trois\Utils\Auth\LegacyPasswordHasher',
],
],
// Cache Storage Engine
'storage' => [
'className' => 'Trois\Utils\Auth\Storage\CacheStorage',
'cache' => 'token'
],
// Authorize
'authorize' => [
'CakeDC/Auth.SimpleRbac' => [
'autoload_config' => 'permissions',
],
],
]
];
Two factor Auth
set up Auth:, (*14)
'Trois\Utils\Auth\TwoFactorAuthenticate' => [
'userModel' => 'ExtranetUsers',
'passwordHasher' => 'App\Auth\NoHasher',
'fields' => [
'username' => 'CLIENT_ID',
'password' => 'Password'
],
'transmitter' => [
'class' => '\Trois\Utils\Auth\TwoFactor\EmailCodeTransmitter', // any child of Trois\Utils\Auth\TwoFactor\AbstractCodeTransmitter
'config' => [
'messages' => [
'success' => 'Un email avec un code vous a été envoyé',
'error' => 'L\'application n\'a pas pu vous envoyer de mail. Veuillez essayer Ă nouveau.'
],
'email' => [
'field' => 'Email',
'profile' => 'default',
'from' => ['info@xxx' => 'Site xxx'],
'subject' => 'Votre code personnel',
'emailFormat' => 'both',
'template' => 'Trois/Utils.default',
'layout' => 'default',
]
]
],
'verifyAction' => [
'prefix' => 'extranet',
'controller' => 'ExtranetUsers',
'action' => 'verify',
'plugin' => false
],
],
Cache
cache settings
in config folder create a cache.php file with as exemple:, (*15)
<?php
return [
'Trois.cache.settings' => [
'default' => 'default', // default cache config to use if not set in rules...
],
'Trois.cache.rules' => [
// cache request
[
'cache' => 'html', // default: 'default', can be a fct($request)
'skip' => false, // default: false, can be a fct($request)
'clear' => false, // default: false, can be a fct($request)
'compress' => true, // default: false, can be a fct($request)
//'key' => 'whatEver',// default is fct($request) => return $request->here()
'method' => ['GET'],
'code' => '200', // must be set or '*' !!!!!
'prefix' => '*',
'plugin' => '*',
'controller' => '*',
'action' => '*',
'extension' => '*'
],
// clear request
[
'cache' => 'html', // default: 'default'
'skip' => false, // default: false
'clear' => true, // default: false,
'key' => '*', // * => Cache::clear(false, cache) (Will clear all keys), 'whatEver' => Cache::delete('whatEver', cache), null => Cache::delete($request->here(), cache)
'method' => ['POST','PUT','DELETE'],
'code' => ['200','201','202'],
'prefix' => '*',
'plugin' => '*',
'controller' => ['Users','Pages'],
'action' => '*',
'extension' => '*'
],
]
];
Cache as your last middleware
in your src/Application.php file add the middleware as last chain block.
This will create or delete view renders as cache ( html/json /etc...), (*16)
<?php
namespace App;
...
use Trois\Utils\Middleware\ResponseCacheMiddleware;
class Application extends BaseApplication
{
public function middleware($middleware)
{
$middleware
...
// Apply Response caching
->add(ResponseCacheMiddleware::class);
return $middleware;
}
}
Retrieve cache via ActionCacheComponent
in your AppController load the component AFTER Auth!!, (*17)
$this->loadComponent('Awallef/Cache.ActionCache');
Retrieve cache via Nginx
First install nginx redis extension. Then set your cache config to store in redis. You can use my plugin..., (*18)
composer require awallef/cakephp-redis
Configure the engine in app.php like follow:, (*19)
'Cache' => [
...
'redis' => [
'className' => 'Trois/Utils.Redis',
'prefix' => 'hello.com:',
'duration' => '+24 hours',
'serialize' => true
],
...
]
Configure cache.php like follow:, (*20)
return [
'Trois.cache.settings' => [
'default' => 'redis', // default cache config to use if not set in rules...
],
'Trois.cache.rules' => [
// cache request
[
'skip' => false, // default: false, can be a fct($request)
'clear' => false, // default: false, can be a fct($request)
'compress' => true, // default: false, can be a fct($request)
//'key' => 'whatEver',// default is fct($request) => return $request->here()
'method' => ['GET'],
'code' => '200', // must be set or '*' !!!!!
'prefix' => '*',
'plugin' => '*',
'controller' => '*',
'action' => '*',
'extension' => '*'
],
// clear request
[
'clear' => true,
'key' => '*',
'method' => ['POST','PUT','DELETE'],
'code' => ['200','201','202','302'], // 302 is often triggered by cakephp in case of success crud operation...
'prefix' => '*',
'plugin' => '*',
'controller' => '*',
'action' => '*',
'extension' => '*'
],
]
];
Configure Nginx too:, (*21)
map $http_accept $hello_com_response_header {
default "text/html; charset=UTF-8";
"~*json" "application/json; charset=UTF-8";
}
server {
listen 443;
server_name hello.com;
ssl on;
...
# redis key
set $redis_key "hello.com:$request_uri";
if ($args) {
set $redis_key "hello.com:$request_uri?$args";
}
location / {
redis_pass 127.0.0.1:6379;
error_page 404 405 502 504 = @fallback;
more_set_headers "Content-Type: $hello_com_response_header";
}
#default cake handling
location @fallback {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Redis caching
This plugin provides a very little bit different redis engine based on cakephp's RedisEngine.
differences are:, (*22)
- Engine config comes with a bool 'serialize' option ( default is true )
- Read and wirte fct use config 'serialize' option
- Keys are stored/read/deleted in order to uses : and :* redis skills!
Configure the engine in app.php like follow:, (*23)
'Cache' => [
...
'redis' => [
'className' => 'Trois/Utils.Redis',
'prefix' => 'www.your-site.com:',
'duration' => '+24 hours',
'serialize' => true
],
...
]