Skip to content

Instantly share code, notes, and snippets.

@zofe
Last active August 29, 2015 14:07
Show Gist options
  • Save zofe/e9ef3fdc7ebf9824c8dc to your computer and use it in GitHub Desktop.
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
<?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'...):
@zofe
Copy link
Author

zofe commented Oct 4, 2014

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');
        ....

@zofe
Copy link
Author

zofe commented Oct 6, 2014

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?

@zofe
Copy link
Author

zofe commented Oct 12, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment