
Originally Posted by
Ren
How do you go about generating urls?
Eg. You want a url that'll get to the ImageControlller, with this image id.
URL-routing and URL-building are obviously tightly integrated. I would build URL's in the same manner as I map the request - iteratively. Each context will have a method url($href) which would return a URL to a resource, relative to that context.
This is an expansion of the previous mock which shows how it works :
PHP Code:
abstract class Context
{
protected $env = Array();
abstract function getEnv($property);
abstract function url($href = "");
}
class HttpContext extends Context
{
protected $response;
function __construct() {
$this->env =& $_SERVER;
$_SERVER['RELATIVE_REQUEST_URI'] = ltrim(preg_replace("~^(".preg_quote(dirname($_SERVER['PHP_SELF']), "~").")~", "", $_SERVER['REQUEST_URI']), "/");
}
function getEnv($property) {
return $this->env[$property];
}
function setResponse($response) {
$this->response = $response;
}
function out() {
echo $this->response;
}
function url($href = "") {
return dirname($_SERVER['PHP_SELF'])."/".$href;
}
}
abstract class Controller extends Context
{
protected $context;
function __construct(Context $context) {
$this->setContext($context);
$this->setEnv('URI_COMMAND', "");
}
function setContext(Context $context) {
$this->context = $context;
}
function getEnv($property) {
if (array_key_exists($property, $this->env)) {
return $this->env[$property];
}
return $this->context->getEnv($property);
}
function setEnv($property, $value) {
$this->env[$property] = $value;
}
abstract function execute();
function url($href = "") {
if ($this->getEnv('URI_COMMAND') == "") {
return $this->context->url($href);
} else {
return $this->context->url($this->getEnv('URI_COMMAND')."/".$href);
}
}
}
class RouteController extends Controller
{
public $commands = Array(
'index' => 'Action_Default_Index',
'edit' => 'Action_Default_Edit',
'create' => 'Action_Default_Create',
'delete' => 'Action_Default_Delete',
);
public $default = "index";
public $notFound = "Action_NotFound";
function execute() {
$uri = $this->context->getEnv('RELATIVE_REQUEST_URI');
$uri_command = "";
if (preg_match('~^([^/?#]+)(.*)$~', strtolower($uri), $matches)) {
if (isset($this->commands[$matches[1]])) {
$command = $this->commands[$matches[1]];
$this->env['RELATIVE_REQUEST_URI'] = ltrim($matches[2], "/");
$uri_command = $matches[1];
} else {
$command = $this->notFound;
}
} else {
$command = $this->commands[$this->default];
$uri_command = $this->default;
}
$child = new $command($this);
$child->setEnv('URI_COMMAND', $uri_command);
return $child->execute();
}
}
class Dummy extends Controller
{
function execute() {
return get_class($this->context).":".get_class($this).":".$this->context->getEnv('RELATIVE_REQUEST_URI');
}
}
class Action_Default_Index extends Dummy {}
class Action_Default_Create extends Dummy {}
class Action_Default_Edit extends Dummy {}
class Action_Default_Delete extends Dummy {}
class MenuRouteController extends RouteController
{
function execute() {
$html = "<div style='border:1px solid black;padding:1em'>";
$html .= "<ul style='margin-left:0'>";
foreach (array_keys($this->commands) as $command) {
$html .= "<li style='display:inline'><a href='".$this->url($command)."'>".$command."</a></li>\n";
}
$html .= "</ul>";
$html .= parent::execute();
$html .= "</div>";
return $html;
}
}
class Commodities extends MenuRouteController
{
function __construct(Context $context) {
parent::__construct($context);
$this->commands['images'] = 'Images';
}
}
class Images extends MenuRouteController
{
function __construct(Context $context) {
parent::__construct($context);
$this->commands['index'] = 'Images_Index';
}
}
class Images_Index extends Controller
{
function execute() {
return "<a href='".$this->context->url("edit/160")."'>edit image#160</a>";
}
}
class Application extends MenuRouteController
{
public $commands = Array(
'commodities' => 'Commodities',
);
public $default = "commodities";
}
$context = new HttpContext();
$dispatcher = new Application($context);
$context->setResponse($dispatcher->execute());
$context->out();
The URL-method is quite primitive in this example. I have expanded it further to contain state too. For example a list-controller would have state for sort/order and possibly paging. Theese would be relevant to the controller and any child-controllers, but not for parents, so it could append the state to the url.
Bookmarks