DRY: Advices how to not duplicate code?
Ok here come my question, I have a database with such scheme
+-------------+ +------------+ +-----------+
+ Object + + car + + computer +
+-------------+ +------------+ +-----------+
+ id + + object_id + + object_id +
+ some_col + + max_speed + + CPU_speed +
+ type + + n_gears + + memory +
+-------------+ +------------+ +-----------+
So to summarize when my user input a computer for example, I create a object record, save all shared fields into it and I create a computer record with all specific data for computers being saved.
So far so good, here comes my problem by doing so I would have to duplicate every action (and开发者_Go百科 controller methods view for each type of objects). So right now in my Controller, I have those actions:
+ new-car
+ new-computer
+ show-car
+ show-computer
+ edit-car
+ edit-computer
+ list-car
+ list-computer
So for each type I basically duplicate all the code and change few lines of codes (i.e replacing the car mapper by computer mapper, same for record object). I have to mention the application you the zend framework and is in PHP.
I would like to do something more "Don't repeat yourself" and not having to duplicate all the code. Any idea how to it more elegantly?
I'll share with you my basic CRUD class. Supplied as-is. Test for yourself. Some bugs may be ;)
<?php
abstract class Controller_Crud extends Controller
{
/* definované v dalších třídách */
protected $_viewKey = '';
protected $_url = 'admin/';
protected $_modelClass;
protected $_formClass;
protected $_filterFormClass = null;
protected $_title = 'Untitled';
/* pro vnitřní potřebu */
protected $_model;
protected $_form;
protected $_filterForm;
private $_defaultViewKey = 'crud';
/*
* Inicializace třídy, vytvoření instancí modelů a formulářů
* @see Tul/Controller/Tul_Controller_Admin#init()
*/
public function init()
{
parent::init();
$this->_model = new $this->_modelClass();
$this->_form = new $this->_formClass();
$this->view->indexUrl = $this->_url;
$this->view->viewKey = $this->_viewKey;
$this->view->title = $this->_title;
}
public function postDispatch()
{
// Po provedení akce je nastaven titlek
$this->view->headTitle($this->_title);
}
/**
* základní akce zabezpečující výpis záznamů a filtrování
* @return void
*/
public function indexAction ()
{
if(null !== $this->_filterFormClass){
$this->_filterForm = new $this->_filterFormClass;
}
$filter = null;
if(null !== $this->_filterFormClass){
if($this->_getParam('filter') && $this->_filterForm->isValid($this->_getAllParams())){
$filter = $this->_filterForm->getValues();
}
$this->_filterForm->populate($this->_getAllParams());
}
/* @var Tul_Paginator */
$paginator = $this->_model->getAllPaginator($filter);
$paginator->setCurrentPageNumber($this->_getParam('page',1));
$this->view->data = $paginator;
$this->view->form = $this->_filterForm;
}
/*
* Načte ID záznamu a pokud není nalezeno přesměruje s chybovou hláškou na hlavní stránku
* @see Tul/Controller/Tul_Controller_Admin#getId()
*/
public function getId()
{
$id = $this->_getParam('id', false);
if(false === $id){
$this->addFlashMessage('error','Neplatné ID.');
$this->_gotoUrl($this->_url);
}
return $id;
}
/**
* akce pro přidání nového záznamu
* @return void
*/
public function pridatAction ()
{
$this->view->headTitle('Přidat');
if ($this->_request->isPost()) {
if ($this->_form->isValid($_POST)) {
$data = $this->_form->getValues();
try {
$this->_beforeAdd($data);
$result = $this->_model->insert($data);
$this->_afterAdd($data, $result);
$this->addFlashMessage('success','Úspěšně přidáno. ');
} catch (Exception $e){
$this->addFlashMessage('error','Došlo k chybě při přidávání:<br />' . htmlspecialchars($e->getMessage()));
}
$this->_gotoUrl($this->_url);
} else {
$this->_form->populate($_POST);
}
}
$this->view->form = $this->_form;
$this->_renderView('pridat.phtml');
}
/**
* Akce pro smazání záznamu
* @return void
*/
public function smazatAction ()
{
$id = $this->getId();
try{
$this->_beforeDelete($id);
$result = $this->_model->delete($this->_model->quoteInto('id = ?',$id));
$this->_afterDelete($id, $result);
$this->addFlashMessage('success','Úspěšně smazáno. ');
} catch (Exception $e){
$this->addFlashMessage('error','Došlo k chybě při mazání:<br />'.htmlspecialchars($e->getMessage()));
}
$this->_gotoUrl($this->_url);
}
/**
* akce pro úpravu záznamu
* @return void
*/
public function upravitAction ()
{
$this->view->headTitle('Upravit');
$id = $this->getId();
$item = $this->_model->getById($id);
if ($this->_request->isPost()) {
if ($this->_form->isValid($_POST)) {
$data = $this->_form->getValues();
try {
$this->_beforeUpdate($data, $item);
$result = $this->_model->update($data, $this->_model->quoteInto('id = ?',$id));
$this->_afterUpdate($data, $result);
$this->addFlashMessage('success','Úspěšně upraveno. ');
} catch (Exception $e){
$this->addFlashMessage('error','Došlo k chybě při úpravě:<br />'.htmlspecialchars($e->getMessage()));
}
$this->_gotoUrl($this->_url);
} else {
$this->_form->populate($_POST);
}
} else {
$this->_form->populate((array)$item);
}
$this->view->form = $this->_form;
$this->_renderView('upravit.phtml');
}
/**
* Pokud existuje načte pohled zděděné třídy, pokud neexistuje použije interní
* @param $name string název pohledu
* @return void
*/
private function _renderView($name)
{
$completeName = $this->_viewKey . '/' . $name;
/* @var Zend_View_Abstract */
foreach ($this->view->getScriptPaths() as $dir) {
if (is_readable($dir . $completeName)) {
return $this->renderScript($completeName);
}
}
return $this->renderScript($this->_defaultViewKey . '/' . $name);
}
/**
* Metoda, která je zavolaná těsně před přidáním záznamu.
* Umožňuje upravit data podle potřeby před vložením do databáze.
* Vyhozením vyjímky přidání záznamu skončí chybou
* @param $data array data, která budou přidána
* @return void
*/
protected function _beforeAdd(&$data){}
/**
* Metoda, která je zavolaná těsně po přidání záznamu.
* Umožňuje upravit data podle potřeby ještě po vložení do databáze.
* Vyhozením vyjímky přidání záznamu skončí chybou
* @param $data array data, která budou přidána
* @param $result mixed primární klíč přidaného záznamu
* @return void
*/
protected function _afterAdd(&$data, $result){}
/**
* Metoda, která je zavolaná těsně před úpravou záznamu.
* Umožňuje upravit data podle potřeby ještě před úpravou v databázi.
* Vyhozením vyjímky uprava záznamu skončí chybou
* @param $data array data, která budou upravena
* @param $item mixed původní data
* @return void
*/
protected function _beforeUpdate(&$data,$item){}
/**
* Metoda, která je zavolaná těsně po úpravě záznamu.
* Umožňuje upravit data podle potřeby po úpravě v databázi.
* Vyhozením vyjímky uprava záznamu skončí chybou
* @param $data array data, která budou upravena
* @param $result integer počet ovlivněných záznamů
* @return void
*/
protected function _afterUpdate(&$data,$result){}
/**
* Metoda, která je zavolána před smazáním záznamu.
* Vyhozením vyjímky smazání záznamu skončí chybou
* @param $id id mazaného záznamu
* @return void
*/
protected function _beforeDelete($id){}
/**
* Metoda, která je zavolána po smazání záznamu.
* Vyhozením vyjímky smazání záznamu skončí chybou
* @param $id id mazaného záznamu
* @param $result počet smazaných řádků
* @return void
*/
protected function _afterDelete($id, $result){}
}
And then a concrete implementation:
<?php
class FunkceController extends Controller_Crud
{
protected $_url = '/admin/funkce/';
protected $_modelClass = 'Admin_Functions';
protected $_formClass = 'Form_Admin_Functions';
protected $_filterFormClass = 'Form_Admin_FilterFunctions';
protected $_viewKey = 'funkce';
protected $_title = 'Funkce';
}
Hope this helps ;)
You'll have to create a meta-table with information on each type of object, i.e. what fields it has, how to display each of them (input or select for example), their order and stuff like that.
Then you can access the main controller, only passing the object name to it and it it then should generate the appropriate CRUD forms.
I did something similar to Tomáš Fejfar...
Create a base CRUD controller, and a base model class which has CRUD methods. You can even use generic view scripts that will work for all content types for basic CRUD templates.
In each controller, all you'd need is a few variables at the top to make any alterations necessary.
精彩评论