301 redirects - the tricky ones

June 14, 2008 – 9:54 pm

Here’s a tip for all who inherited or upgraded an existing site, and have to retain some crucial URLs. I generally hate dealing with this stuff, because it feels like polluting a nice new shiny project with legacy code that fulfills no other purpose than make your site work with outdated systems.. such as, err… Google! ;)
But then again, at the end of the day sites are only useful if you can find the information you’re looking for, and if the whole world is linking to your site already, we have to use redirects to make sure visitors continue to find the content they expect.

So on with it! Needless to say, the place to put redirects is the .htaccess file at your web root.

Now, for static content, redirects are straightforward - you simply write:

redirect 301 -from -to -flags (pseudo code)

for example:

redirect 301 /register.php /customer/join

Now, where it gets tricky is dynamic urls. Say you have an existing site with a product catalog, and your URLs look something like this:

/products/detail.php?product_id=31

While it is possible to redirect this via the same 301 redirect we used above, you would have to keep working with the same query string format, as a normal redirect would only appends it to the new url, resulting in, say, /newscript.php?product_id=31. However, what we really want to do here, is turn this legacy url into a nice sexy new-school url, such as /products/detail/31. In order to do that, we can’t just use redirects, we gotta do some url rewriting by defining a rewrite rule. Rewrite rules - nomen est omen - take a url and reformat it according to certain rules.

The resulting code is:

RewriteCond %{QUERY_STRING} product_id=([0-9]+) [NC]
RewriteRule ^products/detail.php /products/detail/%1? [R=301, L]

What’s happening here? Basically, our rewrite condition checks a server variable for a pattern. Server variables that are made available by apache are accessed via %{VARIABLE_NAME_HERE}, and in this case the variable we want is QUERY_STRING, but different circumstances could call for others, such as HTTP_REFERER, REQUEST_METHOD, DOCUMENT_ROOT, etc.

For a great cheat sheet on all the variables and flags you can use, go to http://www.ilovejackdaniels.com/cheat-sheets/mod_rewrite-cheat-sheet. It’s one of the best cheat-sheets I’ve ever seen since college ;)

Next, the pattern we check QUERY_STRING for is product_id=([0-9]+), as product_id is the name of the GET parameter the site was using to determine which product to show. In this case we know that this id is numeric, so we’re using a pattern that matches 1 or more digits (+ for 1 or more). Only if this condition is met, the following rewrite rule applies. Also, the first matched pattern is stored in a temporary variable %1. For subsequent matches the variable name is %2, %3 etc..

The rewrite rule itself simply rewrites /products/details.php into /products/detail, but we still need to stick the id into the url, so we append the previously matched pattern from the rewrite condition %1 and get /products/detail/%1?

Note that a period (.) has a special meaning in patterns, so you need to escape it via to say you mean a literal period.

Also note the flags we set on the condition and rule. The [NC] stands for “no case,” or “caSeInseNsitiVe.” Not required in this case, but you never know..

The R=301 tells the requesting browser to interpret this redirect as a 301 permanent redirect, and the L states that this is the Last rule that should be applied to this url. This may or may not apply to your case.. nothing prevents you from applying further rules below, in which case you need to remove the L.

That’s it, good rewriting!


Filed under: Rnadom Sftuf — Tags: , , , , , , — by Richtermeister

Symfony - Returning content in default language, no matter what.

March 10, 2008 – 10:28 am

If you have even worked on an international site using Symfony, you’ll be familiar with the ease you can switch between languages on a model level. I’m loving this, because you don’t have to worry about it in the controller or the view, meaning you can easily add i18n dimensions to pretty much any application in one central place ( the propel builder classes ).

