icingaweb2/application/forms/RepositoryForm.php

454 lines
10 KiB
PHP

<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms;
use Exception;
use Icinga\Data\Filter\Filter;
use Icinga\Exception\NotFoundError;
use Icinga\Repository\Repository;
use Icinga\Web\Form;
use Icinga\Web\Notification;
/**
* Form base-class providing standard functionality for extensible, updatable and reducible repositories
*/
abstract class RepositoryForm extends Form
{
/**
* Insert mode
*/
const MODE_INSERT = 0;
/**
* Update mode
*/
const MODE_UPDATE = 1;
/**
* Delete mode
*/
const MODE_DELETE = 2;
/**
* The repository being worked with
*
* @var Repository
*/
protected $repository;
/**
* The target being worked with
*
* @var mixed
*/
protected $baseTable;
/**
* How to interact with the repository
*
* @var int
*/
protected $mode;
/**
* The name of the entry being handled when in mode update or delete
*
* @var string
*/
protected $identifier;
/**
* The data of the entry to pre-populate the form with when in mode insert or update
*
* @var array
*/
protected $data;
/**
* Set the repository to work with
*
* @param Repository $repository
*
* @return $this
*/
public function setRepository(Repository $repository)
{
$this->repository = $repository;
return $this;
}
/**
* Return the target being worked with
*
* @return mixed
*/
protected function getBaseTable()
{
if ($this->baseTable === null) {
return $this->repository->getBaseTable();
}
return $this->baseTable;
}
/**
* Return the name of the entry to handle
*
* @return string
*/
protected function getIdentifier()
{
return $this->identifier;
}
/**
* Return the current data of the entry being handled
*
* @return array
*/
protected function getData()
{
return $this->data;
}
/**
* Return whether an entry should be inserted
*
* @return bool
*/
public function shouldInsert()
{
return $this->mode === self::MODE_INSERT;
}
/**
* Return whether an entry should be udpated
*
* @return bool
*/
public function shouldUpdate()
{
return $this->mode === self::MODE_UPDATE;
}
/**
* Return whether an entry should be deleted
*
* @return bool
*/
public function shouldDelete()
{
return $this->mode === self::MODE_DELETE;
}
/**
* Add a new entry
*
* @param array $data The defaults to use, if any
*
* @return $this
*/
public function add(array $data = null)
{
$this->mode = static::MODE_INSERT;
$this->data = $data;
return $this;
}
/**
* Edit an entry
*
* @param string $name The entry's name
* @param array $data The entry's current data
*
* @return $this
*/
public function edit($name, array $data = null)
{
$this->mode = static::MODE_UPDATE;
$this->identifier = $name;
$this->data = $data;
return $this;
}
/**
* Remove an entry
*
* @param string $name The entry's name
*
* @return $this
*/
public function remove($name)
{
$this->mode = static::MODE_DELETE;
$this->identifier = $name;
return $this;
}
/**
* Fetch and return the entry to pre-populate the form with when in mode update
*
* @return false|object
*/
protected function fetchEntry()
{
return $this->repository
->select()
->from($this->getBaseTable())
->applyFilter($this->createFilter())
->fetchRow();
}
/**
* Return whether the entry supposed to be removed exists
*
* @return bool
*/
protected function entryExists()
{
$count = $this->repository
->select()
->from($this->getBaseTable())
->addFilter($this->createFilter())
->count();
return $count > 0;
}
/**
* Insert the new entry
*/
protected function insertEntry()
{
$this->repository->insert($this->getBaseTable(), $this->getValues());
}
/**
* Update the entry
*/
protected function updateEntry()
{
$this->repository->update($this->getBaseTable(), $this->getValues(), $this->createFilter());
}
/**
* Delete the entry
*/
protected function deleteEntry()
{
$this->repository->delete($this->getBaseTable(), $this->createFilter());
}
/**
* Create and add elements to this form
*
* @param array $formData The data sent by the user
*/
public function createElements(array $formData)
{
if ($this->shouldInsert()) {
$this->createInsertElements($formData);
} elseif ($this->shouldUpdate()) {
$this->createUpdateElements($formData);
} elseif ($this->shouldDelete()) {
$this->createDeleteElements($formData);
}
}
/**
* Prepare the form for the requested mode
*/
public function onRequest()
{
if ($this->shouldInsert()) {
$this->onInsertRequest();
} elseif ($this->shouldUpdate()) {
$this->onUpdateRequest();
} elseif ($this->shouldDelete()) {
$this->onDeleteRequest();
}
}
/**
* Prepare the form for mode insert
*
* Populates the form with the data passed to add().
*/
protected function onInsertRequest()
{
$data = $this->getData();
if (! empty($data)) {
$this->setDefaults($data);
}
}
/**
* Prepare the form for mode update
*
* Populates the form with either the data passed to edit() or tries to fetch it from the repository.
*
* @throws NotFoundError In case the entry to update cannot be found
*/
protected function onUpdateRequest()
{
$data = $this->getData();
if ($data === null) {
$row = $this->fetchEntry();
if ($row === false) {
throw new NotFoundError('Entry "%s" not found', $this->getIdentifier());
}
$data = get_object_vars($row);
}
$this->setDefaults($data);
}
/**
* Prepare the form for mode delete
*
* Verifies that the repository contains the entry to delete.
*
* @throws NotFoundError In case the entry to delete cannot be found
*/
protected function onDeleteRequest()
{
if (! $this->entryExists()) {
throw new NotFoundError('Entry "%s" not found', $this->getIdentifier());
}
}
/**
* Apply the requested mode on the repository
*
* @return bool
*/
public function onSuccess()
{
if ($this->shouldInsert()) {
return $this->onInsertSuccess();
} elseif ($this->shouldUpdate()) {
return $this->onUpdateSuccess();
} elseif ($this->shouldDelete()) {
return $this->onDeleteSuccess();
}
}
/**
* Apply mode insert on the repository
*
* @return bool
*/
protected function onInsertSuccess()
{
try {
$this->insertEntry();
} catch (Exception $e) {
Notification::error($this->getInsertMessage(false));
$this->error($e->getMessage());
return false;
}
Notification::success($this->getInsertMessage(true));
return true;
}
/**
* Apply mode update on the repository
*
* @return bool
*/
protected function onUpdateSuccess()
{
try {
$this->updateEntry();
} catch (Exception $e) {
Notification::error($this->getUpdateMessage(false));
$this->error($e->getMessage());
return false;
}
Notification::success($this->getUpdateMessage(true));
return true;
}
/**
* Apply mode delete on the repository
*
* @return bool
*/
protected function onDeleteSuccess()
{
try {
$this->deleteEntry();
} catch (Exception $e) {
Notification::error($this->getDeleteMessage(false));
$this->error($e->getMessage());
return false;
}
Notification::success($this->getDeleteMessage(true));
return true;
}
/**
* Create and add elements to this form to insert an entry
*
* @param array $formData The data sent by the user
*/
abstract protected function createInsertElements(array $formData);
/**
* Create and add elements to this form to update an entry
*
* Calls createInsertElements() by default. Overwrite this to add different elements when in mode update.
*
* @param array $formData The data sent by the user
*/
protected function createUpdateElements(array $formData)
{
$this->createInsertElements($formData);
}
/**
* Create and add elements to this form to delete an entry
*
* @param array $formData The data sent by the user
*/
abstract protected function createDeleteElements(array $formData);
/**
* Create and return a filter to use when selecting, updating or deleting an entry
*
* @return Filter
*/
abstract protected function createFilter();
/**
* Return a notification message to use when inserting an entry
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
abstract protected function getInsertMessage($success);
/**
* Return a notification message to use when updating an entry
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
abstract protected function getUpdateMessage($success);
/**
* Return a notification message to use when deleting an entry
*
* @param bool $success true or false, whether the operation was successful
*
* @return string
*/
abstract protected function getDeleteMessage($success);
}