374 lines
9.0 KiB
PHP

<?php
use Joomla\CMS\Factory;
// No direct access.
defined('_JEXEC') or die;
abstract class AbstractCommonClubsModel
{
// TODO Adddata validator
// TODO Make setting of values attribute fail in case of problems
// FIXME Add Joins in select statements
private $id;
private $new;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
private $values = null;
protected function getValues()
{
if(is_null($this->values))
$this->loadDataFromDatabase();
return $this->values;
}
public function isNew()
{
return $this->new;
}
public function setValues($values)
{
$this->values = $values;
}
public function markAsNew($new)
{
$this->new = $new;
}
public function fillDefaultValues()
{}
/**
* @return AbstractCommonClubsModelFactory
*/
protected abstract function getFactory();
public function save()
{
$factory = $this->getFactory();
$attribs = $factory->getAttributes();
$db = Factory::getDbo();
$q = $db->getQuery(true);
$db->transactionStart();
if($this->new)
$this->prepareInsert($attribs, $factory, $q);
else
$this->prepareUpdate($attribs, $factory, $q);
$db->setQuery($q);
$db->execute();
if($this->new)
{
$this->finishInsert($db);
}
$db->transactionCommit();
}
private function loadDataFromDatabase()
{
$factory = $this->getFactory();
$attribs = $factory->getAttributes();
$db = Factory::getDbo();
$q = $db->getQuery(true);
foreach($attribs as $k => $v)
{
$rawColName = isset($v['col']) ? $v['col'] : $k;
$q->select($q->qn($rawColName, $k));
}
$q->from($factory->getTableName());
$q->where("id = {$this->id}");
$db->setQuery($q);
$db->execute();
$values = $db->loadAssoc();
$values = $this->unpackExternalReferencesFromKeys($values);
$this->values = $values;
}
private function packExternalReferencesAsKeys($vals)
{
foreach($this->getFactory()->getAttributes() as $k => $v)
{
if($v['type'] !== 'ref')
continue;
if(is_null($vals[$k]))
continue;
$vals[$k] = $vals[$k]->getId();
}
return $vals;
}
private function unpackExternalReferencesFromKeys($vals)
{
foreach($this->getFactory()->getAttributes() as $k => $v)
{
if(empty($v['type']) || $v['type'] !== 'ref')
continue;
if(empty($v['ref']))
throw new Exception('External reference of unknown class found.');
if(empty($vals[$k]))
continue;
$vals[$k] = $this->loadExternalReferenceAsObject($v['ref'], $vals[$k]);
}
return $vals;
}
private function loadExternalReferenceAsObject($className, $value)
{
if(is_string($value) && preg_match('/^[0-9]+$/', $value))
$value = (int) $value;
if(! is_int($value))
throw new Exception('Reference with non-integer value');
$factoryName = $this->getFactoryNameOfClass($className);
$factory = new $factoryName();
return $factory->loadById($value);
}
private const CLASSNAME_MAP = array(
);
/**
* @todo This must be done better and more cleanly
* @param string $className
* @return AbstractCommonClubsModelFactory
*/
private function getFactoryNameOfClass($className)
{
if(empty(self::CLASSNAME_MAP[$className]))
{
$parts = array();
if(preg_match('/^CommonClubsModel(.*)/', $className, $parts))
{
return "CommonClubsModelFactory{$parts[1]}";
}
throw new Exception("Unknown mapping of class $className");
}
return self::CLASSNAME_MAP[$className];
}
/**
*
* @param array $rawData
* @param array $attribs
* @param JDatabaseQuery $q
* @return string[]|number[]|NULL[]
*/
private function quoteData($rawData, $attribs, $q)
{
$quotedData = array();
foreach($attribs as $k => $v)
{
if(empty($v['type']))
$v['type'] = 'string';
if($rawData[$k] === NULL)
$quotedData[$k] = 'NULL';
switch($v['type'])
{
case 'string':
$quotedData[$k] = $q->q($rawData[$k]);
break;
case 'int':
$quotedData[$k] = (int) $rawData[$k];
break;
case 'float':
$quotedData[$k] = (float) $rawData[$k];
break;
case 'ref':
if($v['ref'] === null)
$quotedData[$k] = 'NULL';
else
$quotedData[$k] = $rawData[$k]->getId();
break;
}
}
return $quotedData;
}
protected function filterDatabaseRawData($values)
{
return $values;
}
protected function filterDatabaseQuotedData($quoted)
{
return $quoted;
}
/**
*
* @param array $attribs
* @param JDatabaseQuery $q
* @return array
*/
private function getQuotedData($attribs, $q)
{
$rawData = $this->getValues();
$rawData = $this->filterDatabaseRawData($rawData);
$quotedData = $this->quoteData($rawData, $attribs, $q);
$quotedData = $this->filterDatabaseQuotedData($quotedData);
return $quotedData;
}
/**
*
* @param array $attribs
* @param AbstractCommonClubsModelFactory $factory
* @param JDatabaseQuery $q
*/
private function prepareInsert($attribs, $factory, $q)
{
$q->insert($factory->getTableName());
$dbcols = array();
foreach($attribs as $k => $v)
{
$dbcols[] = isset($v['col']) ? $v['col'] : $k;
}
$q->columns($q->qn($dbcols));
$quotedData = $this->getQuotedData($attribs, $q);
$q->values(join(', ', $quotedData));
}
/**
*
* @param JDatabaseDriver $db
*/
private function finishInsert($db)
{
$this->id = $db->insertid();
$this->new = false;
}
/**
*
* @param array $attribs
* @param AbstractCommonClubsModelFactory $factory
* @param JDatabaseQuery $q
*/
private function prepareUpdate($attribs, $factory, $q)
{
$q->update($factory->getTableName());
$dbcols = array();
foreach($attribs as $k => $v)
$dbcols[] = isset($v['col']) ? $v['col'] : $k;
$quotedData = $this->getQuotedData($attribs, $q);
$q->set(array_map(function($col, $data){
return "$col = $data";
}, $dbcols, $quotedData));
$q->where("id = {$this->id}");
}
// FIXME Add additional filter to remove associations of the object
public function delete()
{
$db = Factory::getDbo();
$q = $db->getQuery(true);
$factory = $this->getFactory();
$q->delete($factory->getTableName());
$q->where("id = {$this->id}");
$db->transactionStart();
$db->setQuery($q);
$db->execute();
$db->transactionCommit();
}
/**
*
* @param AbstractCommonClubsModelFactory $factory
* @param string $colName
*/
protected function fetchAssociatedElements($factory, $colName)
{
$condition = "main.$colName = {$this->id}";
return $factory->loadElements($condition);
}
protected function filterPackData($values)
{
return $values;
}
protected function filterUnpackData($values)
{
return $values;
}
public function pack()
{
$vals = $this->getValues();
$vals = $this->packExternalReferencesAsKeys($vals);
$vals = $this->filterPackData($vals);
$json = json_encode($vals);
return urldecode($json);
}
public function unpack($str)
{
$json = urlencode($str);
$data = json_decode($json, true);
$vals = $this->unpackExternalReferencesFromKeys($data);
$vals = $this->filterUnpackData($vals);
$this->setValues($vals);
}
}