Skip to content

Instantly share code, notes, and snippets.

@jamesshannon
Created October 26, 2012 19:47
Show Gist options
  • Save jamesshannon/3961029 to your computer and use it in GitHub Desktop.
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
<?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