Skip to content

Commit

Permalink
Fixes and improvements
Browse files Browse the repository at this point in the history
New method ActiveRecord::getRelated() allows to get a cached copy of an object that's related to the subject one by a ForeignKey to its db field.
  • Loading branch information
viames committed Feb 6, 2018
1 parent e5a79b2 commit 057939e
Show file tree
Hide file tree
Showing 13 changed files with 640 additions and 413 deletions.
146 changes: 135 additions & 11 deletions src/ActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,11 @@ public function __set($name, $value) {
break;

case 'int':
$this->$name = (int)$value;
if ('' === $value and $this->isNullable($this->getMappedField($name))) {
$this->$name = NULL;
} else {
$this->$name = (int)$value;
}
break;

case 'DateTime':
Expand Down Expand Up @@ -216,11 +220,24 @@ protected function init() {}
/**
* Returns array with matching object property name on related db fields.
*
* @return array
* @return array
*/
protected static function getBinds() {

$db = Database::getInstance();
$columns = $db->describeTable(static::TABLE_NAME);

return array();
$maps = [];

foreach ($columns as $col) {

// get a camelCase name, with first low case
$property = lcfirst(str_replace(' ', '', ucwords(str_replace(['_','\\'], ' ', $col->Field))));
$maps[$property] = $col->Field;

}

return $maps;

}

Expand Down Expand Up @@ -307,7 +324,7 @@ final private function prepareData($properties) {

// join array strings in CSV format
case 'csv':
$ret = implode(',', array_filter($this->$prop));
$ret = implode(',', array_filter((array)$this->$prop));
break;

case 'float':
Expand Down Expand Up @@ -904,6 +921,115 @@ final protected function bindAsCsv() {

}

/**
* Return the Pair\ActiveRecord inherited object related to this by a ForeignKey in DB-table.
*
* @param string Related property name.
*
* @return multitype|NULL
*/
final public function getRelated($relatedProperty) {

$cacheName = $relatedProperty . 'RelatedObject';

// object exists in cache, return it
if ($this->issetCache($cacheName)) {
return $this->getCache($cacheName);
}

// get table foreign-keys
$foreignKeys = $this->db->getForeignKeys(static::TABLE_NAME);

// get field name by mapped property
$relatedField = $this->getMappedField($relatedProperty);

// the table referenced by fk
$referencedTable = NULL;

// search the fk-table
foreach ($foreignKeys as $fk) {
if ($fk->COLUMN_NAME == $relatedField) {
$referencedTable = $fk->REFERENCED_TABLE_NAME;
$referencedColumn = $fk->REFERENCED_COLUMN_NAME;
break;
}
}

// if not table is referenced, raise an error
if (!$referencedTable) {
$this->addError('Property ' . $relatedProperty . ' has not a foreign-key mapped into DB');
return NULL;
}

// class that maps the referenced table
$relatedClass = NULL;
$loadedClasses = \get_declared_classes();

// search in loaded classes
foreach ($loadedClasses as $c) {
if (is_subclass_of($c, 'Pair\ActiveRecord') and $c::TABLE_NAME == $referencedTable) {
$relatedClass = $c;
break;
}
}

// class cannot be found
if (!$relatedClass) {

// if not found, search in the whole application (FIXME encapsulation violated here...)
$classes = Utilities::getActiveRecordClasses();

// search for required one
foreach ($classes as $class => $opts) {
if ($opts['tableName'] == $referencedTable) {
include_once($opts['folder'] . '/' . $opts['file']);
$relatedClass = $class;
break;
}
}

}

// class cannot be found
if (!$relatedClass) {
$this->addError('Table ' . $referencedTable . ' has not any Pair-class mapping');
return NULL;
}

// create the new wanted Pair object
$obj = new $relatedClass($this->$relatedProperty);

// if loaded, return it otherwise NULL
$ret = ($obj->isLoaded() ? $obj : NULL);

// related object is being registered in cache of this object
$this->setCache($cacheName, $ret);

return $ret;

}

/**
* Extended method to return a property value of the Pair\ActiveRecord inherited object related to
* this by a ForeignKey in DB-table.
*
* @param string Related property name.
* @param string Wanted property name.
*
* @return multitype|NULL
*/
final public function getRelatedProperty($relatedProperty, $wantedProperty) {

$obj = $this->getRelated($relatedProperty);

if ($obj) {
return $obj->$wantedProperty;
} else {
return NULL;
}

}

/**
* Create an object for a table column configuration within an object or NULL if column
* doesn’t exist.
Expand Down Expand Up @@ -1639,16 +1765,13 @@ final static public function getMappedField($propertyName) {
*/
final public function populateByRequest() {

$args = func_get_args();
$class = get_called_class();
$args = func_get_args();

// all subclass binds
$binds = $class::getBinds();

$properties = array();
$binds = static::getBinds();

foreach ($binds as $property => $field) {

// check that property is in the args or that args is not defined at all
if (!count($args) or (isset($args[0]) and in_array($property, $args[0]))) {

Expand All @@ -1657,7 +1780,8 @@ final public function populateByRequest() {
if (Input::isSent($property) or 'bool' == $type) {

// assign the value to this object property
$this->__set($property, Input::get($property, $type));
$this->__set($property, Input::get($property));

}

}
Expand Down
106 changes: 36 additions & 70 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Application {
* Singleton property.
* @var Application|NULL
*/
static private $instance;
static protected $instance;

/**
* List of temporary variables.
Expand Down Expand Up @@ -91,12 +91,6 @@ class Application {
*/
private $template;

/**
* Keep the name of a base template in case of derived one.
* @var NULL|Template
*/
private $baseTemplate;

/**
* Template-style’s file name (without extension).
* @var string
Expand Down Expand Up @@ -244,33 +238,36 @@ private function __construct() {
* @return multitype
*/
public function __get($name) {

switch ($name) {


/**
* for login page we need a default template
* @deprecated
*/
case 'templatePath':

// for login page we need a default template
$this->checkTemplate();
$templateName = $this->template->derived ? $this->baseTemplate->name : $this->template->name;
$value = 'templates/' . $templateName . '/';
$value = $this->getTemplate()->getPath();
break;

// useful in html tag to set language code
case 'langCode':

$language = new Language($this->currentUser->languageId);
$value = $language->code;
$translator = Translator::getInstance();
$value = $translator->getCurrentLanguage()->code;
break;

default:

$allowedProperties = ['activeMenuItem', 'currentUser', 'pageTitle', 'pageContent', 'template'];

// search into variable assigned to the template as first
if (array_key_exists($name, $this->vars)) {

$value = $this->vars[$name];

// then search in properties
} else if (property_exists($this, $name)) {
} else if (property_exists($this, $name) and in_array($name, $allowedProperties)) {

$value = $this->$name;

Expand Down Expand Up @@ -321,11 +318,10 @@ protected function setCurrentUser($user) {
if (is_a($user,'Pair\User')) {

$this->currentUser = $user;
$this->checkTemplate();

// sets user language
$tran = Translator::getInstance();
$lang = new Language($user->languageId);
$lang = $user->languageId ? $user->getRelated('languageId') : Language::getDefault();
$tran->setLanguage($lang);

}
Expand Down Expand Up @@ -905,28 +901,6 @@ final public function startMvc() {
// populate the placeholder for the content
$this->pageContent = ob_get_clean();

// login page has no template, needs a default
$this->checkTemplate();

$templatesPath = APPLICATION_PATH . '/templates/' ;

// by default load template style
$styleFile = $templatesPath . $this->template->name . '/' . $this->style . '.php';

// in case of derived template, should load the base template
if ($this->template->derived) {

// try to load derived extend file
$derivedFile = $templatesPath . $this->template->name . '/derived.php';
if (file_exists($derivedFile)) require $derivedFile;

// if no template style, load default template style
if (!file_exists($styleFile) and is_a($this->baseTemplate, 'Pair\Template')) {
$styleFile = $templatesPath . $this->baseTemplate->name . '/' . $this->style . '.php';
}

}

// initialize CSS and scripts
$this->pageStyles = '';
$this->pageScripts = '';
Expand Down Expand Up @@ -971,25 +945,16 @@ final public function startMvc() {
}

try {

if (!file_exists($styleFile)) {
throw new \Exception('Template style file ' . $styleFile . ' was not found');
}

// load the style page file
require $styleFile;

// get output buffer and cleans it
$page = ob_get_clean();

print $page;

$this->getTemplate()->loadStyle($this->style);
} catch (\Exception $e) {

print $e->getMessage();

}

// get output buffer and cleans it
$page = ob_get_clean();

print $page;

}

/**
Expand All @@ -1007,10 +972,11 @@ final private function getMessageScript() {

$types = array('info', 'warning', 'error');
if (!in_array($m->type, $types)) $m->type = 'info';
$script .= '$.showMessage("'.
addslashes($m->title) .'","' .
$script .= '$.showMessage("' .
addslashes($m->title) . '","' .
addcslashes($m->text,"\"\n\r") . '","' . // removes carriage returns and quotes
addslashes($m->type) ."\");\n";
addslashes($m->type) . "\");\n";

}

}
Expand All @@ -1020,24 +986,24 @@ final private function getMessageScript() {
}

/**
* Set the name of base template in case of derived one in use.
* If current selected template is not valid, replace it with the default one. It’s private to avoid
* loops on derived templates load.
*
* @param string Template name.
* @return Pair\Template
*/
final public function setBaseTemplate($templateName) {

$this->baseTemplate = Template::getTemplateByName($templateName);
final private function getTemplate() {

}

/**
* If current selected template is not valid, replace it with the default one.
*/
final private function checkTemplate() {

if (!$this->template or !$this->template->isPopulated()) {
$this->template = Template::getDefault();
}

// if this is derived template, load derived.php file
if ($this->template->derived) {
$derivedFile = $this->template->getBaseFolder() . '/' . strtolower($this->template->name) . '/derived.php';
if (file_exists($derivedFile)) require $derivedFile;
}

return $this->template;

}

Expand Down
Loading

0 comments on commit 057939e

Please sign in to comment.