Note
CRUD already provides the basic Index
, View
, Add
, Edit
and
Delete
actions, so you do not need to implement these on your own.
You can find the documentation for these actions in the menu to the left.
Crud Actions are the backbone of the plugin, this is where most of the logic happens.
A Crud action roughly translates to a normal Controller action. The primary difference is that CRUD actions are made to be as generic and secure out of the box as possible.
You can consider a CRUD action as a more flexible PHP trait that fits nicely within the CakePHP ecosystem.
A nice added bonus is that if all your controllers share a generic controller action, you only have to unit test once, to test every controller in your application.
Below is the code for the Index Crud Action
In the next few sections we will walk through the code and explain how it works, and what every single line of code does. For each section, the relevant lines of code will be highlighted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php
namespace Crud\Action;
class Index extends BaseAction
{
/**
* Generic handler for all HTTP verbs
*
* @return void
*/
protected function _handle()
{
$subject = $this->_subject();
$subject->set(['success' => true, 'viewVar' => $this->viewVar()]);
$this->_trigger('beforePaginate', $subject);
$controller = $this->_controller();
$items = $controller->paginate();
$subject->set(['items' => $items]);
$this->_trigger('afterPaginate', $subject);
$controller->set(['success' => $subject->success, $subject->viewVar => $subject->items]);
$this->_trigger('beforeRender', $subject);
}
}
|
All build-in actions in Crud live in the Crud\Action
namespace.
All actions in Crud, even your own, should inherit from the
Crud\Action\Base
class.
This class is abstract
and provides numerous auxiliary methods which can be
useful for you both as a developer as an action creator.
Where you choose to put your custom Crud actions is up to you, but using src/Crud
is a good place. Remember to
namespace your action class accordingly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php
namespace Crud\Action;
class Index extends BaseAction
{
/**
* Generic handler for all HTTP verbs
*
* @return void
*/
protected function _handle()
{
$subject = $this->_subject();
$subject->set(['success' => true, 'viewVar' => $this->viewVar()]);
$this->_trigger('beforePaginate', $subject);
$controller = $this->_controller();
$items = $controller->paginate();
$subject->set(['items' => $items]);
$this->_trigger('afterPaginate', $subject);
$controller->set(['success' => $subject->success, $subject->viewVar => $subject->items]);
$this->_trigger('beforeRender', $subject);
}
}
|
Next is the method _handle
. A Crud Action can respond to any HTTP verb
(GET
, POST
, PUT
, DELETE
).
Each HTTP verb can be implemented as method, e.g. _get()
for HTTP GET,
_post()
for HTTP POST and _put()
for HTTP PUT.
If no HTTP verb specific method is found in the class, _handle()
will be
executed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php
namespace Crud\Action;
class Index extends BaseAction
{
/**
* Generic handler for all HTTP verbs
*
* @return void
*/
protected function _handle()
{
$subject = $this->_subject();
$subject->set(['success' => true, 'viewVar' => $this->viewVar()]);
$this->_trigger('beforePaginate', $subject);
$controller = $this->_controller();
$items = $controller->paginate();
$subject->set(['items' => $items]);
$this->_trigger('afterPaginate', $subject);
$controller->set(['success' => $subject->success, $subject->viewVar => $subject->items]);
$this->_trigger('beforeRender', $subject);
}
}
|
You can treat the _handle()
method as a catch-all, if your crud action
wants to process all possible HTTP verbs.
An advantage of this setup is that you can separate the logic on a request type level instead of mixing all of the logic into one big block of code.
For example the Edit Crud Action implements _get()
,
_post()
and _put()
methods. The _get()
method simply reads the entity
from the database and passes it to the form, while _put()
handles validation
and saving the entity back to the database.
All Crud actions emit a range of events, and all of these events always contain a Crud Subject. The Crud Subject can
change its state between emitted events. This object is a simple StdClass
which contains the current state of the Crud request.
The real beauty of Crud is the events and the flexibility they provide.
All calls to _trigger()
emit an event, that you as a developer can listen to and inject your own application logic.
These events are in no way magical, they are simply normal
CakePHP events, dispatched like all
other events in CakePHP.
You can for example listen for the beforePaginate
event and add conditions to your pagination query, just with a
few lines of code. Those few lines of code is what makes your application unique. The rest of the code you would
normally have is simply repeated boiler plate code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php
namespace Crud\Action;
class Index extends BaseAction
{
/**
* Generic handler for all HTTP verbs
*
* @return void
*/
protected function _handle()
{
$subject = $this->_subject();
$subject->set(['success' => true, 'viewVar' => $this->viewVar()]);
$this->_trigger('beforePaginate', $subject);
$controller = $this->_controller();
$items = $controller->paginate();
$subject->set(['items' => $items]);
$this->_trigger('afterPaginate', $subject);
$controller->set(['success' => $subject->success, $subject->viewVar => $subject->items]);
$this->_trigger('beforeRender', $subject);
}
}
|
Only the code that you would normally have in your controller is left now.
While these 3 lines seem simple, and the whole Crud implementation a bit overkill at first, the true power of this setup will be clear when your application grows and the requirements increase.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?php
namespace Crud\Action;
class Index extends BaseAction
{
/**
* Generic handler for all HTTP verbs
*
* @return void
*/
protected function _handle()
{
$subject = $this->_subject();
$subject->set(['success' => true, 'viewVar' => $this->viewVar()]);
$this->_trigger('beforePaginate', $subject);
$controller = $this->_controller();
$items = $controller->paginate();
$subject->set(['items' => $items]);
$this->_trigger('afterPaginate', $subject);
$controller->set(['success' => $subject->success, $subject->viewVar => $subject->items]);
$this->_trigger('beforeRender', $subject);
}
}
|
For example adding an API layer to your application later in time will be easy because you don’t need to edit all your applications many controllers.
Using Crud, it would be as simple as loading the API listener and everything would be taken care of. All validation, exceptions, success and error responses would work immediately, and with just a few lines of code.
This is because the powerful event system can hook into the request and hijack the rendering easily and effortlessly; something baked controllers do not offer.
Crud has many actions built-in which come with the plugin.