Skip to content

Instantly share code, notes, and snippets.

@Svel
Created February 10, 2011 22:05
Show Gist options
  • Save Svel/821450 to your computer and use it in GitHub Desktop.
Save Svel/821450 to your computer and use it in GitHub Desktop.
Extend basic sfRequestRoute to accept optional variables
<?php
/**
* Pager block example
*
* @author Svel <[email protected]>
* @url https://gist.github.com/gists/821450
* @param sfDoctrinePager $pager
* @param string $route
* @param array $params
*/
$params = isset($params) ? $params->getRawValue() : array();
$array = create_function('$a,$b', 'return array_merge($b, array(\'page\' => $a,));');
?>
<?php if ($pager->haveToPaginate()) : ?>
<div class="pagination">
<a href="<?php echo url_for($route, $array($pager->getFirstPage(), $params)) ?>" class="first"><?php echo __('First', null, 'pager') ?></a>
<a href="<?php echo url_for($route, $array($pager->getPreviousPage(), $params)) ?>" class="previous"><?php echo __('Previous', null, 'pager') ?></a>
<?php foreach ($pager->getLinks() as $page) : ?>
<?php if ($page == $pager->getPage()) : ?>
<span class="current numbers"><?php echo $page ?></span>
<?php else : ?>
<a class="numbers" href="<?php echo url_for($route, $array($page, $params)) ?>"><?php echo $page ?></a>
<?php endif ?>
<?php endforeach ?>
<a href="<?php echo url_for($route, $array($pager->getNextPage(), $params)) ?>" class="next"><?php echo __('Next', null, 'pager') ?></a>
<a href="<?php echo url_for($route, $array($pager->getLastPage(), $params)) ?>" class="last"><?php echo __('Last', null, 'pager') ?></a>
</div>
<?php endif ?>
<?php
/**
* Extend basic sfRequestRoute to accept optional variables
* use:
* url: /text/:variableRequired/:variableOptional.:sf_format
* matches:
* example.com/text/required/optional.html
* example.com/text/required.html
*
* @see http://trac.symfony-project.org/ticket/2478
* @author Svel <[email protected]>
* @url https://gist.github.com/gists/821450
*/
class myExtendedRequestRoute extends sfRequestRoute
{
/**
* Returns an array of parameters if the URL matches this route, false otherwise.
*
* @param string $url The URL
* @param array $context The context
*
* @return array An array of parameters
*/
public function matchesUrl($url, $context = array())
{
if (!$this->compiled) {
$this->compile();
}
// check the static prefix uf the URL first. Only use the more expensive preg_match when it matches
if ('' !== $this->staticPrefix && 0 !== strpos($url, $this->staticPrefix)) {
return false;
}
if (!preg_match($this->regex, $url, $matches)) {
return false;
}
$defaults = array_merge($this->getDefaultParameters(), $this->defaults);
$parameters = array();
// *
if (isset($matches['_star'])) {
$parameters = $this->parseStarParameter($matches['_star']);
unset($matches['_star'], $parameters['module'], $parameters['action']);
}
// defaults
$parameters = $this->mergeArrays($defaults, $parameters);
// variables
foreach ($matches as $key => $value) {
// FIXED: $value must not be empty
if (!is_int($key) && $value) {
$parameters[$key] = urldecode($value);
}
}
return $parameters;
}
/**
* Generates a URL for the given parameters by using the route tokens.
*
*
* @param array $parameters An array of parameters
*/
protected function generateWithTokens($parameters)
{
$url = array();
$optional = $this->options['generate_shortest_url'];
$first = true;
$tokens = array_reverse($this->tokens);
foreach ($tokens as $kkey => $token) {
switch ($token[0]) {
case 'variable':
// FIXED
if (preg_match('#\?$#', $token[3])) {
$token[3] = substr($token[3], 0, strlen($token[3])-1);
$optional = true;
}
if (!$optional || !isset($this->defaults[$token[3]]) || $parameters[$token[3]] != $this->defaults[$token[3]]) {
$url[] = urlencode($parameters[$token[3]]);
$optional = false;
}
break;
case 'text':
$url[] = $token[2];
$optional = false;
break;
case 'separator':
if (
(false === $optional || $first)
//&&
// custom condition to parse optional request parameters
//(array_key_exists($kkey - 1, $tokens) && !preg_match('#\?$#', $tokens[$kkey - 1][3]))
) {
$url[] = $token[2];
}
break;
default:
// handle custom tokens
if ($segment = call_user_func_array(array($this, 'generateFor'.ucfirst(array_shift($token))), array_merge(array($optional, $parameters), $token))) {
$url[] = $segment;
$optional = false;
}
break;
}
$first = false;
}
$url = implode('', array_reverse($url));
if (!$url) {
$url = '/';
}
return $url;
}
/**
* Variable compiler extension
* Copypasted from sfRoute
*
* @param $separator
* @param $name
* @param $variable
*/
protected function compileForVariable($separator, $name, $variable)
{
$isQuestionMarked = false;
if (preg_match('#.+\?$#', $variable)) {
$variable = substr($variable, 0, strlen($variable) - 1);
$isQuestionMarked = true;
}
if (!isset($this->requirements[$variable])) {
$this->requirements[$variable] = $this->options['variable_content_regex'];
}
if ($isQuestionMarked) {
// makes variable with question mark at the end optional
$this->segments[] = '(?:'.preg_quote($separator, '#').'(?P<'.$variable.'>'.$this->requirements[$variable].'))?';
} else {
// default way of parsing
$this->segments[] = preg_quote($separator, '#').'(?P<'.$variable.'>'.$this->requirements[$variable].')';
}
$this->variables[$variable] = $name;
if (!isset($this->defaults[$variable])) {
$this->firstOptional = count($this->segments);
}
}
/**
* Extend user-defined and predefined variable_regex option by ? sign
* @see sfRoute
*/
protected function initializeOptions()
{
parent::initializeOptions();
$this->options['variable_regex'] .= '\??';
}
}
<?php
require_once(dirname(__FILE__) . '/../../bootstrap/all.php');
/**
* Route: extended sfRequest test
*
* @author Svel <[email protected]>
* @url https://gist.github.com/gists/821450
*/
class myExtendedRequestRouteTest extends myUnitTestCase
{
/**
* Static url generation
* Check for base generation in case we breaks smth =/
*
* @test
*/
public function staticRootUrl()
{
$route = new myExtendedRequestRoute('/', array(), array(), array('extra_parameters_as_query_string' => true));
$this->assertTrue($route->matchesParameters(array()), 'Route matches request');
$this->assertEquals(array(), $route->matchesUrl('/'), 'Route matches url');
$this->assertTrue($route->matchesParameters(array('foo' => 'bar')), 'Extra parameters in query string');
$this->assertEquals('/', $route->generate($params = array(), $context = array(), $isAbs = false));
$this->assertEquals('/?foo=bar', $route->generate($params = array('foo' => 'bar'), $context = array(), $isAbs = false));
}
/**
* Required and not required parameters testing
*
* @test
*/
public function params()
{
$params = array('required' => 'something', 'sf_format' => 'html');
$paramsWithDefault = array_merge($params, array('param' => 1));
$extended = array_merge($params, array('param' => 5));
$route = new myExtendedRequestRoute(
'/static/:param?/:required.:sf_format',
array('param' => 1),
array(), array('extra_parameters_as_query_string' => true)
);
$this->assertFalse($route->matchesParameters(array()), 'Route NOT matches empty request');
$this->assertTrue($route->matchesParameters($params), 'Route matches request with required params');
$this->assertEquals($paramsWithDefault, $route->matchesUrl('/static/something.html'), 'Route matches url');
$this->assertEquals('/static/something.html', $route->generate($params, $context = array(), $isAbs = false));
$this->assertTrue($route->matchesParameters($extended), 'Route matches request with all params');
$this->assertEquals($extended, $route->matchesUrl('/static/5/something.html'), 'Route matches url');
$this->assertEquals('/static/5/something.html', $route->generate($extended, $context = array(), $isAbs = false));
}
}
# example route
cool_route_name:
url: /static/:section/static/:isOld?-:page?.:sf_format # special conditions with question mark
class: myExtendedRequestRoute
params: { module: myModule, action: myAction, page: 1, isOld: false }
options: { segment_separators: [/, -, .] }
requirements:
sf_method: [get, head]
sf_format: html
section: (?:[a-z0-9]{3,})
page: (?:[2-9]|[1-9]\d+)
isOld: (?:1|0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment