Added tests for the Store (MySQL and PostgreSQL).

This commit is contained in:
Dmitry Khomutov 2018-03-01 19:36:28 +07:00
commit 42a8316479
No known key found for this signature in database
GPG key ID: EC19426474B37AAC
14 changed files with 590 additions and 181 deletions

View file

@ -2,8 +2,6 @@
namespace b8;
use b8\Exception\HttpException;
abstract class Store
{
/**
@ -25,19 +23,25 @@ abstract class Store
* @param string $key
* @param string $useConnection
*
* @return Model
* @return Model|null
*/
abstract public function getByPrimaryKey($key, $useConnection = 'read');
/**
* @throws \RuntimeException
*/
public function __construct()
{
if (empty($this->primaryKey)) {
throw new \RuntimeException('Save not implemented for this store.');
}
}
/**
* @param array $where
* @param integer $limit
* @param integer $offset
* @param array $joins
* @param array $order
* @param array $manualJoins
* @param string $group
* @param array $manualWheres
* @param string $whereType
*
* @return array
@ -46,11 +50,7 @@ abstract class Store
$where = [],
$limit = 25,
$offset = 0,
$joins = [],
$order = [],
$manualJoins = [],
$group = null,
$manualWheres = [],
$whereType = 'AND'
) {
$query = 'SELECT * FROM {{' . $this->tableName . '}}';
@ -64,129 +64,21 @@ abstract class Store
if (!is_array($value)) {
$params[] = $value;
$wheres[] = $key . ' = ?';
} else {
if (isset($value['operator'])) {
if (is_array($value['value'])) {
if ($value['operator'] == 'between') {
$params[] = $value['value'][0];
$params[] = $value['value'][1];
$wheres[] = $key . ' BETWEEN ? AND ?';
} elseif ($value['operator'] == 'IN') {
$in = [];
foreach ($value['value'] as $item) {
$params[] = $item;
$in[] = '?';
}
$wheres[] = $key . ' IN (' . implode(', ', $in) . ') ';
} else {
$ors = [];
foreach ($value['value'] as $item) {
if ($item == 'null') {
switch ($value['operator']) {
case '!=':
$ors[] = $key . ' IS NOT NULL';
break;
case '==':
default:
$ors[] = $key . ' IS NULL';
break;
}
} else {
$params[] = $item;
$ors[] = $key . ' ' . $value['operator'] . ' ?';
}
}
$wheres[] = '(' . implode(' OR ', $ors) . ')';
}
} else {
if ($value['operator'] == 'like') {
$params[] = '%' . $value['value'] . '%';
$wheres[] = $key . ' ' . $value['operator'] . ' ?';
} else {
if ($value['value'] === 'null') {
switch ($value['operator']) {
case '!=':
$wheres[] = $key . ' IS NOT NULL';
break;
case '==':
default:
$wheres[] = $key . ' IS NULL';
break;
}
} else {
$params[] = $value['value'];
$wheres[] = $key . ' ' . $value['operator'] . ' ?';
}
}
}
} else {
$wheres[] = $key . ' IN (' . implode(', ', array_map([Database::getConnection('read'), 'quote'], $value)) . ')';
}
}
}
if (count($joins)) {
foreach ($joins as $table => $join) {
$query .= ' LEFT JOIN {{' . $table . '}} AS ' . $join['alias'] . ' ON ' . $join['on'] . ' ';
$countQuery .= ' LEFT JOIN {{' . $table . '}} AS ' . $join['alias'] . ' ON ' . $join['on'] . ' ';
}
}
if (count($manualJoins)) {
foreach ($manualJoins as $join) {
$query .= ' ' . $join . ' ';
$countQuery .= ' ' . $join . ' ';
}
}
$hasWhere = false;
if (count($wheres)) {
$hasWhere = true;
$query .= ' WHERE (' . implode(' ' . $whereType . ' ', $wheres) . ')';
$countQuery .= ' WHERE (' . implode(' ' . $whereType . ' ', $wheres) . ')';
}
if (count($manualWheres)) {
foreach ($manualWheres as $where) {
if (!$hasWhere) {
$hasWhere = true;
$query .= ' WHERE ';
$countQuery .= ' WHERE ';
} else {
$query .= ' ' . $where['type'] . ' ';
$countQuery .= ' ' . $where['type'] . ' ';
}
$query .= ' ' . $where['query'];
$countQuery .= ' ' . $where['query'];
if (isset($where['params'])) {
foreach ($where['params'] as $param) {
$params[] = $param;
}
}
}
}
if (!is_null($group)) {
$query .= ' GROUP BY ' . $group . ' ';
}
if (count($order)) {
$orders = [];
if (is_string($order) && $order == 'rand') {
$query .= ' ORDER BY RAND() ';
} else {
foreach ($order as $key => $value) {
$orders[] = $this->fieldCheck($key) . ' ' . $value;
}
$query .= ' ORDER BY ' . implode(', ', $orders);
foreach ($order as $key => $value) {
$orders[] = $this->fieldCheck($key) . ' ' . $value;
}
$query .= ' ORDER BY ' . implode(', ', $orders);
}
if ($limit) {
@ -197,47 +89,36 @@ abstract class Store
$query .= ' OFFSET ' . $offset;
}
try {
$stmt = Database::getConnection('read')->prepareCommon($countQuery);
$stmt->execute($params);
$res = $stmt->fetch(\PDO::FETCH_ASSOC);
$count = (int)$res['count'];
} catch (\PDOException $ex) {
$count = 0;
$stmt = Database::getConnection('read')->prepareCommon($countQuery);
$stmt->execute($params);
$res = $stmt->fetch(\PDO::FETCH_ASSOC);
$count = (int)$res['count'];
$stmt = Database::getConnection('read')->prepareCommon($query);
$stmt->execute($params);
$res = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$rtn = [];
foreach ($res as $data) {
$rtn[] = new $this->modelName($data);
}
try {
$stmt = Database::getConnection('read')->prepareCommon($query);
$stmt->execute($params);
$res = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$rtn = [];
foreach ($res as $data) {
$rtn[] = new $this->modelName($data);
}
return ['items' => $rtn, 'count' => $count];
} catch (\PDOException $ex) {
throw $ex;
}
return ['items' => $rtn, 'count' => $count];
}
/**
* @param Model $obj
* @param boolean $saveAllColumns
*
* @throws HttpException\BadRequestException
* @throws \RuntimeException
* @throws \InvalidArgumentException
*
* @return Model|null
*/
public function save(Model $obj, $saveAllColumns = false)
{
if (!isset($this->primaryKey)) {
throw new HttpException\BadRequestException('Save not implemented for this store.');
}
if (!($obj instanceof $this->modelName)) {
throw new HttpException\BadRequestException(get_class($obj) . ' is an invalid model type for this store.');
throw new \InvalidArgumentException(get_class($obj) . ' is an invalid model type for this store.');
}
$data = $obj->getDataArray();
@ -315,10 +196,7 @@ abstract class Store
$q = Database::getConnection('write')->prepareCommon($qs);
if ($q->execute($qParams)) {
$id = !empty($data[$this->primaryKey])
? $data[$this->primaryKey]
: Database::getConnection('write')->lastInsertIdExtended($obj->getTableName());
$id = Database::getConnection('write')->lastInsertIdExtended($obj->getTableName());
$rtn = $this->getByPrimaryKey($id, 'write');
}
}
@ -329,18 +207,15 @@ abstract class Store
/**
* @param Model $obj
*
* @throws HttpException\BadRequestException
* @throws \RuntimeException
* @throws \InvalidArgumentException
*
* @return boolean
*/
public function delete(Model $obj)
{
if (!isset($this->primaryKey)) {
throw new HttpException\BadRequestException('Delete not implemented for this store.');
}
if (!($obj instanceof $this->modelName)) {
throw new HttpException\BadRequestException(get_class($obj) . ' is an invalid model type for this store.');
throw new \InvalidArgumentException(get_class($obj) . ' is an invalid model type for this store.');
}
$data = $obj->getDataArray();
@ -355,14 +230,14 @@ abstract class Store
/**
* @param string $field
*
* @throws HttpException
* @throws \InvalidArgumentException
*
* @return string
*/
protected function fieldCheck($field)
{
if (empty($field)) {
throw new HttpException('You cannot have an empty field name.');
throw new \InvalidArgumentException('You cannot have an empty field name.');
}
if (strpos($field, '.') === false) {

View file

@ -13,6 +13,7 @@ class Factory
/**
* A collection of the stores currently loaded by the factory.
*
* @var \b8\Store[]
*/
protected $loadedStores = [];
@ -30,7 +31,8 @@ class Factory
}
/**
* @param $storeName string Store name (should match a model name).
* @param string $storeName Store name (should match a model name).
* @param string $namespace
*
* @return \b8\Store
*/
@ -45,17 +47,20 @@ class Factory
}
/**
* @param $store
* @param string $store
* @param string $namespace
*
* @return \b8\Store;
*/
public function loadStore($store, $namespace = null)
{
if (!isset($this->loadedStores[$store])) {
$namespace = is_null($namespace) ? Config::getInstance()->get('b8.app.namespace') : $namespace;
$class = $namespace . '\\Store\\' . $store . 'Store';
$obj = new $class();
$namespace = is_null($namespace)
? Config::getInstance()->get('b8.app.namespace')
: $namespace;
$class = $namespace . '\\Store\\' . $store . 'Store';
$obj = new $class();
$this->loadedStores[$store] = $obj;
}

View file

@ -129,7 +129,7 @@ class Application extends b8\Application
{
$groups = [];
$groupStore = b8\Store\Factory::getStore('ProjectGroup');
$groupList = $groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']);
$groupList = $groupStore->getWhere([], 100, 0, ['title' => 'ASC']);
foreach ($groupList['items'] as $group) {
$thisGroup = ['title' => $group->getTitle()];

View file

@ -225,7 +225,7 @@ class BuildStatusController extends Controller
{
$criteria = ['project_id' => $projectId];
$order = ['id' => 'DESC'];
$builds = $this->buildStore->getWhere($criteria, 10, 0, [], $order);
$builds = $this->buildStore->getWhere($criteria, 10, 0, $order);
foreach ($builds['items'] as &$build) {
$build = BuildFactory::getBuild($build);

View file

@ -37,7 +37,7 @@ class GroupController extends Controller
$this->requireAdmin();
$groups = [];
$groupList = $this->groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']);
$groupList = $this->groupStore->getWhere([], 100, 0, ['title' => 'ASC']);
foreach ($groupList['items'] as $group) {
$thisGroup = [

View file

@ -273,7 +273,7 @@ class ProjectController extends PHPCensor\Controller
}
$order = ['id' => 'DESC'];
$builds = $this->buildStore->getWhere($criteria, $perPage, $start, [], $order);
$builds = $this->buildStore->getWhere($criteria, $perPage, $start, $order);
$view = new View('Project/ajax-builds');
foreach ($builds['items'] as &$build) {
@ -496,7 +496,7 @@ class ProjectController extends PHPCensor\Controller
$groups = [];
$groupStore = b8\Store\Factory::getStore('ProjectGroup');
$groupList = $groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']);
$groupList = $groupStore->getWhere([], 100, 0, ['title' => 'ASC']);
foreach ($groupList['items'] as $group) {
$groups[$group->getId()] = $group->getTitle();

View file

@ -42,7 +42,7 @@ class UserController extends Controller
*/
public function index()
{
$users = $this->userStore->getWhere([], 1000, 0, [], ['email' => 'ASC']);
$users = $this->userStore->getWhere([], 1000, 0, ['email' => 'ASC']);
$this->view->users = $users;
$this->layout->title = Lang::get('manage_users');

View file

@ -76,7 +76,6 @@ class WidgetAllProjectsController extends Controller
['project_id' => $project->getId()],
1,
0,
[],
['id' => 'DESC']
);
$counts[$project->getId()] = $count['count'];
@ -107,7 +106,7 @@ class WidgetAllProjectsController extends Controller
protected function getGroupInfo()
{
$rtn = [];
$groups = $this->groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']);
$groups = $this->groupStore->getWhere([], 100, 0, ['title' => 'ASC']);
foreach ($groups['items'] as $group) {
$thisGroup = ['title' => $group->getTitle()];
@ -133,7 +132,6 @@ class WidgetAllProjectsController extends Controller
['project_id' => $projectId],
1,
0,
[],
['id' => 'DESC']
);
$counts = $count['count'];

View file

@ -512,7 +512,7 @@ class Project extends Model
}
$order = ['id' => 'DESC'];
$builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 0, [], $order);
$builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 0, $order);
if (is_array($builds['items']) && count($builds['items'])) {
$latest = array_shift($builds['items']);
@ -536,7 +536,7 @@ class Project extends Model
{
$criteria = ['branch' => $branch, 'project_id' => $this->getId()];
$order = ['id' => 'DESC'];
$builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 1, [], $order);
$builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 1, $order);
if (is_array($builds['items']) && count($builds['items'])) {
$previous = array_shift($builds['items']);

View file

@ -33,7 +33,7 @@ class ProjectStore extends Store
* @param integer $key
* @param string $useConnection
*
* @return null|Project
* @return Project|null
*/
public function getByPrimaryKey($key, $useConnection = 'read')
{
@ -46,7 +46,7 @@ class ProjectStore extends Store
* @param integer $id
* @param string $useConnection
*
* @return null|Project
* @return Project|null
*
* @throws HttpException
*/