Implemented renewed model system.

Resolves #86
This commit is contained in:
Abel Hoogeveen 2016-07-19 16:31:06 +02:00
parent 49cd813cde
commit 07976216ba
11 changed files with 400 additions and 176 deletions

1
.gitattributes vendored
View File

@ -1,6 +1,7 @@
.gitattributes export-ignore
.gitignore export-ignore
.gitlab_ci.yml export-ignore
.travis.yml export-ignore
tests/ export-ignore
CI/ export-ignore
Application/ export-ignore

View File

@ -20,14 +20,14 @@
},
"suggest": {
"smarty/smarty": "Allows using Smarty in templates",
"latte/latte": "Allows using Latte in templates"
"latte/latte": "Allows using Latte in templates",
"tracy/tracy": "Allows for extensive debugging"
},
"require-dev": {
"phpunit/phpunit": "5.3.*",
"apigen/apigen": "^4.1",
"mikey179/vfsStream": "1.1.*",
"smarty/smarty": "~3.1",
"tracy/tracy": "*",
"latte/latte": "*"
},
"autoload": {

View File

@ -45,22 +45,22 @@ use FuzeWorks\Event;
class ModelLoadEvent extends Event
{
/**
* The directory the model gets loaded from.
* The directories the model can get loaded from.
*
* @var string|null
* @var array
*/
public $directory = null;
public $directories = array();
/**
* The name of the model to be loaded.
*
* @var string|null
*/
public $model = null;
public $modelName = null;
public function init($model, $directory)
public function init($modelName, $directories)
{
$this->model = $model;
$this->directory = $directory;
$this->modelName = $modelName;
$this->directories = $directories;
}
}

View File

@ -35,7 +35,8 @@ namespace FuzeWorks;
/**
* Abstract class ControllerAbstract.
*
* At this point does nothing, can be extended in the future to allow special controller functions
* Extends all controllers to use the Factory.
* Factory should in the future be replaced with a DI container
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)

View File

@ -1,112 +0,0 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
namespace FuzeWorks;
/**
* Interface for a Module that gives abstract model types
* A model server must contain the methods from this interface in order to correctly serve models.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
*/
interface ModelServer
{
public function giveModel($type);
}
/**
* Abstract class Model.
*
* Abstract for a model data representation, loads the correct parent type
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
*/
abstract class Model
{
/**
* The parent class holder object
* Requests get redirected to this class.
*
* @var Parent Object
*/
private $parentClass;
/**
* Set the type of this model. Eg, use techfuze/databasemodel and Databasemodel to get a SQL connected model.
*
* @param string Module_name, the name of the module where the model can be found
* @param string Model_type, model type to return
*/
protected function setType($module_name, $model_type)
{
$mod = Modules::get($module_name);
$this->parentClass = $mod->giveModel($model_type);
}
/**
* Retrieves a value from the model class.
*
* @param Any key
*
* @return Any value from the model class
*/
public function __get($name)
{
return $this->parentClass->$name;
}
/**
* Sets a value in the model class.
*
* @param Any key
* @param Any value
*/
public function __set($name, $value)
{
$this->parentClass->$name = $value;
}
/**
* Calls a function in the model class.
*
* @param string function_name
* @param array values
*
* @return Function return
*/
public function __call($name, $params)
{
return call_user_func_array(array($this->parentClass, $name), $params);
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
namespace FuzeWorks;
/**
* Abstract class ModelAbstract.
*
* Extends all models to use the Factory.
* Factory should in the future be replaced with a DI container
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
*/
abstract class ModelAbstract extends Factory
{
}

View File

@ -36,77 +36,165 @@ use FuzeWorks\Exception\ModelException;
/**
* Models Class.
*
* Simple loader class for MVC Models. Typically loads models from Application/Models unless otherwise specified.
* If a model is not found, it will load a DatabaseModel type which will analyze the database and can directly be used.
* Simple loader class for MVC Models.
* Typically loads models from Application/Models unless otherwise specified.
*
* If a model is not found, it will load a DatabaseModel type which will
* analyze the database and can directly be used.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
*/
class Models
{
/**
* Array of all the loaded models.
*
* @var array
*/
private static $models_array = array();
/**
* Array of all the existing model types (classes).
*
* @var array
* Paths where Models can be found.
*
* Models will only be loaded if either a directory is supplied or it is in one of the modelPaths
*
* @var array Array of paths where models can be found
*/
private static $model_types = array();
protected $modelPaths = array();
/**
* Load a model.
*
* @param string $name Name of the model
* @param string $directory Optional directory of the model
*
* @return object The Model object.
*/
public static function loadModel($name, $directory = null)
public function __construct()
{
// Model load event
$event = Events::fireEvent('modelLoadEvent', $name, $directory);
$directory = ($event->directory === null ? Core::$appDir . DS . 'Models' : $event->directory);
$name = ($event->model === null ? $name : $event->model);
$this->modelPaths[] = Core::$appDir . DS . 'Models';
}
$file = $directory.DS.'model.'.$name.'.php';
if (isset(self::$model_types[$name])) {
Logger::log('Loading Model: '.get_class(self::$model_types[$name]), get_class(self::$model_types[$name]));
self::$models_array[$name] = self::$model_types[$name];
} elseif (file_exists($file)) {
include_once $file;
$model = "\Application\Model\\".ucfirst($name);
Logger::log('Loading Model: '.$model, $model);
/**
* Get a model.
*
* Supply the name and the model will be loaded from the supplied directory,
* or from one of the modelPaths (which you can add).
*
* @param string $modelName Name of the model
* @param string|null $directory Directory to load the model from, will ignore $modelPaths
* @return ModelAbstract The Model object
*/
public function get($modelName, $directory = null)
{
if (empty($modelName))
{
throw new ModelException("Could not load model. No name provided", 1);
}
return self::$models_array[$name] = new $model();
} else {
throw new ModelException("The requested model: \''.$name.'\' could not be found", 1);
// First get the directories where the model can be located
$directories = (is_null($directory) ? $this->modelPaths : array($directory));
// Fire a model load event
$event = Events::fireEvent('modelLoadEvent', $modelName, $directories);
$directories = $event->directories;
$modelName = $event->modelName;
// If the event is cancelled, stop loading
if ($event->isCancelled())
{
return false;
}
// And attempt to load the model
return $this->loadModel($modelName, $directories);
}
/**
* Load and return a model.
*
* Supply the name and the model will be loaded from one of the supplied directories
*
* @param string $modelName Name of the model
* @param array $directories Directories to try and load the model from
* @return ModelAbstract The Model object
*/
protected function loadModel($modelName, $directories)
{
if (empty($directories))
{
throw new ModelException("Could not load model. No directories provided", 1);
}
// Now figure out the className and subdir
$class = trim($modelName, '/');
if (($last_slash = strrpos($class, '/')) !== FALSE)
{
// Extract the path
$subdir = substr($class, 0, ++$last_slash);
// Get the filename from the path
$class = substr($class, $last_slash);
}
else
{
$subdir = '';
}
$class = ucfirst($class);
// Search for the model file
foreach ($directories as $directory) {
// Determine the file
$file = $directory . DS . $subdir . "model." . strtolower($class) . '.php';
$className = '\Application\Model\\'.$class;
// If the class already exists, return a new instance directly
if (class_exists($className, false))
{
return new $className();
}
// If it doesn't, try and load the file
if (file_exists($file))
{
include_once($file);
return new $className();
}
}
// Maybe it's in a subdirectory with the same name as the class
if ($subdir === '')
{
return $this->loadModel($class."/".$class, $directories);
}
throw new ModelException("Could not load model. Model was not found", 1);
}
/**
* Add a path where models can be found
*
* @param string $directory The directory
* @return void
*/
public function addModelPath($directory)
{
if (!in_array($directory, $this->ModelPaths))
{
$this->modelPaths[] = $directory;
}
}
/**
* Retrieve a model.
*
* @param string $name Name of the model
*
* @return object The Model object
*/
public static function get($name)
* Remove a path where models can be found
*
* @param string $directory The directory
* @return void
*/
public function removeModelPath($directory)
{
// Get the name
$name = strtolower($name);
// Check if it already exists
if (isset(self::$models_array[$name])) {
// Return if it does
return self::$models_array[$name];
} else {
// If not, load and return afterwards
return self::loadModel($name);
if (($key = array_search($directory, $this->modelPaths)) !== false)
{
unset($this->modelPaths[$key]);
}
}
}
/**
* Get a list of all current ModelPaths
*
* @return array Array of paths where models can be found
*/
public function getModelPaths()
{
return $this->modelPaths;
}
}

173
tests/core_modelTest.php Normal file
View File

@ -0,0 +1,173 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks MVC PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2016, Techfuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://fuzeworks.techfuze.net
* @since Version 0.0.1
*
* @version Version 0.0.1
*/
use FuzeWorks\Models;
use FuzeWorks\Events;
use FuzeWorks\EventPriority;
/**
* Class ModelTest.
*
* Will test the FuzeWorks Model System.
*/
class modelTest extends CoreTestAbstract
{
protected $models;
public function setUp()
{
$this->models = new Models();
}
public function testGetModel()
{
$model = $this->models->get('TestGetModel', 'tests/models/testGetModel');
$this->assertInstanceOf('\Application\Model\TestGetModel', $model);
}
/**
* @depends testGetModel
*/
public function testReloadModel()
{
$model = $this->models->get('TestGetModel', 'tests/models/testGetModel');
$this->assertInstanceOf('\Application\Model\TestGetModel', $model);
}
/**
* @expectedException FuzeWorks\Exception\ModelException
*/
public function testFailModelName()
{
$model = $this->models->get('');
}
public function testEventLoadModelChange()
{
// Register the listener
Events::addListener(array($this, 'listener_change'), 'modelLoadEvent', EventPriority::NORMAL);
// Load wrong model
$model = $this->models->get('TestWrongModel', 'tests/models/testWrongDirectory');
$this->assertInstanceOf('\Application\Model\TestRightModel', $model);
}
// Change the directory and model name
public function listener_change($event)
{
// First test input
$this->assertEquals('TestWrongModel', $event->modelName);
$this->assertContains('tests/models/testWrongDirectory', $event->directories);
// Then change variables
$event->modelName = 'TestRightModel';
$event->directories = array('tests/models/testRightDirectory');
// Return the event afterwards
return $event;
}
public function testEventLoadModelCancel()
{
// Register the listener
Events::addListener(array($this, 'listener_cancel'), 'modelLoadEvent', EventPriority::NORMAL);
$this->assertFalse($this->models->get('TestModelCancel'));
}
// Cancel the event
public function listener_cancel($event)
{
$event->setCancelled(true);
return $event;
}
/**
* @expectedException FuzeWorks\Exception\ModelException
*/
public function testNoDirectories()
{
// Register the listener
Events::addListener(array($this, 'listener_nodirectories'), 'modelLoadEvent', EventPriority::NORMAL);
$this->models->get('testNoDirectories');
}
// Clean the directories array
public function listener_nodirectories($event)
{
$event->directories = array();
return $event;
}
/**
* @expectedException FuzeWorks\Exception\ModelException
*/
public function testAddModelPathFail()
{
// First test if the model is not loaded yet
$this->assertFalse(class_exists('TestAddModelPath', false));
// Now test if the model can be loaded (hint: it can not)
$this->models->get('TestAddModelPathFail');
}
/**
* @depends testAddModelPathFail
*/
public function testAddModelPath()
{
// Add the modelPath
$this->models->addModelPath('tests/models/testAddModelPath');
// And try to load it again
$this->assertInstanceOf('Application\Model\TestAddModelPath', $this->models->get('TestAddModelPath'));
}
public function testRemoveModelPath()
{
// Test if the path does NOT exist
$this->assertFalse(in_array('tests/models/testRemoveModelPath', $this->models->getModelPaths()));
// Add it
$this->models->addModelPath('tests/models/testRemoveModelPath');
// Assert if it's there
$this->assertTrue(in_array('tests/models/testRemoveModelPath', $this->models->getModelPaths()));
// Remove it
$this->models->removeModelPath('tests/models/testRemoveModelPath');
// And test if it's gone again
$this->assertFalse(in_array('tests/models/testRemoveModelPath', $this->models->getModelPaths()));
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace Application\Model;
use FuzeWorks\ModelAbstract;
class TestAddModelPath extends ModelAbstract
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace Application\Model;
use FuzeWorks\ModelAbstract;
class TestGetModel extends ModelAbstract
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace Application\Model;
use FuzeWorks\ModelAbstract;
class TestRightModel extends ModelAbstract
{
}