Improve this Doc

Actions

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.

Actions are the backbone of CRUD - this is where most of the logic happens.

A Crud Action contains more or less the exact same code as a normal controller action.

The main difference between your normal Controller actions and a CRUD Action is that the CRUD Action is highly generic and flexible.

What Is An Action?

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.

The Anatomy Of An Action

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
<?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);
	}

}

Class And Namespace

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.

 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
<?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);
	}

}

Request Methods

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
<?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.

Events & Subject

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
<?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);
	}

}

Boilerplate

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
<?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 non-trivial and time consuming if you do not use crud - especially if you have 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.

More On Actions