That being said, I found the default behavior insufficient for sites that don’t have the resources to translate absolutely everything. In many cases, our clients focus their translation efforts on the most important product lines, press releases, or event postings, and are happy to default back to English for everything else.
In some cases this means translating only selected fields, and this is where it gets tricky, because Symfony will only retrieve i18n content for the current culture, so leaving fields blank in a translation won’t prompt it to retrieve the default language instead (so far), so what we need here is a way to make every i18n accessor method smart enough to grab default content if the translation returns nothing.

Enter the propel model builder classes. Thanks to the pattern-al™ ;) foresight of the Symfony creators we have a central access point to all auto generated accessor methods via the sfObjectBuilder class in your Symfony/lib/addon/propel/builder directory, which we can extend and overwrite to meet our requirements.

As a result every i18n accessor method now tests whether there is a string to return, and, if not, the object switches its culture to default and queries again, switching back afterwards.

Now, I have to qualify that what I did here might not be the prettiest solution, and I’m not quite comfortable with the amount of parent code that I had to copy into the child class to make the overridden functions work, but it works, and if anybody feels like cleaning it up, be my guest and send me a copy.

A word about performance

The overall approach I took is clearly geared towards development speed, not neccessarily runtime efficiency, so if you output a long list of i18n objects, odds are that you’ll fire off a query for each individual one. Functionality-wise that works, and speed is actually surprisingly fast, but that’s considering a rather small database and your particular server might puke. Haven’t tested on large system, but I would believe caching would alleviate this.
Also, I would imagine that propel 1.3 could alleviate this via object cache, where we could run one query to retrieve all applicable i18n objects before we output the list, and all future references to i18n content would come from cache. Haven’t tried this yet.

How to use this:

Download and unzip this SFi18nFallbackObjectBuilder class into the Symfony/lib/addon/propel/builder directory and edit the config/propel.ini file to use this file for propel.builder.object.class (instead of sfObjectBuilder).

Then all you should have to do is rebuild the model and activate it in the configuration by adding

use_fallback: true

to your i18n.yml configuration (for all or just some environments). I added this configuration switch, so that you can turn this off for admin areas, where you would otherwise not have empty fields for translations.

That’s it, have fun, and let me know how I could improved this somehow.


Filed under: Symfony — Tags: , , — by Richtermeister

Symfony with php 5.0.1 bug - no “interface_exists()” function

March 2, 2008 – 2:00 pm

I came accross a bug in Symfony today when I installed a copy on our php 5.0.1 development server.

Turns out, php 5.0.1. doesn’t yet have the function “interface_exists()”, which was added in 5.0.2. Minor issue, but it IS being called as part of the Symfony bootstrapping procedure, so it crashes.

Luckily, in 5.0.1 the “class_exists()” method returns the same result, so putting the following lines into the app config.php file (or even into the symfony constants.php) should fix it:

if( !function_exists( 'interface_exists' ) ) {
function interface_exists( $name ) {
return class_exists( $name );
}
}

I say “should,” because in actuality this didn’t fix my problem (but it SHOULD, shouldn’t it? ;) ). So I tried simply returning false, and, well.. it works..

Let me know if you know why that is. For now I’m happy it works, and come next php version, I won’t have to worry about it..

Hope that helps someone out there who has the same problem.


Filed under: Symfony — by Richtermeister

Another Symphony on PHP 5.0.1 bug

– 1:17 pm

Found another interesting bug today when I deployed a symphony app on the “older” php version 5.0.1.

The bug itself has solely to do with php, and it’s already documented here and fixed in the current version, but basically what happened, is that I assigned a couple of methods to objects via the sfMixer class, and in one environment it worked, and in the other it didn’t (”call to undefined method xyz…” error ). Weird!

Turns out, the ReflectionClass::getMethod() method that Symfony uses as part of the sfMixer class, turns every string you pass into into lowercase.. BY REFERENCE!! So assigning a mixer method “getChildren” would actually be saved under “getchildren”, and wouldn’t be found in subsequent calls. What’s more, assigning the string to a “backup” variable to conserve it’s capitalization doesn’t help either, because then somehow both variables are affected. ( I didn’t know that string variable are passed by reference as well???)

