Created abstract structures to keep complexity and code redundancy at bay.

This commit is contained in:
2019-04-18 14:31:13 +02:00
parent 4b2dbff104
commit 6e1326240b
10 changed files with 339 additions and 351 deletions

View File

@@ -0,0 +1,189 @@
<?php
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;
// No direct access.
defined('_JEXEC') or die;
abstract class AbstractClubsController extends BaseController
{
protected abstract function getNameOfElement();
protected function getModelName()
{
return $this->getNameOfElement();
}
protected function getNameOfView()
{
return strtolower($this->getNameOfElement());
}
protected abstract function getDataMapping();
function new()
{
$obj = call_user_func(array('Clubs' . $this->getNameOfElement(), 'create' . $this->getNameOfElement()));
// Fetch the posted data
$values = $this->loadData();
$this->filterPreCheck($values);
// Check the input data
$error = $this->checkData($values);
$view = $this->getNameOfView();
if($error)
{
$urldata = $this->packData($values);
$this->setRedirect(Route::_("index.php?option=com_clubs&view={$view}&id=new&data={$urldata}", false));
return;
}
$this->applyData($obj, $values);
// Do the actual work
$obj->save();
$this->setRedirect(Route::_("index.php?option=com_clubs&view={$view}s", false));
}
function change()
{
$app = Factory::getApplication();
$input = $app->input;
$id = (int) $input->post->getInt('id');
$obj = call_user_func(array('Clubs' . $this->getNameOfElement(), 'load' . $this->getNameOfElement()), (int) $id);
// Fetch the posted data
$values = $this->loadData();
$this->filterPreCheck($values);
// Check the input data
$error = $this->checkData($values);
$view = $this->getNameOfView();
if($error)
{
$urldata = $this->packData($values);
$this->setRedirect(Route::_("index.php?option=com_clubs&view={$view}&id={$id}&data={$urldata}", false));
return;
}
$this->applyData($obj, $values);
// Do the actual work
$obj->save();
$this->setRedirect(Route::_("index.php?option=com_clubs&view={$view}s", false));
}
protected function loadData()
{
$values = array();
$input = Factory::getApplication()->input->post;
foreach($this->getDataMapping() as $m => $f)
{
$filter = (isset($f['filter'])) ? $f['filter'] : 'string';
$values[$m] = $input->get($m, null, $filter);
}
return $values;
}
protected function filterPreCheck(&$values){}
protected function checkData($values)
{
$error = false;
// Check existence of the required fields
foreach ($this->getDataMapping() as $m => $v)
{
if(! isset($v['required']) || ! $v['required'])
continue;
// Field is required
if(! $this->fieldValid($m, $values[$m], $v))
{
$fname = (isset($v['name'])) ? $v['name'] : $m;
Factory::getApplication()->enqueueMessage("Das Feld $fname ist obligatorisch.", 'error');
$error = true;
}
}
return $error;
}
protected function fieldValid($name, $value, $options)
{
if(empty($value))
return false;
if(isset($options['filter']))
{
switch($options['filter'])
{
case 'string':
if(empty(trim($value)))
return false;
}
}
return true;
}
private function packData($values)
{
$data = array();
foreach($this->getDataMapping() as $i)
$data[$i] = $values[$i];
return urlencode(json_encode($data));
}
public function applyData($obj, $values)
{
foreach($this->getDataMapping() as $m => $v)
{
$functionName = $this->getSetterMethodName($m, $v);
$value = (isset($values[$m])) ? $values[$m] : null;
$obj->$functionName($value);
}
}
private function getSetterMethodName($m, $options)
{
if(isset($options['setter']))
return $options['setter'];
$firstChar = substr($m, 0, 1);
$restChars = substr($m, 1);
return 'set' . strtoupper($firstChar) . $restChars;
}
function delete()
{
$app = Factory::getApplication();
$id = $app->input->get->getInt('id');
$name = $this->getNameOfElement();
$app->enqueueMessage("Removal of $name with id $id.");
$className = 'Clubs' . $this->getModelName();
$functionName = 'load' . $this->getModelName();
$element = call_user_func(array($className, $functionName), $id);
$element->delete();
$view = $this->getNameOfView();
$this->setRedirect(Route::_("index.php?option=com_clubs&view={$view}s", false));
}
}

View File

