<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Dave Dash</title>
 <link href="http://davedash.com/tag/google/atom.xml" rel="self"/>
 <link href="http://davedash.com/tag/google"/>
 <updated>2012-04-07T22:42:44-07:00</updated>
 <id>http://davedash.com/</id>
 <author>
   <name>Dave Dash</name>
   <email>dd+atom1@davedash.com</email>
 </author>

 
 <entry>
   <title>Optimizing via YSlow</title>
   <link href="http://davedash.com/2009/03/22/optimizing-via-yslow/"/>
   <updated>2009-03-22T00:00:00-07:00</updated>
   <id>http://davedash.com/2009/03/22/optimizing-via-yslow</id>
   <content type="html">&lt;p&gt;I'm not a performance person per se.  I am a heuristics type of guy.  Meaning you tell me a bunch of things to look out for, I get an understanding for those things, and if I agree, I'll fix them.  I work off lists :)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/yslow/&quot;&gt;YSlow&lt;/a&gt; is therefore a boon to me.  If you aren't familiar with it, it is a plugin for Firefox (with a dependency on Firebug) that will grade the performance of a given site based on a set of rules determined by &lt;a href=&quot;http://developer.yahoo.com/&quot;&gt;YDN&lt;/a&gt;.  This is my list, and YDN went into great detail to explain each of these items is important.&lt;/p&gt;

&lt;p&gt;The following is my journey through optimization.  I went from a D- to a C in a single day.  While that might not seem like much, I could have nabbed an A if I used a CDN, like &lt;a href=&quot;http://aws.amazon.com/cloudfront/&quot;&gt;CloudFront&lt;/a&gt;, or even a &lt;a href=&quot;http://spindrop.us/2009/03/08/appengine-is-not-a-free-cdn/&quot;&gt;fake CDN&lt;/a&gt; and dropped Google AdSense and Analytics.  Those tools are helpful for me, and worth keeping around, so I opted to take the grade penalty.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;Begin: D - 61&lt;/h3&gt;

&lt;p&gt;I began with a D.  It's actually not that bad.  In most dimensions I was getting an A.  Namely because I use a web framework (Django) that is optimized for being cached and served well.&lt;/p&gt;

&lt;h3&gt;YUI Combo-loading: Sneaking up to D - 62&lt;/h3&gt;

&lt;p&gt;My first goal was to reduce the HTTP requests.&lt;/p&gt;

&lt;p&gt;I use the &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt; CSS and javascript framework as the foundation for my CSS and javascript.  One major benefit is that they offer to host this for you on a true &lt;acronym title=&quot;Content Delivery Network&quot;&gt;CDN&lt;/acronym&gt;.  The javascript framework is very thorough and can do quite a lot.  Because it's so robust, it is split into several files.  Originally I had been doing this:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;html&quot;&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/2.6.0/build/yahoo-dom-event/yahoo-dom-event.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/2.6.0/build/connection/connection-min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/2.6.0/build/datasource/datasource-min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/2.6.0/build/autocomplete/autocomplete-min.js&quot;&gt;&lt;/script&gt;

&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;That was 4 separate calls from the web browser to this server.  While they are all fast requests, it'd be better to make a single request that catches all of this.&lt;/p&gt;

&lt;p&gt;Luckily, YUI has &quot;combo-handling&quot;.  I was able to turn those four requests into the following:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;html&quot;&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/combo?2.7.0/build/yahoo-dom-event/yahoo-dom-event.js&amp;2.7.0/build/connection/connection-min.js&amp;2.7.0/build/datasource/datasource-min.js&amp;2.7.0/build/autocomplete/autocomplete-min.js&quot;&gt;&lt;/script&gt;
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;A very quick fix, that brought my overall grade up by a single point.&lt;/p&gt;

&lt;h3&gt;Concatenation and Minification: Instant C- - 72&lt;/h3&gt;

&lt;p&gt;I still had a large number of HTTP requests.  Luckily &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt; has the &lt;a href=&quot;http://developer.yahoo.com/yui/compressor/&quot;&gt;YUI Compressor&lt;/a&gt;.  It compresses javascript and CSS and even gives you tips on writing better code (or more compressible code).&lt;/p&gt;

