Skip to content

Instantly share code, notes, and snippets.

@WinterSilence
Last active May 6, 2020 14:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WinterSilence/43ae6cc5b102ee0500de5c81d88dbf13 to your computer and use it in GitHub Desktop.
Save WinterSilence/43ae6cc5b102ee0500de5c81d88dbf13 to your computer and use it in GitHub Desktop.
Kohana/Koseven/KO7 CRUD controller
<?php
/**
* CRUD controller.
*
* @link https://en.wikipedia.org/wiki/Create,_read,_update_and_delete CRUD
* @link https://koseven.dev/documentation/kohana/tutorials/basic-controller Controller_Base
*/
class Controller_CRUD extends Controller_Base
{
/**
* @var string|ORM model name or ORM instance
*/
protected $model;
/**
* Short-hand for create HTTP exception.
*
* @param int $code HTTP error code
* @param string|array $message Error or `[error, variables]`
* @param Throwable|null $previous Previous exception
* @return HTTP_Exception
*/
protected function http_exception(int $code, $message, Throwable $previous = null): HTTP_Exception
{
$variables = null;
if (is_array($message)) {
[$message, $variables] = $message;
}
return HTTP_Exception::factory($code, $message, $variables, $previous)->request($this->request);
}
/**
* Short-hand for create URI by route of current request.
*
* @param array $params Route parameters
* @return string
*/
protected function uri(array $params = []): string
{
$params += [
'directory' => $this->request->directory(),
'controller' => $this->request->controller(),
'action' => $this->request->action(),
];
return $this->request->route()->uri($params);
}
/**
* Create and optional load model by route `id` parameter.
*/
protected function init_model()
{
if ($this->model === null) {
// model name is controller name without prefix `Controller_`
$this->model = substr(get_class($this), 11);
}
if (is_string($this->model)) {
$id = $this->request->param('id');
try {
$this->model = ORM::factory($this->model, $id);
} catch (Exception $e) {
if ($id) {
throw $this->http_exception(
404,
['Model :model: record :id not found', [':model' => $model->object_name(), ':id' => $id]],
$e
);
} else {
throw $this->http_exception(
400,
['Cannot create model :model' [':model' => $model->object_name()]],
$e
);
}
}
}
}
/**
* Auto create/load model and bind to action template.
*/
public function before()
{
// check auth
parent::before();
// init model
$this->init_model();
}
/**
* Common method for create and update actions.
*/
protected function save_model()
{
$errors = [];
if ($this->request->method() == Request::POST) {
$data = $this->request->post($this->model->object_name());
// external validation for prevent CSRF attacks
$validation = new Validation($this->request->post());
$validation->rule('csrf', ['Security', 'check'], [':value']);
try {
$this->model->values($data)->save($validation);
} catch (ORM_Validation_Exception $e) {
// Display validation errors in form
$errors = $e->errors('models');
} catch (Exception $e) {
throw $this->http_exception(
400,
['Error at :action item', [':action' => $this->request->action()]]
);
}
if (! $errors) {
// redirect to `update` action, set 303 code to send as GET.
static::redirect($this->uri(['action' => 'update', 'id' => $this->model->pk()]), 303);
}
}
$this->action_template->set([
'csrf' => Security::token(),
'errors' => $errors
]);
}
/**
* Add new item.
*/
public function action_create()
{
$this->save_model();
}
/**
* Update item.
*/
public function action_update()
{
$this->save_model();
}
/**
* Delete item.
*/
public function action_delete()
{
try {
$this->model->delete();
} catch (Exception $e) {
throw $this->http_exception(400, ['Error at delete item', [':model' =>]], $e);
}
// redirect to `index` action
static::redirect($this->uri(['action' => 'index']));
}
/**
* Read and display items per page and pagination.
*/
public function action_index()
{
// create pagination instance
$pagination = new Pagination(
['total_items' => $this->model->count_all(), 'page' => $this->request->param('page')],
$this->request
);
// attach pagination to page template
$this->action_template->pagination = $pagination;
// set items per page
$this->model->offset($pagination->offset)
->limit($pagination->items_per_page);
// sort items
if ($this->request->param('sort_column')) {
$this->model->order_by(
$this->request->param('sort_column'),
$this->request->param('sort_direction') == 'desc' ? 'DESC' : 'ASC'
);
}
// attach items to page template
$this->action_template->items = $this->model->find_all();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment