CakePHP ActionsClass plugin
, (*1)
This plugin gives you the ability to manage your CakePHP Controller actions as single classes. Each action of your Controllers will be managed in its own object., (*2)
Requirements
- PHP >= 7.0.0
- CakePHP 3.4.X
Installation
You can install this plugin into your CakePHP application using Composer., (*3)
The recommended way to install it is:, (*4)
composer require havokinspiration/cakephp-actions-class
Loading the plugin
It is recommanded to load this plugin using the bootstrap()
method of your application's Application
class. This is needed if you want to be able to use Integration testing:, (*5)
// in src/Application.php
namespace App;
use Cake\Core\Plugin;
use Cake\Http\BaseApplication;
class Application extends BaseApplication
{
public function bootstrap()
{
parent::bootstrap();
Plugin::load('HavokInspiration/ActionsClass', ['bootstrap' => true]);
}
}
Loading the plugin bootstrap file is mandatory., (*6)
Usage
By default, CakePHP controller management is based around one controller (which represents one part of your application, for instance "Posts") which is a single class containing one public method per actions needed to be exposed in your application:, (*7)
// in src/Controller/PostsController.php
namespace App\Controller;
use Cake\Controller;
class PostsController extends Controller
{
public function index() {}
public function add() {}
public function edit($id) {}
}
As your application grows, your controllers grow as well. In the end, on large applications, you can end up with big controller files with lots of content, making it hard to read through. You might even be in the case where you need to have methods specific to some actions in the middle of other methods dedicated to other actions., (*8)
This is where the cakephp-actions-class plugin is useful. When enabled, you can have your controllers actions as single classes., (*9)
So the PostsController
example given above would become:, (*10)
// in src/Controller/Posts/IndexAction.php
namespace App\Controller\Posts;
use HavokInspiration\ActionsClass\Controller\Action;
class IndexAction extends Action
{
public function execute() {}
}
// in src/Controller/Posts/AddAction.php
namespace App\Controller\Posts;
use HavokInspiration\ActionsClass\Controller\Action;
class AddAction extends Action
{
public function execute() {}
}
// in src/Controller/Posts/EditAction.php
namespace App\Controller\Posts;
use HavokInspiration\ActionsClass\Controller\Action;
class EditAction extends Action
{
public function execute($id) {}
}
Living in the following directory structure :, (*11)
src/
Controller/
Posts/
AddAction.php
EditAction.php
IndexAction.php
Your Action
classes are only expected to hold an execute()
method. It can receive passed parameters as in regular controller actions (meaning the URL /posts/edit/5
will pass 5
to the argument $id
in the execute()
method of the EditAction
class in our previous example)., (*12)
Using the bake
command-line to create Action classes
You first need to load the plugin in your config/bootstrap_cli.php :, (*13)
Plugin::load('HavokInspiration/ActionsClass');
You can then create an Action class file with the following command :, (*14)
bin/cake bake action Posts/Index
The command expects the name to get the controller name and the action name separated by a forward slash. For instance, the above example would create a IndexAction
file for the Posts
controller., (*15)
You can also specify the routing prefix your controller action lives under by using the --prefix
option :, (*16)
bin/cake bake action Posts/Index --prefix Admin
If you want to create an action file for a plugin, you can use the --plugin
option :, (*17)
bin/cake bake action Posts/Index --plugin MyPlugin
You can of course use both together :, (*18)
bin/cake bake action Posts/Index --plugin MyPlugin --prefix Admin
By default, baking an action class will generate the corresponding test file. You can skip the test file generation by using the --no-test
boolean flag :, (*19)
bin/cake bake action Posts/Index --no-test
Compatibility
This plugin was designed to have a maximum compatibility with the regular CakePHP behavior., (*20)
Fallback to CakePHP regular behavior
If you wish to use this plugin in an existing application, it will first try to provide a response using an Action
class. If an action class matching the routing parameters can not be found, it will let CakePHP fallback to its regular behavior (meaning looking for a Controller
class)., (*21)
This also means that you can develop a plugin with controllers implementing this behavior without breaking the base application (since, when the cakephp-actions-class plugin is loaded, the Dispatcher will first try to load an Action
class and fallback to the regular Controller
dispatching behavior if it can not find a proper Action
class to load)., (*22)
Everything you do in Controller can be done in an Action class
Under the hood, Action
classes are instances of \Cake\Controller\Controller
, meaning that everything you do in a regular Controller
class can be done in an Action
class.
Every events (like beforeFilter
or beforeRender
) a controller fires are also fired by Action
classes., (*23)
Loading Components
// in src/Controller/Posts/EditAction.php
namespace App\Controller\Posts;
use HavokInspiration\ActionsClass\Controller\Action;
class EditAction extends Action
{
public function initialize()
{
$this->loadComponent('Flash');
}
public function execute($id)
{
// some logic
$this->Flash->success('Post updated !');
}
}
Actions in Controllers under a routing prefix
Action
classes can live under a routing prefix or a plugin :, (*24)
// in src/Controller/Posts/EditAction.php
// Assuming that `Admin` is a routing prefix
namespace App\Controller\Admin\Posts;
use HavokInspiration\ActionsClass\Controller\Action;
class EditAction extends Action
{
public function execute($id)
{
}
}
Integration testing
The plugin is compatible with the Integration testing feature of CakePHP.
You just need to follow the same directory pattern as you'd do for you application:, (*25)
tests
/TestCase
/Controller
/Posts
/IndexActionTest.php
And a basic test file would look like:, (*26)
// in tests/TestCase/Controller/Posts/IndexActionTest.php
namespace App\Test\TestCase\Controller;
use Cake\TestSuite\IntegrationTestCase;
class IndexActionTest extends IntegrationTestCase
{
public function testIndexAction()
{
$this->get('/posts');
$this->assertResponseOk();
}
}
Make sure you load the plugin in the App\Application::bootstrap()
method, otherwise, Integration tests will not work. See the "Loading the plugin" section of this README to know how to., (*27)
No-op methods
As seen above, Action
classes are instance of \Cake\Controller\Controller
. Some methods in this class are related to actions. But since we are now having objects that represent actions, two methods had to be made "no-op" : \Cake\Controller\Controller::isAction()
and \Cake\Controller\Controller::setAction()
. Using these methods in an Action
subclass will have no effect., (*28)
Configuration
Strict Mode
As seen above, the plugin will let CakePHP handle the request in its regular dispatching cycle if an action can not be found. However, if you wish to only use Action
classes, you can enable the strict mode. With strict mode enabled, the plugin will throw an exception if it can not find an Action
class matching the request currently being resolved., (*29)
To enable the strict mode, set it using the Configure
object in the bootstrap.php file of your application:, (*30)
Configure::write('ActionsClass.strictMode', true);
Contributing
If you find a bug or would like to ask for a feature, please use the GitHub issue tracker.
If you would like to submit a fix or a feature, please fork the repository and submit a pull request., (*31)
Coding standards
This repository follows the PSR-2 standard. Some methods might be prefixed with an underscore because they are an overload from existing methods inside the CakePHP framework.
Coding standards are checked when a pull request is made using the Stickler CI bot., (*32)
License
Copyright (c) 2015 - 2017, Yves Piquel and licensed under The MIT License.
Please refer to the LICENSE.txt file., (*33)