&lt;p&gt;This used to be scary and not something I'd want to deal with, but if I can run it from the commandline, then I can add it to &lt;a href=&quot;http://spindrop.us/2009/03/02/a-stitch-in-fabric-saves-time/&quot;&gt;my deployment script&lt;/a&gt;.  Now on each deployment my CSS and javascript are concatenated and compressed and uploaded to my server.  When my app is in production mode, it seeks out minified code, versus more verbose human-readable code.&lt;/p&gt;

&lt;p&gt;The javascript compression was great.  The output was at least compressed in half.  This skyrocketed the score to 72.  Less HTTP requests and Javascript minification were the individual rules in YSlow that were boosted.&lt;/p&gt;

&lt;h3&gt;Google: They want you to fail&lt;/h3&gt;

&lt;p&gt;This is of course tongue-in-cheek.  Google has some wonderful tools like AdSense and Analytics that do hinder the score and ever so slightly the speed of your site.&lt;/p&gt;

&lt;p&gt;As far as javascript is concerned, I am linking to:
* YUI
* My own JS
* Google Analytics
* Google Adsense&lt;/p&gt;

&lt;p&gt;Google Adsense is by far the worst offender.  You think you're calling just one solitary call to Google to fetch an ad, but it's actually 4 calls.  I'm sure this could be engineered better.  The good news is it's usually not too slow.&lt;/p&gt;

&lt;p&gt;There's an option to self-serve &lt;code&gt;ga.js&lt;/code&gt; the Analytics code.  While this might result in a higher score, there are a few issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The process is more error prone, there's a chance that the file you download from Google might get corrupted and your unit tests might miss it.&lt;/li&gt;
&lt;li&gt;You are serving from your own servers - which could either be expensive or slower.&lt;/li&gt;
&lt;li&gt;Many sites include a call for &lt;code&gt;ga.js&lt;/code&gt; and therefore there's a high likelihood that it's already browser-cached.&lt;/li&gt;
&lt;li&gt;The code could get out of date.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;For this reason, it's better to lose about 5 points for not serving it.  This score is large because it effects a lot of YSlow's rulesets.  If you'd still like to self-serve here's &lt;a href=&quot;http://www.askapache.com/javascript/google-analytics-speed-tips.html&quot;&gt;some discussion on that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This will likely give you an &quot;F&quot; on the Expires Header ruleset as well.&lt;/p&gt;

&lt;h3&gt;Content Delivery Network (CDN)&lt;/h3&gt;

&lt;p&gt;A lot of people freak that CDN is on YSlow and feel like not all sites need/require a CDN.  They are right that the don't need a CDN, but in my opinion they don't necessarily need to get an &quot;A&quot; either.  All this is an approximation of doing the best you can do to make your frontend code as fast and efficient as possible.&lt;/p&gt;

&lt;p&gt;With that said, CDN's are becoming more and more in reach.  Amazon Cloud Files which &lt;a href=&quot;http://spindrop.us/2009/03/08/appengine-is-not-a-free-cdn/&quot;&gt;I mentioned&lt;/a&gt; is a pay as you go system, which is quite affordable (I estimated &amp;lt; $1/month for my needs).&lt;/p&gt;

&lt;p&gt;I configured YSlow to list some of the standard Google and Yahoo API hosts as CDNs.  This didn't affect my score, and I didn't want to integrate S3/Cloudfiles at this stage, so I took the &quot;F&quot;.&lt;/p&gt;

&lt;h3&gt;Gzip files (still at 72)&lt;/h3&gt;

&lt;p&gt;Adding Gzip to the Nginx configuration was easy, however my grade still stayed at 72, but it did up my score for that specific rule.  Gzip compression is supported in most modern browsers and is usually a config change away.&lt;/p&gt;

&lt;p&gt;Again, note that &lt;code&gt;ga.js&lt;/code&gt; is not served compressed.&lt;/p&gt;

&lt;h3&gt;Upgrading from &lt;code&gt;urchin.js&lt;/code&gt; (up to 73)&lt;/h3&gt;

&lt;p&gt;In the process of tweaking my Gzip configuration, I noticed that I was still using the legacy &lt;code&gt;urchin.js&lt;/code&gt; instead of the more modern &lt;code&gt;ga.js&lt;/code&gt;.  Simply changing this boosted me to 73.&lt;/p&gt;

&lt;h3&gt;Move Javascript to the bottom (still at 73)&lt;/h3&gt;

&lt;p&gt;Moving the javascript to the bottom is another trick.  This at least loads most of the HTML before the Javascript loads (and in many cases, waits).&lt;/p&gt;

&lt;p&gt;Some refactoring of Django templates makes this a breeze.&lt;/p&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;I was able to raise the score from a low D to a mid C, at some point I could make the score a high C, but it was inevitably not worth it.&lt;/p&gt;

&lt;p&gt;From my perspective, Google could do a few things to help webmasters who use their widgets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a decent expires header to &lt;code&gt;ga.js&lt;/code&gt; - this file is safe to cache for some degree of time.  Being at the latest and greatest &lt;code&gt;ga.js&lt;/code&gt; is nice, but not necessary.&lt;/li&gt;
&lt;li&gt;Serve files Gzipped&lt;/li&gt;
&lt;li&gt;Use a single host for serving public APIs.  Yahoo can improve on this as well.  They can potentially do combo-hosting a la Yahoo Developer Network&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Ultimately YSlow is a good guideline for speeding up your page render times.  It's not perfect, nor exact.  There's no realistic way to determine a users experience with page load times.  Slow network connectivity and bad browsers can make all your attempts in vain.  However, YSlow is an easy to follow set of heuristics which every site owner should attempt to implement as well as possible.&lt;/p&gt;

&lt;p&gt;YSlow's &lt;a href=&quot;http://www.slideshare.net/stoyan/yslow-20-presentation&quot;&gt;future incarnations&lt;/a&gt; will yield some more flexibility.  Even in its default state it should yield better scores for people who are doing the right thing.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Google Apps: In search of a worthy email system</title>
   <link href="http://davedash.com/2008/10/12/google-apps-in-search-of-a-worthy-email-system/"/>
   <updated>2008-10-12T00:00:00-07:00</updated>
   <id>http://davedash.com/2008/10/12/google-apps-in-search-of-a-worthy-email-system</id>
   <content type="html">&lt;p&gt;Email, in a lot of ways, is one of the most critical applications for me.&lt;/p&gt;

&lt;p&gt;There was a point in my life where I made sure all my email was kept together indefinitely, but as time grew, so did this task.&lt;/p&gt;

&lt;p&gt;For the longest time, I had my own hosted @davedash.com, @ftmax.com addresses.  These were hosted diligently at pair Networks.  pair introduced the qmail system which gave birth to my trend of using &quot;minus addressing&quot; (if your email is user@domain.com you can use user-&lt;em&gt;anything&lt;/em&gt;@domain.com).  It was my way of seeing which sites spammed me (so far I only outed one company using this trick) and just to organize my inbox.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;Gmail...&lt;/h3&gt;

&lt;p&gt;In 2004, I decided to experiment with Gmail.  I had a lot of email to manage and Gmail's ajax-enabled features made its webmail service far superior to any competitors and even made desktop apps cower in shame.&lt;/p&gt;

&lt;p&gt;The ideology of search don't sort, and archive don't delete, made Gmail a perfect storage system for all my emails.  I never had to lose track of email again.  Just about any conversation, or discussion I've had over email - I can pull up very quickly using Gmail.&lt;/p&gt;

&lt;p&gt;I'd say after 4 years of solid use, the experiment is over, I was sold on Gmail.&lt;/p&gt;

&lt;h3&gt;Switching hosts.&lt;/h3&gt;

