Improve this Doc

Contents

Introduction

CRUD was built to be scaffolding on steroids, and allow developers to have enough flexibility to use it for both rapid prototyping and production applications, even on the same code base – saving you time.

Why Use Crud

  • CRUD is very fast to install, a few minutes tops.
  • CRUD is very flexible and has tons of configuration options (but very sane defaults, just like CakePHP).
  • CRUD aims to stay out of your way, and if it happens to get in your way, you can change the undesired behavior very easily.
  • CRUD relies heavily on CakePHP events making it possible to override, extend, or disable almost all of CRUD’s functionality either globally or for one specific action.
  • CRUD removes the boilerplate code from your controllers, which mean less code to maintain, and less code to spend time unit testing.
  • CRUD will dynamically add the actions to your controller so you don’t have to re-implement them over and over again.
  • CRUD does not have the same limitations as CakePHP’s own scaffolding, which is “my way or the highway.” CRUD allows you to hook into all stages of a request, only building the controller code needed specifically for your business logic, outsourcing all the heavy boiler-plating to CRUD.
  • CRUD allows you to use your own views, baked or hand-crafted, in addition to adding the code needed to fulfill your application logic, using events. It is by default compatible with CakePHP’s baked views.
  • CRUD also provides built in features for JSON API for any action you have enabled through CRUD, which eliminates maintaining both a HTML frontend and a JSON and/or XML interface for your applications – saving you tons of time and having a leaner code base.
  • CRUD uses the MIT license, just like CakePHP.

Bugs

If you happen to stumble upon a bug, please feel free to create a pull request with a fix (optionally with a test), and a description of the bug and how it was resolved.

You can also create an issue with a description to raise awareness of the bug.

Features

If you have a good idea for a Crud feature, please join us on IRC and let’s discuss it. Pull requests are always more than welcome.

Support / Questions

You can join us on IRC in the #FriendsOfCake channel on irc.freenode.net for any support or questions.

Installation

Installing composer is quick and simple.

Requirements

  • CakePHP 3.x
  • PHP 5.4

Getting the source code

You can get the Crud source code using either composer or git.

Using composer

The recommended installation method for this plugin is by using composer.

Using the inline require for composer:

composer require friendsofcake/crud:~4.0

Or add this to your composer.json configuration:

{
        "require" : {
                "FriendsOfCake/crud": "~4.0"
        }
}

Using git submodule

Or add it as a git module, this is recommended over git clone since it’s easier to keep up to date with development that way:

git submodule add git://github.com/FriendsOfCake/crud.git Plugin/Crud
cd Plugin/Crud

Loading the plugin

Add the following to your /App/Config/bootstrap.php

Plugin::load('Crud');

Configuring the controller

In your AppController add the following code:

<?php
namespace App\Controller;

class AppController extends \Cake\Controller\Controller {

        use \Crud\Controller\ControllerTrait;

}

Note

It’s not required to add the ControllerTrait to AppController - you can add it to any specific controller as well if you don’t want Crud installed application wide

Adding the ControllerTrait itself do not enable anything CRUD, but simply installs the code to handle the \Cake\Error\MissingActionException exception so you don’t have to implement an action in your controller for Crud to work. This will make a lot of sense later.

The Configuration page explains how to setup and configure the Crud component.

Quick Start

You are busy, and you just want to get things done™, so let’s get going.

After installation, you are ready to CRUD-ify your app.

The application

So the application our pointy-haired boss has tasked us to create today is a Blog.

App Controller

Since CRUD is awesome, and you already started to kinda love it, we want to enable CRUD for our entire application.

Let’s setup CRUD to handle all index(), add(), edit(), view() and delete() actions automatically, we do this by enabling Crud in the AppController with the correct actions configuration.

<?php
namespace App\Controller;

class AppController extends \Cake\Controller\Controller {

  use \Crud\Controller\ControllerTrait;

  public $components = [
    'Crud.Crud' => [
      'actions' => [
        'Crud.Index',
        'Crud.Add',
        'Crud.Edit',
        'Crud.View',
        'Crud.Delete'
      ]
    ]
  ];
}

There we go, that was easy.

Posts Controller

So, our new shiny Blog needs a Posts Controller to, well, manage the posts.

<?php

namespace App\Controller;

class PostsController extends AppController {

}

(...) and that’s it! we don’t really need any logic there for now, since we have configured CRUD to take care of all actions

But... since CRUD doesn’t provide any views (yet), you will have to bake the views for now

Console/cake bake view posts

Let’s check out our new application, go to /posts and behold, a nice paginated ìndex() template, all without any code in your controller.

You should now be able to navigate to /posts/add as well and create your first post.

Creating an API

This section is WIP.

Configuration

Configuration of Crud is done through the Crud component - either on the fly anywhere in you application, or by providing the configuration in the Controller::$components property.

Assuming you have followed the installation guide we will now begin the actual configuration of Crud.

Crud is loaded like any other Component in CakePHP - simply by adding it to the $components variable in the controller

class AppController extends \Cake\Controller\Controller {

  public $components = ['Crud.Crud'];

}

At this time, the Crud Component is loaded and ready for usage.

However, Crud has not been configured to handle any controller actions yet.

Actions

Configuring Crud to handle actions is simple.

The list of actions is provided either as Component configuration, or on the fly.

An example of Component configuration:

class AppController extends \Cake\Controller\Controller {

  public $components = [
    'Crud.Crud' => [
      'actions' => ['Crud.Index']
    ]
  ];

}

An example of on the fly enabling an Crud action:

class AppController extends \Cake\Controller\Controller {

  public function beforeFilter(\Cake\Event\Event $event) {
    $this->Crud->mapAction('index', 'Crud.Index');
  }

}

The examples above are functionally identical, and instructs Crud to handle the index action in controllers using Crud.Index action class.

Note

If you do not wish for Crud to be enabled across all controllers, or even use all actions provided by Crud you can pick and chose which to use. Crud will not force take-over any application logic, and you can enable/disable them as you see fit.

Action configuration

Note

Each Crud Action have a different set of configuration settings, please see their individual documentation page for more information.

Passing in configuration for an action is simple.

Note

In the examples below, we reconfigure the Index Action to render my_index.ctp instead of index.ctp

An example of Component configuration

class AppController extends \Cake\Controller\Controller {

  public $components = [
    'Crud.Crud' => [
      'actions' => [
        'index' => ['className' => 'Crud.Index', 'view' => 'my_index']
      ]
    ]
  ];

}

An example of on the fly enabling an Crud action with configuration

class AppController extends \Cake\Controller\Controller {

  public function beforeFilter(\Cake\Event\Event $event) {
    $this->Crud->mapAction('index', [
      'className' => 'Crud.Index',
      'view' => 'my_index'
    ]);
  }

}

Disabling loaded actions

