Author Topic: Action Parameters  (Read 7120 times)

Celeb

  • Guest
Action Parameters
« on: December 20, 2007, 10:14:26 am »
Maybe I'm too blind to see it, but how am I supposed to access the parameters submitted via
http://[host]/index.php/[controller]/[action]/[param1]/[param2]/[param3]...
in my Controller?

The first thing I did was hacking TinyMVC.php to pass a previously created array of Parameters seems nice at first but I don't really like it. You can only access your parameters by the order of appearance in the request string. There's no way to name them and it involves hacking which I don't like in the first place.

So I created a plugin library.Parameters.class to do the work for me.
It does the following thing: It writes all the passed Parameters into an internal array. Parameters, that have a '=' sign will be split up into
parameter name = parameter value
It also loads $_GET and $_POST into the internal array. I'm not sure if I really like that point, maybe I'll find some smoother way to handle those superglobals in my Plugin.

It provides a __get() method so parameters that have been passed this way
http://localhost/members/show/id=1
can be accessed this way
$this->param->id;
in the Controller.

Here's the code:
Code: [Select]
<?php
/**
 * library.parameters.php
 * Plugin for Tiny_MVC to provide information about submitted parameters on to the controllers
 * 
 * @package  TinyMVC
 * @author   Daniel Gaberszig
 */
 
class Parameters {

  
/**
   * $_params
   *
   * Internal list of gathered parameters
   *
   * @access private
   */
  
var $_params = array();

  
/** 
   * class constructor
   * 
   * @access public
   */
  
function __construct() {

    
// Get the path information
    
$path_info = !empty($_SERVER['PATH_INFO']) ? explode('/',$_SERVER['PATH_INFO']) : null;

    
/* $path_info holds the following values:
     *   [0] => ???
     *   [1] => controller name
     *   [2] => action name
     *   [3]...[n] => parameters
     * 
     * Every parameter will we written into the $_params array. If a parameter
     * looks like .*=.* the left side of the = will be handled as array key
     * and the right side will be handled as array value
     */
    
for($i 3$cnt count($path_info);$i $cnt$i++) {
      
// Get the current parameter
      
$param $path_info[$i];
      
      
// Look for an '='
      
$pos strpos($param'=');
      if (
$pos 0) {
        
// Found -> split the parameter up
        
$param_name substr($param0$pos);
        
$param      substr($param$pos+1);
        
$this->_params[$param_name] = $param;
      } else
        
// Not found -> assign the parameter 
        
$this->_params[] = $param;
    }

    
// Get all variables passed via GET into the $_params array
    
foreach($_GET as $key => $val) {
      
$this->_params[$key] = $val;
    }

    
// Get all variables passed via POST into the $_params array    
    
foreach($_POST as $key => $val) {
      
$this->_params[$key] = $val;
    }

  } 
// function __construct()

  /**
   * __get
   *
   * Make a parameter named 'id' will accessible via the 
   * following syntax in the controller:
   * $this->params->id 
   *
   * @access public
   * @param  string $key The key we are trying to access
   * @return mixed The value of the parameter or null if no such key exists
   */
  
function __get($key) {
    return 
$this->get($key);
  }
  
  
/**
   * get
   *
   * Get the value of a parameter
   *
   * @access public
   * @param  string $key The key we are trying to access
   * @return mixed The value of the parameter or null if no such key exists
   */
  
function get($key) {
    return 
array_key_exists($key$this->_params) ?
      
$this->_params[$key] : null;
  }
  
  
/**
   * get_all
   * 
   * Get all parameters
   * 
   * @access public
   * @return array An array of all parameters
   */
  
function get_all() {
    return 
$this->_params;
  }
  
// class Parameters

?>

(( Is there any other way of creating a box with syntax highlighted code than [code ]<?php ...[/code ]? ))

mohrt

  • Administrator
  • Sr. Member
  • *****
  • Posts: 276
    • View Profile
Re: Action Parameters
« Reply #1 on: December 20, 2007, 02:10:47 pm »
Yep, this is the kind of stuff I'm expecting :) TinyMVC is super bare bones, but you can do anything with plugins (such as extract the URL params.)

You are on the right track, TinyMVC doesn't come with a way to access them yet.

mohrt