Thanks to my smart boss we found a workaround, which is appending something to the copy of the string, and removing it later when you reassign it to the original variable.. I suggest the appendix “save_this_f%$#@#$_variable”. Worked for me! ;)


Filed under: Symfony — by Richtermeister

Outputting Data in Excel .xls format

January 22, 2008 – 5:51 pm

All clients love data! The only thing they like more than data is data in a spreadsheet, and that’s where we would be in all sorts of trouble if we had to rely on special export libraries that in turn have to keep track with the dataformat - whims of Microsoft. Thankfully there’s a more durable solution, so your application will still be happily exporting in the future.

So here’s how you can quickly spit out a table for Excel:

(more…)


Filed under: Rnadom Sftuf — by Richtermeister

PHP Short Opening Tag not supported… null problemo!

January 20, 2008 – 9:28 pm

Here’s another config - particularity of PHP, right up there with my beloved register_globals run-ins and $_REQUEST access issues..
Recently I’ve had to deploy a site on a server that had short opening tags disabled, and no access to the php.ini. Wow, didn’t see that coming.

Now, for my scripts that didn’t really mean a thing, since I habitually start a multi-line php code block with <?php, however, I do like using <?= within my “templates” (or view files), rather than <?php echo, because, well it keeps things “template-ier” IMHO. Almost as short as the smarty tag, eh? ;)

So what to do avoid string replacing the hell out of your front-end?

(more…)


Filed under: Rnadom Sftuf — by Richtermeister

XAMPP all-in-one dev environment

January 19, 2008 – 1:05 pm

For everyone looking for a clean & simple Apache + php + mySQL development environment for their local machine, XAMP is an all-in-one solution that installs in seconds. I use it on my Windows XP laptop, but it’s also available for Linux and Mac OS.. plus, it’s free. ( get it here )

Here’s how you go about running multiple sites on your local machine (and this might be common knowledge, but it took me a little research to find how to do it..):

(more…)


Filed under: Php - Tools, Toolz that Rulez — by Richtermeister

Updated jQuery Niceforms

January 18, 2008 – 5:50 pm

I may be wrong, but there really only seems to be one “niceforms” plugin available for JS (this one by BadBoy), and I was so happy to see that it’s been ported to jQuery style by Lucian Lature (get it here).. Recently I’ve used it on a project, and it turned out that it breaks under certain conditions:

  • only works properly for one form on the page (if used on more than one form, selects reference the wrong elements)
  • the “onChange” event disappeared

Since the client already signed off on the design, I tried fixing it, and heeey, it actually works! Based on the fact that I only had to change about 3 lines, I’d say it’s probably right ;) . (See: The implicit rightness of elegant code)

Here’s the updated version: (Updated Niceforms for jQuery)

Changes: “niceform” class should now be attached to a container (div, for example) that the form resides in. Keep in mind that there might still be broken functionality under the hood. I only fixed it to the point where I could use it for my project.

Enjoy

PS: Post a comment if you find a newer / better version of this plugin.


Filed under: JavaScript Tools, Rnadom Sftuf, Toolz that Rulez — Tags: , , — by Richtermeister

FileSystem Package

– 5:46 pm

This package provides functionality to manipulate files on the server. This includes type and size validation, uploading, image resizing, MP3 - ID3 tag parsing, etc..

The need for this package really came up around the third time I had to accept file uploads from a site… first, there is a lot of steps you have to take that the file is actually there and valid (depending on varying criteria), and then I could never remember the proper syntax for moving files. This doesn’t even include validating target destinations and creating directories.. Pretty annoying!

So as you can see, plenty of opportunity for encapsulation. Enter the FileSystem Package…

(more…)


Filed under: Packages — Tags: , , , , , — by Richtermeister
« Newer Posts

Powered by WordPress