If you’ve loaded an action in eg. your AppController - but don’t want it included in a specific controller, it can be disabled with the $this->Crud->disable(['action_name']).

Example of disable a loaded action:

class AppController extends \Cake\Controller\Controller {

  public $components = [
    'Crud.Crud' => [
      'actions' => ['Crud.Index', 'Crud.View', 'Crud.Delete', 'Crud.Edit']
    ]
  ];

}
class PostsController extends AppController {

  public function beforeFilter(\Cake\Event\Event $event) {
    parent::beforeFilter($event);

    $this->Crud->disable(['Edit', 'Delete']);
  }

}

Built-in actions

Crud provides the default CRUD actions out of the box.

It’s possible to create your own Crud Action as well, or overwrite the built-in ones.

Simply provide the className configuration key for an action, and Crud will use that one instead.

Listeners

Note

Each Crud Listener have a different set of configuration settings, please see their individual documentation page for more information.

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

Index

The Index Crud Action paginates over the primary model in the controller.

On a high level it’s basically just calling Controller::paginate().

Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

view

Get or set the view file to render at the end of the request.

The view setting is passed directly and unmodified to Controller::render().

To get the current configured view call the view method without any arguments.

$this->Crud->action()->view();

To change the view to render, pass a string as first argument.

$this->Crud->action()->view('my_custom_view');

Note

If the first parameter is NULL - which is the default value - the normal CakePHP behavior will be used.

Warning

Due to the nature of this method, once a custom view has been set, it’s not possible to revert back to the default behavior by calling ->view(null) as it will return the current configuration.

viewVar

Note

This maps directly to the $key argument in Controller::set($key, $value)

Change the name of the variable which contains the result of a index or view action query result.

To get the current configured viewVar call the viewViar method without any arguments.

$this->Crud->action()->viewVar();

To change the viewVar, pass a string as first argument.

$this->Crud->action()->viewVar('items');

For Index Action the default is plural version of the controller name.

Having a controller named PostsController would mean that the viewVar would be posts by default.

For View Action the default is singular version of the controller name.

Having a controller named PostsController would mean that the viewVar would be post by default.

serialize

Note

This setting is only relevant if you use the API listener.

Note

The API listener will always enforce success and data to be part of the _serialize array.

This method is intended to allow you to add additional keys to your API responses with ease. An example of this is the API Query Log.

To get the current configured serialize keys call the serialize method without any arguments.

$this->Crud->action()->serialize();

To change the serialize keys, pass a string or an array as first argument.

If a string is passed, it will be cast to array automatically.

$this->Crud->action()->serialize(['my', 'extra', 'keys']);
Events

This is a list of events emitted from the Index Crud Action.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforePaginate

This event is emitted before Controller::paginate() is called.

Add Conditions
public function index() {
  $this->Crud->on('beforePaginate', function(\Cake\Event\Event $event) {
    $this->paginate['conditions']['is_active'] = true;
  });

  return $this->Crud->execute();
}
Crud.afterPaginate

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.

Modify the Result
public function index() {
  $this->Crud->on('afterPaginate', function(\Cake\Event\Event $event) {
    foreach ($event->subject->entities as $entity) {
      // $entity is an entity
    }
  });

  return $this->Crud->execute();
}
Crud.beforeRender

Invoked right before the view will be rendered.

This is also before the controllers own beforeRender callback.

View

The View Crud Action will read a record from a data source based on the ID that is part of the request.

Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

findMethod

The 1st parameter to Table::find() - the default value is all.

To get the current configured findMethod keys call the findMethod method without any arguments.

$this->Crud->action()->findMethod();

To change the findMethod value pass an string argument to the method

$this->Crud->action()->findMethod('my_custom_finder');
view

Get or set the view file to render at the end of the request.

The view setting is passed directly and unmodified to Controller::render().

To get the current configured view call the view method without any arguments.

$this->Crud->action()->view();

To change the view to render, pass a string as first argument.

$this->Crud->action()->view('my_custom_view');

Note

If the first parameter is NULL - which is the default value - the normal CakePHP behavior will be used.

Warning

Due to the nature of this method, once a custom view has been set, it’s not possible to revert back to the default behavior by calling ->view(null) as it will return the current configuration.

viewVar

Note

This maps directly to the $key argument in Controller::set($key, $value)

Change the name of the variable which contains the result of a index or view action query result.

To get the current configured viewVar call the viewViar method without any arguments.

$this->Crud->action()->viewVar();

To change the viewVar, pass a string as first argument.

$this->Crud->action()->viewVar('items');

For Index Action the default is plural version of the controller name.

Having a controller named PostsController would mean that the viewVar would be posts by default.

For View Action the default is singular version of the controller name.

Having a controller named PostsController would mean that the viewVar would be post by default.

serialize

Note

This setting is only relevant if you use the API listener.

Note

The API listener will always enforce success and data to be part of the _serialize array.

This method is intended to allow you to add additional keys to your API responses with ease. An example of this is the API Query Log.

To get the current configured serialize keys call the serialize method without any arguments.

$this->Crud->action()->serialize();

To change the serialize keys, pass a string or an array as first argument.

If a string is passed, it will be cast to array automatically.

$this->Crud->action()->serialize(['my', 'extra', 'keys']);
Events

This is a list of events emitted from the View Crud Action.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeFind

The event is emitted before calling the find method in the table.

The Crud Subject contains the following keys:

  • id The ID that was originally passed to the action and usually the primary key value of your table.
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A 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 Crud.recordNotFound event is emitted instead of Crud.afterFind.

Add Conditions
public function delete($id) {
  $this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
    $event->subject->query->where(['author' => $this->Auth->user('id')]);
  });

  return $this->Crud->execute();
}
Crud.afterFind

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.

Logging the Found Item
public function delete($id) {
  $this->Crud->on('afterFind', function(\Cake\Event\Event $event) {
    $this->log("Found item: $event->subject->entity->id in the database");
  });

  return $this->Crud->execute();
}
Crud.recordNotFound

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

Crud.beforeRender

Invoked right before the view will be rendered.

This is also before the controllers own beforeRender callback.

Add

The Add Crud Action will create a new record if the request is POST and the data validates - otherwise it will attempt to render a form to the end-user.

Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

view

Get or set the view file to render at the end of the request.

The view setting is passed directly and unmodified to Controller::render().

To get the current configured view call the view method without any arguments.

$this->Crud->action()->view();

To change the view to render, pass a string as first argument.

$this->Crud->action()->view('my_custom_view');

Note

If the first parameter is NULL - which is the default value - the normal CakePHP behavior will be used.

Warning

Due to the nature of this method, once a custom view has been set, it’s not possible to revert back to the default behavior by calling ->view(null) as it will return the current configuration.

saveMethod

The method to execute on Table:: when saving an entity - the default value is save.

To get the current configured saveMethod keys call the saveMethod method without any arguments.

