Last active
August 29, 2015 14:07
-
-
Save zofe/e9ef3fdc7ebf9824c8dc to your computer and use it in GitHub Desktop.
Internal router for rapyd-laravel that match "uri" and/or "query string", it can run before Laravel to Fire widget events
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php namespace Zofe\Rapyd; | |
/** | |
* Class Router | |
* the rapyd router, works "before" laravel router to check uri/query string | |
* it set widgets status / actions. | |
* | |
* @package Zofe\Rapyd | |
* | |
* @method public static get($uri=null, $query=null, Array $route) | |
* @method public static post($uri=null, $query=null, Array $route) | |
* @method public static patch($uri=null, $query=null, Array $route) | |
* @method public static put($uri=null, $query=null, Array $route) | |
* @method public static delete($uri=null, $query=null, Array $route) | |
*/ | |
class Router | |
{ | |
public static $routes = array(); | |
public static $qs = array(); | |
public static $remove = array(); | |
public static $methods = array(); | |
public static $callbacks = array(); | |
/** | |
* little bit magic here, to catch all http methods to define a named route | |
* <code> | |
* Router::get(null, 'page=(\d+)', array('as'=>'page', function ($page) { | |
* //with this query string: ?page=n this closure will be triggered | |
* })); | |
* </code> | |
* @param $method | |
* @param $params | |
* @return static | |
*/ | |
public static function __callstatic($method, $params) | |
{ | |
self::checkParams($method, $params); | |
$uri = ltrim($params[0],"/"); | |
$qs = $params[1]; | |
$name = $params[2]['as']; | |
self::$routes[$name] = $uri; | |
self::$qs[$name] = $qs; | |
self::$remove[$name] = array(); | |
self::$methods[$name] = strtoupper($method); | |
self::$callbacks[$name] = $params[2][0]; | |
return new static(); | |
} | |
/** | |
* dispatch the router: check for all defined routes and call closures | |
*/ | |
public static function dispatch() | |
{ | |
$uri = \Request::path(); | |
$qs = parse_url(\Request::fullUrl(), PHP_URL_QUERY); | |
$method = \Request::method(); | |
foreach (self::$routes as $name=>$route) { | |
$matched = array(); | |
if ($route=='' || preg_match('#' . $route . '#', $uri, $matched) && self::$methods[$name] == $method) { | |
array_shift($matched); | |
if (self::$qs[$name]!='' && preg_match('#' . self::$qs[$name] . '#', $qs, $qsmatched)) { | |
array_shift($qsmatched); | |
$matched = array_merge($matched, $qsmatched); | |
call_user_func_array(self::$callbacks[$name], $matched); | |
} elseif (self::$qs[$name] == '') { | |
call_user_func_array(self::$callbacks[$name], $matched); | |
} | |
} | |
} | |
} | |
public static function isRoute($name, $params) | |
{ | |
$uri = \Request::path(); | |
$qs = parse_url(\Request::fullUrl(), PHP_URL_QUERY); | |
$method = \Request::method(); | |
$route = @self::$routes[$name]; | |
$matched = array(); | |
if ($route=='' || preg_match('#' . $route . '#', $uri, $matched) && self::$methods[$name] == $method) { | |
array_shift($matched); | |
if (self::$qs[$name]!='' && preg_match('#' . self::$qs[$name] . '#', $qs, $qsmatched)) { | |
array_shift($qsmatched); | |
$matched = array_merge($matched, $qsmatched); | |
if ($matched == $params) | |
return true; | |
} elseif (self::$qs[$name] == '') { | |
if ($matched == $params) | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* check if route method call is correct | |
* @param $method | |
* @param $params | |
*/ | |
private static function checkParams($method, $params) | |
{ | |
if (! in_array($method, array('get','post','patch','put','delete'))) | |
throw new \BadMethodCallException("valid methods are 'get','post','patch','put','delete'"); | |
if (count($params)<3 || | |
!is_array($params[2]) || | |
!array_key_exists('as', $params[2]) || | |
!array_key_exists('0', $params[2]) || | |
!($params[2][0] instanceof \Closure) ) | |
throw new \InvalidArgumentException('third parameter should be an array containing a | |
valid callback: array(\'as\'=>\'routename\', function () { }) '); | |
} | |
/** | |
* queque to remove from url one or more named route | |
* | |
* @return static | |
*/ | |
public function remove() | |
{ | |
$routes = (is_array(func_get_arg(0))) ? func_get_arg(0) : func_get_args(); | |
end(self::$routes); | |
self::$remove[key(self::$routes)] = $routes; | |
return new static(); | |
} | |
/** | |
* return a link starting from routename and params | |
* like laravel link_to_route() but working with rapyd widgets/params | |
* | |
* @param $name route name | |
* @param $param one or more params required by the route | |
* @return string | |
*/ | |
public static function linkRoute($name, $params, $url = null) | |
{ | |
$url = ($url != '') ? $url : \Request::fullUrl(); | |
$url_arr = explode('?', $url); | |
$url = $url_arr[0]; | |
$qs = (isset($url_arr[1])) ? $url_arr[1] : ''; | |
if (!is_array($params)) $params = (array)$params; | |
//If required we remove other routes (from segments or qs) | |
if (count(self::$remove[$name])) { | |
foreach (self::$remove[$name] as $route) { | |
if (self::$routes[$route]) | |
$url = preg_replace('#(\/?)'.self::$routes[$route].'#', '', $url); | |
if (self::$qs[$route]) { | |
$url = preg_replace('#(&?)'.self::$qs[$route].'#', '', $url); | |
} | |
} | |
} | |
//if this route works with uri | |
//I check for all params to build the append segment with given params, | |
//then I strip current rule and append new one. | |
if (self::$routes[$name]) { | |
$append = self::$routes[$name]; | |
if (preg_match_all('#\(.*\)#U',$append, $matches)) { | |
foreach ($params as $key=>$param) { | |
$append = str_replace($matches[0][$key],$param, $append); | |
} | |
} | |
$url = preg_replace('#(\/?)'.self::$routes[$name].'#', '', $url); | |
$url = ltrim($url."/".$append,'/'); | |
} | |
//if this route works on query string | |
//I check for all params to buid the "append" string with given params, | |
//then I strip current rule and append the new one. | |
if (self::$qs[$name]) { | |
$append = self::$qs[$name]; | |
if (preg_match_all('#\(.*\)#U',$append, $matches)) { | |
foreach ($params as $key=>$param) { | |
$append = str_replace($matches[0][$key],$param, $append); | |
} | |
} | |
$qs = preg_replace('#(&?)'.self::$qs[$name].'#', '', $qs); | |
$qs = str_replace('&&','&',$qs); | |
$qs = rtrim($qs, '&'); | |
$qs = $qs .'&'.$append; | |
} | |
if ($qs != '') $qs = '?'.$qs; | |
return $url.$qs; | |
} | |
} | |
....... | |
//example | |
\Zofe\Rapyd\Router::get(null, null, array('as'=>'current', function() { | |
//will be execuded every request (no uri and no query if filtered) | |
})); | |
## DataGrid | |
\Zofe\Rapyd\Router::get(null, 'ord=(-?)(\w+)', array('as'=>'orderby', function($direction, $field) { | |
//orderby will be executed for every request where there is a "ord" parameter | |
\Event::queue('dataset.sort', $direction, $field); | |
}))->reset('page'); | |
\Zofe\Rapyd\Router::get(null, 'page=(\d+)', array('as'=>'page', function($page) { | |
\Event::queue('dataset.page', $page); | |
})); | |
## DataForm | |
\Zofe\Rapyd\Router::post(null, 'process' , array('as'=>'process', function() { | |
\Event::queue('dataform.save'); | |
}); | |
## DataEdit | |
\Zofe\Rapyd\Router::post(null, 'insert=(\d+)', array('as'=>'insert', function($id) { | |
\Event::queue('dataedit.insert', $id); | |
}); | |
\Zofe\Rapyd\Router::patch(null, 'update=(\d+)', array('as'=>'update', function($id) { | |
\Event::queue('dataedit.update', $id); | |
}); | |
\Zofe\Rapyd\Router::get(null, 'delete=(\d+)', array('as'=>'delete', function($id) { | |
//delete confirmation | |
\Event::queue('dataedit.delete', $id); | |
})->persist(false); | |
\Zofe\Rapyd\Router::delete(null, 'do_delete=(\d+)', array('as'=>'do_delete', function($id) { | |
\Event::queue('dataedit.dodelete', $id); | |
}); | |
//Alternative o variazioni possibili: | |
## DataGrid | |
\Zofe\Rapyd\Router::get('/ord/(-?)(\w+)', null , array('as'=>'orderby', function($direction, $field) { | |
//orderby will be executed for all request where there is a uri segment /ord/field or /ord/-field | |
}); | |
\Zofe\Rapyd\Router::get('/page/(\d+)', null, array('as'=>'page', function($page) { | |
//limit offset | |
}); | |
##### altri parametri.. | |
/* | |
indica di non preservare i parametri delle route indicate | |
(es. la route dell'orderby resetta la paginazione) | |
nota: per default tutte le route post/patch vengono rimosse sempre. | |
*/ | |
->remove('nomeroute', 'nomealtraroute'...): | |
problema con paginazione & dataset (come scavalcare il paginator di laravel)
risolto con nel listener:
Paginator::setCurrentPage($pag)
e l'override di link nel dataset
$links = $this->paginator->links($view);
$links = preg_replace('@href="(.*\?page=(\d+))"@U',
'href="'.Rapyd::linkRoute('page', '$2').'"', $links);
sembra funzionare bene tutta la logica.. si dovrebbe fare un handler ma al momento basta:
- creare i metodi nei widget
- fare i listen sul metodo statico principale (source)
- e al build fare il flush/fire degli eventi:
<?php
...
public function sort($direction, $field)
{
...
}
public function page($page)
{
\Paginator::setCurrentPage($page);
}
public static function source($source)
{
$ins = new static();
$ins->source = $source;
\Event::listen('dataset.sort', array($ins, 'sort'));
\Event::listen('dataset.page', array($ins, 'page'));
....
}
public function build()
{
\Event::flush('dataset.sort');
\Event::flush('dataset.page');
....
Limiti:
Ora come ora.. si è limitati comunque all'uso della PK nelle route di CRUD, perchè il "find" dei record viene appunto fatto su id.
Laravel di suo ha una roba potentissima che è il Route model binding, http://laravel.com/docs/4.2/routing#route-model-binding
per fare DI del model, definendo eventualmente il modo con cui viene caricato model.
Si puo' trovare un modo di sfruttarlo?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
obiettivo attuale..
fare named route, obbligatoriamente (per poterle lavorare nei widget devono avere un nome)prevedere oltre al->persist(false);
un->remove(array(named.route1, named.route2));
per permettere la rimozione di uno o più parametri (es. se sto' costruendo il link per fare l'orderby, devo necessariamente resettare la paginazione, e via dicendo).. verificare la possibilità di fare un removeAll()al momento nelle closure ho previsto queue di eventi dei vari widget.
Poi nei widget andranno creati i singoli metodi e i listener, poi al build o nel costruttore.. a seconda dei casi ci và un
Event::flush('nomewidget.*')
per scatenare gli eventi.In questo modo si dovrebbe riuscire a fare a meno del controller, e (poichè si usano gli eventi) dovrebbe essere semplice, per chi ne ha la necessità, controllare il comportamento dei widget, e "crearne di nuovi".