-
-
Save zofe/e9ef3fdc7ebf9824c8dc to your computer and use it in GitHub Desktop.
| <?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'...): | |
obiettivo attuale..
fare named route, obbligatoriamente (per poterle lavorare nei widget devono avere un nome)- il terzo parametro potenzialmente deve poter essere solo la closure da eseguire (per compatibilità con il router di laravel)
<?php
\Zofe\Rapyd\Router::get(null, 'ciccio=(\d+)', function($x) {
die('hai richiesto ?ciccio='.$x);
}));- deve essere possibile definire una route con un array, per semplificare la possibilità di salvarli in un file di configurazione, sovrascrivibile dagli utenti finali es:
<?php
Zofe\Rapyd\Router::route(array(
'method'=>'patch',
'qs'=>'update=(\d+)',
'as'=>'update',
'remove'=>array('orderby'),
'action'=>function($id) {
//closure..
}));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()- i link interni andranno fatti così (già funziona)
<?php
$orderby = Rapyd::linkRoute('orderby', '-', 'title');
// on /rapyd-demo/grid?ord=-id&page=2&custom=3
// will output /rapyd-demo/grid?custom=3&ord=-title- spostare la logica degli "sniff action/status" nelle closure :
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à unEvent::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".
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?
il "router" (anche se è improprio chiamarlo così) serve a centralizzare la logica degli eventi e degli stati dei widgets, ed a creare le url internamente con un meccanismo simile alle named-routes (ti dico la route, ti passo i parametri e mi costruisci il link).
Serve anche a personalizzare completamente le uri.. es: non ti piace la query string? usa le uri (anche scavalcando il router di laravel)
funziona così: