Events are the backbone of Crud, and your primary gateway into customization of Crud and fitting it to your applications.
You can subscribe to events from almost everywhere, and in multiple ways.
We override the implementedEvents()
method in the controller, and bind
the Crud.beforeFind
event to the _beforeFind()
method in the controller.
When using this technique, you need to prefix all the event names with Crud.
Most of the other ways to listen for events do not need this, as it’s done automatically.
namespace app\Controller;
class BlogsController extends AppController
{
public function implementedEvents()
{
return parent::implementedEvents() + ['Crud.beforeFind' => '_beforeFind'];
}
public function _beforeFind(\Cake\Event\Event $event)
{
}
}
Note
It’s important that the controller event method is public
, since it’s called
from the CakePHP event manager, outside of the Controller scope.
The added _
prefix is there only to prevent it being executed as an controller
action.
You can bind events directly in your controller actions, simply call the on()
method in Crud and provide a
callback. The example below uses a closure
for the callback, but everything that is valid for call_user_func()
can be used
public function view($id)
{
$this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
// Will only execute for the view() action
});
return $this->Crud->execute();
}
Note
When implementing events in your controller actions, it’s important to
include return $this->Crud->execute();
otherwise Crud will not process the action.
The benefit of the controller method is that you can easily share it between two actions, like below.
public function view($id)
{
$this->Crud->on('beforeFind', [$this, '_beforeFind']);
return $this->Crud->execute();
}
public function admin_view($id)
{
$this->Crud->on('beforeFind', [$this, '_beforeFind']);
return $this->Crud->execute();
}
public function _beforeFind(\Cake\Event\Event $event)
{
// Will execute for both view() and admin_view()
}
Different Crud actions will emit a different combination of events during their execution, with different Subject data. If you are looking for events specific to an action, check the specific Crud action documentation page.
This is a full list of all events emitted from Crud.
Triggered when a CrudAction
is going to handle a CakePHP request.
It’s emitted from CrudComponent::beforeFilter
and thus is fired in the same cycle as all Controller::beforeFilter
events.
Called after the Controller::beforeFilter()
and before the Crud action.
It’s emitted from CrudComponent::startup()
and thus is fired in the same cycle
as all Component::startup()
events.
This event is emitted before calling Table::delete
.
The Crud Subject contains the following keys:
Entity
from the find()
call.To abort a delete()
simply stop the event by calling
$event->stopPropagation()
.
public function delete($id)
{
$this->Crud->on('beforeDelete', function(\Cake\Event\Event $event) {
// Stop the delete event, the entity will not be deleted
if ($event->getSubject()->entity->author !== 'admin') {
$event->stopPropagation();
}
});
return $this->Crud->execute();
}
This event is emitted after Table::delete()
has been called.
The Crud Subject contains two keys:
true
the delete()
call succeeded, false
otherwisepublic function delete($id)
{
$this->Crud->on('afterDelete', function(\Cake\Event\Event $event) {
if (!$event->getSubject()->success) {
$this->log("Delete failed for entity " . $event->getSubject()->id);
}
});
return $this->Crud->execute();
}
The event is emitted before calling the find method in the table.
The Crud Subject contains the following keys:
Repository
(Table
) which the query will be executed against.Query
object from the Repository
where $PrimaryKey => $IdFromRequest
is already added to the conditions.This is the last place you can modify the query before it’s executed against the database.
Note
An example
Given the URL: /posts/view/10
the repository
object will be an instance of PostsTable
and the query
includes a WHERE
condition with Posts.id = 10
After the event has emitted, the database query is executed with LIMIT 1
.
If a record is found the Crud.afterFind
event is emitted.
Warning
If no record is found in the database, the recordNotFound
event is emitted instead of Crud.afterFind
.
public function delete($id)
{
$this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
$event->getSubject()->query->where(['author' => $this->Auth->user('id')]);
});
return $this->Crud->execute();
}
After the query has been executed, and a record has been found this event is emitted.
The Crud Subject contains two keys:
id
The ID that was originally passed to the action and is usually the primary key of your model.entity
The record that was found in the database.Note
If an entity is not found, the RecordNotFound
event is emitted instead.
public function delete($id)
{
$this->Crud->on('afterFind', function(\Cake\Event\Event $event) {
$this->log("Found item: " . $event->getSubject()->entity->id . " in the database");
});
return $this->Crud->execute();
}
Note
Do not confuse this event with the beforeSave
callback in the ORM layer
Called right before calling Table::save()
.
The Crud Subject contains the following keys:
entity
object marshaled with the HTTP POST
data from the request.string
with the saveMethod
.array
with the saveOptions
.All modifications to these keys will be passed into the Table::$saveMethod
.
Warning
After this event has been emitted, changes done through the $action->saveMethod()
or $action->saveOptions()
methods will no longer affect the code, as the rest of the code uses the values from the Crud Subject
Note
Do not confuse this event with the afterSave
callback in the ORM layer.
This event is emitted right after the call to Table::save()
.
The Crud Subject contains the following keys:
Table::save()
call succeed or not.true
if the record was created
and false
if the record was updated
.entity
object marshaled with the HTTP POST
data from the request and the save()
logic.public function edit($id)
{
$this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
if ($event->getSubject()->created) {
$this->log("The entity was created");
} else {
$this->log("The entity was updated");
}
});
return $this->Crud->execute();
}
public function edit($id)
{
$this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
if ($event->getSubject()->success) {
$this->log("The entity was saved successfully");
} else {
$this->log("The entity was NOT saved successfully");
}
});
return $this->Crud->execute();
}
public function add()
{
$this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
if ($event->getSubject()->created) {
$this->log("The entity was created with id: " . $event->getSubject()->entity->id);
}
});
return $this->Crud->execute();
}
This event is emitted before Controller::paginate()
is called.
public function index()
{
$this->Crud->on('beforePaginate', function(\Cake\Event\Event $event) {
$this->paginate['conditions']['is_active'] = true;
});
return $this->Crud->execute();
}
This event is emitted right after the call to Controller::paginate()
.
The entities
property of the event object contains all the database records found in the pagination call.
public function index()
{
$this->Crud->on('afterPaginate', function(\Cake\Event\Event $event) {
foreach ($event->getSubject()->entities as $entity) {
// $entity is an entity
}
});
return $this->Crud->execute();
}
Simple and event driven wrapper for Controller::redirect()
.
The Crud Subject contains the following keys:
Controller::redirect()
.Controller::redirect()
.Controller::redirect()
.Entity
from the previously emitted event.All keys can be modified as you see fit, at the end of the event cycle they will be passed
directly to Controller::redirect()
.
The redirect $url
can be changed on the fly either by posting a redirect_url
field from your
form or by providing a redirect_url
HTTP query key.
The default for most redirects are simply to return to the index()
action.
Invoked right before the view will be rendered.
This is also before the controllers own beforeRender callback.
Note
This event will throw an exception.
The default configuration will thrown an Cake\Error\NotFoundException
which will yield a 404 response.
The event is triggered after a find
did not find any records in the database.
You can modify the exception class thrown using CrudComponent::message
method
Simple and event driven wrapper for SessionComponent::setFlash
.
The Crud Subject contains the following keys:
SessionComponent::setFlash
.SessionComponent::setFlash
.SessionComponent::setFlash
.SessionComponent::setFlash
.Entity
from the previously emitted event.All keys can be modified as you see fit, at the end of the event cycle they will be passed
directly to SessionComponent::setFlash
.
Defaults are stored in the messages
configuration array for each action.
If you do not want to use this feature, simply stop the event by calling its stopPropagation()
method.
If you’d like to customise the flash messages that are used, perhaps you’re using friendsofcake/bootstrap-ui. It’s actually quite simple to do, and can be done as part of the component configuration or on the fly.
public function initialize()
{
$this->loadComponent('Crud.Crud', [
'actions' => [
'edit' => [
'className' => 'Crud.Edit',
'messages' => [
'success' => [
'params' => ['class' => 'alert alert-success alert-dismissible']
],
'error' => [
'params' => ['class' => 'alert alert-danger alert-dismissible']
]
],
]
]
]);
}
If you’d like to configure it on the fly you can use the eventManager to change the event subject as the event is emitted.
$this->eventManager()->on('Crud.setFlash', function (Event $event) {
if ($event->getSubject()->success) {
$event->getSubject()->params['class'] = ['alert', 'alert-success', 'alert-dismissible'];
}
});