&lt;p&gt;I did, however, move my domain management over to Dreamhost.  This is where it still resides, and that was a problem.  Minus addressing was not a part of Dreamhost.  I had to painstakingly create procmail files that simulated this.  Inevitably pushing anything and everything to my gmail account if it matched the syntax I chose.&lt;/p&gt;

&lt;p&gt;I knew Gmail supported plus addressing (user&lt;strong&gt;+&lt;/strong&gt;&lt;em&gt;anything&lt;/em&gt;@gmail.com), but I never took the plunge.  My habit, was and is still minus style.&lt;/p&gt;

&lt;h3&gt;Google Apps&lt;/h3&gt;

&lt;p&gt;The Dreamhost account I used has been tossed from one server to the next, unfortunately there's a lot of hand-holding I need to do in order to make sure procmail and all my other tweaks I've made to my accounts work.  I finally threw in the towel today and settled on Google Apps.&lt;/p&gt;

&lt;p&gt;Both my wife and I use Gmail (with aliased emails @katiebonn.com and @davedash.com respectively), so I enabled an app and did the painstaking &lt;a href=&quot;http://glomerate.wordpress.com/2008/08/20/migrating-from-gmail-to-google-apps/&quot;&gt;migration process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While it's not that complicated, you don't ever realize how entangled your Google account can be.  We both use GTalk and Google Calendar and we both have large Gmail accounts.  Mine has 70K+ addresses and is slowly being migrated to my Google Apps email address.&lt;/p&gt;

&lt;p&gt;In the end, I think this struggle will be worth it, since I'll have my addresses managed in a clean efficient way.&lt;/p&gt;

&lt;p&gt;This will also pave the path for me to move more of my web sites off of Dreamhost and onto Slicehost.&lt;/p&gt;

&lt;p&gt;Wish me luck.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Easy Yahoo! Maps and  GeoRSS with symfony</title>
   <link href="http://davedash.com/2006/04/26/easy_yahoo_maps_and_georss_with_symfony/"/>
   <updated>2006-04-26T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/04/26/easy_yahoo_maps_and_georss_with_symfony</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;GeoRSS&lt;/a&gt; is an extension of RSS that incorporates geographic data (i.e. latitude/longitude coordinates).  This is useful for plotting any data that might need to be placed on a map.&lt;/p&gt;

&lt;p&gt;While building out the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; map, I decided to use the &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps API&lt;/a&gt; versus the &lt;a href=&quot;http://www.google.com/apis/maps/&quot;&gt;Google Maps API&lt;/a&gt; because I wanted to gain some familiarity with another API.&lt;/p&gt;

&lt;p&gt;It was worth trying &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo!'s API&lt;/a&gt;.  First of all, &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; has addresses for restaurants and Yahoo! provides a simple &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Geocoding REST&lt;/a&gt; service.  This made it easy for me to convert street addresses to latitude and longitude pairs (even though this wasn't required as we'll soon see).&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;  The real selling point of &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo!&lt;/a&gt; was the &lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;GeoRSS&lt;/a&gt; functionality.  I can extend an RSS feed (which &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; generates quite easily) to add latitude or longitude points (or even the street address), direct my &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! map&lt;/a&gt; to the feed and voila, all the locations in that feed are now on the map, and when I click on them, the RSS item is displayed.  That cut down on a lot of development time.&lt;/p&gt;

&lt;!--break--&gt;


&lt;!--more--&gt;


