Skip to content

Commit

Permalink
Merge pull request #12341 from Jurigag/3.1.x-binding
Browse files Browse the repository at this point in the history
[WIP]Added Phalcon\Mvc\Model\Binder
  • Loading branch information
sergeyklay authored Nov 19, 2016
2 parents 2c33f6e + e6d887d commit a4bfbc2
Show file tree
Hide file tree
Showing 11 changed files with 1,370 additions and 39 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# [3.1.0](https://github.com/phalcon/cphalcon/releases/tag/v3.1.0) (2016-XX-XX)``
# [3.1.0](https://github.com/phalcon/cphalcon/releases/tag/v3.1.0) (2016-XX-XX)
- Added `Phalcon\Validation\Validator\Callback`, `Phalcon\Validation::getData`
- Added the ability to truncate database tables
- Added `Phalcon\Mvc\Model\Binder`, class used for binding models to parameters in dispatcher, micro, added `Phalcon\Dispatcher::getBoundModels` and `Phalcon\Mvc\Micro::getBoundModels` to getting bound models, added `Phalcon\Mvc\Micro\Collection\LazyLoader::callMethod`

# [3.0.2](https://github.com/phalcon/cphalcon/releases/tag/v3.0.2) (2016-XX-XX)
- Fixed saving snapshot data while caching model [#12170](https://github.com/phalcon/cphalcon/issues/12170), [#12000](https://github.com/phalcon/cphalcon/issues/12000)
Expand Down
121 changes: 88 additions & 33 deletions phalcon/dispatcher.zep
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use Phalcon\DispatcherInterface;
use Phalcon\Events\ManagerInterface;
use Phalcon\Di\InjectionAwareInterface;
use Phalcon\Events\EventsAwareInterface;
use Phalcon\Mvc\Model\Binder;
use Phalcon\Mvc\Model\BinderInterface;

/**
* Phalcon\Dispatcher
Expand Down Expand Up @@ -78,6 +80,8 @@ abstract class Dispatcher implements DispatcherInterface, InjectionAwareInterfac

protected _modelBinding = false;

protected _modelBinder = null;

const EXCEPTION_NO_DI = 0;

const EXCEPTION_CYCLIC_ROUTING = 1;
Expand Down Expand Up @@ -322,11 +326,61 @@ abstract class Dispatcher implements DispatcherInterface, InjectionAwareInterfac
/**
* Enable/Disable model binding during dispatch
*
* @param boolean value
* <code>
* $di->set('dispatcher', function() {
* $dispatcher = new Dispatcher();
*
* $dispatcher->setModelBinding(true, 'cache');
* return $dispatcher;
* });
* </code>
*/
public function setModelBinding(boolean value)
public function setModelBinding(boolean value, var cache = null) -> <Dispatcher>
{
var dependencyInjector;

if typeof cache == "string" {
let dependencyInjector = this->_dependencyInjector;
let cache = dependencyInjector->get(cache);
}

let this->_modelBinding = value;
if value {
let this->_modelBinder = new Binder(cache);
}

return this;
}

/**
* Enable model binding during dispatch
*
* <code>
* $di->set('dispatcher', function() {
* $dispatcher = new Dispatcher();
*
* $dispatcher->setModelBinder(new Binder(), 'cache');
* return $dispatcher;
* });
* </code>
*/
public function setModelBinder(<BinderInterface> modelBinder, var cache = null) -> <Dispatcher>
{
var dependencyInjector;

if typeof cache == "string" {
let dependencyInjector = this->_dependencyInjector;
let cache = dependencyInjector->get(cache);
}

if cache != null {
modelBinder->setCache(cache);
}

let this->_modelBinding = true;
let this->_modelBinder = modelBinder;

return this;
}

/**
Expand Down Expand Up @@ -362,9 +416,8 @@ abstract class Dispatcher implements DispatcherInterface, InjectionAwareInterfac
int numberDispatches;
var value, handler, dependencyInjector, namespaceName, handlerName,
actionName, params, eventsManager,
actionSuffix, handlerClass, status, actionMethod, reflectionMethod, methodParams,
className, paramKey, methodParam, modelName, bindModel,
wasFresh = false, e;
actionSuffix, handlerClass, status, actionMethod, modelBinder, wasFresh = false,
e, bindCacheKey;

let dependencyInjector = <DiInterface> this->_dependencyInjector;
if typeof dependencyInjector != "object" {
Expand Down Expand Up @@ -551,34 +604,10 @@ abstract class Dispatcher implements DispatcherInterface, InjectionAwareInterfac
}
}

if this->_modelBinding === true {
//Check if we can bind a model based on what the controller action is expecting
let reflectionMethod = new \ReflectionMethod(handlerClass, actionMethod);
let methodParams = reflectionMethod->getParameters();

for paramKey, methodParam in methodParams {
if methodParam->getClass() {
let className = methodParam->getClass()->getName();
if typeof className == "string" {
//If we are in a base class and the child implements BindModelInterface we getModelName
if className == "Phalcon\\Mvc\\Model" {
if in_array("Phalcon\\Mvc\\Controller\\BindModelInterface", class_implements(handlerClass)) {
let modelName = call_user_func([handlerClass, "getModelName"]);
let bindModel = call_user_func_array([modelName, "findFirst"], [params[paramKey]]);
let params[paramKey] = bindModel;
break;
}
}

//Check if Model is defined
if is_subclass_of(className, "Phalcon\\Mvc\\Model") {
let bindModel = call_user_func_array([className, "findFirst"], [params[paramKey]]);
let params[paramKey] = bindModel;
break;
}
}
}
}
if this->_modelBinding {
let modelBinder = this->_modelBinder;
let bindCacheKey = "_PHMB_" . handlerClass . "_" . actionMethod;
let params = modelBinder->bindToHandler(handler, params, bindCacheKey, actionMethod);
}

let this->_lastHandler = handler;
Expand Down Expand Up @@ -736,6 +765,32 @@ abstract class Dispatcher implements DispatcherInterface, InjectionAwareInterfac
return call_user_func_array([handler, actionMethod], params);
}

/**
* Returns bound models from binder instance
*
* <code>
* class UserController extends Controller
* {
* public function showAction(User $user)
* {
* $boundModels = $this->dispatcher->getBoundModels(); // return array with $user
* }
* }
* </code>
*/
public function getBoundModels() -> array
{
var modelBinder;

let modelBinder = this->_modelBinder;

if modelBinder != null {
return modelBinder->getBoundModels();
}

return [];
}

/**
* Set empty properties to their defaults (where defaults are available)
*/
Expand Down
87 changes: 84 additions & 3 deletions phalcon/mvc/micro.zep
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use Phalcon\Http\ResponseInterface;
use Phalcon\Mvc\Router\RouteInterface;
use Phalcon\Mvc\Micro\MiddlewareInterface;
use Phalcon\Mvc\Micro\CollectionInterface;
use Phalcon\Mvc\Controller;
use Phalcon\Mvc\Model\BinderInterface;

/**
* Phalcon\Mvc\Micro
Expand Down Expand Up @@ -76,6 +78,8 @@ class Micro extends Injectable implements \ArrayAccess

protected _returnedValue;

protected _modelBinder;

/**
* Phalcon\Mvc\Micro constructor
*/
Expand Down Expand Up @@ -365,7 +369,7 @@ class Micro extends Injectable implements \ArrayAccess
public function mount(<CollectionInterface> collection) -> <Micro>
{
var mainHandler, handlers, lazyHandler, prefix, methods, pattern,
subHandler, realHandler, prefixedPattern, route, handler, name;
subHandler, realHandler, prefixedPattern, route, handler, name, modelBinder;

/**
* Get the main handler
Expand Down Expand Up @@ -581,7 +585,7 @@ class Micro extends Injectable implements \ArrayAccess
var dependencyInjector, eventsManager, status = null, router, matchedRoute,
handler, beforeHandlers, params, returnedValue, e, errorHandler,
afterHandlers, notFoundHandler, finishHandlers, finish, before, after,
response;
response, modelBinder, bindCacheKey, routeName, realHandler = null, methodName, lazyReturned;

let dependencyInjector = this->_dependencyInjector;
if typeof dependencyInjector != "object" {
Expand Down Expand Up @@ -690,17 +694,52 @@ class Micro extends Injectable implements \ArrayAccess

let params = router->getParams();

let modelBinder = this->_modelBinder;

/**
* Bound the app to the handler
*/
if typeof handler == "object" && handler instanceof \Closure {
let handler = \Closure::bind(handler, this);
if modelBinder != null {
let routeName = matchedRoute->getName();
if routeName != null {
let bindCacheKey = "_PHMB_" . routeName;
} else {
let bindCacheKey = "_PHMB_" . matchedRoute->getPattern();
}
let params = modelBinder->bindToHandler(handler, params, bindCacheKey);
}
}

/**
* Calling the Handler in the PHP userland
*/
let returnedValue = call_user_func_array(handler, params);

if typeof handler == "array" {

let realHandler = handler[0];

if realHandler instanceof Controller && modelBinder != null {
let methodName = handler[1];
let bindCacheKey = "_PHMB_" . get_class(realHandler) . "_" . methodName;
let params = modelBinder->bindToHandler(realHandler, params, bindCacheKey, methodName);
}
}

/**
* Instead of double call_user_func_array when lazy loading we will just call method
*/
if realHandler != null && realHandler instanceof LazyLoader {
let methodName = handler[1];
/**
* There is seg fault if we try set directly value of method to returnedValue
*/
let lazyReturned = realHandler->callMethod(methodName, params, modelBinder);
let returnedValue = lazyReturned;
} else {
let returnedValue = call_user_func_array(handler, params);
}

/**
* Update the returned value
Expand Down Expand Up @@ -1040,4 +1079,46 @@ class Micro extends Injectable implements \ArrayAccess
{
return this->_handlers;
}

/**
* Sets model binder
*
* <code>
* $micro = new Micro($di);
* $micro->setModelBinder(new Binder(), 'cache');
* </code>
*/
public function setModelBinder(<BinderInterface> modelBinder, var cache = null) -> <Micro>
{
var dependencyInjector;

if typeof cache == "string" {
let dependencyInjector = this->_dependencyInjector;
let cache = dependencyInjector->get(cache);
}

if cache != null {
modelBinder->setCache(cache);
}

let this->_modelBinder = modelBinder;

return this;
}

/**
* Returns bound models from binder instance
*/
public function getBoundModels() -> array
{
var modelBinder;

let modelBinder = this->_modelBinder;

if modelBinder != null {
return modelBinder->getBoundModels();
}

return [];
}
}
30 changes: 28 additions & 2 deletions phalcon/mvc/micro/lazyloader.zep
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

namespace Phalcon\Mvc\Micro;

use Phalcon\Mvc\Model\BinderInterface;

/**
* Phalcon\Mvc\Micro\LazyLoader
*
Expand All @@ -28,6 +30,8 @@ class LazyLoader
{
protected _handler;

protected _modelBinder;

protected _definition {
get
};
Expand All @@ -49,19 +53,41 @@ class LazyLoader
*/
public function __call(string! method, arguments)
{
var handler, definition;
var handler, definition, modelBinder, bindCacheKey;

let handler = this->_handler;

let definition = this->_definition;

if typeof handler != "object" {
let definition = this->_definition;
let handler = new {definition}();
let this->_handler = handler;
}

let modelBinder = this->_modelBinder;

if modelBinder != null {
let bindCacheKey = "_PHMB_" . definition . "_" . method;
let arguments = modelBinder->bindToHandler(handler, arguments, bindCacheKey, method);
}

/**
* Call the handler
*/
return call_user_func_array([handler, method], arguments);
}

/**
* Calling __call method
*
* @param string method
* @param array arguments
* @return mixed
*/
public function callMethod(string! method, arguments, <BinderInterface> modelBinder = null)
{
let this->_modelBinder = modelBinder;

return this->__call(method, arguments);
}
}
Loading

0 comments on commit a4bfbc2

Please sign in to comment.