  • Administrator
  • Sr. Member
  • *****
  • Posts: 276
    • View Profile
Re: Action Parameters
« Reply #2 on: December 20, 2007, 02:26:59 pm »
Here are my first thoughts:

here is a sample URL:

Code: [Select]
http://[host]/index.php/[controller]/[action]/[foo]/[bar]/[blah]
It could work like so:

Code: [Select]
$this->load->library('uri');
$controller = $this->uri->get(1);
$action = $this->uri->get(2);
$param1 = $this->uri->get(3);

Then, to access the values as an associative array:

Code: [Select]
http://localhost/index.php/mail/read/id/4/from/jake
Code: [Select]
$this->load->library('uri');
$uri = $this->uri->get_assoc(3);

This would start with the third URL value, and fetch them as an associative array:

Code: [Select]
array('id'=>'4','from'=>'jake')
Then you just train yourself to always provide key/val in the URL. Other frameworks use a similar approach, it seems to work well.





« Last Edit: December 20, 2007, 03:41:00 pm by mohrt »

Celeb

  • Guest
Re: Action Parameters
« Reply #3 on: December 20, 2007, 04:40:58 pm »
Yes, the
Code: [Select]
.../id/4/from/jakewas my first thought too .. I even coded that approach but I discarded it again, because it seems a little less vulnerable to errors this way.

Either way, it doesn't really matter, that's just syntactic sugar. Important for me is that i can name my parameters, so I'm less depending on parameter order. Also optional parameters are implemented easier if they are named. It might be a little less performant, but I can live with that :)
I especially like the
Code: [Select]
$this->params->idpart :)

mindfriction

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Action Parameters
« Reply #4 on: March 12, 2008, 07:59:12 pm »
Here are my first thoughts:

here is a sample URL:

Code: [Select]
http://[host]/index.php/[controller]/[action]/[foo]/[bar]/[blah]
It could work like so:

Code: [Select]
$this->load->library('uri');
$controller = $this->uri->get(1);
$action = $this->uri->get(2);
$param1 = $this->uri->get(3);

Then, to access the values as an associative array:

Code: [Select]
http://localhost/index.php/mail/read/id/4/from/jake
Code: [Select]
$this->load->library('uri');
$uri = $this->uri->get_assoc(3);

This would start with the third URL value, and fetch them as an associative array:

Code: [Select]
array('id'=>'4','from'=>'jake')
Then you just train yourself to always provide key/val in the URL. Other frameworks use a similar approach, it seems to work well.


Here's bare bones of what I had in mind for the URI class based on your post Mohrt

Code: [Select]
class URI {

var $segments;

function URI() {
global $path_info;
$this->segments = isset($path_info[1]) ? array_slice($path_info,1) : false;
}

function get($n) {
return (isset($this->segments[$n])) ? $this->segments[$n] : false);
}

// treat uri as associative name/value pairs
function get_assoc($n) {
$n = $n*2;
return $this->get($n);
}

function total_segments(){
return count($this->segments);
}

}

What do ya think?

mindfriction

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Action Parameters
« Reply #5 on: March 12, 2008, 08:43:13 pm »
Hmm another thing to consider is whether we have a getr($n) function which offsets from the controller and method segments

Code: [Select]

function getr($n) {
  // offset from controller and method segments
  $n = $n+2;
  $this->get($n);
}


mohrt

  • Administrator
  • Sr. Member
  • *****
  • Posts: 276
    • View Profile
Re: Action Parameters
« Reply #6 on: March 12, 2008, 10:03:16 pm »
Not a bad start. Things I would do different:

* use __construct() instead of URI() for the constructor name (you are using the PHP 4.0 way of creating constructors.)

* do not use global variables. Use the $_SERVER['PATH_INFO'] superglobal to extract the URI data. I know I have a $path_info global var, but don't use it. I will probably rewrite that to retain the information in a singleton or more object-oriented friendly manner.

* keep two property values, an indexed array of the URI variables, and an associative array of key/value pairings for a particular offset. Upon a get() or get_assoc() call, it would determine at that time if it needs to create the property array. Then it would return the appropriate value.

mindfriction

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Action Parameters
« Reply #7 on: March 13, 2008, 12:50:21 am »
Heh old PHP 4 habits die hard...

Well I'm a bit of a newb when it comes to MVC architecture but how about move the section which manipulates the php_info into the URI constructor (?)

Code: [Select]
class URI {

var $segments;

function __construct() {
$path_info = $input->server('PATH_INFO'); // NB: Input is a wrapper for $_SERVER, $_GET, $_POST with xss filtering etc

$segments = isset($path_info[1]) ? array_slice($path_info,1) : false;
// reset segments first index to '1' not '0' to make access a no-brainer
$i = 1;
foreach ($segments as $val) {
$this->segments[$i++] = $val;
}
unset($this->segments[0]);
}

TinyMVC.php
Code: [Select]
$controller = $this->uri(1) ? $this->uri(1) : $config['default_controller'];
$controller_file = TMVC_MYAPPDIR . DS . 'controllers' . DS . "{$controller}.php";