$this->Crud->action()->saveMethod();

To change the saveMethod value pass an string argument to the method

$this->Crud->action()->saveMethod('my_custom_save_method');
saveOptions

The 2nd parameter to Table::save() - the default value is ['validate' => true, 'atomic' => true].

To get the current configured saveOptions keys call the saveOptions method without any arguments.

$this->Crud->action()->saveOptions();

To change the saveOptions value pass an array argument to the method

$this->Crud->action()->saveOptions(['atomic' => false]);

Sometimes you need to change the accessible fields before you update your entity.

$this->Crud->action()->saveOptions(['accessibleFields' => ['role_id' => true]]);
serialize

Note

This setting is only relevant if you use the API listener.

Note

The API listener will always enforce success and data to be part of the _serialize array.

This method is intended to allow you to add additional keys to your API responses with ease. An example of this is the API Query Log.

To get the current configured serialize keys call the serialize method without any arguments.

$this->Crud->action()->serialize();

To change the serialize keys, pass a string or an array as first argument.

If a string is passed, it will be cast to array automatically.

$this->Crud->action()->serialize(['my', 'extra', 'keys']);
Events

This is a list of events emitted from the Add Crud Action.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeSave

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:

  • item An entity object marshaled with the HTTP POST data from the request.
  • saveMethod A string with the saveMethod.
  • saveOptions An 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

Crud.afterSave

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:

  • id The newly inserted ID. It’s only available if the call to Table::save() was successful.
  • success indicates whether or not the Table::save() call succeed or not.
  • created true if the record was created and false if the record was updated.
  • item An entity object marshaled with the HTTP POST data from the request and the save() logic.
Check Created Status
public function edit($id) {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
    if ($event->subject->created) {
      $this->log("The entity was created");
    } else {
      $this->log("The entity was updated");
    }
  });

  return $this->Crud->execute();
}
Check Success Status
public function edit($id) {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
    if ($event->subject->success) {
      $this->log("The entity was saved successfully");
    } else {
      $this->log("The entity was NOT saved successfully");
    }
  });

  return $this->Crud->execute();
}
Get Entity ID
public function add() {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
  if ($event->subject->created) {
      $this->log("The entity was created with id: $event->subject->id");
    }
  });

  return $this->Crud->execute();
}
Crud.setFlash

Simple and event driven wrapper for SessionComponent::setFlash.

The Crud Subject contains the following keys:

  • text The 1st argument to SessionComponent::setFlash.
  • element The 2nd argument to SessionComponent::setFlash.
  • params The 3rd argument to SessionComponent::setFlash.
  • key The 4th argument to SessionComponent::setFlash.
  • item (Optional) The 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 it’s stopPropagation() method.

Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Crud.beforeRender

Invoked right before the view will be rendered.

This is also before the controllers own beforeRender callback.

Edit

The Edit Crud Action will update an existing record if the request is POST or PUT and the data validates - otherwise it will attempt to render a form to the end-user.

Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

view

Get or set the view file to render at the end of the request.

The view setting is passed directly and unmodified to Controller::render().

To get the current configured view call the view method without any arguments.

$this->Crud->action()->view();

To change the view to render, pass a string as first argument.

$this->Crud->action()->view('my_custom_view');

Note

If the first parameter is NULL - which is the default value - the normal CakePHP behavior will be used.

Warning

Due to the nature of this method, once a custom view has been set, it’s not possible to revert back to the default behavior by calling ->view(null) as it will return the current configuration.

findMethod

The 1st parameter to Table::find() - the default value is all.

To get the current configured findMethod keys call the findMethod method without any arguments.

$this->Crud->action()->findMethod();

To change the findMethod value pass an string argument to the method

$this->Crud->action()->findMethod('my_custom_finder');
saveMethod

The method to execute on Table:: when saving an entity - the default value is save.

To get the current configured saveMethod keys call the saveMethod method without any arguments.

$this->Crud->action()->saveMethod();

To change the saveMethod value pass an string argument to the method

$this->Crud->action()->saveMethod('my_custom_save_method');
saveOptions

The 2nd parameter to Table::save() - the default value is ['validate' => true, 'atomic' => true].

To get the current configured saveOptions keys call the saveOptions method without any arguments.

$this->Crud->action()->saveOptions();

To change the saveOptions value pass an array argument to the method

$this->Crud->action()->saveOptions(['atomic' => false]);

Sometimes you need to change the accessible fields before you update your entity.

$this->Crud->action()->saveOptions(['accessibleFields' => ['role_id' => true]]);
serialize

Note

This setting is only relevant if you use the API listener.

Note

The API listener will always enforce success and data to be part of the _serialize array.

This method is intended to allow you to add additional keys to your API responses with ease. An example of this is the API Query Log.

To get the current configured serialize keys call the serialize method without any arguments.

$this->Crud->action()->serialize();

To change the serialize keys, pass a string or an array as first argument.

If a string is passed, it will be cast to array automatically.

$this->Crud->action()->serialize(['my', 'extra', 'keys']);
Events

This is a list of events emitted from the Edit Crud Action.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeFind

The event is emitted before calling the find method in the table.

The Crud Subject contains the following keys:

  • id The ID that was originally passed to the action and usually the primary key value of your table.
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A 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 Crud.recordNotFound event is emitted instead of Crud.afterFind.

Add Conditions
public function delete($id) {
  $this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
    $event->subject->query->where(['author' => $this->Auth->user('id')]);
  });

  return $this->Crud->execute();
}
Crud.afterFind

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.

Logging the Found Item
public function delete($id) {
  $this->Crud->on('afterFind', function(\Cake\Event\Event $event) {
    $this->log("Found item: $event->subject->entity->id in the database");
  });

  return $this->Crud->execute();
}
Crud.beforeSave

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:

  • item An entity object marshaled with the HTTP POST data from the request.
  • saveMethod A string with the saveMethod.
  • saveOptions An 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

Crud.afterSave

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:

  • id The newly inserted ID. It’s only available if the call to Table::save() was successful.
  • success indicates whether or not the Table::save() call succeed or not.
  • created true if the record was created and false if the record was updated.
  • item An entity object marshaled with the HTTP POST data from the request and the save() logic.
Check Created Status
public function edit($id) {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
    if ($event->subject->created) {
      $this->log("The entity was created");
    } else {
      $this->log("The entity was updated");
    }
  });

  return $this->Crud->execute();
}
Check Success Status
public function edit($id) {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
    if ($event->subject->success) {
      $this->log("The entity was saved successfully");
    } else {
      $this->log("The entity was NOT saved successfully");
    }
  });

  return $this->Crud->execute();
}
Get Entity ID
public function add() {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
  if ($event->subject->created) {
      $this->log("The entity was created with id: $event->subject->id");
    }
  });

  return $this->Crud->execute();
}
Crud.setFlash

Simple and event driven wrapper for SessionComponent::setFlash.

