Separation Anxiety - Refactoring Guidance Needed
Posted: Mon Jan 21, 2008 12:47 pm
I'm trying to redo my little news article system to be able to use it in other areas that are often called something other than "news".
Any suggestions would be great.
UPDATE
I've stripped out everything except the program flow if() statements and just have comments explain what the code in each part does. Does it make any more sense now? Does the organization/separation seem any better?
Any suggestions would be great.
UPDATE
I've stripped out everything except the program flow if() statements and just have comments explain what the code in each part does. Does it make any more sense now? Does the organization/separation seem any better?
Code: Select all
<?php
// news.php (the "page based controller I guess??")
ob_start();
include_once('config.php');
include_once('class.NewsModule.php');
include_once ('functions.php');
// get current action from GET or POST
if(!isset($cmd)){$cmd='searchform'; }
if(!isset($behaviour)){$behaviour='insert'; }
if(!empty($id) and $cmd==='showform'){$behaviour = 'update';}
// the AdminCoreModule object's __constructor sets default form fields:
// headline, published, summary, body, credit, feature, link
$M = new NewsModule;
// you can more form fields or modify or remove the defaults
unset($M->formfields['summary']); // client says they don't need a summary field
// These variables MUST be set before you can really start using the object
// I could extend a child class from AdminCoreModule that has these already set (new NewsModule)
$M->table = 'news';
$M->section ='news';
$M->depttext = 'Fire Dept';
$M->behaviour = $behaviour;
$M->id = @$id; // tell object the id of the record your working on
// now start manipulating the object
// UseDepartments also has an effect on the Form
// in that they determine whether the Departments field is visible
// and whether the javascript that enables cloning multiple Department fields is active.
// Should those 2 not be a part of this section?
// High level users (0 & 1) can assign/search things by Departments
if ($_SESSION['level'] < 2)
{
$M->UseDepartments();
}
/////////////////////////////////////////////////
// Search Form
/////////////////////////////////////////////////
if ($cmd === "searchform") // Display a form to search for articles
{
$search = new HTMLWidget_searchform;
// configure with same values as $M a few lines up since it's working with the same tables
$search->table = 'news';
$search->section ='news';
$search->depttext = 'Fire Dept';
echo $search->ShowForm();
}
/////////////////////////////////////////////////
// Search Results
/////////////////////////////////////////////////
if ($cmd === "searchresults")
{
$results = new HTMLWidget_searchresults;
$results->table = 'news';
$results->section ='news';
$results->depttext = 'Fire Dept';
echo $results->ShowResults();
}
/////////////////////////////////////////////////
// Database manipulation
// Insert or Update a record
/////////////////////////////////////////////////
// If the add/edit form is submitted this method is triggered
// it handles error checking and adding/updating the database
// If there were errors then nothing gets updated
// and $M->errors array is populated with error messages
if($cmd=== 'add_confirm' or $cmd === 'edit_confirm')
{
$M->AddEditConfirm();
}
/////////////////////////////////////////////////
// Show the Form
/////////////////////////////////////////////////
// if $cmd is 'showform' or if $M->errors has error messages in it
// show the add/update form (again)
if($cmd === 'showform' or count($M->GetErrors()) > 0 )
{
if($behaviour === 'update') // Display the update form
{
// query to get all the data for the $id being edited
// Modify that data as needed to work properly later on.
// The modifications needed here would change from section to section.
// now loop through the $M->FormFields array
// and tell all the formfield objects where to get their values to display for editing
// Now that the $M object is prepared properly,
// include a file that makes use of the formfield objects to display the form.
// This file is a bit of a mix of php & html since depending on certain values set
// in the $M object some parts of the form will be displayed differently.
// The form is a bit complex. It uses a lot of javascript for drag/drop elements and a little
// ajax for combo-boxes for categories and sub-categories.
// Another reason for the html/php mix is that sometimes parts of this file
// need to show user data from their $_SESSION.
// I don't know of any other way to display session data other than to put some php inside
// of the html......???
include('tpl.form_core.php');
}
// else
if($behaviour === 'insert') // Display an empty form
{
// Don't bother querying anything, just display an empty form
include('tpl.form_core.php');
}
}
if ($cmd === "delete_question")// Warn of deletion
{
// some reusable html that asks the user if they are sure
// they want to delete the current record
// submit buttons [yes] [cancel]
include('tpl.delete_question.php');
}
if($cmd === "delete_perform") // actually delete the current record
{
$M->DeleteItem();
// display a little confirmation message here
}
$content = ob_get_contents();
ob_end_clean();
// the view could be an include that uses $content
// or a template or something class that you send the $content variable to
//------------------
// VIEW
//------------------
$H = new AdminHead;
$H->PrefixTitle('Public News - ');
$H->ExtraCSS('some css specific to this page');
echo $H->Output(); //echoes a standard <head>
?>
<div id="admin-container">
<?php include('../includes/inc.admin_header.php'); ?>
<?php include('../includes/inc.admin_navigation.php'); ?>
<div id="content">
<? echo $content ?>
</div>
<?php include('../includes/inc.admin_footer.php');?>
</div>
</body>
</html>Code: Select all
<? // class.NewsModule.php
class NewsModule extends AdminCoreModule
{
public $table; // the main table of the module being worked with
public $id; // the id of the record in the main table being worked on
public $rel8cat = 'catrelations'; // name of table containing relationships between categories table and records in other tables
public $rel8dept = 'deptrelations'; // name of table containing relationships between departments table and records in other tables
public $photosizes; // ?? array of thumb/medium/large sizes?
private $newPhotoNames = array(); // holds the generated names of uploaded photos.
private $newDocumentNames = array(); // holds the generated names of uploaded documents.
public $cmd = ''; // The current "Action" to be performed
public $behaviour = ''; // If database related actions occur, sets if it's and INSERT or UPDATE
public $category_cloning = true; //whether or not the <form> allows you to pick multiple categories
public $formfields = array();
// array of objects made with the FormField class for the regular fields - use AddFormField($object)
// in the file that uses this class this array is then looped through and spat out to build
// the form elemnts that are seen
// Before spitting out the fields you can add to this array via the class methods
public $errors = array(); // errors to be shown to user if things don't go right.
public $debugs = array(); // errors only the developers should see (mysql_error() etc.)
function __construct()
{
// set up defaults used throughout
// Define size of thumbnail/full view of uploaded images
$this->photoField = 'photo';
$this->photoSizes = array(100,200,400);
$this->photoDestinations = array();
// Define the Fields shown in the form and add
// the form field objects to the $formfields array of this class
$f = new FFtext('headline');
$f->Label->text = 'Head Line';
$f->defaultvalue = 'This article needs a Head Line';
$f->required = true;
$this->AddFormField($f, 'headline');
$f = new FFselectMDY('published');
$f->Label->text = 'Date Published';
$f->required = true;
$this->AddFormField($f, 'published');
$f = new FFtextarea('summary','enter short description here');
$this->AddFormField($f, 'summary');
$f = new FFtextarea('body','enter article body here');
$f->requird=true;
$this->AddFormField($f, 'body');
$frequency = array('yes'=>'Yes, Feature','no'=>'Do not feature');
$f = new FFradioGroup('testradio', $frequency );
$f->required = true;
$f->errormessage = 'Featuring of the article was not indicated';
$f->grouplabel = '<p>* Feature this item:</p>';
$this->AddFormField($f, 'feature');
}
/**
* executes a series of methods & functions to add/update an item in the database
* This would have been modified from whatever was in the base class
*/
function AddEditConfirm()
{
// error checks on required fields
// Loop through the array of defined form field objects.
// formfield objects perform basic validation on themselves
// if they are set as "required" fields
// Upload any submitted photo files
if (count($this->GetErrors()) < 1) {
$this->UploadPhotos();
$this->UploadDocuments();
}
// Only continue if there are no errors from errorchex or uploading files
if (count($this->GetErrors()) < 1)
{
// Clean submitted data
// build the SQL string for adding/updating the database
// update the main data table
// update the related tables
$this->updateRelations('category', 'subcategory', 'catrelations');
$this->updateRelations('department','subdepartment','deprelations');
$this->UpdateMedia();
// Tell the user that everything worked.
echo "<h2>Successful $this->behaviour made.</h2>";
echo '<p><a href="'.thispage().'?cmd=showform&id='.$lastid.'">Continue editing this Item.</a></p>';
}
}
} // END AddEditConfirm method
// The rest of the methods are from the base class and would usually have no reason to ever be changed
// no matter what the module was being used for (at least from my experience with dozens of corporate clients)
/**
* Adds the FormField class object to the array of form fields within this class and associates each field object with a database field in the main table.
* @param object $obj A FormField object
* @param string $databaseField The name of the field in the DB that this form input is associated with.
*/
public function AddFormField($obj, $databaseField)
{
$this->formfields[$databaseField] = $obj;
}
//loops through all the form fields and echos their markup
public function ShowFormFields()
{...}
// displays a link that the javascript will look for to enable assigning multiple categories
public function CloneCategoryLink()
{...}
// displays a special combo box for adding categories
public function CategorySelect_adding()
{...}
// displays a special combo box for editing categories
public function CategorySelect_editing()
{...}
/**
* Gathers Data for display on search results page.
* This function is a bit of a monster. Probably needs refactoring
*/
function SearchResults()
{...}
function errorchex($field, $label='', $custom_message='')
{...}
function SetError($error)
{...}
function GetErrors()
{...}
function ShowErrors()
{...}
function UploadPhotos()
{...}
function UploadDocuments()
{...}
function UpdateMedia()
{...}
function UpdateRelations($postParent, $postSub, $relateTable)
{...}
function DeleteItem()
{...}
} // end class
?>Code: Select all
<?php // tpl.delete_question.php
$result=mysql_query("SELECT * FROM $M->table WHERE id='$M->id' LIMIT 1" ) or die(mysql_error());
$details = mysql_fetch_assoc($result);
?>
<h2>WARNING: Delete News Item</h2>
<p>You are about to permanently delete the following news item including it's photos and documents:</p>
<p><?php echo convert_date($details['published']);?> <?php echo $details['headline'];?></p>
<p>Are you sure you wish to proceed?</p>
<p><a href="<?=thispage()?>"><b>No, I do not wish to delete this article.<b></a></p>
<p><a href="<?=thispage()?>?cmd=delete_perform&id=<?php echo $id;?>">Yes, delete this article</a></p>
Code: Select all
tpl.form_core.php
<h2>News Article</h2>
<p>Fields marked with (<span class="required">*</span>) are required.</p>
<?
if(!is_object($M)){ die('$M object is required'); }
echo $M->ShowErrors(); //if the form was submitted and had errors they would be shown here
?>
<form id="form" action="<?=thispage()?>" method="post" enctype="multipart/form-data">
<? if($behaviour === 'insert') { ?>
<input type="hidden" name="cmd" value="add_confirm" />
<? } ?>
<? if($behaviour === 'update') { ?>
<input type="hidden" name="cmd" value="edit_confirm" />
<input type="hidden" name="behaviour" value="update" />
<input type="hidden" name="id" value="<?=$id?>" />
<? }
// Show the special <select> combo box field for categories
if ($behaviour === 'insert')
{ $M->CategorySelect_adding; } // empty one for inserting
else { $M->CategorySelect_editing(); } // populated one for updating
// Now show all the default/developer defined fields from the $M object
$M->ShowFormFields();
?>
<div>
<input type="submit" name="submit" id="submit" class="submit" value="<?=ucfirst($behaviour)?> Article" />
</div>
<!--
100x More html and a little more php goes here.
Makes use of the info in the $M object about photos, documents, videos related to the current record
and builds an ajax based interface for rearranging, deleting, editing captions on those files.
-->
</form>