&lt;h3&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;Yahoo's GeoRSS&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The GeoRSS format that Yahoo uses is fairly simple to grasp if you know what an RSS feed looks like.  Here's a typical RSS feed:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;rss version=&quot;2.0&quot;&amp;gt;
    &amp;lt;channel&amp;gt;
        &amp;lt;title&amp;gt;Latest restaurants&amp;lt;/title&amp;gt;
        &amp;lt;link&amp;gt;http://reviewsby.us/&amp;lt;/link&amp;gt;
        &amp;lt;description&amp;gt;A list of the latest restaurants posted to my reviewsby.us&amp;lt;/description&amp;gt;
        &amp;lt;language&amp;gt;en&amp;lt;/language&amp;gt;
        &amp;lt;item&amp;gt;
            &amp;lt;title&amp;gt;Bubba Gump Shrimp Co. Restaurant and Market&amp;lt;/title&amp;gt;
            &amp;lt;description&amp;gt;A *Forest Gump* themed restaurant.  Featuring a large selection of seafood items.&amp;lt;/description&amp;gt;
            &amp;lt;link&amp;gt;http://reviewsby.us/restaurant/bubba-gump-shrimp-co-restaurant-and-market&amp;lt;/link&amp;gt;
            &amp;lt;guid&amp;gt;25&amp;lt;/guid&amp;gt;
            &amp;lt;pubDate&amp;gt;Sun, 23 Apr 2006 08:04:00 -0700&amp;lt;/pubDate&amp;gt;
        &amp;lt;/item&amp;gt;
        &amp;lt;item&amp;gt;
            &amp;lt;title&amp;gt;Famous Dave's Barbeque&amp;lt;/title&amp;gt;
            &amp;lt;link&amp;gt;http://reviewsby.us/restaurant/famous-daves-barbeque&amp;lt;/link&amp;gt;
            &amp;lt;guid&amp;gt;24&amp;lt;/guid&amp;gt;
            &amp;lt;pubDate&amp;gt;Wed, 19 Apr 2006 19:58:08 -0700&amp;lt;/pubDate&amp;gt;
        &amp;lt;/item&amp;gt;
    &amp;lt;/channel&amp;gt;
&amp;lt;/rss&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;item /&amp;gt;&lt;/code&gt; in this example is a restaurant.  To turn this into a GeoRSS feed, we only need to change a few things:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;rss version=&quot;2.0&quot; xmlns:geo=&quot;http://www.w3.org/2003/01/geo/wgs84_pos#&quot;&amp;gt;
    &amp;lt;channel&amp;gt;
        &amp;lt;title&amp;gt;Latest restaurants' locations&amp;lt;/title&amp;gt;
        &amp;lt;link&amp;gt;http://reviewsby.us/&amp;lt;/link&amp;gt;
        &amp;lt;description&amp;gt;A geocoded list of the latest restaurants' locations posted to my reviewsby.us&amp;lt;/description&amp;gt;
        &amp;lt;language&amp;gt;en&amp;lt;/language&amp;gt;
        &amp;lt;item&amp;gt;
            &amp;lt;title&amp;gt;Bubba Gump Shrimp Co. Restaurant and Market (Mall of America (Bloomington, MN))&amp;lt;/title&amp;gt;
            &amp;lt;link&amp;gt;http://reviewsby.us/restaurant/bubba-gump-shrimp-co-restaurant-and-market/location/mall-of-america&amp;lt;/link&amp;gt;
            &amp;lt;guid&amp;gt;18&amp;lt;/guid&amp;gt;
            &amp;lt;pubDate&amp;gt;Sun, 23 Apr 2006 08:08:19 -0700&amp;lt;/pubDate&amp;gt;
            &amp;lt;geo:lat&amp;gt;44.85380173&amp;lt;/geo:lat&amp;gt;
            &amp;lt;geo:long&amp;gt;-93.24040222&amp;lt;/geo:long&amp;gt;
        &amp;lt;/item&amp;gt;
    &amp;lt;/channel&amp;gt;
&amp;lt;/rss&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We just added &lt;code&gt;xmlns:geo=&quot;http://www.w3.org/2003/01/geo/wgs84_pos#&quot;&lt;/code&gt; as an attribute to the &lt;code&gt;&amp;lt;rss/&amp;gt;&lt;/code&gt; tag and added &lt;code&gt;&amp;lt;geo:lat /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;geo:long /&amp;gt;&lt;/code&gt; tags to any given item.  If you'd rather use address, city, state and zip fields instead of latitude and longitude coordinates, &lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;the Yahoo! Georss page&lt;/a&gt; will tell you how.&lt;/p&gt;

&lt;h3&gt;&lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; and Geodata&lt;/h3&gt;

&lt;p&gt;I knew full well that &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps&lt;/a&gt; did not require me to have everything in latitude longitude coordinates, but I felt that from an efficiency standpoint, it made more sense for me to convert them once using a &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;geocoder&lt;/a&gt; and then Yahoo! wouldn't have to translate them later.  Also, I'm trying to think ahead when more than just Minneapolis restaurants become a part of the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; site, I now have an easy way of determining distance between a user's home and a given restaurant.  Also, if Yahoo! Maps doesn't work out, I can use these coordinates in other mapping systems.&lt;/p&gt;

&lt;h4&gt;Extending the model&lt;/h4&gt;

&lt;p&gt;Models in &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; lend themselves to being easily extended.  So we can easily take a model for a location:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;table name=&quot;location&quot; phpName=&quot;Location&quot;&amp;gt;
    &amp;lt;column name=&quot;id&quot; type=&quot;integer&quot; primaryKey=&quot;true&quot; autoIncrement=&quot;true&quot;/&amp;gt;
    &amp;lt;column name=&quot;restaurant_id&quot; type=&quot;integer&quot; /&amp;gt;
    &amp;lt;foreign-key foreignTable=&quot;restaurant&quot;&amp;gt;
        &amp;lt;reference local=&quot;restaurant_id&quot; foreign=&quot;id&quot;/&amp;gt;
    &amp;lt;/foreign-key&amp;gt;
    &amp;lt;column name=&quot;stripped_title&quot; type=&quot;varchar&quot; size=&quot;255&quot; /&amp;gt;
    &amp;lt;column name=&quot;name&quot; type=&quot;varchar&quot; size=&quot;255&quot; /&amp;gt;
    &amp;lt;column name=&quot;address&quot; type=&quot;varchar&quot; size=&quot;255&quot; /&amp;gt;
    &amp;lt;column name=&quot;city&quot; type=&quot;varchar&quot; size=&quot;128&quot; /&amp;gt;
    &amp;lt;column name=&quot;state&quot; type=&quot;varchar&quot; size=&quot;16&quot; /&amp;gt;
    &amp;lt;column name=&quot;zip&quot; type=&quot;varchar&quot; size=&quot;9&quot; /&amp;gt;
    &amp;lt;column name=&quot;phone&quot; type=&quot;varchar&quot; size=&quot;16&quot; /&amp;gt;
    &amp;lt;column name=&quot;approved&quot; type=&quot;boolean&quot; /&amp;gt;
    &amp;lt;column name=&quot;updated_at&quot; type=&quot;TIMESTAMP&quot; /&amp;gt;
    &amp;lt;column name=&quot;created_at&quot; type=&quot;TIMESTAMP&quot; /&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and simply add two columns inside the &lt;code&gt;&amp;lt;table/&amp;gt;&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;column name=&quot;latitude&quot; type=&quot;float&quot; size=&quot;10&quot; scale=&quot;8&quot;/&amp;gt;
&amp;lt;column name=&quot;longitude&quot; type=&quot;float&quot; size=&quot;10&quot; scale=&quot;8&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Easy!  However, we don't want to have to set the latitude and longitude by hand each time we update a &lt;code&gt;Location&lt;/code&gt;.  So first we write a function that takes an address and converts it to latitude/longitude.  I placed mine in a &lt;code&gt;myTools.class.php&lt;/code&gt; in my &lt;code&gt;lib&lt;/code&gt; folder:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class myTools
{
    public static function getLatLng($address, $city = null, $state = null, $zip = null)
    {
        $url = sfConfig::get('app_yahoo_geocode');
        $query['appid'] = sfConfig::get('app_yahoo_app_id');
        $query['street'] = $address;
        $query['city'] = $city;
        $query['state'] = $state;
        $query['zip'] = $zip;
        $query['output'] = 'php'; 
        $url .= '?' . http_build_query($query); 
        $response = @file_get_contents($url);
        if ($response) {
            $response = unserialize($response);
            return $response['ResultSet']['Result'];
        }
        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Defined in my &lt;code&gt;app.yml&lt;/code&gt; is &lt;code&gt;app_yahoo_geocode&lt;/code&gt; and my &lt;code&gt;app_yahoo_app_id&lt;/code&gt;.  &lt;code&gt;myTools::getLatLng()&lt;/code&gt; queries the &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Yahoo! REST&lt;/a&gt; service and returns the coordinates that Yahoo! delivers.  Note that the generated query string includes &lt;code&gt;output=php&lt;/code&gt;.  Yahoo! supports serializing output as PHP instead of XML.  This can save a bundle of time over decoding XML.&lt;/p&gt;

&lt;p&gt;So now let's look at our &lt;code&gt;Location.php&lt;/code&gt; and override its inherited save function:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function save($con = null)
{
    // save latitude and longitude
    $locdata = myTools::getLatLng($this-&amp;gt;getAddress(), $this-&amp;gt;getCity(), $this-&amp;gt;getState(), $this-&amp;gt;getZip());
    if ($locdata) 
    {
        $this-&amp;gt;setLatitude($locdata['Latitude']);
        $this-&amp;gt;setLongitude($locdata['Longitude']);
    }
    parent::save($con);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you stop here, you'll at least now know how to add latitude and longitude coordinates to an object automagically.&lt;/p&gt;

&lt;h4&gt;Producing a GeoRSS feed&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; very easily will allow you to &lt;a href=&quot;http://www.symfony-project.com/content/book/page/syndication.html&quot;&gt;generate an RSS feed&lt;/a&gt;.  How do we create a &lt;a href=&quot;http://developer.yahoo.com/maps/georss/index.html&quot;&gt;GeoRSS&lt;/a&gt; feed?  Just extend the &lt;code&gt;sfFeed&lt;/code&gt; class.  Rather than instantiating a feed like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$feed = sfFeed::newInstance('rss201rev2');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$feed = sfFeed::newInstance('geoRSS');  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then create an &lt;a href=&quot;http://spindrop.us/files/symfony/sfGeoRssFeed.class.txt&quot;&gt;sfGeoRssFeed.class.php&lt;/a&gt; and we're done.  We've created a GeoRSS feed fairly easily.  Comb through &lt;a href=&quot;http://spindrop.us/files/symfony/sfGeoRssFeed.class.txt&quot;&gt;sfGeoRssFeed.class.php&lt;/a&gt; and compare it to the &lt;a href=&quot;http://www.symfony-project.com/trac/browser/trunk/lib/symfony/addon/sfFeed/sfRss201rev2Feed.class.php?rev=403&quot;&gt;sfRss201rev2Feed.class.php&lt;/a&gt;, you'll notice it's not that different and that it's fairly easy to extend the sfFeed plugin for &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Adding your feed to a Yahoo! Map.&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/ajax/index.html#ex6&quot;&gt;Adding a GeoRSS feed to Yahoo! maps&lt;/a&gt; is simple.  Before I embedded the RSS feed into my Yahoo! Map I was prepared to write an algorithm to cluster only on the points in my RSS feed, lucky for me (and you), &lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps&lt;/a&gt; does this automatically.  One pitfall you might reach during development is that Yahoo! Maps must be able to reach your GeoRSS feed.  My development machine is my personal laptop, so this didn't work so well until I uploaded to a publically accessible staging server.  &lt;a href=&quot;http://reviewsby.us/&quot;&gt;The maps&lt;/a&gt; worked like a charm as you can see.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/maps/index.html&quot;&gt;Yahoo! Maps&lt;/a&gt; are very powerful, and &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; is up to the task.  I hope you found this tutorial useful.  If you have any trouble, let me know.  I hope your &lt;a href=&quot;http://reviewsby.us/&quot;&gt;next meal&lt;/a&gt; is a good one.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;
            &lt;p&gt;
                Using Yahoo! Maps wasn't a requisite of using the Yahoo! Geocoder.  
                The datasets that were returned could have been used to populate a Google Map just as easily.
                &lt;a href=&quot;#fnr1&quot;  class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;
            &lt;/p&gt;
        &lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 

</feed>