@@ -0,0 +1,187 @@
<?php
// No direct access.
use Joomla\CMS\Factory;
defined('_JEXEC') or die;
abstract class AbstractClubsModel
{
protected $id;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
protected abstract function getDataMappings();
protected abstract function getTableName();
protected function loadData(array $data)
{
$this->id = $data['id'];
foreach($this->getDataMappings() as $m)
$this->$m = $data[$m];
}
protected static function loadElements(string $tableName, string $className)
{
$dbo = Factory::getDbo();
$q = $dbo->getQuery(true);
$q->select('*')
->from($tableName);
$dbo->setQuery($q);
$dbo->execute();
$list = $dbo->loadAssocList();
$ret = array();
foreach($list as $row)
{
$obj = new $className();
$obj->loadData($row);
$ret[] = $obj;
}
return $ret;
}
/**
* @param int $id
* @throws Exception
* @return ClubsOffer
*/
protected static function loadElement(int $id, string $tableName, string $className)
{
$dbo = Factory::getDbo();
$q = $dbo->getQuery(true);
$q->select('*')->from($tableName)->where('id=' . (int) $id);
$dbo->setQuery($q);
$dbo->execute();
$row = $dbo->loadAssoc();
if($row == null)
{
throw new Exception("No object of class $className found.");
// TODO
}
$obj = new $className();
$obj->loadData($row);
return $obj;
}
public function save()
{
if($this->id === 'new')
$this->insertElement();
else
$this->updateElement();
}
private function insertElement()
{
if(! $this->isDataValid())
throw new Exception('Data is invalid');
$dbo = Factory::getDbo();
$q = $dbo->getQuery(true);
$mappings = $this->getDataMappings();
$values = array();
foreach($mappings as $m)
$values[$m] = $this->$m;
$this->filter($values);
foreach($mappings as $m)
$values[$m] = $q->q($values[$m]);
$this->postQuoteFilter($values);
$q->insert($this->getTableName())
->columns(array_map(array($q, 'qn'), $mappings))
->values(join(',', $values))
;
$dbo->transactionStart();
$dbo->setQuery($q);
$dbo->execute();
$this->id = $dbo->insertid();
$dbo->transactionCommit();
}
private function updateElement()
{
if(! $this->isDataValid())
throw new Exception('Data is invalid');
$dbo = Factory::getDbo();
$q = $dbo->getQuery(true);
$mapping = $this->getDataMappings();
$values = array();
foreach($mapping as $m)
$values[$m] = $this->$m;
$this->filter($values);
foreach($mapping as $m)
$values[$m] = $q->q($values[$m]);
$this->postQuoteFilter($values);
$q->update($this->getTableName());
foreach($mapping as $m)
$q->set($q->qn($m) . '=' . $values[$m]);
$q->where("id=". (int) $this->id);
$dbo->setQuery($q);
$dbo->execute();
}
protected function prepareDelete(){}
public function delete()
{
if($this->id === 'new')
return;
$this->prepareDelete();
$dbo = Factory::getDbo();
$q = $dbo->getQuery(true);
$q->delete($this->getTableName())
->where('id=' . (int) $this->id);
$dbo->setQuery($q);
$dbo->execute();
}
protected function getRequiredDataMappings()
{
return $this->getDataMappings();
}
protected function isDataValid()
{
foreach($this->getRequiredDataMappings() as $m)
{
if(!isset($this->$m) || empty($this->$m) || $this->$m === null)
return false;
}
return true;
}
protected function filter(&$values){}
protected function postQuoteFilter(&$values){}
}

View File

@@ -0,0 +1,77 @@
<?php
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\View\HtmlView;
use Joomla\CMS\Router\Route;
// No direct access.
defined('_JEXEC') or die;
abstract class AbstractClubsViewSingle extends HtmlView
{
protected $address;
protected $object;
protected $isNew;
function display($tpl = null)
{
$input = Factory::getApplication()->input;
$id = $input->get->get('id');
$modelClass = 'Clubs' . $this->getModelName();
$controller = $this->getControllerName();
if($id === 'new')
{
$this->address = Route::_("index.php?option=com_clubs&task={$controller}.new");
$this->object = call_user_func(array($modelClass, 'create' . $this->getModelName()));
$this->isNew = true;
}
else if(is_numeric($id))
{
$this->address = Route::_("index.php?option=com_clubs&task={$controller}.change");
$this->object = call_user_func(array($modelClass, 'load' . $this->getModelName()), (int) $id);
$this->isNew = false;
}
else
throw new Exception('Need an object id.');
if($input->get->get('data', null, 'json') != null)
{
// Restore previous data
$dataurl = $input->get->get('data', null, 'json');
$data = json_decode($dataurl, true);
$controller = $this->getElementController();
$controller->applyData($this->object, $data);
}
parent::display($tpl);
}
protected abstract function getViewName();
protected function getControllerName()
{
return $this->getViewName();
}
protected function getModelName()
{
$name = $this->getViewName();
return $this->capitalize($name);
}
private function capitalize($s)
{
$first = substr($s, 0, 1);
$rest = substr($s, 1);
return strtoupper($first) . $rest;
}
protected abstract function getElementController();
}