<?php
require_once('XMLReturn.php');
require_once('SOAPReturn.php');
require_once('ireturn.php');
/**
* @author Josh Team <jteam@creditsolutions.com>
* @version 1.0
*/
/**
* A namespace for exceptions thrown in the CRUD
* @version 1.0
* @author Josh Team <hteam@creditsolutions.com>
* @package Exception
*/
class CRUDException extends Exception{}
/**
* Creation, Read, Update, Delete, FindID, Validate Model Objects
*
* The CRUD class is the backbone to all DataStores. DataStores are generated for you
* based on your database. All of your DataStores extend CRUD which handles the core
* functionality of each Store. You shall not instantiate the CRUD itself however extend it
* and then change whatever functionality you need. For instance, the CRUD only supports
* grabbing one row of a table per ID passed into the read method. If you need to grab other
* records as per a join, or a subselect you would need to make your own read method in the
* corresponding DataStore to do so:
* <code>
* //will over ride the CURD::read() method
* class MyDataStore extends CRUD {
* public function read($id=0) {
* //Anything custom you need
* }
* }
* </code>
*
* All creation and updates are passed through Validation that matches it per Database
* Meta information.
*
* @abstract
* @package Model
*/
abstract class CRUD {
/**
* Stores an array of all fields to ignore when doing queries. You can extend
* per DataStore to add or take away as need, per DataStore:
* <code>
* class MyDataStore extends CRUD {
* $this->ignoreFields = array('propCount','anotherNonSelectableField');
* }
* </code>
*
* <b>Warning:</b> If you decide to add more fields in your implemintation class
* YOU MUST ADD propCount or it will start selecing, validating, etc. the propCount
* which would cause unforseen errors.
*
* @var array a protected default only has propCount, can add to via DataStore.
*/
protected $ignoreFields = array('propCount');
/**
* Stores the return type of the read method. If not defined in the controller
* then it will return php types (Arrays/Strings), otherwise it looks for
* XML or Soap.
*
* @var string The return type you expect. PHP by default (xml/soap)
*/
public $returnType = 'php';
/**
* Will return an entire record from the DataStore corresponding DB table, it
* pulls all values that are stored in the DataStore's props array, except for fields
* that are stored in the ignoreFields array.
*
* <code>
* class MyDataStore extends CRUD {
* //Will store an array of client with the id of 5 if exist
* $client5 = $this->read(5);
* }
* </code>
*
* @todo I need to build logic at some point that allows you to ignore items in the DataStore based off a variable.
*
* @param mixed $id Can be an array or an ID
* @return array - Will return an array, or a multi-dimensional array.
*/
public function read( $id = null ) {
if(is_numeric($id) || $id == null) {
$fields = array_keys($this->props);
if( is_array( $fields ) ) {
foreach($fields as $field) {
if(!in_array($field, $this->ignoreFields) && substr($field,0,8) != "link_ID_" ) {
$select .= "$field, ";
}
}
if( $id > 0 ) {
$whereClause = " WHERE ".$fields[0]." = $id";
}
$select = substr($select, 0, -2);
$query = "SELECT $select FROM ". substr(get_class( $this ),0,-5) ."$whereClause";
if($id > 0) {
try {
return $this->dbObj->getRow($query);
} catch (Exception $e) {
$this->_handleError( $e );
}
} else {
try {
return $this->returnData($this->dbObj->getAll($query));
} catch (Exception $e) {
$this->_handleError( $e );
}
}
}
} else if(is_array($id)) {
$this->read($id);
} else {
throw new CRUDException('You must pass in an ID to '.get_class($this).'::read()');
}
}
/**
* Will return the value of ID in the table mapped by the DataStore where the
* field you pass in is equal to the value you also passed in.
*
* <code>
* class MyDataStore extends CRUD {
* //Will store the ID of the record where the field email has 'myemail@something.com'
* $ID = $this->findID('email','myemail@something.com');
* }
* </code>
*
* @param string $field Field Searching
* @param string $value Value Searching
* @return integer
*/
public function findID( $field, $value ) {
$query = "
SELECT
ID
FROM
". substr(get_class( $this ),0,-5) ."
WHERE
". $field ." = '". $value ."'
LIMIT 1
";
try {
return ((int)$this->dbObj->getField( $query ));
} catch (Exception $e) {
$this->_handleError($e);
}
}
/**
* Takes an array of values and validates the values based on the properties
* of the DataStore which holds all meta data then calls the protected method
* save which simply loops through the array and puts the value of the index
* in the field of the index.
*
* <code>
* $client = array('first_name'=>'Joe','last_name'=>'Bob');
*
* //Will create a record in the client table, and set fields accordingly
* //Assumes that no other undefined fields are required
* $c = new ClientStore();
* $c->create($client);
* </code>
*
* @todo Will need to go back and add logic to match the values to the table properties and such.
*
* @param array $values
*/
public function create( $values ) {
try {
$this->_validate( $values, $this->props );
$this->save('INSERT into',$values);
} catch (Exception $e) {
$this->_handleError($e);
}
}
/**
* Takes an array of values and validates the values based on the properties
* of the DataStore which holds all meta data then calls the protected method
* save which simply loops through the array and puts the value of the index
* in the field of the index where the ID matches the ID passed in.
*
* <code>
* class MyDataStore extends CRUD {
* //Gets client1
* $client1 = $this->read(1);
*
* //Changes first name
* $client1['first_name'] = 'Bob';
*
* //Save Changes
* $this->update($client1);
* }
* </code>
*
* @todo Will need to go back and add logic to match the values to the table properties and such.
*
* @param int $id The ID of the record you want to save
* @param array $values
*/
public function update($id, $values) {
try {
$this->_validate( $values, $this->props );
$this->save('UPDATE',$values);
} catch (Exception $e) {
$this->_handleError($e);
}
}
protected function save($type, $values) {
/**
* Extract the ID and store it elsewhere so that we can't change the
* Id of the row that we are changing. I will need to write a method later
* that allows you to change the ID.
*/
if(isset($values['ID'])) {
$id = $values['ID'];
unset($values['ID']);
}
$i = 1;
//Loops through the values and builds out the SQL
foreach($values as $index=>$value) {
$v .= $index ." = '$value'";
if(count($values) > $i) {
$v .= ", \n";
}
$i++;
}
//If we are updating, add the where clause
if($type == 'UPDATE') {
$v .= ' WHERE ID = '. $id ;
}
//Puts the entire query togather.
$query = "
$type
". substr(get_class( $this ),0,-5) ."
SET
".$v."
";
try {
$this->dbObj->query( $query );
} catch (Exception $e) {
$this->_handleError($e);
}
}
/**
* Removes the entire record from the table mapped by the DataStore
*
* For now, it simply deletes the record from the table where the ID is the ID that
* you pass in.
*
* <code>
* class MyDataStore extends CRUD {
* //Grabs ID of row
* $id = $this->findID('email','something@someone.com');
*
* //Removes the record
* $this->delete($id);
* }
* </code>
*
* @todo I need to go back and write logic in this that allows you to flag rather to delete the record or to mark the record as Active=0. I also need to write some logic that will allow you to store more information when a delete happens and also be able to flag rows as not deletable.
* @param int $id The ID of the record you want to delete
*/
public function delete( $id ) {
$query = "
DELETE FROM
". substr(get_class( $this ),0,-5) ."
WHERE ID = ". $id ."
LIMIT 1
";
try {
$this->dbObj->query( $query );
} catch (Exception $e) {
$this->_handleError( $e );
}
}
private function _validate($values, $properties) {
$v = new Validation();
try {
$v->validate($values, $properties);
} catch (Exception $e) {
throw new CRUDException($e->getMessage());
}
}
private function _handleError(Exception $e) {
$l = new Log();
$l->saveException( $e );
throw new CRUDException($e->getMessage());
}
/**
* Returns the last ID entered
*/
public function getLastID() {
return $this->dbObj->getLastID();
}
/**
* A factory on what type of data to return
*/
private function returnData($data) {
switch($this->returnType) {
case "xml":
$class = 'XMLReturn';
break;
case "soap":
$class = 'SOAPReturn';
break;
default:
return $data;
break;
$r = new $class();
return $r->read($data);
}
}
}
?>