The Crud Subject contains the following keys:

  • text The 1st argument to SessionComponent::setFlash.
  • element The 2nd argument to SessionComponent::setFlash.
  • params The 3rd argument to SessionComponent::setFlash.
  • key The 4th argument to SessionComponent::setFlash.
  • item (Optional) The 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 it’s stopPropagation() method.

Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Crud.beforeRender

Invoked right before the view will be rendered.

This is also before the controllers own beforeRender callback.

Delete

The Delete Crud Action will delete a record by the id provided in the URL.

Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

findMethod

The 1st parameter to Table::find() - the default value is all.

To get the current configured findMethod keys call the findMethod method without any arguments.

$this->Crud->action()->findMethod();

To change the findMethod value pass an string argument to the method

$this->Crud->action()->findMethod('my_custom_finder');
serialize

Note

This setting is only relevant if you use the API listener.

Note

The API listener will always enforce success and data to be part of the _serialize array.

This method is intended to allow you to add additional keys to your API responses with ease. An example of this is the API Query Log.

To get the current configured serialize keys call the serialize method without any arguments.

$this->Crud->action()->serialize();

To change the serialize keys, pass a string or an array as first argument.

If a string is passed, it will be cast to array automatically.

$this->Crud->action()->serialize(['my', 'extra', 'keys']);
Events

This is a list of events emitted from the Delete Crud Action.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeFind

The event is emitted before calling the find method in the table.

The Crud Subject contains the following keys:

  • id The ID that was originally passed to the action and usually the primary key value of your table.
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A 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 Crud.recordNotFound event is emitted instead of Crud.afterFind.

Add Conditions
public function delete($id) {
  $this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
    $event->subject->query->where(['author' => $this->Auth->user('id')]);
  });

  return $this->Crud->execute();
}
Crud.afterFind

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.

Logging the Found Item
public function delete($id) {
  $this->Crud->on('afterFind', function(\Cake\Event\Event $event) {
    $this->log("Found item: $event->subject->entity->id in the database");
  });

  return $this->Crud->execute();
}
Crud.beforeDelete

This event is emitted before calling Table::delete.

The Crud Subject contains the following keys:

  • id The ID of the entity, from the URL
  • item The Entity from the find() call.

To abort a delete() simply stop the event by calling $event->stopPropagation().

Stop Delete
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->subject->item->author !== 'admin') {
      $event->stopPropagation();
    }
  });

  return $this->Crud->execute();
}
Crud.afterDelete

This event is emitted after Table::delete() has been called.

The Crud Subject contains two keys:

  • success if true the delete() call succeeded, false otherwise
  • id The ID that was originally passed to the action and is usually the primary key of your model.
  • item The record that was found in the database.
Check Success
public function delete($id) {
  $this->Crud->on('afterDelete', function(\Cake\Event\Event $event) {
    if (!$event->subject->success) {
      $this->log("Delete failed for entity $event->subject->id");
    }
  });

  return $this->Crud->execute();
}
Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Bulk

If you need to perform an action against a number of records, you can extend the abstract Bulk\BaseAction class to create your own.

Three BulkAction classes exist in the core:

  • Delete: Deletes a set of entities
  • SetValue: Sets a field to a value for a set of entities
  • Toggle: Toggles the value of a boolean field for a set of entities

To create your own BulkAction, simply create a new action class with a _bulk method. This method takes a CakePHP Query object as it’s first argument

<?php
namespace App\Crud\Action;

use Cake\ORM\Query;
use Crud\Action\Bulk\BaseAction;

class ApproveAction extends BaseAction
{
  /**
   * Set the value of the approved field to true
   * for a set of entities
   *
   * @param \Cake\ORM\Query $query The query to act upon
   * @return boolean
   */
  protected function _handle(Query $query)
  {
    $query->update()->set(['approved' => true]);
    $statement = $query->execute();
    $statement->closeCursor();
    return $statement->rowCount();
  }
}
Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

findMethod

The 1st parameter to Table::find() - the default value is all.

To get the current configured findMethod keys call the findMethod method without any arguments.

$this->Crud->action()->findMethod();

To change the findMethod value pass an string argument to the method

$this->Crud->action()->findMethod('my_custom_finder');
Events

This is a list of events emitted from actions that extend Bulk\BaseAction.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeBulk

This event is emitted before _bulk() is called on a Bulk Crud action.

The Crud Subject contains the following keys:

  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.

To abort a bulk action, simply stop the event by calling $event->stopPropagation().

Stop Bulk Action
public function bulk($id) {
  $this->Crud->on('beforeBulk', function(\Cake\Event\Event $event) {
    // Stop the bulk event, the action will not continue
    if ($event->subject->item->author !== 'admin') {
      $event->stopPropagation();
    }
  });

  return $this->Crud->execute();
}
Crud.afterBulk

This event is emitted after calling _bulk() on a Bulk Crud action.

The Crud Subject contains two keys:

  • success if true the _bulk() call succeeded, false otherwise
  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.
Check Success
public function bulk($id) {
  $this->Crud->on('afterBulk', function(\Cake\Event\Event $event) {
    if (!$event->subject->success) {
      $this->log("Bulk action failed");
    }
  });

  return $this->Crud->execute();
}
Crud.setFlash

Simple and event driven wrapper for SessionComponent::setFlash.

The Crud Subject contains the following keys:

  • text The 1st argument to SessionComponent::setFlash.
  • element The 2nd argument to SessionComponent::setFlash.
  • params The 3rd argument to SessionComponent::setFlash.
  • key The 4th argument to SessionComponent::setFlash.
  • item (Optional) The 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 it’s stopPropagation() method.

Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Bulk Delete

You can use the Bulk\DeleteAction class to delete a group of database records.

<?php
namespace App\Controller;

class PostsController extends AppController
{
  public function initialize()
  {
    parent::initialize();
    $this->Crud->mapAction('deleteAll', 'Crud.Bulk/Delete');
  }
}
Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

findMethod

The 1st parameter to Table::find() - the default value is all.

To get the current configured findMethod keys call the findMethod method without any arguments.

$this->Crud->action()->findMethod();

To change the findMethod value pass an string argument to the method

$this->Crud->action()->findMethod('my_custom_finder');
Events

This is a list of events emitted from actions that extend Bulk\BaseAction.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeBulk

This event is emitted before _bulk() is called on a Bulk Crud action.

The Crud Subject contains the following keys:

  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.

To abort a bulk action, simply stop the event by calling $event->stopPropagation().

Stop Bulk Action
public function bulk($id) {
  $this->Crud->on('beforeBulk', function(\Cake\Event\Event $event) {
    // Stop the bulk event, the action will not continue
    if ($event->subject->item->author !== 'admin') {
      $event->stopPropagation();
    }
  });

  return $this->Crud->execute();
}
Crud.afterBulk

This event is emitted after calling _bulk() on a Bulk Crud action.

The Crud Subject contains two keys:

  • success if true the _bulk() call succeeded, false otherwise
  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.
Check Success
public function bulk($id) {
  $this->Crud->on('afterBulk', function(\Cake\Event\Event $event) {
    if (!$event->subject->success) {
      $this->log("Bulk action failed");
    }
  });

  return $this->Crud->execute();
}
Crud.setFlash

Simple and event driven wrapper for SessionComponent::setFlash.

The Crud Subject contains the following keys:

  • text The 1st argument to SessionComponent::setFlash.
  • element The 2nd argument to SessionComponent::setFlash.
  • params The 3rd argument to SessionComponent::setFlash.
  • key The 4th argument to SessionComponent::setFlash.
  • item (Optional) The 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 it’s stopPropagation() method.

Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Bulk Set Value

You can use the Bulk\SetValueAction class to specify the value of a given field for a group of database records.

<?php
namespace App\Controller;

class PostsController extends AppController
{
  public function initialize()
  {
    parent::initialize();
    $this->Crud->mapAction('publishAll', [
      'className' => 'Crud.Bulk/SetValue',
      'field' => 'status',
      'value' => 'publish',
    ]);
  }
}
Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

findMethod

The 1st parameter to Table::find() - the default value is all.

To get the current configured findMethod keys call the findMethod method without any arguments.

$this->Crud->action()->findMethod();

To change the findMethod value pass an string argument to the method

$this->Crud->action()->findMethod('my_custom_finder');
Events

This is a list of events emitted from actions that extend Bulk\BaseAction.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeBulk

This event is emitted before _bulk() is called on a Bulk Crud action.

The Crud Subject contains the following keys:

  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.

To abort a bulk action, simply stop the event by calling $event->stopPropagation().

Stop Bulk Action
public function bulk($id) {
  $this->Crud->on('beforeBulk', function(\Cake\Event\Event $event) {
    // Stop the bulk event, the action will not continue
    if ($event->subject->item->author !== 'admin') {
      $event->stopPropagation();
    }
  });

  return $this->Crud->execute();
}
Crud.afterBulk

This event is emitted after calling _bulk() on a Bulk Crud action.

The Crud Subject contains two keys:

  • success if true the _bulk() call succeeded, false otherwise
  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.
Check Success
public function bulk($id) {
  $this->Crud->on('afterBulk', function(\Cake\Event\Event $event) {
    if (!$event->subject->success) {
      $this->log("Bulk action failed");
    }
  });

  return $this->Crud->execute();
}
Crud.setFlash

Simple and event driven wrapper for SessionComponent::setFlash.

The Crud Subject contains the following keys:

  • text The 1st argument to SessionComponent::setFlash.
  • element The 2nd argument to SessionComponent::setFlash.
  • params The 3rd argument to SessionComponent::setFlash.
  • key The 4th argument to SessionComponent::setFlash.
  • item (Optional) The 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 it’s stopPropagation() method.

Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Bulk Toggle

You can use the Bulk\ToggleAction class to toggle the value of a boolean field for a group of database records.

<?php
namespace App\Controller;

class PostsController extends AppController
{
  public function initialize()
  {
    parent::initialize();
    $this->Crud->mapAction('toggleActive', [
      'className' => 'Crud.Bulk/Toggle',
      'field' => 'toggle',
    ]);
  }
}
Configuration

Note

Before applying any configuration to an action it must be mapped first.

If the action has not been mapped an exception will be raised.

enabled

Test or modify if the Crud Action is enabled or not.

When a CrudAction is disabled, Crud will not handle any requests to the action, and CakePHP will raise the normal \Cake\Error\MissingActionException exception if you haven’t implemented the action in your controller.

To test if an action is enabled, call the enabled method on the action.

$this->Crud->action()->enabled();

To disable an action, call the disable method on the action.

$this->Crud->action()->disable();

To enable an action, call the enable method on the action.

$this->Crud->action()->enable();

To disable or enable multiple actions at the same time, Crud Component provides helper methods.

The enable and disable method can take a string or an array, for easy mass-updating.

$this->Crud->enable('index');
$this->Crud->enable(['index', 'add']);

$this->Crud->disable('index');
$this->Crud->disable(['index', 'add']);

Note

These methods simply calls the enable and disable method in each Crud Action class, and do not provide any magic other than mass updating.

Warning

While it’s possible to update the enabled property directly on an action using the config methods, it’s not recommend, as important cleanup logic will not be applied if you use the config() method directly.

findMethod

The 1st parameter to Table::find() - the default value is all.

To get the current configured findMethod keys call the findMethod method without any arguments.

$this->Crud->action()->findMethod();

To change the findMethod value pass an string argument to the method

$this->Crud->action()->findMethod('my_custom_finder');
Events

This is a list of events emitted from actions that extend Bulk\BaseAction.

Please see the events documentation for a full list of generic properties and how to use the event system correctly.

Crud.startup

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.

Crud.beforeFilter

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.

Crud.beforeBulk

This event is emitted before _bulk() is called on a Bulk Crud action.

The Crud Subject contains the following keys:

  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.

To abort a bulk action, simply stop the event by calling $event->stopPropagation().

Stop Bulk Action
public function bulk($id) {
  $this->Crud->on('beforeBulk', function(\Cake\Event\Event $event) {
    // Stop the bulk event, the action will not continue
    if ($event->subject->item->author !== 'admin') {
      $event->stopPropagation();
    }
  });

  return $this->Crud->execute();
}
Crud.afterBulk

This event is emitted after calling _bulk() on a Bulk Crud action.

The Crud Subject contains two keys:

  • success if true the _bulk() call succeeded, false otherwise
  • ids A list of ids of entities, from the request data
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A Query object from the Repository where $PrimaryKey => $IdFromRequest is already added to the conditions.
Check Success
public function bulk($id) {
  $this->Crud->on('afterBulk', function(\Cake\Event\Event $event) {
    if (!$event->subject->success) {
      $this->log("Bulk action failed");
    }
  });

  return $this->Crud->execute();
}
Crud.setFlash

Simple and event driven wrapper for SessionComponent::setFlash.

The Crud Subject contains the following keys:

  • text The 1st argument to SessionComponent::setFlash.
  • element The 2nd argument to SessionComponent::setFlash.
  • params The 3rd argument to SessionComponent::setFlash.
  • key The 4th argument to SessionComponent::setFlash.
  • item (Optional) The 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 it’s stopPropagation() method.

Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Custom

If you are not satisfied with the Actions bundled with CRUD - you can easily add your own.

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.

<?php
namespace App\Crud\Action;

class Index extends \Crud\Action\BaseAction {

        /**
        * Generic handler for all HTTP verbs
        *
        * @return void
        */
        protected function _handle() {

        }

}

Listeners

Tip

