Created
October 26, 2012 19:47
-
-
Save jamesshannon/3961029 to your computer and use it in GitHub Desktop.
#concrete5 Model and DatabaseItemList class extensions to make for easier active record based CRUD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace LSL\Libraries; | |
defined('C5_EXECUTE') or die('Access Denied.'); | |
/** | |
* @package lerteco_shared_libraries | |
* @subpackage helpers | |
* @copyright James Shannon | |
*/ | |
/** | |
* Base model for all Lerteco models, based on ADODB Active Record and c5's model | |
*/ | |
abstract class Model extends \ADOdb_Active_Record { | |
protected $errors; | |
/** | |
* Sets up active record with a pointer to the database. Uses $this->_table | |
*/ | |
public function __construct() { | |
$db = \Loader::db(); | |
parent::__construct($this->_table, false, $db); | |
} | |
/** | |
* Loads a record that has the common format of a int PK named "ID" | |
* @param int $id | |
* @param boolean $bindarr | |
*/ | |
function Load($id, $bindarr = false) { | |
if (is_numeric($id)) { | |
parent::Load("ID = $id", $bindarr); | |
} | |
} | |
/** | |
* Loads a record that does not follow typical format | |
* @param strin $where SQL where clause | |
* @param boolean $bindarr | |
*/ | |
function customLoad($where, $bindarr = false) { | |
parent::Load($where, $bindarr); | |
} | |
/** | |
* Refresh the record from the database | |
*/ | |
function refresh() { | |
$this->Load($this->ID); | |
} | |
/** | |
* Returns a LertecoFwValidationFormHelper object with appropriate validations set up | |
* @return \LertecoFwValidationFormHelper | |
*/ | |
public function getModelValidation() { | |
return \Loader::helper('validation/form', 'lerteco_fw'); | |
} | |
public function validate($data = null, $token = null) { | |
$val = $this->getModelValidation(); /* @var $val \LertecoFwValidationFormHelper */ | |
if ($token) { | |
$val->addRequiredToken($token, 'For security reasons, you must resubmit this form'); | |
} | |
$data = ($data) ? $data : $_REQUEST; | |
$val->setData($data); | |
//shouldn't be any harm in calling this every time | |
$val->setFiles(); | |
$pass = $val->test(); | |
if (! $pass) { | |
$this->errors = $val->getError(); | |
} | |
return $pass; | |
} | |
public function getErrors() { | |
return $this->errors; | |
} | |
/* deprecated?? (also, the private variable within the class) | |
function loadError($error) { | |
$this->error = $error; | |
} | |
function isError() { | |
$args = func_get_args(); | |
if ($args[0]) { | |
return $this->error == $args[0]; | |
} else { | |
return $this->error; | |
} | |
} | |
function getError() { | |
return $this->error; | |
} */ | |
public function setPropertiesFromArray($arr, $whitelist) { | |
if (! is_array($whitelist)) { | |
$whitelist = explode(',', $whitelist); | |
} | |
foreach($arr as $key => $prop) { | |
if (in_array($key, $whitelist)) { | |
$this->{$key} = $prop; | |
} | |
} | |
} | |
/* deprecated? | |
public function setGetFromProperties() { | |
foreach ($this->GetAttributeNames() as $key) { | |
$_GET[$key] = $this->{$key}; | |
} | |
} */ | |
function __get($name) { | |
if (method_exists($this, "get$name")) { | |
return call_user_func(array($this, "get$name")); | |
} else { | |
return parent::__get($name); | |
} | |
} | |
function __set($name, $value) { | |
if (method_exists($this, "set$name")) { | |
call_user_func(array($this, "set$name"), $value); | |
} else { | |
$this->$name = $value; | |
} | |
} | |
protected function propsToArray($props) { | |
$output = array(); | |
foreach (explode(',', $props) as $prop) { | |
$output[$prop] = $this->$prop; | |
} | |
return $output; | |
} | |
protected function idsFromObjects($objs) { | |
$ids = array(); | |
foreach ($objs as $obj) { | |
$ids[] = $obj->getSecondaryMappingID(); | |
} | |
return $ids; | |
} | |
//expects an array of iMappingRecords | |
function setPostedChildren($existingMappings, $postedIDs, $className) { | |
$dbIDs = array(); | |
$postedIDs = (array) $postedIDs; | |
foreach ($existingMappings as $map) { | |
if (in_array($map->getSecondaryMappingID(), $postedIDs)) { | |
//entry exists that matches the submitted values. add to new array so we can compare and find gaps | |
$dbIDs[] = $map->getSecondaryMappingID(); | |
} else { | |
//does not exist in submitted valeus. delete it from db | |
$map->Delete(); | |
} | |
} | |
//remaining IDs don't exist in DB (those IDs in $postedIDs but not in $dbIDs) | |
foreach (array_diff($postedIDs, $dbIDs) as $newID) { | |
if (is_numeric($newID)) { | |
$map = new $className; | |
$map->setSecondaryMappingID($newID); | |
$existingMappings[] = $map; | |
} | |
} | |
//if $existingMappings comes from a "parameter" (ie, $this->something), updating the reference doesn't do any good | |
return $existingMappings; | |
} | |
function importFile($file, $propertyName, $fileSetName) { | |
if (is_array($file) && $file['size'] > 0) { | |
\Loader::library("file/importer"); | |
\Loader::model('file'); | |
\Loader::model('file_set'); | |
$f = null; | |
$fi = new \FileImporter(); | |
if ($propertyName && $this->$propertyName) { | |
$f = \File::getByID($this->$propertyName); | |
} | |
$fv = $fi->import($file['tmp_name'], $file['name'], $f); | |
if (! $f && $fileSetName && ($set = \FileSet::getByName($fileSetName))) { | |
$set->addFileToSet($fv->getFile()); | |
} | |
if ($propertyName) { | |
$this->$propertyName = $fv->getFileID(); | |
} | |
return $fv; | |
} | |
} | |
protected function getFile($fID) { | |
if ($fID) { | |
\Loader::model('file'); | |
return \File::getByID($fID); | |
} | |
} | |
protected function findUserID($user) { | |
if ($user == null || ! is_numeric($user) || !($user instanceof User)) { | |
$user = new \User(); | |
} | |
if ($user instanceof \User && $user->uID) { | |
return $user->uID; | |
} else if (is_numeric($user)) { | |
return $user; | |
} else { | |
throw new \Exception("findUserID(): could not get a user id"); | |
} | |
} | |
/* | |
protected function isFieldTouched($field) { | |
$table = $this->TableInfo(); | |
$i = 0; | |
foreach ($table->flds as $name => $field) { | |
//$i can be used to check against $this->_original[$i]. see $this->Save() | |
$i++; | |
} | |
} | |
*/ | |
/** | |
* Returns a string representation of "now" suitable for putting into SQL | |
* @return string | |
*/ | |
public function sqlNow() { | |
return date(DATE_ISO8601); | |
} | |
} | |
abstract class ModelSimple extends Model { | |
function __construct($id = null) { | |
parent::__construct(); | |
if ($id != null) { | |
$this->Load($id); | |
} | |
} | |
/** | |
* Loads and returns a single record based on $col = $name | |
* @param string $name | |
* @param string $col | |
* @return this | |
*/ | |
public static function getByName($name, $col = 'name') { | |
$clsName = get_called_class(); | |
$cls = new $clsName; | |
$cls->customLoad("$col = '" . mysql_escape_string($name) . "'"); | |
return $cls; | |
} | |
} | |
/** | |
* (model) Defines a "deleteable" object (deleted by setting deleteDate NOT NULL) | |
* Provides methods to instantiate with a userID and delete by setting the deleteDate | |
*/ | |
abstract class ModelDeletable extends Model { | |
/** | |
* Creates new object, loads existing from database if ID is provided, otherwise sets createUID | |
* @param int $id Object ID to load if provided | |
*/ | |
function __construct($id = null) { | |
parent::__construct(); | |
if ($id != null) { | |
$this->Load($id); | |
} else { | |
//AmpModelDeletable's follow the standard ->createUID format | |
//even if an existing record eventually gets loaded this should be OK as createUID will be overwritten | |
$user = new \User(); | |
$this->createUID = $user->getUserID(); | |
$this->createDate = $this->sqlNow(); | |
} | |
} | |
/** | |
* Returns the user that created this record (if exists, otherwise returns nothing) | |
* @return UserInfo | |
*/ | |
function getCreateUserInfo() { | |
if ($this->createUID) { | |
return \UserInfo::getByID($this->createUID); | |
} | |
} | |
/** | |
* Loads and returns a single record based on $col = $name where deleteDate IS NULL | |
* @param string $name | |
* @param string $col | |
* @return this | |
*/ | |
public static function getByName($name, $col = 'name') { | |
$clsName = get_called_class(); | |
$cls = new $clsName; | |
$cls->customLoad("$col = '" . mysql_escape_string($name) . "' AND deleteDate IS NULL"); | |
return $cls; | |
} | |
/** | |
* "Deletes" object from database by setting deleteDate | |
* Immediately saves changes to db | |
* @param int $uID userID to set as the deleting user; if null, then will be grabbed from the user object | |
*/ | |
function Delete($user = null) { | |
$uID = $this->findUserID($user); | |
$this->deleteDate = date("Y-m-d H:i:s"); | |
$this->deleteUID = $uID; | |
$this->Save(); | |
} | |
} | |
abstract class DatabaseItemList extends \DatabaseItemList { | |
protected $_instanceClassName = ''; | |
protected $itemsPerPage = 10; | |
protected $ids = array(); | |
public $sets = array(); | |
/** | |
* Helper function to provide an ID from an object | |
* If a specific type of object is passed in, ID is returned. If ID is passed in, that's returned. Otherewise exception. | |
* @param Object|int $arg Object or ID, as provided to model | |
* @param string $class String representation of class. If null, then we still try to get the $idCol property if $arg is an object | |
* @param string $idCol Column name to check | |
* @return int | |
*/ | |
function findObjID($arg, $class = null, $idCol = 'ID') { | |
if (($class == null && is_object($arg)) || $arg instanceof $class) { | |
$arg = $arg->$idCol; | |
} | |
if (is_numeric($arg)) { | |
return $arg; | |
} else { | |
throw new \Exception("getObjID(): Invalid Argument"); | |
} | |
} | |
/** | |
* Helper function to get a user id. | |
* @param int|null|User $user If an ID (int), returns it.<br> | |
* If a User or UserInfo object, returns the ID. | |
* Otherwise, returns user ID of currently logged in user. | |
* @return int User ID | |
* @throws \Exception if it resorts to getting currently logged in user and that's not available | |
*/ | |
protected function findUserID($user = null) { | |
if (! is_numeric($user) && !($user instanceof \User) && ! ($user instanceof \UserInfo)) { | |
$user = new \User(); | |
} | |
if (($user instanceof \User || $user instanceof \UserInfo) && $user->getUserID()) { | |
return $user->getUserID(); | |
} else if (is_numeric($user)) { | |
return $user; | |
} else { | |
throw new \Exception("findUserID(): could not get a user id"); | |
} | |
} | |
public function get($itemsToGet = 0, $offset = 0) { | |
if ($this->_instanceClassName) { | |
//run the get() here, returning the appropriate array of classes | |
//$className = $this->_instanceClassName; | |
if (! count($this->sets)) { | |
$r = parent::get($itemsToGet, $offset); | |
foreach($r as $row) { | |
$this->ids[] = $row['ID']; | |
$obj = new $this->_instanceClassName($row['ID']); | |
$this->sets[] = $obj; | |
} | |
} | |
return $this->sets; | |
} else { | |
//this is an old get() call, just pass it up | |
return parent::get($itemsToGet, $offset); | |
} | |
} | |
function getArrayList() { | |
$els = $this->get(); | |
$arr = array(); | |
foreach ($els as $el) { | |
$arr[] = $el->toArray(); | |
} | |
return $arr; | |
} | |
function getHashList() { | |
$els = $this->get(); | |
$arr = array(); | |
foreach ($els as $el) { | |
$arr[$el->getID()] = $el->getName(); | |
} | |
return $arr; | |
} | |
public function getIDs($itemsToGet = 0, $offset = 0) { | |
$objs = $this->get($itemsToGet, $offset); | |
$ids = array(); | |
//@TODO: ensure that the object includes an ID column | |
foreach ($objs as $obj) { | |
$ids[] = $obj->ID; | |
} | |
return $ids; | |
} | |
} | |
interface iMappingRecord { | |
public function getPrimaryMappingID(); | |
public function setPrimaryMappingID($id); | |
public function getSecondaryMappingID(); | |
public function setSecondaryMappingID($id); | |
public function Delete(); | |
} | |
interface iSelectable { | |
public function getID(); | |
public function getName(); | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment