I know I am a little bit cheeky but may I post my current Doctrine-powered MVC and you tell me what's wrong with my current approach (by theory) and what would you suggest to change, please

In general: I use Doctrine. I want to have a generic CRUD Controller/Model classes that will cover 90% of the CRUD functionality I need.
My requests/replies are in JSON format - POST only.
So ...
Base CRUD controller class:
Code: Select all
<?php/**
* @property CRUD_Model _model
*/
abstract class CRUD_Controller extends Auth_Controller
{
protected $dataPostName = 'response';
protected $_model = null;
function __construct($model = null)
{
parent::__construct();
$this->setModel($model);
}
/**
* Add an object to the Model
*
* @access public
* @return bool
*/
public function add()
{
if ($data = $this->validateInput())
foreach ($this->getModel()->add($data) as $key => $value)
RowResponse::register($key, $value);
$this->respond();
}
/**
* Add an object to the Model
*
* @access public
* @return bool
*/
public function save()
{
if ($data = $this->validateInput())
foreach ($this->getModel()->save($data) as $key => $value)
RowResponse::register($key, $value);
$this->respond();
}
/**
* Remove an object from the Model
*
* @access public
* @return bool
*/
public function remove()
{
if ($data = $this->validateInput())
foreach ($this->getModel()->remove($data) as $key => $value)
RowResponse::register($key, $value);
$this->respond();
}
/**
* Update an object in the Model
*
* @access public
* @return bool
*/
public function update()
{
if ($data = $this->validateInput())
foreach ($this->getModel()->update($data) as $key => $value)
RowResponse::register($key, $value);
$this->respond();
}
/**
* Get Model data
*
* @access public
* @return bool
*/
protected function get($method = 'get', $conf = null)
{
$this->getModel()->{'set'.ucfirst($method).'Dql'}($conf);
foreach ($this->getModel()->get() as $key => $value)
RowResponse::register($key, $value);
$this->respond();
}
/**
* Set the Model class
*
* @access protected
*/
protected function setModel($model)
{
if (!empty($model))
{
$this->_model = $model;
$this->_model->_assign_libraries();
}
}
/**
* Get the Model
*
* @access protected
* @return DB_List_Model
*/
protected function getModel()
{
if (!$this->_model)
$this->defaultInit();
return $this->_model;
}
/**
* Set the default Model class
*
* @access private
*/
private function defaultInit()
{
$class = get_class($this).'_CRUD_Model';
require_once(APPPATH.'/models/'.strtolower($class).EXT);
$this->setModel(new $class);
}
/**
* Validate and transform JSON input data
*
* @access private
* @return stdClass|false
*/
protected function validateInput()
{
if (!$data = json_decode($this->input->post($this->dataPostName), true))
{
Error::register('???????? ?????? ?? ???????.');
return false;
}
return $data;
}
/**
* Load View for ExtJS response
*
* @access protected
*/
protected function respond()
{
$this->view('response');
}
protected function getRemapping($method, $arguments)
{
$remapping = parent::getRemapping($method, $arguments);
if (strpos($remapping->method, 'get') === 0)
{
$remapping->arguments = array($remapping->method, $_POST);
$remapping->method = 'get';
}
return $remapping;
}
}
* getRemapping() method will in fact call get() method when URLs like /controller/get* is requested.
An inheritance:
Code: Select all
class City extends CRUD_Controller
{
function __construct()
{
parent::__construct();
$this->setModel(new Pagination(new City_CRUD_Model()));
}
}
Base CRUD Model class
Code: Select all
/**
* @property string $tableName
* @property Doctrine_Collection $collection
* @property Doctrine_Record $record
* @property Doctrine_Table $table
* @property Doctrine_Query $currentQuery
* @property String $tableAlias
*
*/
class CRUD_Model extends Model
{
protected $recordClass = null;
protected $table = null;
protected $collection = null;
protected $record = null;
protected $currentQuery = null;
protected $tableAlias = '_tempTable';
public function __construct($recordClass = null)
{
parent::__construct();
$this->setRecordClass($recordClass);
$this->setGetDql();
}
public function getCurrentQuery()
{
return $this->currentQuery;
}
public function getTableAlias()
{
return $this->tableAlias;
}
/**
* Load the collection of objects.
*
* @access public
* @return bool
*/
public function setGetDql()
{
$this->currentQuery = $this->getTable()->createQuery($this->tableAlias);
}
/**
* Load the collection of objects.
*
* @access public
* @return bool
*/
public function load()
{
$this->collection = $this->getCurrentQuery()->execute();
}
/**
* Return the object collection
*
* @access public
* @return Array
*/
public function get()
{
if (empty($this->collection))
$this->load();
return array
(
'response' => $this->collection->toArray(true)
);
}
/**
* Add an object to the collection.
*
* @access public
* @param mixed $data
* @return bool
*/
public function add($data)
{
try
{
$this->record = $this->createFromObject($data, false);
$this->record->save();
}
catch (Exception $E)
{
Error::register($E->getMessage());
return array
(
'response' => new stdClass()
);
}
return array
(
'response' => $this->record->toArray(true)
);
}
/**
* Update the object in the collection.
*
* @access public
* @return bool
*/
public function update($data)
{
try
{
$this->record = $this->createFromObject($data);
$this->record->save();
}
catch (Exception $E)
{
Error::register($E->getMessage());
return array
(
'response' => new stdClass()
);
}
return array
(
'response' => $this->record->toArray(true)
);
}
/**
* Save the object in the collection.
*
* @access public
* @return bool
*/
public function save($data)
{
try
{
$this->record = $this->createFromObject($data);
$this->record->save();
}
catch (Exception $E)
{
Error::register($E->getMessage());
return array
(
'response' => new stdClass()
);
}
return array
(
'response' => $this->record->toArray(true)
);
}
/**
* Remove an object from the collection
*
* @access public
* @return bool
*/
public function remove($data)
{
try
{
$this->record = $this->createFromObject($data);
$this->record->delete();
}
catch (Exception $E)
{
Error::register($E->getMessage());
}
return array
(
'response' => $this->record->toArray(true)
);
}
public function setRecordClass($recordClass)
{
if (!empty($recordClass))
{
$this->recordClass = $recordClass;
$this->table = Doctrine_Manager::connection()->getTable($this->recordClass);
}
}
/**
* Return the ORM table
*
* @access public
* @return Doctrine_Table
*/
public function getTable()
{
if (!$this->table)
$this->defaultInit();
return $this->table;
}
private function defaultInit()
{
$this->setRecordClass(substr('R'.get_class($this), 0, -strlen('_CRUD_Model')));
}
protected function createFromObject($data, $assignID = true)
{
$record = $this->getTable()->create();
if ($assignID)
if ($ids = array_intersect_key($data, array_flip($this->getTable()->getIdentifierColumnNames())))
$record->assignIdentifier($ids);
$record->synchronizeWithArray($data, true);
return $record;
}
}
* API: getByMunicipality(), add(), save(), remove()
An inheritance:
Code: Select all
class City_CRUD_Model extends CRUD_Model
{
public function setGetByMunicipalityDql($conf = null)
{
$this->currentQuery = $this->getTable()
->createQuery($this->tableAlias)
->andWhere('municipality_id = ?', isset($conf['municipality_id']) ? $conf['municipality_id'] : 0);
}
}
My "decorator" base class
Code: Select all
/**
* @property CRUD_Model $model
* @property mixed $conf
*
*/
abstract class Facade_CRUD_Model extends CRUD_Model
{
protected $model = null;
protected $conf = null;
public function __construct($model, $conf = null)
{
$this->model = $model;
$this->conf = $conf;
$this->_assign_libraries();
}
protected function responseObject()
{
return new stdClass();
}
public function getCurrentQuery()
{
return $this->currentQuery;
}
public final function getTableAlias()
{
return $this->model->getTableAlias();
}
public function load()
{
$this->collection = $this->currentQuery->execute();
}
public function get()
{
return $this->model->get();
}
public function add($data)
{
return $this->model->add($data);
}
public function update($data)
{
return $this->model->update($data);
}
public function save($data)
{
return $this->model->save($data);
}
public function remove($data)
{
return $this->model->remove($data);
}
public function setRecordClass($recordClass)
{
$this->model->setRecordClass($recordClass);
}
public function getTable()
{
return $this->model->getTable();
}
private function defaultInit()
{
$this->model->defaultInit();
}
public function __call($method, $arguments)
{
return call_user_func_array(array($this->model, $method), $arguments);
}
}
A Pagination (it has all of the data-grid functionality, so it's not just a pagination. Wrong name I guess ):
Code: Select all
/**
* Pagination facade class
*
* This class is to be used in Model for pagination, ordering and filtering purposes.
*
* @package MyMoto
* @subpackage Libraries
* @category Libraries
* @author VladSun
*
* @property string $paramStartPage
* @property string $paramLimitPage
* @property string $paramSortField
* @property string $paramSortDir
* @property string $paramStartPage
* @property string $paramFilter
* @property string $paramFilter
* @property string $totalField
*
* @property CRUD_Model $model
*/
class Pagination extends Facade_CRUD_Model
{
public $paramStartPage = 'start';
public $paramLimitPage = 'limit';
public $paramSortField = 'sort';
public $paramSortDir = 'dir';
public $paramFilter = 'filter';
public $totalField = 'total';
public $responseField = 'response';
private $startPageValue = 0;
private $limitPageValue = 0;
private $sortFieldValue = '';
private $sortDirValue = '';
private $filterValue = array();
private $totalCount = 0;
public function __construct($model, $conf = null)
{
parent::__construct($model, $conf);
}
public function getCurrentQuery()
{
$this->currentQuery = $this->model->getCurrentQuery();
$this->apply();
return $this->currentQuery;
}
public function load($params = null)
{
if (!$this->currentQuery)
$this->getCurrentQuery();
$this->collection = $this->currentQuery->execute();
}
public function get()
{
if (empty($this->collection))
$this->load();
$o = $this->responseObject();
$o->{$this->responseField} = $this->currentQuery->execute()->toArray();
$o->{$this->totalField} = $this->totalCount;
return $o;
}
protected function apply()
{
$this->setParameterValues($_POST);
if ($this->filter() === false)
Error::register('?????? ??? ???????????? ?? ???????.');
elseif ($this->orderBy() === false)
Error::register('?????? ??? ??????????? ?? ???????.');
elseif ($this->limit() === false)
Error::register('?????? ??? ?????????????? ?? ???????.');
else
{
Warning::register($this->currentQuery->getSql());
$this->totalCount = $this->currentQuery->count();
}
return null;
}
private function orderBy()
{
if (empty($this->sortFieldValue) || empty($this->sortDirValue))
return true;
if (strtoupper($this->sortDirValue) != 'ASC' && strtoupper($this->sortDirValue) != 'DESC')
return false;
if (!$this->model->getTable()->hasField($this->sortFieldValue))
return false;
$this->currentQuery = $this->currentQuery->orderBy($this->model->getTableAlias().'.'.$this->sortFieldValue.' '.$this->sortDirValue);
return true;
}
private function limit()
{
if (!empty($this->limitPageValue))
$this->currentQuery = $this->currentQuery->limit($this->limitPageValue)->offset($this->startPageValue);
return true;
}
private function filter()
{
foreach ($this->filterValue as $filterData)
{
if (($filter = Filter_Provider::get($filterData, $this->model->getTable())) === false)
return false;
elseif ($filter->apply($this->currentQuery) === false)
return false;
}
return true;
}
public function setParameterNames($data)
{
$this->paramStartPage = $data['paramStartPage'] ? $data['paramStartPage'] : 'start';
$this->paramLimitPage = $data['paramLimitPage'] ? $data['paramLimitPage'] : 'limit';
$this->paramSortField = $data['paramSortField'] ? $data['paramSortField'] : 'sort';
$this->paramSortDir = $data['paramSortDir'] ? $data['paramSortDir'] : 'dir';
$this->paramFilter = $data['paramFilter'] ? $data['paramFilter'] : 'filter';
$this->totalField = $data['totalField'] ? $data['totalField'] : 'totalCount';
}
public function setParameterValues($data)
{
$this->startPageValue = isset($data[$this->paramStartPage]) ? intval($data[$this->paramStartPage]) : 0;
$this->limitPageValue = isset($data[$this->paramLimitPage]) ? intval($data[$this->paramLimitPage]) : 0;
$this->sortFieldValue = isset($data[$this->paramSortField]) ? $data[$this->paramSortField] : null;
$this->sortDirValue = isset($data[$this->paramSortDir]) ? $data[$this->paramSortDir] : null;
$this->filterValue = isset($data[$this->paramFilter]) ? $data[$this->paramFilter] : array();
}
}
I know it's a lot of code, but please let me been excused

I just want to have a clear view of what I'm doing wrong once and for all
EDIT: Wrong version, updated.