While CRUD provides many listeners, it’s definitely possible and recommended that you add your own reusable listeners for your application needs

Listeners are the foundation for the extreme flexibility CRUD provides you as an application developer.

The event system allows you to hook into the most important part of the CRUD action flow and customize it to your unique application needs.

The Anatomy Of A Listener

The listener system is simply the Events System from CakePHP, and all the official documentation and usage also applies to CRUD.

The CRUD event system uses two methods trigger() and on() to interface the underlying CakePHP event system.

The only hard requirement for a CRUD listener is that it needs to either implement the implementedEvents() method or extend \Crud\Listener\Base.

Below is the code for a simple CRUD listener.

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
<?php
namespace Crud\Listener;

class Example extends \Crud\Listener\BaseListener {

/**
 * Returns a list of all events that will fire in the lister during the
 * CRUD life-cycle.
 *
 * @return array
 */
  public function implementedEvents() {
    return [
      'Crud.beforeRender' => ['callable' => 'beforeRender']
    ];
  }

/**
 * Executed when Crud.beforeRender is emitted
 *
 * @param \Cake\Event\Event $event
 * @return void
 */
  public function beforeRender(Cake\Event\Event $event) {
    $this->_response()->header('X-Powered-By', 'CRUD 4.0');
  }

}

Class And Namespace

All built-in listeners in CRUD live in the Crud\Listener namespace.

All listeners in CRUD, even your own, should inherit from the Crud\Listener\Base class. This class is abstract and provides numerous auxiliary methods which can be useful for you both as a developer and 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
28
<?php
namespace Crud\Listener;

class Example extends \Crud\Listener\BaseListener {

/**
 * Returns a list of all events that will fire in the lister during the
 * CRUD life-cycle.
 *
 * @return array
 */
  public function implementedEvents() {
    return [
      'Crud.beforeRender' => ['callable' => 'beforeRender']
    ];
  }

/**
 * Executed when Crud.beforeRender is emitted
 *
 * @param \Cake\Event\Event $event
 * @return void
 */
  public function beforeRender(Cake\Event\Event $event) {
    $this->_response()->header('X-Powered-By', 'CRUD 4.0');
  }

}

Implemented Events

As documented in the CakePHP Events System all listeners must contain a implementedEvents method.

In this example, we simply request that beforeRender in our class is executed every time a Crud.beforeRender event is emitted.

 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
<?php
namespace Crud\Listener;

class Example extends \Crud\Listener\BaseListener {

/**
 * Returns a list of all events that will fire in the lister during the
 * CRUD life-cycle.
 *
 * @return array
 */
  public function implementedEvents() {
    return [
      'Crud.beforeRender' => ['callable' => 'beforeRender']
    ];
  }

/**
 * Executed when Crud.beforeRender is emitted
 *
 * @param \Cake\Event\Event $event
 * @return void
 */
  public function beforeRender(Cake\Event\Event $event) {
    $this->_response()->header('X-Powered-By', 'CRUD 4.0');
  }

}

Note

The Crud.beforeRender event is similar to the Controller and View event of the same name, but Crud.beforeRender is called first, and can halt the entire rendering process

The Callback

This method gets executed every time a Crud.beforeRender event is emitted from within CRUD or by you as a developer.

When the event is emitted, we append a header to the client HTTP response named X-Powered-By with the value CRUD 4.0.

 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
<?php
namespace Crud\Listener;

class Example extends \Crud\Listener\BaseListener {

/**
 * Returns a list of all events that will fire in the lister during the
 * CRUD life-cycle.
 *
 * @return array
 */
  public function implementedEvents() {
    return [
      'Crud.beforeRender' => ['callable' => 'beforeRender']
    ];
  }

/**
 * Executed when Crud.beforeRender is emitted
 *
 * @param \Cake\Event\Event $event
 * @return void
 */
  public function beforeRender(Cake\Event\Event $event) {
    $this->_response()->header('X-Powered-By', 'CRUD 4.0');
  }

}

More on listeners

API

This listener allows you to easily create a JSON or XML Api built on top of Crud.

Introduction

The API listener depends on the RequestHandler to be loaded before Crud

[Please also see the CakePHP documentation on JSON and XML views] (http://book.cakephp.org/2.0/en/views/json-and-xml-views.html#enabling-data-views-in-your-application)

Setup
Routing

You need to tell the Router to parse extensions else it won’t be able to process and render json and xml URL extension

// config/routes.php
Router::extensions(['json', 'xml']);

Ensure this statement is used before connecting any routes.

Controller

Attach it on the fly in your controller beforeFilter, this is recommended if you want to attach it only to specific controllers and actions

<?php
class SamplesController extends AppController {

  public function beforeFilter(\Cake\Event\Event $event) {
    parent::beforeFilter();
    $this->Crud->addListener('Crud.Api');
  }
}

Attach it using components array, this is recommended if you want to attach it to all controllers, application wide

<?php
class AppController extends Controller {

  public $components = [
    'RequestHandler',
    'Crud.Crud' => [
      'actions' => ['Crud.Index', 'Crud.View'],
      'listeners' => ['Crud.Api']
    ]
  ];

}
New CakeRequest detectors

The Api Listener creates 3 new detectors in your CakeRequest object.

is(‘json’)

Checks if the extension of the request is .json or if the requester accepts json as part of the HTTP accepts header

is(‘xml’)

Checks if the extension of the request is .xml or if the requester accepts XML as part of the HTTP accepts header

is(‘api’)

Checking if the request is either is('json') or is('xml')

Default behavior

If the current request doesn’t evaluate is('api') to true, the listener won’t do anything at all.

All it’s callbacks will simply return NULL and don’t get in your way.

Exception handler

The Api listener overrides the Exception.renderer for api requests, so in case of an error, a standardized error will be returned, in either json or xml - according to the API request type.

Request type enforcing

The API listener will try to enforce some best practices on how an API should behave.

For a request to index and view the HTTP request type must be HTTP GET - else an MethodNotAllowed exception will be raised.

For a request to add the HTTP request type must be HTTP POST - else an MethodNotAllowed exception will be raised.

For a request to edit the HTTP request type must be HTTP PUT - else an MethodNotAllowed exception will be raised.

For a request to delete the HTTP request type must be HTTP DELETE - else an MethodNotAllowed exception will be raised.

Response format

The default response format for both XML and JSON is two root keys, success and data.

It’s possible to add your own root keys simply by _serialize view var.

JSON
{
  "success": true,
  "data": {

  }
}
XML
<response>
  <success>1</success>
  <data></data>
</response>
Exception response format

The data.exception key is only returned if debug is > 0

JSON
{
  "success": false,
  "data": {
    "code": 500,
    "url": "/some/url.json",
    "name": "Some exception message",
    "exception": {
      "class": "CakeException",
      "code": 500,
      "message": "Some exception message",
      "trace": []
    }
  }
}
XML
<response>
  <success>0</success>
  <data>
    <code>500</code>
    <url>/some/url.json</url>
    <name>Some exception message</name>
    <exception>
      <class>CakeException</class>
      <code>500</code>
      <message>Some exception message</message>
      <trace></trace>
      <trace></trace>
    </exception>
    <queryLog/>
  </data>
</response>
HTTP POST / PUT (add / edit)

success is based on the event->subject->success parameter from Add or Edit action.

If success is false a HTTP response code of 400 will be returned, and the data property will be the list of validation errors from the model.

If success is true a HTTP response code of 201 will be returned if the model item was created else a 301 response code will be used.

A success will always include a HTTP Location header to the view action with the existing or newly created id of the record

HTTP DELETE (delete)

success is based on the event->subject->success parameter from the delete action.

data will always be null.

No special HTTP codes is sent.

Not Found (view / edit / delete)

In case an id is provided to a crud action and the id does not exist in the database, a 404 NotFoundException` will be thrown.

Invalid id (view / edit / delete)

In case a ìd is provided to a crud action and the id is not valid according to the database type a 500 BadRequestException will be thrown

API Pagination

Note

This feature requires the API listener to work.

This listener appends pagination information to the API responses that is contain pagination information.

Setup

Attach it on the fly in your controller beforeFilter, this is recommended if you want to attach it only to specific controllers and actions

<?php
class SamplesController extends AppController {

        public function beforeFilter(\Cake\Event\Event $event) {
                $this->Crud->addListener('Crud.Api'); // Required
                $this->Crud->addListener('Crud.ApiPagination');
        }

}

Attach it using components array, this is recommended if you want to attach it to all controllers, application wide

<?php
class SamplesController extends AppController {

        public $components = [
                'RequestHandler',
                'Crud.Crud' => [
                        'listeners' => [
                                'Crud.Api', // Required
                                'Crud.ApiPagination'
                        ]
                ];

}
Output

Paginated results will include a

{
        "success": true,
        "data":[

        ],
        "pagination":{
                "page_count": 13,
                "current_page": 1,
                "count": 25,
                "has_prev_page": false,
                "has_next_page": true
        }
}

API Query Log

Note

This feature requires the API listener to work.

This listener appends query log information to the API responses

The listener will only append the queryLog key if debug is set to true.

Setup

Attach it on the fly in your controller beforeFilter, this is recommended if you want to attach it only to specific controllers and actions

<?php
class SamplesController extends AppController {

        public function beforeFilter(\Cake\Event\Event $event) {
                $this->Crud->addListener('Crud.Api'); // Required
                $this->Crud->addListener('Crud.ApiQueryLog');
        }

}

Attach it using components array, this is recommended if you want to attach it to all controllers, application wide

<?php
class SamplesController extends AppController {

        public $components = [
                'RequestHandler',
                'Crud.Crud' => [
                        'listeners' => [
                                'Crud.Api', // Required
                                'Crud.ApiQueryLog'
                        ]
                ];

}
Output

Paginated results will include a

{
        "success": true,
        "data": [

        ],
        "queryLog": {
                "default": {
                        "log": [
                                {
                                        "query": "SELECT SOMETHING FROM SOMEWHERE",
                                        "took": 2,
                                        "params": [

                                        ],
                                        "affected": 25,
                                        "numRows": 25
                                },
                                {
                                        "query": "SELECT SOMETHING FROM SOMEWHERE'",
                                        "params": [

                                        ],
                                        "affected": 1,
                                        "numRows": 1,
                                        "took": 0
                                }
                        ]
                }
        }
}

Redirect listener

Enable more complex redirect rules.

Setup

Attach it on the fly in your controller beforeFilter, this is recommended if you want to attach it only to specific controllers and actions:

<?php
class SamplesController extends AppController {

  public function beforeFilter(\Cake\Event\Event $event) {
    $this->Crud->addListener('Crud.Redirect');

    parent::beforeFilter();
  }
}
?>

Attach it using components array, this is recommended if you want to attach it to all controllers, application wide:

<?php
class SamplesController extends AppController {

  public $components = [
    'Crud.Crud' => [
      'actions' => ['index', 'view'],
      'listeners' => ['Crud.Redirect']
    ];

}
?>
Configuration
Readers

A reader is a closure that can access a field in an object through different means.

Below is a list of the build-in readers you can use:

Name Pseudo code Description
request.key $this->request->{$field} Access a property directly on the Request object
request.data $this->request->data($field) Access a HTTP POST data field using Hash::get() compatible format
request.query $this->request->query($field) Access a HTTP query argument using Hash::get() compatible format
model.key $Model->{$field} Access a property directly on the Model instance
model.data $Model->data[$field] Access a model data key using Hash::get() compatible format
model.field $Model->field($field) Access a model key by going to the database and read the value
subject.key $CrudSubject->{$key} Access a property directly on the event subject
Adding your own reader

Adding or overriding a reader is very simple.

The closure takes two arguments:

  1. CrudSubject $subject
  2. $key = null
<?php
class SamplesController extends AppController {

  public function beforeFilter() {
    $listener = $this->Crud->listener('Redirect');
    $listener->reader($name, Closure $closure);

    // Example on a reader using Configure
    $listener->reader('configure.key', function(CrudSubject $subject, $key)) {
      return Configure::read($key);
    });

    parent::beforeFilter();
  }
}
?>
Action defaults

Below is the defaults provided by build-in Crud actions:

Add action

By default Add Crud Action always redirect to array('action' => 'index') on afterSave

Name Reader Key Result Description
post_add request.data _add array('action' => 'add') By providing _add as a post key, the user will be redirected back to the add action
post_edit request.data _edit array('action' => 'edit', $id) By providing _edit as a post key, the user will be redirected to the edit action with the newly created ID as parameter
Edit action

By default Edit Crud Action always redirect to array('action' => 'index') on afterSave

Name Reader Key Result Description
post_add request.data _add array('action' => 'add') By providing _add as a post key, the user will be redirected back to the add action
post_edit request.data _edit array('action' => 'edit', $id) By providing _edit as a post key, the user will be redirected to the edit action with the same ID as parameter as the current URL
Configuring your own redirect rules

It’s very simple to modify existing or add your own redirect rules:

<?php
class SamplesController extends AppController {

  public function beforeFilter() {
    // Get all the redirect rules
    $rules = $this->Crud->action()->redirectConfig();

    // Get one named rule only
    $rule = $this->Crud->action()->redirectConfig('add');

    // Configure a redirect rule:
    //
    // if $_POST['_view'] is set then redirect to
    // 'view' action with the value of '$subject->id'
    $this->Crud->action()->redirectConfig('view',
      [
        'reader' => 'request.data',  // Any reader from the list above
        'key'    => '_view',         // The key to check for, passed to the reader
        'url'    => [                // The url to redirect to
          'action' => 'view',        // The final url will be '/view/$id'
          ['subject.key', 'id']      // If an array is encountered, it will be expanded the same was as 'reader'+'key'
        ]
      ]
    );

    parent::beforeFilter();
  }
}
?>

Custom

Any class can be used as a CRUD Listener, even the controller.

Example Controller

We override the implementedEvents() method in the controller, and bind the Crud.beforeFind event to the _beforeFind() method in the controller.

<?php
namespace app\Controller;

class BlogsController extends AppController {

  public function implementedEvents() {
    return parent::implementedEvents() + [
      'Crud.beforeFind' => '_beforeFind'
    ];
  }

  public function _beforeFind(\Cake\Event\Event $event) {

  }

}

Events

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.

Controller

implementedEvents

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.

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

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(); - else CRUD will not process the action.

This is functionality wise more or less the same as using an controller method instead.

The benefit of the controller method is that you can easily share it between two actions, like showcased 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()
}

All CRUD Events

This is a full list of all events emitted from CRUD.

Each individual CRUD action contains the same documentation, but only the events relevant for that action.

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:

  • id The ID of the entity, from the URL
  • item The Entity from the find() call.

To abort a delete() simply stop the event by calling $event->stopPropagation().

Stop Delete

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->subject->item->author !== 'admin') {
      $event->stopPropagation();
    }
  });

  return $this->Crud->execute();
}
Crud.afterDelete

This event is emitted after Table::delete() has been called.

The Crud Subject contains two keys:

  • success if true the delete() call succeeded, false otherwise
  • id The ID that was originally passed to the action and is usually the primary key of your model.
  • item The record that was found in the database.

Check Success

public function delete($id) {
  $this->Crud->on('afterDelete', function(\Cake\Event\Event $event) {
    if (!$event->subject->success) {
      $this->log("Delete failed for entity $event->subject->id");
    }
  });

  return $this->Crud->execute();
}
Crud.beforeFind

The event is emitted before calling the find method in the table.

The Crud Subject contains the following keys:

  • id The ID that was originally passed to the action and usually the primary key value of your table.
  • repository An instance of the Repository (Table) which the query will be executed against.
  • query A 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 Crud.recordNotFound event is emitted instead of Crud.afterFind.

Add Conditions

public function delete($id) {
  $this->Crud->on('beforeFind', function(\Cake\Event\Event $event) {
    $event->subject->query->where(['author' => $this->Auth->user('id')]);
  });

  return $this->Crud->execute();
}
Crud.afterFind

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.

Logging the Found Item

public function delete($id) {
  $this->Crud->on('afterFind', function(\Cake\Event\Event $event) {
    $this->log("Found item: $event->subject->entity->id in the database");
  });

  return $this->Crud->execute();
}
Crud.beforeSave

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:

  • item An entity object marshaled with the HTTP POST data from the request.
  • saveMethod A string with the saveMethod.
  • saveOptions An 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

Crud.afterSave

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:

  • id The newly inserted ID. It’s only available if the call to Table::save() was successful.
  • success indicates whether or not the Table::save() call succeed or not.
  • created true if the record was created and false if the record was updated.
  • item An entity object marshaled with the HTTP POST data from the request and the save() logic.

Check Created Status

public function edit($id) {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
    if ($event->subject->created) {
      $this->log("The entity was created");
    } else {
      $this->log("The entity was updated");
    }
  });

  return $this->Crud->execute();
}

Check Success Status

public function edit($id) {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
    if ($event->subject->success) {
      $this->log("The entity was saved successfully");
    } else {
      $this->log("The entity was NOT saved successfully");
    }
  });

  return $this->Crud->execute();
}

Get Entity ID

public function add() {
  $this->Crud->on('afterSave', function(\Cake\Event\Event $event) {
  if ($event->subject->created) {
      $this->log("The entity was created with id: $event->subject->id");
    }
  });

  return $this->Crud->execute();
}
Crud.beforePaginate

This event is emitted before Controller::paginate() is called.

Add Conditions

public function index() {
  $this->Crud->on('beforePaginate', function(\Cake\Event\Event $event) {
    $this->paginate['conditions']['is_active'] = true;
  });

  return $this->Crud->execute();
}
Crud.afterPaginate

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.

Modify the Result

public function index() {
  $this->Crud->on('afterPaginate', function(\Cake\Event\Event $event) {
    foreach ($event->subject->entities as $entity) {
      // $entity is an entity
    }
  });

  return $this->Crud->execute();
}
Crud.beforeRedirect

Simple and event driven wrapper for Controller::redirect().

The Crud Subject contains the following keys:

  • url The 1st argument to Controller::redirect().
  • status The 2nd argument to Controller::redirect().
  • exit The 3rd argument to Controller::redirect().
  • item (Optional) The 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.

Crud.beforeRender

Invoked right before the view will be rendered.

This is also before the controllers own beforeRender callback.

Crud.recordNotFound

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

Crud.setFlash

Simple and event driven wrapper for SessionComponent::setFlash.

The Crud Subject contains the following keys:

  • text The 1st argument to SessionComponent::setFlash.
  • element The 2nd argument to SessionComponent::setFlash.
  • params The 3rd argument to SessionComponent::setFlash.
  • key The 4th argument to SessionComponent::setFlash.
  • item (Optional) The 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 it’s stopPropagation() method.

Unit Testing

To ease with unit testing of Crud Listeners and Crud Actions, it’s recommended to use the proxy methods found in [CrudBaseObject]({{site.url}}/api/develop/class-CrudBaseObject.html).

These methods are much easier to mock than the full CrudComponent object.

They also allow you to just mock the methods you need for your specific test, rather than the big dependency nightmare the CrudComponent can be in some cases.

Proxy methods

These methods are available in all CrudAction and CrudListener objects.

_crud()

Get the CrudComponent instance

$this->_crud()

_action($name)

Get an CrudAction object by it’s action name

$this->_action()
$this->_action($name)

_trigger($eventName, $data = [])

Trigger a Crud Event

$this->_trigger('beforeSave')
$this->_trigger('beforeSave', ['data' => 'keys'])
$this->_trigger('beforeSave', $this->_subject(['data' => 'keys']))

_listener($name)

Get a Listener by its name

$this->_listener('Api')

_subject($additional = [])

Create a Crud Subject - used in $this->_trigger

$this->_subject()
$this->_subject(['data' => 'keys'])

_session()

Get the Session Component instance

$this->_session()

_controller()

Get the controller for the current request

$this->_controller()

_request()

Get the current Cake\Network\Request for this HTTP Request

$this->_request()

_response()

Get the current Cake\Network\Response for this HTTP Request

$this->_response()

_entity()

Get the entity instance that is created from Controller::$modelClass

$this->_entity()

_table()

Get the table instance that is created from Controller::$modelClass

$this->_table()