SitePoint Sponsor

User Tag List

Results 1 to 10 of 10

Thread: URL via array!

  1. #1
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    319
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Lightbulb URL via array!

    Hello, I've seen similar systems developed and I'd like to create one.

    Essentially, the way I figure it, you have an array of urls which has an array of the variables to get round the mvc framework.

    Heres what I have so far.

    .htaccess
    Code:
    RewriteEngine on
    
    RewriteRule ^([-A-Za-z0-9]+)/([-A-Za-z0-9]+)?$ 														index.php?module=$1&case=2 [L]
    RewriteRule ^([-A-Za-z0-9]+)/([-A-Za-z0-9]+)/([-A-Za-z0-9]+)?$ 										index.php?module=$1&case=2&id=$3 [L]
    RewriteRule ^([-A-Za-z0-9]+)/([-A-Za-z0-9]+)/([-A-Za-z0-9]+)/([-A-Za-z0-9]+)?$ 						index.php?module=$1&case=2&id=$3&page=$4 [L]
    RewriteRule ^([-A-Za-z0-9]+)/([-A-Za-z0-9]+)/([-A-Za-z0-9]+)/([-A-Za-z0-9]+)/([-A-Za-z0-9]+)?$ 		index.php?module=$1&case=2&id=$3&page=$4&extra=$5 [L]
    RewriteRule ^([-A-Za-z0-9]+)?$ 																		index.php?module=$1 [L]
    index.php
    Code PHP:
    	$URLS = array(
    		array('URL' => '', 					'module' => 'Index', 		'case' => 'Index'),
    		array('URL' => 'home', 				'module' => 'Index', 		'case' => 'Index'),
    		array('URL' => 'forum', 			'module' => 'Forum', 		'case' => 'Index'),
    		array('URL' => 'active-topics',		'module' => 'Thread', 		'case' => 'Active',		'id' => '([0-9]+)', 	'page' => '([0-9]+)'),
    	);
     
     
     
    	foreach ($URLS as $item => $area) {
    		if ( $_GET['module'] == $area['URL'] ) {
    			$area['template'] = $area['module'].DS.$area['case'].'.tpl.php';
    			echo _DEBUG == 1 ? $area['template'] : NULL;		
    			require_once(_MODULE.DS.$area['module'].DS.'index.inc.php');
    		}
    	}

    What I need todo now is:
    - Improve the above so it does preg replace for numbers and a-z in the URL and a function for the templates which are essentially given:

    <a href="<?php echo $URL->Make(array('URL' => 'forum', 'module' => 'Forum', 'case' => 'View', 'id' => $forum->id)">The Forum</a>

    Essentially any input or code samples are very welcome Could be useful for lots of people!

  2. #2
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Your best bet would be to utilise something called a Front Controller, here's a quick and dirty example.

    .htaccess
    Code:
    # Turn on URL rewriting
    RewriteEngine On
    
    # Allow any files or directories that exist to be displayed directly
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    
    # Rewrite all other URLs to index.php/URL
    RewriteRule (.*) index.php?$0 [PT,L]
    index.php
    PHP Code:
    <?php
    $sSlug 
    $_SERVER['QUERY_STRING'];
    ?>
    So, given the URL http://www.domain.com/view/blog/page/5, $sSlug would become view/blog/page/5. You can now use this variable as you wish, search a database for a matching page, redirect etc...
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  3. #3
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    319
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Funnily enough about 9 years ago thats how I did my first site.

    Its far to basic and very limiting. I need some system that will crawl the arrays and make the urls dynamically via the URL.

    Not that its a bad suggestion, so thanks

  4. #4
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    I wouldn't say so, I would find the fact you need many rewrites, each implementing concrete routes in your proposal more limiting.

    Also, the suggestion I posted would also be IIS compatible without any application modification, yours would not.

    Each to their own.
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  5. #5
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    319
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    To my guess though, that would mean if I wanted to pass anything through Thread

    It would have to be Thread/Active/1, which I wouldn't want, I'd want active-topics.

  6. #6
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Not at all, you would just add logic to handle thread/active/1 in a way you see appropriate.

    For all the application cares, it could be completely unrelated.
    PHP Code:
    <?php
    $route 
    = new Route('thread/active/1');
    $route->getController(); #chicken
    $route->getMethod(); #recipies
    ?>
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  7. #7
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    319
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Okay so far I've come up with

    Code PHP:
    	class Route {
     
     
    		public $URL;
    		public $exploded;
    		public static $URLS;
     
    		public $module;
    		public $case;
     
    		public $id 		= 1;
    		public $page 	= 0;
    		public $order 	= 0;
     
     
    		function _construct ($URL) {
    			$this->URL		= $URL;
    			$this->exploded = explode('/', $this->URL);
    		}
     
     
    		public function Breakdown() {
     
    			foreach ($this->URLS as $key => $arguments) {
    				if (preg_match($arguments['URL'], $this->URL, $matches)) {
    					$this->module 	= $arguments['module'];
    					$this->case 	= $arguments['case'];
    					$this->id 		= isset($arguments['id']) 		? $arguments['id'] 		: NULL;
    					$this->page 	= isset($arguments['page']) 	? $arguments['page'] 	: 0;
    					$this->order 	= isset($arguments['order']) 	? $arguments['order']	: 0;
    					$this->extra 	= isset($arguments['extra']) 	? $arguments['extra'] 	: 0;					
    				}
    			}
     
     
    			//loop through URLS and match with $URLS;
     
    		}
     
     
    		public function URLS () {
    			$this->URLS = array(
    				array('URL' => '', 								'module' => 'Index', 	'case' => 'Index'),
    				array('URL' => 'home', 							'module' => 'Index', 	'case' => 'Index'),
    				array('URL' => 'forum/([0-9]+)/([0-9]+)', 		'module' => 'Forum', 	'case' => 'Index',		'id' => '[1]'),
    				array('URL' => 'active-topics/([0-9]+)',		'module' => 'Thread', 	'case' => 'Active',		'id' => '[0]'),
    			);
    		}
     
     
    		public function Make() {#used in template to create URL from module
    		}
     
     
    	}
     
     
    	$route = new Route($_SERVER['QUERY_STRING']);
    	$route->URLS();
    	$route->Breakdown();

    However I get
    Code:
    Warning: preg_match() [function.preg-match]: Empty regular expression in /home/wuggawoo/public_html/forums/newCodebase/index.php on line 93
    
    Warning: preg_match() [function.preg-match]: Delimiter must not be alphanumeric or backslash in /home/wuggawoo/public_html/forums/newCodebase/index.php on line 93
    
    Warning: preg_match() [function.preg-match]: Delimiter must not be alphanumeric or backslash in /home/wuggawoo/public_html/forums/newCodebase/index.php on line 93
    
    Warning: preg_match() [function.preg-match]: Delimiter must not be alphanumeric or backslash in /home/wuggawoo/public_html/forums/newCodebase/index.php on line 93
    I'm not really sure I'm going along the right route either...hmmm

  8. #8
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,144
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Logically a "clean" URL can be broken up into an array. Then you can displace the index onto onto a naming array. This can all be done on the application side resulting in a very small amount of code with a few lines in the .htacces to send everything to the index page.

    Example:

    www.mysite.com/blog/2/3

    1.) blog
    2.) id
    3.) page (comments perhaps)

    In my own system the first item there is actually the controller name that is created and executed. Everything that follows is displaced onto a map though. The map is held inside the database and forms a 1:m relationship with the controller. The controllers are stored in the database as well so they can be easily linked to their options.

    The bootstrap file to responsible for locating the controller and its options. Then a separate class is responsible for parsing/displacing the naming map onto the everything following the controller name starting at index 1.

    The below images show the basic schema I use to accomplish this in the database. This is a application side replacement for the $_GET array that results in clean URL's without a hideous .htaccess file.
    Attached Images Attached Images

  9. #9
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,144
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    The object responsible for creating a interface that replaces the $_GET vars is called pathway. This instance is passed into the controller onto the view and may be access in the controller via:

    $this->_path

    or in the view:

    $this->getVar('path')

    I can then easily use the option names as they appear in the database to see if a option is actually defined for the given controller:

    // inside BlogEntryController
    $this->_path->isDefined('page'); // true
    $this->_path->isDefined('happy'); // false - happy option has not been defined in controller_options

    I can then see whether there is a value defined for any option by name by:

    $this->_path->hasOption('page'); // url: /blog-entry/10 - does not have a page option yet: url: /blog-entry/10/2 does.

    Then I can finally access the value for any option via:

    $this->_path->getOption('page'); // returns null if option doesn't exists throws exception if option is not defined for controller.

    So if we are in the BlogEntryController I can easily get the id of the blog to show via:

    $id = $this->_path->isDefined('id') && $this->_path->hasOption('id')?$this->_path->getOption('id'):null;

    // check to make sure id exists
    if(is_null($id)) {
    // throw exception or show error page becasue id hasn't been passed.
    }

    That is my solution to clean urls and replacing the $_GET array with a object oriented interface.

    I also have a "special" option in place called action. If the action option is defined for any controller then when passed it executes a method of the same name replacing _defaultAction() inside the contrtoller. This makes it possible to place the action anywhere in the url not just directly after the controller name.

  10. #10
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,144
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Here is the class you can play around with it if you want and below is a test case. The great thing about this is that options can be added and removed very easily when generating urls that may point to the same controller/area but the page may change (take a look at the test case).

    PHP Code:
    <?php
    class Pathway {

        const 
    entry '/index.php';
        
    //const entry = '';

        
    private $_options;
        private 
    $_path;
        
        public function 
    __construct($pPath=null) {
        
            
    $this->_options = array();
            
    $this->_path is_null($pPath)?array():$pPath;
        
        }

        public function 
    add(ControllerOption $pOption) {
            
            
    $option $pOption->getProperty('name');
            
    $this->_options[$option] = $pOption;    
        
        }
        
        public function 
    hasOption($pName) {
        
            return 
    array_key_exists($pName,$this->_options);
        
        }
        
        public function 
    isDefined($pName) {
        
            if(
    $this->hasOption($pName)===false) {
            
                return 
    false;
            
            }
            
            
    $index $this->indexFor($pName);
            
            return 
    $this->indexExists($index);
        
        }
        
        public function 
    indexFor($pName) {
        
            return 
    $this->_options[$pName]->getProperty('path_index');
        
        }
        
        public function 
    indexExists($pIndex) {
        
            return 
    array_key_exists($pIndex,$this->_path);
        
        }
        
        public function 
    getOption($pName) {
        
            if(
    $this->isDefined($pName)===false) {
            
                return 
    false;
            
            }
            
            
    $index $this->indexFor($pName);
            
            return 
    $this->_path[$index];
            
        }
        
        public function 
    getController() {
        
            return 
    $this->_path[0];
        
        }
        
        public function 
    makeUrl($pReplacements=null,$pRemove=null,$pController=true) {
            
            
    $str self::entry;
            
            if(
    $pController===true) {
                
                
    $str.= '/'.$this->getController();
                
            }
            
            
    $options = array();
            foreach(
    $this->_options as $name=>$option) {
            
                
    $index $option->getProperty('path_index');
                
                if(!empty(
    $pReplacements) && array_key_exists($name,$pReplacements)) {
                
                    
    $options[$index] = $pReplacements[$name];
                
                } else if(
    $this->isDefined($name)===true) {
                    
                    if(
    is_null($pRemove) || (!empty($pRemove) && !in_array($name,$pRemove))) {
                        
    $options[$index] = $this->_path[$index];
                    }
                    
                } else {
                
                    
    $options[$index] = '';
                
                }
            
            }
            
            return 
    preg_replace('/\/+$/','',$str.'/'.implode('/',$options).'/');
        
        }

    }
    Test Case:
    PHP Code:
    class ControllerOption {
        private 
    $data;
        public function 
    __construct($data) {
            
    $this->data $data;
        }
        
        public function 
    getProperty($name) {
            if(
    array_key_exists($name,$this->data)) {
                return 
    $this->data[$name];
            }
        }
    }

    $p = new Pathway(array('blog-entry','1','10'));
    $p->add(new ControllerOption(array('name'=>'id','path_index'=>1)));
    $p->add(new ControllerOption(array('name'=>'page','path_index'=>2)));

    // print url as is
    echo '<p>',$p->makeUrl(),'</p>';

    // change page
    echo '<p>',$p->makeUrl(array('page'=>5)),'</p>';

    // remove page
    echo '<p>',$p->makeUrl(array(),array('page')),'</p>';

    // change page and id
    echo '<p>',$p->makeUrl(array('page'=>1,'id'=>333)),'</p>'


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •