As promised earlier, here’s the save function. This is an almost magical function. It makes every write you’ll almost ever do exceedingly simple.

function save() {
	$this->dataChecks();
	$this->id = objectSave($this->tableName, $this->id, $this->data, $this->databaseName, array('writeAudit'=>true));
	$this->data['id'] = $this->id;
	$this->postSaveFunctions();
}

OK, so that magical save function just basically calls other functions. What’s the big deal? Each of those functions, particularly objectSave() make life easier.

I’ll take them in order, starting with dataChecks(). This function is defined on a per-class basis in a way that meets the needs of the class. For most of my classes, I include a datetime_created field in my MySQL table, so I like to set that value in my dataCheck function. I also take care of filling in any missing fields or special calculations that are needed. The example below shows how I’d set the datetime_created and set an id_client value if it’s missing and id_company was set.

function dataChecks() {
	if($this->data['datetime_created'] == "" && $this->id==''){
		$this->data['datetime_created'] = dateFormat("now","mysql");
	}
	
	
	if($this->data['id_client']=='' && $this->data['id_company']!='') {
		$companyObj = new company($this->data['id_company']);
		$this->setIDclient($companyObj->getIDclient());
	}
}

I’ll go over the dateFormat function at another time. It’s one of those functions that makes me love PHP and is really simple to create.

The next function is the real meat of this post: objectSave(). objectSave() has been an evolution of various needs over time and if I were to design it from scratch today, I’d write it slightly differently. (First thing, pass all data via one $parameters array rather than the listed parameters that it has now.) You’ll also see in this function a function for writeAuditLog(). That’s a function that is worth it’s own posting later as well. And, since this was written for a particular system, it has some database names and table names hard-coded. That, ideally, should be abstracted. Overall though, this function is called many thousands (perhaps hundreds of thousands?) of times a day and saves us countless time and effort.

function objectSave($tableName, $id=NULL, $dataArray, $databaseName='dtrm', $extraParams=array()) {
	
	$writeAudit = false;
	if($extraParams['writeAudit']===true) {
		$writeAudit = true;
	}
	
	if($id==NULL) {
		if($dataArray['id']!=NULL) {
			$id = $dataArray['id'];
		}
	}
	
	if($id==NULL) {
		mysql_query("SET NAMES utf8",$GLOBALS['standardDBlink']);
		$preferredID = $extraParams['preferredID'];
		$query = "INSERT INTO ".$databaseName.".".$tableName." (`id`) VALUES ('".$preferredID."')";
		$q_insert = mysql_query($query);showMySQLError(mysql_error($GLOBALS['standardDBlink']),$query);
		$sqlError = mysql_error($GLOBALS['standardDBlink']);
		if($sqlError!='') {
			$query2 = "INSERT INTO dtrm.badQueryLog (`query`, `page`, `datetime_created`, `error_text`) 
						VALUES ('".encodeSQL($q_insert)."', '".encodeSQL(curPageURL())."',NOW(), 
								'".encodeSQL($sqlError)."')";
			insertMySQLData($query2);
		}
		$id = mysql_insert_id();
		
		if($writeAudit===true) {
			$subParams = array();
			$subParams['databaseName']=$databaseName;
			$subParams['tableName']=$tableName;
			$subParams['primaryKeyName']='id';
			$subParams['primaryKeyValue']=$id;
			$subParams['arbitraryValue']="New Record";
			writeAuditLog($subParams);
		}
	}

	if($writeAudit===true) {
		$subParams = array();
		$subParams['databaseName']=$databaseName;
		$subParams['tableName']=$tableName;
		$subParams['primaryKeyName']='id';
		$subParams['primaryKeyValue']=$id;
		$subParams['newDataArray']=$dataArray;
		writeAuditLog($subParams);
	}
	
	mysql_query("SET NAMES utf8",$GLOBALS['standardDBlink']);
	
	$query = "UPDATE ".$databaseName.".".$tableName." SET ";
	foreach ($dataArray AS $key=>$val) {
		if($key!='id') {
			$query .= "`".$key."`=";
			if($val==='' || $val===NULL) {
				$query .= "NULL, ";
			} else {
				$query .= "'".mysql_real_escape_string($val)."', ";
			}
		}
	}
	$query = trim($query, ", ");
	$query .= " WHERE id=".$id;
	$update_q = mysql_query($query);showMySQLError(mysql_error($GLOBALS['standardDBlink']),$query);
	$sqlError = mysql_error($GLOBALS['standardDBlink']);
	if($sqlError!='') {
		$query2 = "INSERT INTO dtrm.badQueryLog (`query`, `page`, `datetime_created`, `error_text`) 
					VALUES ('".encodeSQL($update_q)."', '".encodeSQL(curPageURL())."',NOW(), 
							'".encodeSQL($sqlError)."')";
		insertMySQLData($query2);
	}
	
	return $id;
	
}

The above function will take the $data array from the class structure that has already been defined and write those fields, correctly, to a MySQL table. That saves infinite time trying to match up fields in code to MySQL insert and update statements. It also handles returning the $id value whether it stays the same or is created as a result of performing the save. Plus, it logs any errors that happen so you can easily see what you’re doing wrong if you make typos in your code. Oh, and it handles NULL values correctly so you don’t end up with blank or ‘0’ values in your table.

The last function in our save function is postSaveFunctions(). This function isn’t used nearly as much as the others listed here, but it can be really handy if you need to have some code execute after you have a primary key identified or otherwise need to have data written before executing the code. I often create a postSaveTriggers array within an object if I am going to need to perform postSaveFunctions so that in other places in the class I can add items to the postSaveTriggers array and then have the class execute them automatically later when it is saved.

function doPostSaveTriggers() {
	$triggersPerformed = array();
	foreach($this->postSaveTriggers AS $triggerName) {

		// skip the trigger if it has already been performed
		if(in_array($triggerName,$triggersPerformed)) {
			continue;
		}
		
		if($triggerName=="human_audit_created") {
			// do stuff here
		}

		if($triggerName=="hire_nostart_payoutEmail") {
			// do stuff here
		}

	}
	$this->postSaveTriggers = array();
}

I want to make sure to make one point clear: the save(), dataChecks(), and postSaveFunctions() functions all all class-specific. The objectSave() function is a single piece of code that should be included via a common file for use everywhere in your system.

August 20th, 2013

Posted In: Functions

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: