0

The title might be misleading but I'm trying to do something very simple but cant figure it out.

Lets say I have a Question controller and show action and question id is the primary key with which I look up question details - so the URL looks like this

http://www.example.com/question/show/question_id/101

This works fine - So when the view is generated - the URL appears as shown above.

Now in the show action, what I want to do is, append the question title (which i get from database) to the URL - so when the view is generated - the URL shows up as

http://www.example.com/question/show/question_id/101/how-to-make-muffins

Its like on Stack overflow - if you take any question page - say

http://stackoverflow.com/questions/5451200/

and hit enter The question title gets appended to the url as

http://stackoverflow.com/questions/5451200/make-seo-sensitive-url-avoid-id-zend-framework

Thanks a lot

Gublooo
  • 2,550
  • 8
  • 54
  • 91

2 Answers2

2

You will have to add a custom route to your router, unless you can live with an url like:

www.example.com/question/show/question_id/101/{paramName}/how-to-make-muffins

You also, if you want to ensure that this parameter is always showing up, need to check if the parameter is set in the controller and issue a redirect if it is missing.

So, in your bootstrap file:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
  public function _initRoutes ()
  {
    // Ensure that the FrontController has been bootstrapped:
    $this->bootstrap('FrontController');
    $fc = $this->getResource('FrontController');
    /* @var $router Zend_Controller_Router_Rewrite */
    $router = $fc->getRouter();

    $router->addRoutes( array ( 
      'question' => new Zend_Controller_Router_Route (
        /* :controller and :action are special parameters, and corresponds to
         * the controller and action that will be executed.
         * We also say that we should have two additional parameters:
         * :question_id and :title. Finally, we say that anything else in
         * the url should be mapped by the standard {name}/{value}
         */
        ':controller/:action/:question_id/:title/*',
        // This argument provides the default values for the route. We want
        // to allow empty titles, so we set the default value to an empty
        // string
        array (
           'controller' => 'question',
           'action' => 'show',
           'title' => ''
        ),
        // This arguments contains the contraints for the route parameters.
        // In this case, we say that question_id must consist of 1 or more
        // digits and nothing else.
        array (
           'question_id' => '\d+'
        )
      )
    ));
  }
}

Now that you have this route, you can use it in your views like so:

<?php echo $this->url(
         array(
            'question_id' => $this->question['id'], 
            'title' => $this->question['title']
         ),
         'question'
      );
      // Will output something like: /question/show/123/my-question-title 
?>

In your controller, you need to ensure that the title-parameter is set, or redirect to itself with the title set if not:

public function showAction ()
{
  $question = $this->getQuestion($this->_getParam('question_id'));
  if(!$this->_getParam('title', false)) {
     $this->_helper->Redirector
        ->setCode(301) // Tell the client that this resource is permanently 
                       // residing under the full URL
        ->gotoRouteAndExit(
           array(
             'question_id' => $question['id'],
             'title' => $question['title']
           )
        );
  }
  [... Rest of your code ...]
}
PatrikAkerstrand
  • 45,315
  • 11
  • 79
  • 94
  • Thanks Patrik for the detailed information. I'm not sure about the change you've made to the controller. Although in all my views I'm setting the title as part of the URL - but its possible that a user might just type the URL as www.example.com/question/show/123 - without the title - so title is not required. But what I want is even if user types the URL above - when the page is rendered - the URL should have the title. Like in stackoverflow if you type http://stackoverflow.com/questions/5451200/ - the title gets appended automatically. So is that what u are doing in the controller here. – Gublooo Mar 07 '12 at 10:28
  • Yeah, the title is optional to provide, but if it is not provided, the controller does a 301 Permanent Redirect to the same url, but with the title appended. You must do a redirect, you can't change the appearance of the url. – PatrikAkerstrand Mar 07 '12 at 10:30
  • oh Thanks I just saw your reply - So when I add that code to the controller - i'm getting redirected to the homepage. – Gublooo Mar 07 '12 at 10:33
  • If a redirect is what is required to append the title - I can do it this way right - this works return $this->_redirect('/question/show/'.$question->question_id.'/'.preg_replace('/\s+/','-',$question->title)); – Gublooo Mar 07 '12 at 10:41
  • @Gublooo: Don't! It hard codes your url, which is what we try to avoid using routes. I made a mistake in my code which I have now corrected. The mistake was `$this->_helper->Redirector**()**`. Without the parenthesis, the Redirector helper is returned, and the rest of my code is correctly executed, automatically generating the correct url. The Redirector generates the route in much the same way as the Url-view helper. – PatrikAkerstrand Mar 07 '12 at 11:52
  • The preg_replace is a good idea however. If possible, consider adding a method on the question-object that encapsulates that logic. I.e, something along the lines of $question->getUrlTitle(), or $question->getSlug() – PatrikAkerstrand Mar 07 '12 at 11:54
  • Perfect makes sense. Thank you so much for assisting me thru this. This was my 3rd day trying to figure this out. Appreciate your help – Gublooo Mar 07 '12 at 13:22
0

This is done using a 301 redirect.

Fetch the question, filter out and/or replace URL-illegal characters, then construct the new URL. Pass it to the Redirector helper (in your controller: $this->_redirect($newURL);)

bububaba
  • 2,840
  • 6
  • 25
  • 29