RSS
 

Posts Tagged ‘redirect’

Forms and redirecting

20 Aug

Redirecting in your CakePHP app

My CommonComponent now contains three redirecting methods:

/**
	 * @param mixed $url
	 * @param bool $useReferer
	 * returns nothing and automatically redirects
	 * 2010-11-06 ms
	 */
	public function autoRedirect($whereTo, $useReferer = true) {
		if ($useReferer && $this->Controller->referer() != '/' . $this->Controller->params['url']['url']) {
			$this->Controller->redirect($this->Controller->referer($whereTo, true));
		} else {
			$this->Controller->redirect($whereTo);
		}
	}
 
	/**
	 * should be a 303, but:
	 * Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.
	 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
	 * @param mixed $url
	 * TODO: change to 303 with backwardscompatability for older browsers?
	 * 2011-06-14 ms
	 */
	public function postRedirect($whereTo, $status = 302) {
		$this->Controller->redirect($whereTo, $status);
	}
 
	/**
	 * only redirect to itself if cookies are on
	 * prevents problems with lost data
	 * Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.
	 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
	 * TODO: change to 303 with backwardscompatability for older browsers?
	 * 2011-08-10 ms
	 */
	public function prgRedirect($status = 302) {
		if (!empty($_COOKIE[Configure::read('Session.cookie')])) {
			$this->Controller->redirect('/'.$this->Controller->params['url']['url'], $status);
		}
	}

autoRedirect and postRedirect

Where do I use it? In pretty much every form (for instance edit action):

if (empty($id) || !($user = $this->User->find('first', array('conditions'=>array('User.id'=>$id))))) {
	$this->Common->flashMessage(__('invalid record', true), 'error');
	$this->Common->autoRedirect(array('action' => 'index'));
}
if (!empty($this->data)) {
	if ($this->User->save($this->data)) {
		$var = $this->data['User']['id'];
		$this->Common->flashMessage(sprintf(__('record edit %s saved', true), h($var)), 'success');
		$this->Common->postRedirect(array('action' => 'index'));
	} else {
		$this->Common->flashMessage(__('formContainsErrors', true), 'error');
	}
}
if (empty($this->data)) {
	$this->data = $user;
}

The autoRedirect automatically redirects back to the site the user came from if possible (if the user clicked a link). Otherwise it will use the provided fallback url.
The postRedirect is a wrapper for the future where one day 303 can be used without causing trouble (read further for details).

prgRedirect

Why is this necessary in some forms? Most search forms do a simply post. What they should do is a POST + GET afterwards. Thats called PRG pattern and is described here.
Especially after posting search forms or entering data you want to avoid a nasty message like some modern browsers produce if you then hit the back button. You want to graciously display the page prior to the post. Thats where this extra redirect comes into play. Always redirect after a post – quite easy to remember.

if (!empty($this->data)) {
	if ($this->Model->search($this->data)) {
		# save POST search to session, redirect and display the search result as GET
		$this->Common->prgRedirect();
	}
}
if ($search = $this->Session->read(...)) {
	...
}

As the comment in the method head as well as other sources explain one should NOT use 303 or you end up with broken forms for some users.

Note the failsafe with the cookie. If cookies are disabled this would result in empty sessions and therefore never work. For disabled cookies there cannot be a redirect and therefore needs the POST to display the data.
Therefore your forms should work with both POST and GET for this very same reason. The “prg” redirect is only an enhancement to provide better functionality in the normal use case (where users do use the back button).

Moved/deleted content

If you moved or deleted some content you can use the 301 redirect to tell search engines and browsers where to find the same content at the new location or where to go to instead:

if (empty($manufacturer)) {
	$this->Session->setFlash(__('Invalid Manufacturer', true));
	$this->redirect(array('action'=>'index'), 301);
}

In the example I use this to redirect from views back to index if no manufacturer (retrieved via slug) was found. This is necessary to avoid duplicate content for invalid slugs.

Complete list of browsers that are not capable of handling 303s

//TODO – does anyone have infos on that matter?

 
No Comments

Posted in CakePHP

 

Redirect Root Domain to WWW Subdomain

13 Jul

The Problem

“When you have two different addresses pointing to the same page, like www.example.com/offers.html and example.com/offers.html, many search engines (or so we are led to believe) will treat those two URLs as two separate pages. When you, as a human, see those two pages and notice they are identical, you will automatically realise (correctly) that they are actually the same page. Apparently, the search engines do not make this assumption, and will regard those as different pages with duplicate content.”
(source: www.thesitewizard.com)

I personally think that apart from the SEO problem there shouldn’t be two different urls to the same content in the first place.

So what do we do?

The bad thing to do would be to disable one of the domains (root or www).
We want to select one (www.example.com) as default domain and if someone just enters “example.com” he will be automatically redirected to our default domain.
Again – the bad thing would be to use meta redirects. We want to use so called permanent redirects ( code 301) in order to notify search engines and browsers about the reason we want to redirect.

Using Mod Rewrite

For the cake app to be available per “www.example.com” (and a 301 redirect from mydomain.de) you just need to modify the htaccess file in the /app/webroot/ folder:

<IfModule mod_rewrite.c>
	RewriteEngine On
 
	RewriteCond %{HTTP_HOST} !^www\. [NC]
	RewriteCond %{HTTP_HOST} !^localhost [NC]
	RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
 
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

The “localhost” part is not necessary – but it prevents your local htaccess file to redirect on your development computer. That is if you have your htaccess file in svn or git and therefore both versions (local and stage) are the same.

The reason why i did not lay out the other direction (www to root) is simple: I don’t think this makes sense. Most people not so comfortable with the internet usually type the complete address anyway – they just expect a “www” in front of the domain for the main website.
And with the internet getting more and more complex, some guidelines should just remain. One is that you don’t use root domains for websites. (I could go into details here – about other side effects like “technical” cookie problems occuring with root domain sites etc – but i will leave that out for now)

Using Apache Directives

This is way faster because the server doesn’t have to open the htaccess files for it. It can directly use what it has to have available anyway.

Inside /etc/apache2/sites-available/ there should be your domain file.
Between <Directory> and </Directory> you can add the above lines.
To actually increase the performance we now need to disable htaccess files for this domain.
Add AllowOverride None directly above your new lines. This prevents the server from looking for, opening and processing htaccess files.

Note: Usually this is only available on root servers.