<?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/programming/atom.xml" rel="self"/>
 <link href="http://davedash.com/tag/programming"/>
 <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>tunneling and mysql</title>
   <link href="http://davedash.com/2008/10/22/tunneling-and-mysql/"/>
   <updated>2008-10-22T00:00:00-07:00</updated>
   <id>http://davedash.com/2008/10/22/tunneling-and-mysql</id>
   <content type="html">&lt;p&gt;I have a mysql server for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; that requires tunneling, and after establishing a tunnel like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;deveshistan:~ dash* ssh user@host -L3307:database_server:3306
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I thought I could just do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;mysql -u mysql_user -P3307 -h localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But I was wrong, &lt;code&gt;localhost&lt;/code&gt; should be &lt;code&gt;127.0.0.1&lt;/code&gt;, otherwise mysql will do things in socket mode:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;mysql -u mysql_user -P3307 -h 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>Why code rewrites are often coupled with redesigns</title>
   <link href="http://davedash.com/2008/08/13/why-code-rewrites-are-often-coupled-with-redesigns/"/>
   <updated>2008-08-13T00:00:00-07:00</updated>
   <id>http://davedash.com/2008/08/13/why-code-rewrites-are-often-coupled-with-redesigns</id>
   <content type="html">&lt;p&gt;I've done some rewrites of code, and they usually are coupled with redesigns.&lt;/p&gt;

&lt;p&gt;Redesigns and rewrites are tricky.  With web sites existing users tend to prefer incremental changes with each.  Changing a design element or a feature are pretty much equivalent when its done incrementally.&lt;/p&gt;

&lt;p&gt;Usually small changes are like surgery.  You make a small change, you possibly announce it, and people look at it and usually say yes, this change is better.  But mostly a small change can go unnoticed.&lt;/p&gt;

&lt;p&gt;Tiny atomic changes are nice and they improve the product.&lt;/p&gt;

&lt;p&gt;A rewrite of code however is a discrete change.  They take forever.  Second system and all that.  They inevitably involve a redesign as well.  They tend to be received with mixed reviews.&lt;/p&gt;

&lt;p&gt;Generally a design of a site is grafted onto code.  Sometimes by templating languages, sometimes in a tightly coupled system.  In either case there's usually two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make a faithful port of the design.&lt;/li&gt;
&lt;li&gt;Redesign&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;The latter is generally preferred since this saves a lot of time.  Generally there are bugs with a design, and there is a design backlog.   A faithful port of the design would mean porting every broken HTML code, every non user-friendly element, etc.&lt;/p&gt;

&lt;p&gt;Unfortunately this leaves web developers with a scary situation: Launching a product which changes peoples functional and UI experience.&lt;/p&gt;

&lt;p&gt;The good thing is redesigns usually mean evolutionary leaps.  Meaning, iterations can happen faster, both in design and functionality.  We can easily please new customers, and we can quickly bring current customers up to speed.&lt;/p&gt;

&lt;p&gt;In my spare time, I've been rewriting code that I wrote in symfony (PHP) into django (python).  It's a tiresome long process that can easily kill a side project.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>postgresql and python in osx</title>
   <link href="http://davedash.com/2008/08/10/postgresql-and-python-in-osx/"/>
   <updated>2008-08-10T00:00:00-07:00</updated>
   <id>http://davedash.com/2008/08/10/postgresql-and-python-in-osx</id>
   <content type="html">&lt;p&gt;I want to start dabbling with postgreSQL on OS X.  After several SVN checkouts, binary packages, etc, I've realize the easiest path to success is just installing from fink unstable.&lt;/p&gt;

&lt;p&gt;Running:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;fink install psycopg2-py2.5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will get you everything you need, &lt;code&gt;python&lt;/code&gt;, the &lt;code&gt;pyscopg2&lt;/code&gt; driver and even &lt;code&gt;postgresql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately I had MacPython binary and a postgres binary which I couldn't easily install (and if anybody knows how to uninstall at least the former, I'm all ears).  I decided to just merge the site-packages for both the fink installed python2.5 and the MacPython:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rm -rf /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/
$ ln -s /sw/lib/python2.5/site-packages /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now if I installed more python packages via fink they will work in MacPython.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Optimization heuristics</title>
   <link href="http://davedash.com/2008/02/26/optimization-heuristics/"/>
   <updated>2008-02-26T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/26/optimization-heuristics</id>
   <content type="html">&lt;p&gt;I decided to play version control private-eye today when my coworker mentioned that we make a system call to check the time several times per request on a few specific pages.&lt;/p&gt;

&lt;p&gt;My analysis was we didn't need to have the calls but it had me thinking...&lt;/p&gt;

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


&lt;h3&gt;The micro-caching heuristic&lt;/h3&gt;

&lt;p&gt;One way of optimizing code is by calculating expensive operations once and storing them in a specific location.  It's a micro-cache if you will of data.&lt;/p&gt;

&lt;p&gt;One obvious example ends up being loops:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
// inefficient
for ($i = 0; $i &lt; count($arr); $i++)
{
// magic
}

// efficient
$arr_size = count($arr);
for ($i = 0; $i &lt; $arr_size; $i++)
{
// magic
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The inefficient example computes &lt;code&gt;count($arr)&lt;/code&gt; &lt;em&gt;n&lt;/em&gt; times where &lt;em&gt;n&lt;/em&gt; is the size of the array &lt;code&gt;$arr&lt;/code&gt;.  The latter computes it once.&lt;/p&gt;

&lt;p&gt;When we write and review code we should look out for loops and repeated code.  These elements can be pulled out.&lt;/p&gt;

&lt;p&gt;With some data lookups like system calls or queries we can apply a request-wide caching.  In a symfony app, for example, we might need to calculate the time stamp in several locations throughout the code.  Rather than calling &lt;code&gt;time()&lt;/code&gt; throughout the code, we can create a site wide accessible attribute that has the same value.&lt;/p&gt;

&lt;p&gt;For example, we can subclass &lt;code&gt;sfContext&lt;/code&gt; and add a method called &lt;code&gt;getTime()&lt;/code&gt; which runs &lt;code&gt;time()&lt;/code&gt; once and only once per request and stores it internally.  Whenever part of the app needs the time stamp, &lt;code&gt;myContext::getInstance()-&amp;gt;getTime()&lt;/code&gt; (or an equivalent shortcut) can be called.  Of course, I haven't tested this, and &lt;code&gt;myContext&lt;/code&gt; might not be the best place for this, but a similar strategy will work.&lt;/p&gt;

&lt;p&gt;-dd&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to code... or do anything.</title>
   <link href="http://davedash.com/2008/02/01/how-to-code-or-do-anything/"/>
   <updated>2008-02-01T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/01/how-to-code-or-do-anything</id>
   <content type="html">&lt;p&gt;Imagine the most ideal way for something to work, assume that it's going to work, and then make it work like that.&lt;/p&gt;

&lt;p&gt;It's amazing how much bad coupled code exists for no good reason.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to code... or do anything.</title>
   <link href="http://davedash.com/2008/02/01/how-to-code-or-do-anything/"/>
   <updated>2008-02-01T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/02/01/how-to-code-or-do-anything</id>
   <content type="html">&lt;p&gt;Imagine the most ideal way for something to work, assume that it's going to work, and then make it work like that.&lt;/p&gt;

&lt;p&gt;It's amazing how much bad coupled code exists for no good reason.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>reusability</title>
   <link href="http://davedash.com/2008/01/30/reusability/"/>
   <updated>2008-01-30T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/30/reusability</id>
   <content type="html">&lt;p&gt;[tags]django, plugins, apps, projects, symfony[/tags]&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;A project is a collection of settings for an instance of Django, including database configuration, Django-specific options, and application-specific settings.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.djangobook.com/en/1.0/chapter02/&quot;&gt;The Django Book, Chapter 2&lt;/a&gt;:&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;A few people have been asking for more comparisons between symfony and Django.  For me it's a great way of understanding Django and python as well as symfony and PHP.&lt;/p&gt;

&lt;p&gt;Reusability is at the core of Django, not an afterthought.  The only unique part of an app is the settings and the views.  Everything else is an application that can exist independently of your app.  It's nice and decoupled.&lt;/p&gt;

&lt;p&gt;This wouldn't be impossible to do in symfony.  Each module could be designed from the start as a plugin.  Complete with its own set of models and default templates.  The configuration of a project/app could then make the web app unique.&lt;/p&gt;

&lt;p&gt;Right now the bulk of my symfony models are tightly coupled to their apps.  It's a little confusing, but there isn't a direct correlation between Django projects, Django apps and symfony Projects, apps and modules.  Each kind of overlaps one another.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Templating</title>
   <link href="http://davedash.com/2008/01/28/templating/"/>
   <updated>2008-01-28T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/28/templating</id>
   <content type="html">&lt;p&gt;[tags]smarty, symfony, php, python, django[/tags]&lt;/p&gt;

&lt;p&gt;I used to use the Smarty templating system quite heavily in my PHP apps.  Then after switching over to &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, I broke that habit.&lt;/p&gt;

&lt;p&gt;PHP &lt;em&gt;is&lt;/em&gt; a templating language for better or worse.  Smarty was a way of saying that &quot;well things could be easier&quot;, but that's against the grain of what PHP fundamentally is.&lt;/p&gt;

&lt;p&gt;One frustration of PHP is this.  A &quot;pure&quot; PHP file with no HTML looks like this:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    &lt;?php
    
    // code goes here
    // ...
    
    // end of file
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;PHP is an HTML templating system, by default everything in PHP is just stuff waiting to be sent untouched to the browser.  So in essence, PHP web apps aren't really web apps... they are very complicated templates all talking to each other.&lt;/p&gt;

&lt;p&gt;So you'll note in the above example, even a pure PHP file must begin with &lt;code&gt;&amp;lt;?php&lt;/code&gt;, and signal that we're actually doing code.  In fact, to prevent any unintended whitespace being sent to the browser, a PHP only file will by convention omit the closing &lt;code&gt;?&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So my shiny new Django framework is a breath of fresh air.  There's no &lt;code&gt;&amp;lt;?py&lt;/code&gt; tags, it's all just python.  And template files are explicitly template files parsed by a template engine built on top of a scripting language.&lt;/p&gt;

&lt;p&gt;I can easily see PHP evolving into something similar one day.  Where all PHP files are just PHP by default.  It would be more in line with the way MVC development is going.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>YUI Autocomplete the easy way</title>
   <link href="http://davedash.com/2008/01/27/yui-autocomplete-the-easy-way/"/>
   <updated>2008-01-27T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/27/yui-autocomplete-the-easy-way</id>
   <content type="html">&lt;p&gt;[tags]yui, autocomplete, javascript, jquery, symfony[/tags]&lt;/p&gt;

&lt;p&gt;I'm redoing the UI for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; and for the Javascript library I am standardizing on is &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt;.  The trick with &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt; is that it's very verbose and very configurable.&lt;/p&gt;

&lt;p&gt;Unfortunately to do simple things you have to write quite a lot.  Which, coming from a jQuery background, is not what I'm used to.  Yahoo provides a lot of useful examples for Auto Complete, I'll provide you with another (built in &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, but anything in any server-side language will work).  You'll need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A data source&lt;/li&gt;
&lt;li&gt;Some YUI&lt;/li&gt;
&lt;li&gt;Some javascript of your own&lt;/li&gt;
&lt;li&gt;And some HTML&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;We're building a tool to grab random items from our database and put them into our Autocomplete field.  We also want to capture and id of these items and set them in our form.&lt;/p&gt;

&lt;p&gt;Here we go...&lt;/p&gt;

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


&lt;h3&gt;Data Source&lt;/h3&gt;

&lt;p&gt;YUI gives you a lot of options with data sources.  In many cases a remote script that returns JSON will be best.  JSON is nice, compact, human debuggable and machine-readable.&lt;/p&gt;

&lt;p&gt;We can make a &lt;code&gt;searchJSON&lt;/code&gt; method that simply takes your database results and wraps them in to a formatted array and then using PHP's &lt;code&gt;json_encode&lt;/code&gt; we can return a nice JSON object.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public static function searchJSON($phrase, $exact = false, $offset = 0, $max = 10)
    {
        $rs = self::doSearch($phrase, $exact, $offset, $max);

        // Manage the results
        $restaurants = array();
        while ($rs-&gt;next())
        {
          $r                           = self::retrieveByPK($rs-&gt;getInt(1));
            $objs[] = array('Id'=&gt;$rs-&gt;getInt(1), 'Title'=&gt;$r-&gt;getName());
        }

    return json_encode(array('ResultSet' =&gt; array(&quot;Result&quot; =&gt; $objs)));
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;ResultSet&lt;/code&gt; and &lt;code&gt;Result&lt;/code&gt; keys are important as we'll see later.&lt;/p&gt;

&lt;p&gt;We can serve this JSON method very easily in symfony like so:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
  public function executeAjaxList()
  {
    $q    = $this-&gt;getRequestParameter('query');
    $objs = ObjPeer::searchJSON($q);
    return $this-&gt;renderText($objs);
  }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Note that we use &lt;code&gt;renderText&lt;/code&gt; rather than a separate &lt;code&gt;ajaxListSuccess.php&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;YUI&lt;/h3&gt;

&lt;p&gt;There's some &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt; code that you need.  It's described well in the &lt;a href=&quot;http://developer.yahoo.com/yui/autocomplete/&quot;&gt;Autocomplete documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add the following to your page:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;http://yui.yahooapis.com/2.4.1/build/yahoo-dom-event/yahoo-dom-event.js&quot; type=&quot;text/javascript
http://yui.yahooapis.com/2.4.1/build/connection/connection-min.js type=&quot;text/javascript
http://yui.yahooapis.com/2.4.1/build/autocomplete/autocomplete-min.js
http://yui.yahooapis.com/2.4.1/build/autocomplete/assets/skins/sam/autocomplete.css
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These are the bare YUI requirements to get Autocomplete working.&lt;/p&gt;

&lt;h3&gt;Javascript&lt;/h3&gt;

&lt;p&gt;There's a good deal of Javascript that you'll need to provide on your own.  Here's what I wrote:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;javascript&quot;&gt;
    var MA = {};
    MA.autocomplete = function()
    {
      var e = YAHOO.util.Event;
      var w = YAHOO.widget;
  
      return {
        init: function()
        {
           e.onAvailable(&quot;myInput&quot;, this.fnHandler);
        },
        fnHandler: function()
        {
          var rDS = new w.DS_XHR(&quot;/ajax/object/list&quot;, [&quot;ResultSet.Result&quot;,&quot;Title&quot;]);

          rDS.maxCacheEntries    = 60; 
          rDS.queryMatchContains = true;

          var rAC = new w.AutoComplete(&quot;myInput&quot;,&quot;myACContainer&quot;, rDS); 

          rAC.formatResult = function(item, query) 
          {
            return item[1].Title;
          };
      
          rAC.forceSelection           = true; 
          rAC.allowBrowserAutocomplete = false; 
      
          rAC.itemSelectEvent.subscribe(
            function(sType, aArgs) 
            { 
              var data = aArgs[2];
              document.getElementById(&quot;object_id&quot;).value = aArgs[2][1]['Id'];
            }
          ); 
        },
    
      }
    }();

    MA.autocomplete.init();

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


&lt;p&gt;&lt;code&gt;MA&lt;/code&gt; is my own private namespace.  &lt;code&gt;/ajax/object/list&lt;/code&gt; is the url of our data source we defined earlier.  &lt;code&gt;myInput&lt;/code&gt;, &lt;code&gt;myACContainer&lt;/code&gt; and &lt;code&gt;object_id&lt;/code&gt; are all IDs of elements in our DOM which we'll look at next.&lt;/p&gt;

&lt;h3&gt;HTML&lt;/h3&gt;

&lt;p&gt;Okay I went through this backwards, now we have the HTML.&lt;/p&gt;

&lt;p&gt;This is all you need:&lt;/p&gt;

&lt;div&gt;
&lt;textarea name=&quot;code&quot; class=&quot;html&quot;&gt;
    &lt;div id=&quot;myAutoComplete&quot;&gt;
      &lt;?php echo input_tag('myInput',null,'class=text') ?&gt;
      &lt;div id=&quot;myACContainer&quot;&gt;&lt;/div&gt;
      &lt;?php echo input_hidden_tag('object_id') ?&gt;
    &lt;/div&gt;
&lt;/textarea&gt;
&lt;/div&gt;


&lt;h3&gt;The End&lt;/h3&gt;

&lt;p&gt;That's it.  It might seem like a lot, but &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt; offers a tested working solution that works and is very customizable.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>python: relative paths</title>
   <link href="http://davedash.com/2008/01/27/python-relative-paths/"/>
   <updated>2008-01-27T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/27/python-relative-paths</id>
   <content type="html">&lt;p&gt;So I started yesterday with Django, and I decided I didn't want to futz with creating another mysql database that I'd need to manage, etc.  Instead I'll just use &lt;code&gt;sqlite&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to keep my &lt;code&gt;sqlite&lt;/code&gt; database within my project regardless of where I might move my project later.  So I did this:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
    import os        
    DATABASE_NAME = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/db.sqlite')        # Or path to database file if using sqlite3.
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;I confused a lot of people on IRC, but it's really quite easy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;__file__&lt;/code&gt; is the filename of the current script, very similar to PHP's &lt;code&gt;__FILE__&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;os.path.abspath&lt;/code&gt; calculates the absolute path, hence the absolute path of the current file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;os.path.join&lt;/code&gt; does all the nasty business of joining paths together and figuring out what type of slashes are needed, etc.&lt;/li&gt;
&lt;li&gt;'data/db.sqlite' is a string&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So really all we were doing is creating a relative path, but setting it absolutely.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>GeoDjango</title>
   <link href="http://davedash.com/2008/01/27/geodjango/"/>
   <updated>2008-01-27T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/27/geodjango</id>
   <content type="html">&lt;p&gt;[tags]geodjango, django, gis[/tags]&lt;/p&gt;

&lt;p&gt;I just discovered &lt;a href=&quot;http://code.djangoproject.com/wiki/GeoDjango&quot;&gt;GeoDjango&lt;/a&gt;.  The existence of this is awesome.&lt;/p&gt;

&lt;p&gt;The GIS layer of &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; is incredibly complicated, and if I do another project of that magnitude, it'll be nice to know that a framework exists to tackle problems.  It looks functional, but not quite documentation complete for usage, but if I endeavor to learn more about GIS, I'm going to dive-in.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>django experiment: Day 1 and Model Inheritence</title>
   <link href="http://davedash.com/2008/01/27/django-experiment-day-1-and-model-inheritence/"/>
   <updated>2008-01-27T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/27/django-experiment-day-1-and-model-inheritence</id>
   <content type="html">&lt;p&gt;[tags]django, inheritance[/tags]&lt;/p&gt;

&lt;p&gt;I spent a few hours diving into Django.  I knew enough python to get around, and also to be dangerous.&lt;/p&gt;

&lt;p&gt;Here's a summary of what I've learned.&lt;/p&gt;

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


&lt;h3&gt;Model Inheritence&lt;/h3&gt;

&lt;p&gt;So far I've learned that &lt;a href=&quot;http://code.djangoproject.com/wiki/ModelInheritance&quot;&gt;ModelInheritence is not quite ready&lt;/a&gt;. In other words, you can't define in your &lt;code&gt;models.py&lt;/code&gt; something like this:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;python&quot;&gt;
from django.db import models

class ContentZone(models.Model):
  slug     = models.SlugField()
  content  = models.TextField()
  notes    = models.CharField(max_length=200)
  pub_date = models.DateTimeField('date published')
  def __unicode__(self):
    return self.slug

class Page(ContentZone):
  title    = models.CharField(max_length=200)
  def __unicode__(self):
    return self.title
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The model itself will work, and it will save, but things like &lt;code&gt;Page.objects.all()&lt;/code&gt; will return empty lists.&lt;/p&gt;

&lt;h3&gt;Where I'm still shaky&lt;/h3&gt;

&lt;p&gt;I...
* still have to learn how to create templates.
* want better understanding of the available field types.
* want to find a good CMS plugin
* want to know if there's the idea of environments in Django (like in symfony)
* want to override &lt;code&gt;.save()&lt;/code&gt; on my models
* want to learn how to have self-referencing models (E.g. a hierarchical category)&lt;/p&gt;

&lt;p&gt;I'm going to keep on plugging along, it's really easy to figure out django.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>python: it begins</title>
   <link href="http://davedash.com/2008/01/26/python-it-begins/"/>
   <updated>2008-01-26T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/26/python-it-begins</id>
   <content type="html">&lt;p&gt;[tags]php, python, symfony, frameworks, programming[/tags]&lt;/p&gt;

&lt;p&gt;The problem I have with python, is I really like what I can do with it, and it makes me hate that I have to use PHP.&lt;/p&gt;

&lt;p&gt;PHP works.  I've used it for over 10 years without problem.  My web development methodology was this: I want to do &lt;code&gt;x&lt;/code&gt;, but I need to first learn &lt;code&gt;y&lt;/code&gt;.  Well one day &lt;code&gt;y&lt;/code&gt; became PHP/FI and then PHP 3 and then PHP 4 and then PHP5.  It was a natural progression.&lt;/p&gt;

&lt;p&gt;I came from a (what was at the time) traditional C/C++/Java background and decided that PHP while amateurish was still a better solution for web development.  A few years later I was proven right by companies like Yahoo! and Facebook.&lt;/p&gt;

&lt;p&gt;I did have my issues, and I worked through them, symfony and other frameworks like it, have made PHP development so much better, but eventually you get let in on little secrets.  Secrets like other scripting languages.&lt;/p&gt;

&lt;p&gt;In the last year I dabbled with ruby, but decided I didn't want to look at rails.  A few people pointed me in the direction of python and Django as an alternative to PHP alternatives.  So I've spent time learning python.&lt;/p&gt;

&lt;p&gt;Yesterday and today I gave myself a real project.  I started hacking away at del.icio.us for a Winter Hack Day at Yahoo!  I would write a few lines of code, and then realize that I can write it a little better a little cleaner using some neat little python constructs.  It's enjoyable in the way that PHP is not.&lt;/p&gt;

&lt;p&gt;It might just be a phase, but I sincerely doubt it.  A lot of people I know who use python love it.  Many have been using it for years (even while I secretly thought, &quot;python? really?&quot;  Well, I'm starting to think they've been onto something.  We'll see where I end up.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>python: it begins</title>
   <link href="http://davedash.com/2008/01/26/python-it-begins/"/>
   <updated>2008-01-26T00:00:00-08:00</updated>
   <id>http://davedash.com/2008/01/26/python-it-begins</id>
   <content type="html">&lt;p&gt;[tags]php, python, symfony, frameworks, programming[/tags]&lt;/p&gt;

&lt;p&gt;The problem I have with python, is I really like what I can do with it, and it makes me hate that I have to use PHP.&lt;/p&gt;

&lt;p&gt;PHP works.  I've used it for over 10 years without problem.  My web development methodology was this: I want to do &lt;code&gt;x&lt;/code&gt;, but I need to first learn &lt;code&gt;y&lt;/code&gt;.  Well one day &lt;code&gt;y&lt;/code&gt; became PHP/FI and then PHP 3 and then PHP 4 and then PHP5.  It was a natural progression.&lt;/p&gt;

&lt;p&gt;I came from a (what was at the time) traditional C/C++/Java background and decided that PHP while amateurish was still a better solution for web development.  A few years later I was proven right by companies like Yahoo! and Facebook.&lt;/p&gt;

&lt;p&gt;I did have my issues, and I worked through them, symfony and other frameworks like it, have made PHP development so much better, but eventually you get let in on little secrets.  Secrets like other scripting languages.&lt;/p&gt;

&lt;p&gt;In the last year I dabbled with ruby, but decided I didn't want to look at rails.  A few people pointed me in the direction of python and Django as an alternative to PHP alternatives.  So I've spent time learning python.&lt;/p&gt;

&lt;p&gt;Yesterday and today I gave myself a real project.  I started hacking away at del.icio.us for a Winter Hack Day at Yahoo!  I would write a few lines of code, and then realize that I can write it a little better a little cleaner using some neat little python constructs.  It's enjoyable in the way that PHP is not.&lt;/p&gt;

&lt;p&gt;It might just be a phase, but I sincerely doubt it.  A lot of people I know who use python love it.  Many have been using it for years (even while I secretly thought, &quot;python? really?&quot;  Well, I'm starting to think they've been onto something.  We'll see where I end up.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Python, Named arguments: Pure Genius</title>
   <link href="http://davedash.com/2007/12/21/python-named-arguments-pure-genius/"/>
   <updated>2007-12-21T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/12/21/python-named-arguments-pure-genius</id>
   <content type="html">&lt;p&gt;I decided I want to learn python, if only to learn Django and to &quot;get&quot; what all the python hub-bub is about.&lt;/p&gt;

&lt;p&gt;Python's named arguments in function calls is pure genius.  Let me explain.&lt;/p&gt;

&lt;p&gt;In PHP, and many other languages you can define a function as such:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo($a = 2, $b = 2)
{
    return pow($a,$b);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you follow, &lt;code&gt;foo()&lt;/code&gt; will give you &lt;code&gt;4&lt;/code&gt;.  &lt;code&gt;foo(3)&lt;/code&gt; is &lt;code&gt;9&lt;/code&gt; and &lt;code&gt;foo(99,0)&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;.  In python we can do the same thing, but it'll pay to use some better variable names:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def foo(base=2, exponent=2):
    return base**exponent
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Similarly &lt;code&gt;foo()&lt;/code&gt; will give you &lt;code&gt;4&lt;/code&gt;.  &lt;code&gt;foo(3)&lt;/code&gt; is &lt;code&gt;9&lt;/code&gt; and &lt;code&gt;foo(99,0)&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;.  But what if we forgot what the order was?  Did base come first or was it exponent?  We can do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo(exponent=99, base=2)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since &lt;code&gt;base&lt;/code&gt; and &lt;code&gt;exponent&lt;/code&gt; both have default values, we can even omit &lt;code&gt;base&lt;/code&gt; and let it use the default:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo(exponent=10)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This means rather than passing an &lt;code&gt;$options&lt;/code&gt; array to my functions and checking whether an option was set or not, I can just specify which options I want in my function call.  Or instead of remembering the order of the arguments, I can use whatever order suits me.  Or instead of calling a function like &lt;code&gt;bar(null, null, null, 2)&lt;/code&gt; I can just skip those first three arguments all together.&lt;/p&gt;

&lt;p&gt;A side effect of this, is now there's a real use, even for simple functions, to give your variables easy to remember names.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Python, Named arguments: Pure Genius</title>
   <link href="http://davedash.com/2007/12/21/python-named-arguments-pure-genius/"/>
   <updated>2007-12-21T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/12/21/python-named-arguments-pure-genius</id>
   <content type="html">&lt;p&gt;I decided I want to learn python, if only to learn Django and to &quot;get&quot; what all the python hub-bub is about.&lt;/p&gt;

&lt;p&gt;Python's named arguments in function calls is pure genius.  Let me explain.&lt;/p&gt;

&lt;p&gt;In PHP, and many other languages you can define a function as such:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo($a = 2, $b = 2)
{
    return pow($a,$b);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you follow, &lt;code&gt;foo()&lt;/code&gt; will give you &lt;code&gt;4&lt;/code&gt;.  &lt;code&gt;foo(3)&lt;/code&gt; is &lt;code&gt;9&lt;/code&gt; and &lt;code&gt;foo(99,0)&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;.  In python we can do the same thing, but it'll pay to use some better variable names:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def foo(base=2, exponent=2):
    return base**exponent
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Similarly &lt;code&gt;foo()&lt;/code&gt; will give you &lt;code&gt;4&lt;/code&gt;.  &lt;code&gt;foo(3)&lt;/code&gt; is &lt;code&gt;9&lt;/code&gt; and &lt;code&gt;foo(99,0)&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;.  But what if we forgot what the order was?  Did base come first or was it exponent?  We can do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo(exponent=99, base=2)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since &lt;code&gt;base&lt;/code&gt; and &lt;code&gt;exponent&lt;/code&gt; both have default values, we can even omit &lt;code&gt;base&lt;/code&gt; and let it use the default:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo(exponent=10)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This means rather than passing an &lt;code&gt;$options&lt;/code&gt; array to my functions and checking whether an option was set or not, I can just specify which options I want in my function call.  Or instead of remembering the order of the arguments, I can use whatever order suits me.  Or instead of calling a function like &lt;code&gt;bar(null, null, null, 2)&lt;/code&gt; I can just skip those first three arguments all together.&lt;/p&gt;

&lt;p&gt;A side effect of this, is now there's a real use, even for simple functions, to give your variables easy to remember names.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Better than ORM Object Persistence</title>
   <link href="http://davedash.com/2007/12/20/better-than-orm-object-persistence/"/>
   <updated>2007-12-20T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/12/20/better-than-orm-object-persistence</id>
   <content type="html">&lt;p&gt; After talking to people about the benefits and disadvantages of various ORMs... and reading up a little on non RDMBSs like &lt;a href=&quot;http://www.amazon.com/gp/browse.html?node=342335011&quot;&gt;Amazon SimpleDB&lt;/a&gt; I came to the realization that ORM is really a hack to get RDBMSs to work as a storage for objects.&lt;/p&gt;

&lt;p&gt;I'm being liberal with the term hack.  It really does work for a lot of situations, but it's not very elegant.  The workflow is more or less this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You create a database.&lt;/li&gt;
&lt;li&gt;You create some objects&lt;/li&gt;
&lt;li&gt;You define tables to store attributes of objects&lt;/li&gt;
&lt;li&gt;You establish a mapping&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;There's a lot that goes into database definition.  It would be nice to breakout from this line of thinking and do things a bit differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create database&lt;/li&gt;
&lt;li&gt;Save named (e.g. Person, Place, Log, Restaurant, Rating, Review) serialized objects to the database.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Let the database learn from the saving of the object how to define the type.  In fact, it should be flexible and let us have mixmatched types, etc.&lt;/p&gt;

&lt;p&gt;Let the database index everything, and keep the indexes up to date, but prioritize it based on usage.  In other words if we usually query a database of Persons in order by a field called lastname, then make sure that index on lastname is always at hand.  We should be able to query this data back out of storage based on how we stored it.&lt;/p&gt;

&lt;p&gt;We should also be able to reach other objects in the database in a similar manner.&lt;/p&gt;

&lt;p&gt;The key here is letting the database layer do the heavy-thinking about what to do with serialized data, versus having some middle layer take care of it.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;so I might just be naive about data and databases.  But if this idea is worthwhile and some database people can validate me, I'd be willing to work on it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>jQuery shortcut functions and jQuery plugins</title>
   <link href="http://davedash.com/2007/09/15/jquery-shortcut-functions-and-jquery-plugins/"/>
   <updated>2007-09-15T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/09/15/jquery-shortcut-functions-and-jquery-plugins</id>
   <content type="html">&lt;p&gt;[tags]js, javascript, readability, yui, jquery, shortcuts[/tags]&lt;/p&gt;

&lt;p&gt;One of the the things that's kept me away from the YUI javascript libraries is longhand.  Or rather, the appeal to jQuery (and Prototype) was the almight $ shorthand.&lt;/p&gt;

&lt;p&gt;Of course there are times when it pays to write in long hand.  Marc Grabanski has written a fantastic &lt;a href=&quot;http://marcgrabanski.com/code/jquery-calendar/&quot;&gt;calendar plugin&lt;/a&gt; which uses a lot of jQuery shorthand.  &lt;code&gt;$()&lt;/code&gt;, &lt;code&gt;$.extend&lt;/code&gt; and whatnot are great for quick code, but disaster when you need to be in maximum compatibility mode.  For example, use jQuery with some other libraries and you can get disaster.  It's better to opt for the long-hand &lt;code&gt;jQuery&lt;/code&gt; and &lt;code&gt;jQuery.extend&lt;/code&gt; methods.  After some search and replaces, I may have gotten things right, but I think I need to talk to Marc Grabanski and make sure the changes I made make sense.&lt;/p&gt;

&lt;p&gt;So remember, long hand is great when other developers are using stuff and namespace conflicts are afoot ;)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Symfony Camp: Ajax and Zend, what would you like to know?</title>
   <link href="http://davedash.com/2007/08/16/symfony-camp-ajax-and-zend-what-would-you-like-to-know/"/>
   <updated>2007-08-16T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/08/16/symfony-camp-ajax-and-zend-what-would-you-like-to-know</id>
   <content type="html">&lt;p&gt;[tags]symfonyCamp, symfony, netherlands, ajax, zend search lucene, zsl, jquery[/tags]&lt;/p&gt;

&lt;p&gt;I've been asked to speak at &lt;a href=&quot;http://www.symfonycamp.com/&quot;&gt;SymfonyCamp&lt;/a&gt; (&lt;code&gt;symfony['camp']&lt;/code&gt;) next month (you should all go if you can) and I thought I'd present as well as I could on Ajax and the Zend Framework Bridge (including Zend Search Lucene).&lt;/p&gt;

&lt;p&gt;If you're attending the camp and/or would like to hear about these topics please let me know any specific questions you might have about &quot;&lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; and Ajax&quot; and &quot;symfony and Zend&quot; and I'll try to address them in my presentations.&lt;/p&gt;

&lt;p&gt;If you are unable to go fear not, I'll try to post my notes on this site.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using sfDoctrine to match allowed email domains</title>
   <link href="http://davedash.com/2007/07/09/using-sfdoctrine-to-match-allowed-email-domains/"/>
   <updated>2007-07-09T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/07/09/using-sfdoctrine-to-match-allowed-email-domains</id>
   <content type="html">&lt;p&gt;[tags]php, propel, doctrine, validators, symfony, optiopt, startup[/tags]&lt;/p&gt;

&lt;p&gt;I'm a co-founder at an online finance web site and I'm in charge with building out the site.  Our rollout strategy is to let a a handful of companies at a time, so we're limiting registration based on your company's email address.&lt;/p&gt;

&lt;p&gt;I decided to follow the bandwagon and use PHP Doctrine.  We'll define two objects, &lt;code&gt;Company&lt;/code&gt; and &lt;code&gt;CompanyDomain&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Company:
  tableName: company
  columns:
    name: {type: string(100)}
    created_at: timestamp
    updated_at: timestamp

CompanyDomain:
  tableName: company_domain
  columns:
    company_id:
      foreignClass: Company
      cascadeDelete: true
    pattern: {type: string(100)}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Where a &lt;code&gt;CompanyDomain&lt;/code&gt; represents a domain name for a company.  E.g. Motorola might have both &lt;code&gt;motorola.com&lt;/code&gt; and &lt;code&gt;mot.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can validate a signup form and see if we've got an email address from a domain we recognize.  I like using the &lt;code&gt;validationXXX&lt;/code&gt; methods in an action class for specific validation.  I made one called &lt;code&gt;validateSignup&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function validateSignup()
{
    if ($this-&amp;gt;isPost())
    {
        $email = $this-&amp;gt;getRequestParameter('company_email');
        if (!$company = CompanyTable::match($email))
        {
            $this-&amp;gt;getRequest()-&amp;gt;setError('company_email', 'Your work email doesn\'t appear to belong to any of the registered companies.');
            return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In our &lt;code&gt;CompanyTable&lt;/code&gt; we create a static function &lt;code&gt;CompanyTable::match&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;static public function match($email)
{
    $company = Doctrine_Query::create()-&amp;gt;from('Company c, c.CompanyDomains d')-&amp;gt;where('? LIKE CONCAT(\'%\', pattern)', $email)-&amp;gt;execute()-&amp;gt;getFirst();

    return $company;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that we transparently do a join in the &lt;code&gt;-&amp;gt;from()&lt;/code&gt; statement.  If there's a match we get a company object (which we can associate with the new user) otherwise we get a &lt;code&gt;null&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Enjoy.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Boosting terms in  Zend Search Lucene</title>
   <link href="http://davedash.com/2007/05/29/boosting-terms-in-zend-search-lucene/"/>
   <updated>2007-05-29T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/05/29/boosting-terms-in-zend-search-lucene</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl[/tags]&lt;/p&gt;

&lt;h3&gt;Boosting terms &amp;mdash; some fields are better than others&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Lucene&lt;/a&gt; supports boosting or weighting terms.  For example, if I search for members of a web site, and I type in &lt;q&gt;Dash&lt;/q&gt;, I want people with the name &lt;q&gt;Dash&lt;/q&gt; to take precendence over somebody who has a hobby of running the 50-yard Dash.&lt;/p&gt;

&lt;p&gt;If we look at our &lt;code&gt;generateZSLDocument()&lt;/code&gt; method we defined we just need to adjust a few lines:&lt;/p&gt;

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

        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname()));
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Should be turned into:&lt;/p&gt;

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

        $field = Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname());
        $field-&gt;boost = 1.5;
        $doc-&gt;addField($field);
        $field = Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname());
        $field-&gt;boost = 1.5;
        $doc-&gt;addField($field);

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


&lt;p&gt;This is pretty straight forward way to add weight (1.5 times the weight of a normal term) and you can customize it to the needs of your site.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Finding things using Zend Search Lucene in symfony</title>
   <link href="http://davedash.com/2007/05/23/finding-things-using-zend-search-lucene-in-symfony/"/>
   <updated>2007-05-23T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/05/23/finding-things-using-zend-search-lucene-in-symfony</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl[/tags]&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;notice&quot;&gt;This is part of an &lt;a href=&quot;http://spindrop.us/tag/zsl&quot;&gt;on going series&lt;/a&gt; about the Zend Search Lucene libraries and symfony.  We'll pretty everything up when we're done =)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;We now know how to &lt;a href=&quot;http://spindrop.us/2007/04/24/creating-updating-deleting-documents-in-a-lucene-index-with-symfony/&quot;&gt;manipulate the index via our model classes&lt;/a&gt;.  But let's actually do something useful with our search engine... let's search!&lt;/p&gt;

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


&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl[/tags]&lt;/p&gt;

&lt;p&gt;At the time of this writing we're dealing with Propel which uses &lt;code&gt;Peer&lt;/code&gt; classes which are meant for dealing with multiple objects&lt;sup id=&quot;#fnr_1&quot;&gt;&lt;a href=&quot;#fn_1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.  This is the perfect place for a &lt;code&gt;::search()&lt;/code&gt; method.  In otherwords, &lt;code&gt;UserPeer::search('dave');&lt;/code&gt; should query Lucene for users matching &quot;dave&quot;.  Let's make that happen:&lt;/p&gt;

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

    public static function search($query)
    {
        $index = self::getLuceneIndex();
        
        $hits = $index-&gt;find(strtolower($query));
        $pks = array();
    
        foreach($hits AS $hit)
        {
            $pks[] = $hit-&gt;user_id;
        }
        
        return self::retrieveByPks($pks);
    }

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


&lt;p&gt;What we're doing is retrieving our Lucene index.  Somewhere between tutorials we wrote this &lt;code&gt;Peer&lt;/code&gt; function to handle that:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public static function getLuceneIndex($autoIndex = true)
    {
        try 
        {
            return $index = Zend_Search_Lucene::open(sfConfig::get(self::$luceneIndex));
        } 
        catch (Exception $e) 
        {
            $index = $autoIndex ? self::reindex() : null;
            return $index;
        }
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;If our index is missing we'll conveniently create it on the fly.  We then use the Zend Search Lucene API to retrieve the matching hits in this index and then use some Propel trickery to retrieve by an array of primary keys.&lt;/p&gt;

&lt;p&gt;It's now simple to use &lt;code&gt;::search()&lt;/code&gt; functions in the same manner as you use &lt;code&gt;::doSelect()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point you should be able to create a basic symfony app that can utilize a Lucene index.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn_1&quot;&gt;The examples refer to using Propel, but it's trivial to adapt this to sfDoctrine &lt;a href=&quot;#fnr_1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote  in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Equal height columns with jQuery</title>
   <link href="http://davedash.com/2007/05/22/equal-height-columns-with-jquery/"/>
   <updated>2007-05-22T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/05/22/equal-height-columns-with-jquery</id>
   <content type="html">&lt;p&gt;[tags]css, jQuery, layout, javascript, equal, columns, equal columns[/tags]&lt;/p&gt;

&lt;p&gt;I've seen a few examples of how to equalize column heights using javascript, and none of them seem appealing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.tomdeater.com/jquery/equalize_columns/&quot;&gt;jquery.equalizecols.js&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This required a few other libraries, and I wanted more flexibility (e.g. where the column should grow in order to equalize)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.projectseven.com/tutorials/css/pvii_columns/index.htm&quot;&gt;Project 7&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;The Project 7 approach was the most interesting, but the code seemed a bit messy and not so open source friendly (even thought it might have been).  It would let you specify which element was to grow inside a column.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.html.it/articoli/nifty/index.html&quot;&gt;Nifty Corners&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I had trouble with the syntax, but I liked how it just created a new element out of thin air...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So I wrote my own:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$(&quot;#col1, #col2&quot;).equalizeCols();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will equalize the columns as expected&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$(&quot;#col1, #col2&quot;).equalizeCols(&quot;p,p&quot;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will equalize the columns and add the extra space after the &lt;code&gt;p&lt;/code&gt; tag in &lt;code&gt;#col1&lt;/code&gt; or &lt;code&gt;#col2&lt;/code&gt; (whichever is shorter).&lt;/p&gt;

&lt;p&gt;Here's our function:&lt;/p&gt;

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

(function($) {
  $.fn.equalizeCols = function(children){
    var child = Array(0);
    if (children) child = children.split(&quot;,&quot;);
    var maxH = 0;
    this.each(
      function(i) 
      {
        if (this.offsetHeight&gt;maxH) maxH = this.offsetHeight;
      }
    ).css(&quot;height&quot;, &quot;auto&quot;).each(
      function(i)
      {
        var gap = maxH-this.offsetHeight;
        if (gap &gt; 0)
        {
          t = document.createElement('div');
          $(t).attr(&quot;class&quot;,&quot;fill&quot;).css(&quot;height&quot;,gap+&quot;px&quot;);
          if (child.length &gt; i)
          {
            $(this).find(child[i]).children(':last-child').after(t);
          } 
          else 
          {
            $(this).children(':last-child').after(t);
          }
        }
      }  
    );
    
  }
})(jQuery);

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


&lt;p&gt;This requires jQuery of course, and it hasn't been tested much.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Debugging yaml configuration with the symfony web debugger</title>
   <link href="http://davedash.com/2007/05/21/debugging-yaml-configuration-with-the-symfony-web-debugger/"/>
   <updated>2007-05-21T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/05/21/debugging-yaml-configuration-with-the-symfony-web-debugger</id>
   <content type="html">&lt;p&gt;[tags]symfony, yaml, configuration, web debug, debug[/tags]
There's no doubt that &lt;a href=&quot;http://www.yaml.org/&quot;&gt;yaml&lt;/a&gt; configuration files in &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; can be troublesome to debug.&lt;/p&gt;

&lt;p&gt;The symfony web debugger comes to the rescue.&lt;/p&gt;

&lt;p&gt;Let's say you're &lt;code&gt;app.yml&lt;/code&gt; has the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app:
  admin:
    email: me@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In your development environment you can look at click on &quot;vars &amp;amp; config&quot;  and under settings see all the &lt;code&gt;sfConfig::get&lt;/code&gt;able variables.  You'll find the above listed as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app_admin_email: me@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This technique is very handy when trying to figure out what key you need to use in &lt;code&gt;sfConfig::get()&lt;/code&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Lucene Search Index and symfony</title>
   <link href="http://davedash.com/2007/04/23/the-lucene-search-index-and-symfony/"/>
   <updated>2007-04-23T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/23/the-lucene-search-index-and-symfony</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl, index[/tags]&lt;/p&gt;

&lt;p&gt;This article is meant to followup &lt;a href=&quot;http://spindrop.us/2007/04/10/sfzendplugin/&quot;&gt;sfZendPlugin&lt;/a&gt; where we learn a newer way of obtaining the &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial we're going to delve into the Lucene index.  &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt; relies on building a Lucene index.  This is a directory that contains files that can be indexed and queried by Lucene or other ports.  In our example we'll be creating a search for user profiles.&lt;/p&gt;

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


&lt;p&gt;We'll want to store in our &lt;code&gt;app.yml&lt;/code&gt; the precise location of this index file so we can refer to it in our app&lt;sup id=&quot;#fnr_lucene_index1&quot;&gt;&lt;a href=&quot;#fn_lucene_index1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;all:
  search:
    user_index: /tmp/myapp.user.lucene.index
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now when we need to refer to the index we can do &lt;code&gt;sfConfig::get('app_search_user_index')&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Index Something&lt;/h3&gt;

&lt;p&gt;Let's try a user search where we can find a user by their name or email address.  It's fairly simple to accomplish, and hardly requires the use of &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt;, but by using &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt; we can easily extend it to do a full-text search of a user's profile or any other textual data.&lt;/p&gt;

&lt;p&gt;Each &quot;thing&quot; stored in the index is a Lucene &quot;document&quot;.  Each document then consists of several &quot;fields&quot; (&lt;code&gt;Zend_Search_Lucene_Field&lt;/code&gt; objects).  In our example, each document will be an individual user and the fields will be relevant attributes of the user (username, first name, last name, email, the text of their profile).&lt;/p&gt;

&lt;p&gt;Initially we'll want to populate our index.  We may also want to regularly reindex all the users at once to optimize the search performance.  Since reindexing involves multiple users it would make sense to have a static &lt;code&gt;reindex&lt;/code&gt; method in our &lt;code&gt;UserPeer&lt;/code&gt; class&lt;sup id=&quot;#fnr_lucene_index2&quot;&gt;&lt;a href=&quot;#fn_fn_lucene_index2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
class UserPeer extends BaseUserPeer
{
    public static function reindex()
    {
        $index = Zend_Search_Lucene::create(sfConfig::get('app_search_user_index'));

        $user = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            $index-&gt;addDocument($user-&gt;generateZSLDocument());
        }

        return $index;
    }
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Very simply, we're creating a new index, getting all the users, adding a document to the index and then committing the index (to disk).  You might have noticed that there's a strange function, &lt;code&gt;User::generateZSLDocument()&lt;/code&gt;.  This function contains all the magic.  In order to not repeat ourselves we keep the internals of making a document for the Lucene index in the &lt;code&gt;User&lt;/code&gt; class itself.  Let's look at it:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function generateZSLDocument()
    {
        $doc = new Zend_Search_Lucene_Document();
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('uid', $this-&gt;getId()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $this-&gt;getUsername()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $this-&gt;getEmail()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname()));
        /* An unstored contents field as an aggregate 
          * of all data is no longer needed in *ZEND* Lucene 
          * But it's here.
          */
        $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', implode(' ', array($this-&gt;getEmail(), $this-&gt;getFirstname(), $this-&gt;getLastname(), $this-&gt;getUsername())));
        return $doc;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We're really just dumping the relevant search terms into this document.  The beauty of keeping this code internalized in the &lt;code&gt;User&lt;/code&gt; class is we can reuse it later if we need to index a single &lt;code&gt;User&lt;/code&gt; at a time.&lt;/p&gt;

&lt;p&gt;A couple things to note.  &lt;code&gt;Zend_Search_Lucene_Field::Keyword&lt;/code&gt; allows us to store data that we can lookup later.  We store the &lt;code&gt;User::id&lt;/code&gt; in a field called &lt;code&gt;uid&lt;/code&gt; since &lt;code&gt;id&lt;/code&gt; is a reserved word for the index and we can't access it from &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a batch script or a reindex action we can now just call &lt;code&gt;UserPeer::reindex()&lt;/code&gt; and have a working search index for our users.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn_lucene_index1&quot;&gt;Storing things in &lt;code&gt;app.yml&lt;/code&gt; is great for indexes that don't need to be searched in multiple applications. &lt;a href=&quot;#fnr_lucene_index1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
        &lt;li id=&quot;fn_lucene_index2&quot;&gt;
Since we're using a Lucene index, which has an open documented structure, we aren't limited to just using Zend Search Lucene or Apache Lucene (java).  We can mix and match and read and write to the same index file.  For very large indexes (65,000+ documents), I rewrote a Java application to index all the documents at once as PHP would time out during such a task.
&lt;a href=&quot;#fnr_lucene_index2&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 2 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Tips for symfony and Subversion</title>
   <link href="http://davedash.com/2007/04/17/tips-for-symfony-and-subversion/"/>
   <updated>2007-04-17T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/17/tips-for-symfony-and-subversion</id>
   <content type="html">&lt;p&gt;[tags]symfony, subversion[/tags]&lt;/p&gt;

&lt;p&gt;There's some tricks you can do to running a symfony project with subversion:&lt;/p&gt;

&lt;h3&gt;Ignoring files in &lt;code&gt;cache/&lt;/code&gt; and &lt;code&gt;log/&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The first thing you can do (and this is well documented in the &lt;a href=&quot;http://www.symfony-project.com/askeet/1&quot;&gt;askeet tutorial&lt;/a&gt;) is ignore files in &lt;code&gt;cache/&lt;/code&gt; and &lt;code&gt;log/&lt;/code&gt;.  These files are specific to each instance of your app and don't contain anything that needs to be in version control.&lt;/p&gt;

&lt;p&gt;Run the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd $SF_ROOT
rm -rf log/* cache/*
svn propedit svn:ignore log
svn propedit svn:ignore cache
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;svn propedit&lt;/code&gt; will bring up a text editor, in both instances you want to save the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;*
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Ignore other auto-generated files&lt;/h3&gt;

&lt;p&gt;Eric Sink wrote an excellent &lt;a href=&quot;http://www.ericsink.com/scm/source_control.html&quot;&gt;tutorial on source control&lt;/a&gt;.  In his chapter on &lt;a href=&quot;http://www.ericsink.com/scm/scm_repositories.html&quot;&gt;repositories&lt;/a&gt; he recommends checking in &lt;em&gt;only&lt;/em&gt; hand edited source code.  If a property file generates another file, check in the property file, not the auto-generated result.  This not only keeps your repository clean, it prevents a lot of unnecessary check-ins.&lt;/p&gt;

&lt;p&gt;If you use propel for your &lt;acronym title=&quot;Object Relational Mapping&quot;&gt;ORM&lt;/acronym&gt; layer there are a few files you can ignore using &lt;code&gt;svn propedit svn:ignore {dirname}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;$SF_ROOT/config&lt;/code&gt; we can ignore:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;*schema-transformed.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These are &lt;code&gt;xml&lt;/code&gt; files that propel generates from &lt;code&gt;schema.xml&lt;/code&gt; (or &lt;code&gt;schema.yml&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;$SF_ROOT/data/sql&lt;/code&gt; we can ignore:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;lib.model.schema.sql
plugins.*.sql
sqldb.map
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These are created from &lt;code&gt;schema.xml&lt;/code&gt; (or &lt;code&gt;schema.yml&lt;/code&gt;) as well.&lt;/p&gt;

&lt;p&gt;The real savings will come with your model.  The propel model creates customizable php classes in &lt;code&gt;lib/model&lt;/code&gt; which inherit from auto-generated files in &lt;code&gt;lib/om&lt;/code&gt; there are also auto-generated map files in `lib/map'&lt;/p&gt;

&lt;p&gt;We can run from &lt;code&gt;$SF_ROOT&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;svn propedit svn:ignore lib/model/om
svn propedit svn:ignore lib/model/map
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and enter&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;*
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;for both properties.&lt;/p&gt;

&lt;p&gt;If you've mistakenly checked in some of these files you will need to remove them from your repository via &lt;code&gt;svn delete&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Linking the symfony Library&lt;/h3&gt;

&lt;p&gt;I prefer to embed the symfony library into each of my symfony apps rather than relying on a shared PEAR library.  This lets me run multiple versions of symfony without much fuss.  With subversion we can use the &lt;code&gt;svn:externals&lt;/code&gt; property to directly link our app with the symfony subversion repository.&lt;/p&gt;

&lt;p&gt;At first this sounds like danger, but externals can be linked to specific revisions.  However, the &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; team tags their repository with version numbers.  To get this to work we need to do 3 things.  (&lt;strong&gt;UPDATE:&lt;/strong&gt; See Fabien's comment about using the lib/vendor directory)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Modify &lt;code&gt;config/config.php&lt;/code&gt; to look for symfony internally.  Just open it up and change it so it says this:
 &lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
 &amp;lt;?php
 $sf_symfony_lib_dir  = dirname(&lt;strong&gt;FILE&lt;/strong&gt;).'/../lib/symfony';
 $sf_symfony_data_dir = dirname(&lt;strong&gt;FILE&lt;/strong&gt;).'/../data/symfony';
 &lt;/textarea&gt;&lt;/div&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;svn propedit svn:externals lib&lt;/code&gt; from &lt;code&gt;$SF_ROOT&lt;/code&gt; and enter:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; symfony http://svn.symfony-project.com/tags/RELEASE_1_0_2/lib/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or whatever version of symfony you want to link to, at the time of this post, &lt;code&gt;RELEASE_1_0_2&lt;/code&gt; is fairly fresh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;svn propedit svn:externals data&lt;/code&gt; from &lt;code&gt;$SF_ROOT&lt;/code&gt; and enter:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; symfony http://svn.symfony-project.com/tags/RELEASE_1_0_2/data/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or whatever version of symfony you want to link to, at the time of this post.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Now when you do &lt;code&gt;svn update&lt;/code&gt; you'll have the &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; library all linked up.  Furthermore this keeps all the developers on the same version of &lt;code&gt;symfony&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also you may want to start running symfony using &lt;code&gt;./symfony&lt;/code&gt; versus &lt;code&gt;symfony&lt;/code&gt;.  The former looks at your configuration settings to determine which &lt;code&gt;symfony&lt;/code&gt; command to use, the latter is generally linked to your system wide command (which is generally the PEAR installed command).&lt;/p&gt;

&lt;h3&gt;Linking to symfony Plugins&lt;/h3&gt;

&lt;p&gt;I have my hands in a number of symfony plugins because I work on a lot of projects which tend to share a lot of similar functionality.  Many of the plugins are in early stages of development, so I find it helpful to have them linked from svn as well.  This way I can get the benefits of any new functionality and if the occasion should arise, I can contribute any useful changes I make.&lt;/p&gt;

&lt;p&gt;To link to the plugins you run &lt;code&gt;svn propedit svn:externals plugins&lt;/code&gt; and enter one plugin per line in the following format:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{plugin_name} -r{REVISION} {URL}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For one of my projects I use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sfPrototypeTooltipPlugin http://svn.symfony-project.com/plugins/sfPrototypeTooltipPlugin
sfGuardPlugin http://svn.symfony-project.com/plugins/sfGuardPlugin
sfZendPlugin http://svn.symfony-project.com/plugins/sfZendPlugin
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I've omitted the revision, because I live dangerously and want to use the latest &lt;code&gt;$HEAD&lt;/code&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>sfZendPlugin</title>
   <link href="http://davedash.com/2007/04/10/sfzendplugin/"/>
   <updated>2007-04-10T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/10/sfzendplugin</id>
   <content type="html">&lt;p&gt;[tags]Zend, Zend Search Lucene, Search, Lucene, php, symfony, zsl, plugins[/tags]&lt;/p&gt;

&lt;p&gt;I originally intended to rewrite &lt;a href=&quot;http://spindrop.us/2006/08/25/using-zend-search-lucene-in-a-symfony-app/&quot;&gt;my Zend Search Lucene tutorial&lt;/a&gt;, but &lt;a href=&quot;http://archivemati.ca/2007/03/08/zend-search-lucene-symfony-and-the-ica-atom-application/&quot;&gt;Peter Van Garderen&lt;/a&gt; covered the bulk of what's changed and I was too busy developing search functionality for &lt;a href=&quot;http://lyro.com/&quot;&gt;lyro.com&lt;/a&gt; (not to mention finding inconsistencies with the Zend Search Lucene port and Lucene) to finish the tutorial.  So I broke it up into smaller pieces.&lt;/p&gt;

&lt;p&gt;I packaged &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; into a &lt;a href=&quot;http://www.symfony-project.com/trac/browser/plugins/sfZendPlugin&quot;&gt;symfony plugin&lt;/a&gt;.  &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; is easily extended using plugins.&lt;/p&gt;

&lt;p&gt;You can obtain this from subversion with the following command (from your &lt;code&gt;/plugins&lt;/code&gt; directory):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;svn export http://svn.symfony-project.com/plugins/sfZendPlugin
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; has a &lt;a href=&quot;http://www.symfony-project.com/book/trunk/17-Extending-Symfony#Bridges%20to%20Other%20Framework%20Components&quot;&gt;Zend Framework Bridge&lt;/a&gt; which let's us autoload the framework by adding the following to &lt;code&gt;settings.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;.settings:
  zend_lib_dir:   %SF_ROOT_DIR%/plugins/sfZendPlugin/lib
  autoloading_functions:
    - [sfZendFrameworkBridge, autoload]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;First we define &lt;code&gt;sf_zend_lib_dir&lt;/code&gt; to be in our plugin's &lt;code&gt;lib&lt;/code&gt; directory.  Then we autoload the bridge framework.&lt;/p&gt;

&lt;p&gt;After setting this up, all the Zend classes will be available and auto-loaded from elsewhere in your code.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Caching REST with sfFunctionCache</title>
   <link href="http://davedash.com/2007/04/07/caching-rest-with-sffunctioncache/"/>
   <updated>2007-04-07T00:00:00-07:00</updated>
   <id>http://davedash.com/2007/04/07/caching-rest-with-sffunctioncache</id>
   <content type="html">&lt;p&gt;[tags]geocoding, caching, REST, symfony, Cache_Lite, php, cache, sfFunctionCache[/tags]&lt;/p&gt;

&lt;p&gt;For &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; we do a lot of geocoding.  To facilitate we use &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Yahoo! Geocoding API&lt;/a&gt;.  This helps us normalize data, obtain latitude and longitude, interpret location specific searches.&lt;/p&gt;

&lt;p&gt;These &lt;acronym title=&quot;REpresentational State Transfer&quot;&gt;REST&lt;/acronym&gt; queries happen a lot and will continue to happen, but this data that Yahoo! provides is fairly static.  We're basically querying a database of sorts.  So it makes sense that we should cache this data.&lt;/p&gt;

&lt;p&gt;We'll demonstrate how to cache these queries using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;'s &lt;a href=&quot;http://www.symfony-project.com/book/trunk/18-Performance#Caching%20the%20Result%20of%20a%20Function%20Call&quot;&gt;&lt;code&gt;sfFunctionCache&lt;/code&gt;&lt;/a&gt; class.&lt;/p&gt;

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


&lt;p&gt;I wrote a wrapper (I'll release it as a plugin if requested) for the &lt;a href=&quot;http://developer.yahoo.com/maps/rest/V1/geocode.html&quot;&gt;Geocoding API&lt;/a&gt;, the bulk of the work (the &lt;acronym title=&quot;REpresentational State Transfer&quot;&gt;REST&lt;/acronym&gt; call) occurs in a function called &lt;code&gt;doQueryGIS&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
        public static function doQueryGIS($location)
        {
            $url               = sfConfig::get('app_yahoo_geocode');

            $query             = array();
            $query['appid']    = sfConfig::get('app_yahoo_app_id');
            $query['location'] = $location;
            $query['output']   = 'php';

            $url .= '?' . http_build_query($query,null,'&amp;');    
              $curl = curl_init($url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exe c($curl); 
              // note there should be no space between curl_exe and c($curl) 
              // wordpress is just dumb
        

            if ($response != 'Array') 
            {
                return unserialize($response);
            } 
            else 
            {
                throw new sfException('Yahoo! GeoCoder does not understand: &quot;'. $location . &quot;\&quot;\n&quot;);
            }
            return false;   
            
            
        }

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


&lt;p&gt;The call to this function is always wrapped with &lt;code&gt;queryGIS&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
      protected function queryGIS()
        {
            $function_cache_dir = sfConfig::get('sf_cache_dir').'/function';
            $cache = new sfFunctionCache($function_cache_dir);
            $this-&gt;rawData = $cache-&gt;call(array('YahooGeo','doQueryGIS'), $this-&gt;rawLocation);
            return $this-&gt;rawData;
            
        }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;This wrapper creates a &lt;code&gt;sfFunctionCache&lt;/code&gt; objet and calls the function and caches it for subsequent queries.&lt;/p&gt;

&lt;p&gt;What this means is once Yahoo! teaches &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; that India is located at (25.42&amp;deg;, 77.830002&amp;deg;) and that the precision is 'country' we remember it in the future.&lt;/p&gt;

&lt;p&gt;These features will be incorporated into future versions of &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>PHP double versus single quotes</title>
   <link href="http://davedash.com/2007/03/03/php-double-versus-single-quotes/"/>
   <updated>2007-03-03T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/03/03/php-double-versus-single-quotes</id>
   <content type="html">&lt;p&gt;[tags]best practices, php[/tags]&lt;/p&gt;

&lt;p&gt;If you're familiar with PHP, the difference between double and single quotes is obvious.  Double quotes evaluate variables and control characters (e.g. &lt;code&gt;\n&lt;/code&gt; or &lt;code&gt;\r&lt;/code&gt;), whereas single quotes do not.&lt;/p&gt;

&lt;p&gt;I've been indoctrinated with the &quot;use single quotes whenever possible&quot; methodology, but I never really put it to the test.  Is it really worth it for me to go back and look at old code that uses double quotes and change them?  Like all best practices, the answer is &quot;maybe.&quot;&lt;/p&gt;

&lt;p&gt;So I wrote a simple test harness (there's a lot of room for error with these, but I tried my best).&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
&lt;?php

define ('MAX',2000000);

function f1()
{
    for($i=0;$i&lt;MAX; $i++)
    {
        $c = &quot;test &quot; . $i;
    }
}


function f2()
{
    for($i=0;$i&lt;MAX; $i++)
    {
        $c = &quot;test $i&quot;;
    }
}


function f3()
{
    for($i=0;$i&lt;MAX; $i++)
    {
        $c = 'test ' . $i;
    }
}



$t1 = microtime(true);
f1();
echo 'Time 1: ' , (microtime(true) - $t1) , &quot;\n&quot;;

$t2 = microtime(true);
f2();
echo 'Time 2: ' , (microtime(true) - $t2) , &quot;\n&quot;;

$t3 = microtime(true);
f3();
echo 'Time 3: ' , (microtime(true) - $t3) , &quot;\n&quot;;



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


&lt;p&gt;I tried different permutations of which order to run &lt;code&gt;f1()&lt;/code&gt;, &lt;code&gt;f2()&lt;/code&gt; and &lt;code&gt;f3()&lt;/code&gt; they seemed to give similar results no matter which order they were run.&lt;/p&gt;

&lt;p&gt;My results were:&lt;/p&gt;

&lt;pre&gt;
Time 1: 2.898087978363
Time 2: 3.5480048656464
Time 3: 2.8503499031067
&lt;/pre&gt;


&lt;p&gt;My interpretation is that quotes versus double quotes isn't as big of a deal as concatenation versus putting variables within double quotes.  My guess is that PHP 5.2.0 (which is what I used for the tests) is smarter with strings than older versions.&lt;/p&gt;

&lt;p&gt;So really if you look at the test harness there isn't any discernible differences until you hit 2 million iterations and even then nobody gets fired over 0.6 seconds of performance.  Chances are it doesn't matter too much, but over time you can write enough code in the right spots or shared in the right open source projects and that few seconds will snowball.  After all, I'm not so much concerned about how optimized a script is that I write, but rather how optimized is a script that everyone ends up using.  But rather than do mental calculations about whether or not to optimize something, let's just assume that everything should be as optimal as we can stand to write it.&lt;/p&gt;

&lt;p&gt;Update: 6 March 2006, I updated the test harness to reflect my intended tests.
Update: 7 March 2006, I updated the results to be more clear.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How do you find good programmers?</title>
   <link href="http://davedash.com/2007/03/01/how-do-you-find-good-programmers/"/>
   <updated>2007-03-01T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/03/01/how-do-you-find-good-programmers</id>
   <content type="html">&lt;p&gt;[tags]programmers, hiring[/tags]&lt;/p&gt;

&lt;p&gt;I have a hard time finding good programmers.  Usually I'm asked to look for one in the most inopportune times.  Namely when we're on a tight deadline and it's really too late to add new people.  Unfortunately, it looks like &lt;a href=&quot;http://www.codinghorror.com/blog/archives/000781.html&quot;&gt;I'm not alone&lt;/a&gt; in learning that the programmers for hire aren't up to snuff.&lt;/p&gt;

&lt;p&gt;Looking for a &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; developer is worse, because there is such a low barrier to entry.  If you look for a Java developer or a C/C++ developer there's some reasonable assurance that they either &lt;em&gt;a.&lt;/em&gt; learned it very well or &lt;em&gt;b.&lt;/em&gt; was taught it formally.  With &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; the barrier to entry is so low that anybody can claim to be a &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; developer.  I can safely attest that if you embark on a quest to learn more about &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; or even programming in general there's a lot more out there.&lt;sup id=&quot;fnr-prog1&quot;&gt;&lt;a href=&quot;#fn-prog1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;I think the best situation is to interview for people during your downtime and keep the communication open with them.  In my situation I know more positions that are open that require talented developers versus the number of talented developers I know.  So even if I can't use a person when they happen to be free, I can at least pass them on good leads.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn-prog1&quot;&gt;Early last month I set on a quest to learn more about PHP and learned a lot about design patterns and relating them to PHP.  This subsequently highlighted how much more I don't know yet. &lt;a href=&quot;#fnr-prog1&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote -prog1 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Parsing a list of Key:Value pairs</title>
   <link href="http://davedash.com/2007/02/24/parsing-a-list-of-kv-pairs/"/>
   <updated>2007-02-24T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/02/24/parsing-a-list-of-kv-pairs</id>
   <content type="html">&lt;p&gt;[tags]best practices,php,openID[/tags]
I'm working on implementing &lt;a href=&quot;http://openid.net/&quot;&gt;openID&lt;/a&gt; for &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; and for use in &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony&lt;/a&gt; apps.  One thing I was having trouble with was parsing key value pairs, which is one of the requirements to reading responses.  It's a fairly easy task, but &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; offers so many ways to do this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://openid.net/&quot;&gt;openID&lt;/a&gt; calls for the following &lt;a href=&quot;http://openid.net/specs/openid-authentication-1_1.html#anchor32&quot;&gt;Key-Value format&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Lines of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; some_key:some value&lt;/li&gt;
&lt;li&gt; There MUST NOT be a space before or after the colon.&lt;/li&gt;
&lt;li&gt; Newline characters MUST be Unix-style, just ASCII character 10 (&quot;\n&quot;).&lt;/li&gt;
&lt;li&gt; Newlines MUST BE at end of each line as well as between lines.&lt;/li&gt;
&lt;li&gt; MIME type is unspecified, but text/plain is RECOMMENDED.&lt;/li&gt;
&lt;li&gt; Character encoding MUST BE UTF-8.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here is my attempt at parsing something like this as efficiently and error free as possible:&lt;/p&gt;

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


&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
function splitKV($response) 
{
    $r = array();
    preg_match_all('|^\s*([^:]+):([^:\n]+)[ ]*$|m', $kvs, $matches);
    for($i = 0; $i &lt; count($matches[0]); $i++) {
        $r[$matches[1][$i]] = $matches[2][$i];
    }
    return $r;
}
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;I wrote this function as I was writing the post... and it came out way faster than my previous implementations using &lt;code&gt;strtok&lt;/code&gt; or a combination of &lt;code&gt;explode&lt;/code&gt; and &lt;code&gt;trim&lt;/code&gt;.  Which is good to hear since I do use &lt;code&gt;preg_&lt;/code&gt; functions quite a bit in PHP and they definitely have their place.&lt;/p&gt;

&lt;p&gt;I'm curious if people have found a faster way of parsing through a string of Key-Value pairs.  I'll run it in a test harness and stand corrected if someone comes through with something faster ;).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Make Adium show your Facebook status</title>
   <link href="http://davedash.com/2007/02/03/make-adium-show-your-facebook-status/"/>
   <updated>2007-02-03T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/02/03/make-adium-show-your-facebook-status</id>
   <content type="html">&lt;p&gt;By using a simple &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feed, you can have Adium report your Facebook status.&lt;/p&gt;

&lt;p&gt;I am a late-adopter to social networks.  I participated in LiveJournal years after many of my friends started using it.  I finally got into Facebook months after it was &quot;openned up.&quot;  I like this strategy because I can immediately find my friends on the network and add them and amass 100s in a few short days.&lt;/p&gt;

&lt;p&gt;Facebook is nice for its simple and clean interface.  Even the features are fairly simple.  I like the status feature... for no good reason.  It's a simple AJAX status update that updates your status on the site.  What I don't like to do is repeat myself.  I wanted Adium to repeat what Facebook said.&lt;/p&gt;

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


&lt;p&gt;My first inkling was to use the Facebook &lt;acronym title=&quot;Application Programming Interface&quot;&gt;API&lt;/acronym&gt;, but that seemed less appealing since it would involve authentication.  I almost gave up and then discovered that in Facebook you can go to &quot;My Profile&quot; and under &quot;Status&quot; go to &quot;See All&quot; and there's an RSS link to your status messages.  Awesome.  All I needed to do is grab the first one.&lt;/p&gt;

&lt;p&gt;I decided this looked like a task for &lt;a href=&quot;http://us3.php.net/simplexml/&quot;&gt;Simple XML&lt;/a&gt; which can take an XML string and turn it into a PHP object.&lt;/p&gt;

&lt;p&gt;The first step of course was to fetch the &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feed into a string.  &lt;code&gt;file_get_contents&lt;/code&gt; made the most sense, but Facebook does a browser check even for getting RSS feeds.  I'm not fond of browser sniffing, but its easy to work around if you use &lt;a href=&quot;http://www.php.net/curl&quot;&gt;cURL&lt;/a&gt;.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
&amp;lt;?php
    $url = &quot;http://mynework.facebook.com/feeds/status.php?replace=with&amp;your=own&amp;feed=url&quot;;

    // setup curl
    $ch = curl_init();
    curl_setopt ($ch, CURLOPT_URL, $url);
    curl_setopt ($ch, CURLOPT_HEADER, 0);

    //spoof Firefox
    curl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) Gecko/20061223 Firefox/2.0.0.1&quot;);

    // begin output buffering
    ob_start();
    curl_exec ($ch);
    curl_close ($ch);
    // save buffer to string
    $xmlstr = ob_get_contents();

    ob_end_clean();

    // convert string to xml object
    $xml = new SimpleXMLElement($xmlstr);

    // status messages start with 'Dave is...' 
    // I just want everything after my name
    echo str_replace('Dave ', null,$xml-&gt;channel-&gt;item-&gt;title);

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


&lt;p&gt;Excellent... this does exactly what I want - but it's slow.  Like any good script, we should use caching if it makes sense.  Obviously we don't want to overload the servers at Facebook (or for that matter any place that serves up &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feeds) so we implement &lt;acronym title=&quot;PHP Extension and Application Repository&quot;&gt;PEAR&lt;/acronym&gt;'s &lt;a href=&quot;http://pear.php.net/package/Cache_Lite&quot;&gt;Cache_Lite&lt;/a&gt; package.  We'll tell it to cache results for 15 minutes.  The code changes are marked with &lt;code&gt;//+++&lt;/code&gt; at the end of each new line:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
&amp;lt;?php

    require_once('Cache/Lite.php'); //+++
    
    $url = &quot;http://mynework.facebook.com/feeds/status.php?replace=with&amp;your=own&amp;feed=url&quot;;

    $options = array('cacheDir' =&gt; '/tmp/', 'lifeTime' =&gt; 600); //+++

    $Cache_Lite = new Cache_Lite($options); //+++

    $data = null; //+++
    // attempt to load the data from cache,  //+++
    // otherwise load it anew from RSS //+++
    if (!($data = $Cache_Lite-&gt;get($id))) { //+++
    // setup curl
        $ch = curl_init();
        curl_setopt ($ch, CURLOPT_URL, $url);
        curl_setopt ($ch, CURLOPT_HEADER, 0);

        //spoof Firefox
        curl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) Gecko/20061223 Firefox/2.0.0.1&quot;);

        // begin output buffering
        ob_start();
        curl_exec ($ch);
        curl_close ($ch);
        // save buffer to string
        $xmlstr = ob_get_contents();

        ob_end_clean();

        // convert string to xml object
        $xml = new SimpleXMLElement($xmlstr);

        // status messages start with 'Dave is...' 
        // I just want everything after my name
        $data = str_replace('Dave ', null,$xml-&gt;channel-&gt;item-&gt;title); // +++

        $Cache_Lite-&gt;save($data); //+++

    } //+++

    echo $data; //+++
    
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;That's it on the PHP side.  On the Adium side you'll need to use the &lt;a href=&quot;http://www.adiumxtras.com/index.php?a=xtras&amp;amp;xtra_id=1255&quot;&gt;exec AdiumXtra&lt;/a&gt;.  It'll allow you to set your status to something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/exec {/usr/local/php5/bin/php /usr/local/bin/facebook.php}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Enjoy.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Make Adium show your Facebook status</title>
   <link href="http://davedash.com/2007/02/03/make-adium-show-your-facebook-status/"/>
   <updated>2007-02-03T00:00:00-08:00</updated>
   <id>http://davedash.com/2007/02/03/make-adium-show-your-facebook-status</id>
   <content type="html">&lt;p&gt;By using a simple &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feed, you can have Adium report your Facebook status.&lt;/p&gt;

&lt;p&gt;I am a late-adopter to social networks.  I participated in LiveJournal years after many of my friends started using it.  I finally got into Facebook months after it was &quot;openned up.&quot;  I like this strategy because I can immediately find my friends on the network and add them and amass 100s in a few short days.&lt;/p&gt;

&lt;p&gt;Facebook is nice for its simple and clean interface.  Even the features are fairly simple.  I like the status feature... for no good reason.  It's a simple AJAX status update that updates your status on the site.  What I don't like to do is repeat myself.  I wanted Adium to repeat what Facebook said.&lt;/p&gt;

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


&lt;p&gt;My first inkling was to use the Facebook &lt;acronym title=&quot;Application Programming Interface&quot;&gt;API&lt;/acronym&gt;, but that seemed less appealing since it would involve authentication.  I almost gave up and then discovered that in Facebook you can go to &quot;My Profile&quot; and under &quot;Status&quot; go to &quot;See All&quot; and there's an RSS link to your status messages.  Awesome.  All I needed to do is grab the first one.&lt;/p&gt;

&lt;p&gt;I decided this looked like a task for &lt;a href=&quot;http://us3.php.net/simplexml/&quot;&gt;Simple XML&lt;/a&gt; which can take an XML string and turn it into a PHP object.&lt;/p&gt;

&lt;p&gt;The first step of course was to fetch the &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feed into a string.  &lt;code&gt;file_get_contents&lt;/code&gt; made the most sense, but Facebook does a browser check even for getting RSS feeds.  I'm not fond of browser sniffing, but its easy to work around if you use &lt;a href=&quot;http://www.php.net/curl&quot;&gt;cURL&lt;/a&gt;.&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
&amp;lt;?php
    $url = &quot;http://mynework.facebook.com/feeds/status.php?replace=with&amp;your=own&amp;feed=url&quot;;

    // setup curl
    $ch = curl_init();
    curl_setopt ($ch, CURLOPT_URL, $url);
    curl_setopt ($ch, CURLOPT_HEADER, 0);

    //spoof Firefox
    curl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) Gecko/20061223 Firefox/2.0.0.1&quot;);

    // begin output buffering
    ob_start();
    curl_exec ($ch);
    curl_close ($ch);
    // save buffer to string
    $xmlstr = ob_get_contents();

    ob_end_clean();

    // convert string to xml object
    $xml = new SimpleXMLElement($xmlstr);

    // status messages start with 'Dave is...' 
    // I just want everything after my name
    echo str_replace('Dave ', null,$xml-&gt;channel-&gt;item-&gt;title);

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


&lt;p&gt;Excellent... this does exactly what I want - but it's slow.  Like any good script, we should use caching if it makes sense.  Obviously we don't want to overload the servers at Facebook (or for that matter any place that serves up &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feeds) so we implement &lt;acronym title=&quot;PHP Extension and Application Repository&quot;&gt;PEAR&lt;/acronym&gt;'s &lt;a href=&quot;http://pear.php.net/package/Cache_Lite&quot;&gt;Cache_Lite&lt;/a&gt; package.  We'll tell it to cache results for 15 minutes.  The code changes are marked with &lt;code&gt;//+++&lt;/code&gt; at the end of each new line:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
&amp;lt;?php

    require_once('Cache/Lite.php'); //+++
    
    $url = &quot;http://mynework.facebook.com/feeds/status.php?replace=with&amp;your=own&amp;feed=url&quot;;

    $options = array('cacheDir' =&gt; '/tmp/', 'lifeTime' =&gt; 600); //+++

    $Cache_Lite = new Cache_Lite($options); //+++

    $data = null; //+++
    // attempt to load the data from cache,  //+++
    // otherwise load it anew from RSS //+++
    if (!($data = $Cache_Lite-&gt;get($id))) { //+++
    // setup curl
        $ch = curl_init();
        curl_setopt ($ch, CURLOPT_URL, $url);
        curl_setopt ($ch, CURLOPT_HEADER, 0);

        //spoof Firefox
        curl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) Gecko/20061223 Firefox/2.0.0.1&quot;);

        // begin output buffering
        ob_start();
        curl_exec ($ch);
        curl_close ($ch);
        // save buffer to string
        $xmlstr = ob_get_contents();

        ob_end_clean();

        // convert string to xml object
        $xml = new SimpleXMLElement($xmlstr);

        // status messages start with 'Dave is...' 
        // I just want everything after my name
        $data = str_replace('Dave ', null,$xml-&gt;channel-&gt;item-&gt;title); // +++

        $Cache_Lite-&gt;save($data); //+++

    } //+++

    echo $data; //+++
    
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;That's it on the PHP side.  On the Adium side you'll need to use the &lt;a href=&quot;http://www.adiumxtras.com/index.php?a=xtras&amp;amp;xtra_id=1255&quot;&gt;exec AdiumXtra&lt;/a&gt;.  It'll allow you to set your status to something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/exec {/usr/local/php5/bin/php /usr/local/bin/facebook.php}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Enjoy.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Not taking frameworks for granted</title>
   <link href="http://davedash.com/2006/11/21/not-taking-frameworks-for-granted/"/>
   <updated>2006-11-21T00:00:00-08:00</updated>
   <id>http://davedash.com/2006/11/21/not-taking-frameworks-for-granted</id>
   <content type="html">&lt;p&gt;One of my clients approached me with a relatively easy project.  She gave me a log file of PHP errors and I was supposed to fix her scripts.  I fixed about 100+ different errors in a few hours.  It was fairly straightforward.&lt;/p&gt;

&lt;p&gt;Throughout the site I could understand the previous developer and the choices he or she made for better or worse.  It did look like a struggle however.&lt;/p&gt;

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


&lt;p&gt;Ultimately it felt that there were some fairly simple things that each script needed to do, but each task was a challenge.  Validating forms, storing data across pages, decorating the site, interacting with the database.  Everything seemed very kludged together.&lt;/p&gt;

&lt;p&gt;I realized that this is exactly how I used to write code, sure... my way of course was better and more logical, yada, yada, yada... but ultimately I was there before.&lt;/p&gt;

&lt;p&gt;My client noted that these scripts were made from another contract programmer, and then a light-bulb went on... frameworks (whether it be &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, &lt;acronym title=&quot;Ruby On Rails&quot;&gt;ROR&lt;/acronym&gt;, Django, CakePHP, etc) help iron out and standardize these tasks.&lt;/p&gt;

&lt;p&gt;Since I know &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; best, I'll cover what I think could have helped in this last project.  I'm sure other major frameworks have their equivalents.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Form validation&lt;/strong&gt;: &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; lets you define form validation in a very simple manner.  The validation logic is also separate from the rest of the code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storing Data&lt;/strong&gt;: Without a framework, you generally have to rely on the &lt;code&gt;$_SESSION&lt;/code&gt; array in PHP.  While very useful and easy to use, storing parameters and attributes to a user object is done a lot more cleanly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;decorating the site&lt;/strong&gt;: My biggest problem was with each page I had,, I used to have to call headers, sidebars, etc, etc.  &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;'s layout system was a boon.  I had a common layout for all pages (maybe a few alternates) and hooks inside them if they needed to be adjusted.  Then the various actions had their own seperate templates that were injected into the common layout.  It made adding new pages easy, since I didn't need to remember &lt;code&gt;header()&lt;/code&gt; and &lt;code&gt;footer()&lt;/code&gt; functions for each and every page.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interacting with the database&lt;/strong&gt;: &lt;a href=&quot;http://spindrop.us/2006/08/07/how-object-relational-mapping-saves-time-and-makes-your-code-sexy/&quot;&gt;I've covered before the benefits of ORM&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Not only do the bulk of these problems disappear with a framework, a lot of the difficulties of switching developers melt away.  If you tell me, a developer, that I'm walking into a project made with a framework, I can learn about the framework and be able to understand its ins and outs.&lt;/p&gt;

&lt;p&gt;If you just tell me it's written in PHP, chances are I'm going to want to do things my own way.  It's hard to understand the logic that another programmer was using so we fall back to standards whether they are your own or borrowed.&lt;/p&gt;

&lt;p&gt;When we use a framework, we can find some mutually agreed upon standards and usually people who specialize in that framework and are willing to help.  So my advice: stick to frameworks.  The coding style will be no worse than the whims of a programmer, but at best it'll be something that anyone can pick up.  The general case is that even a bad coder can only do so much damage within a framework.  The bullet points I covered above will cut down on development time tremendously.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to make Prototype Window Class as easy as Lightbox Gone Wild</title>
   <link href="http://davedash.com/2006/11/13/how-to-make-prototype-window-class-as-easy-as-lightbox-gone-wild/"/>
   <updated>2006-11-13T00:00:00-08:00</updated>
   <id>http://davedash.com/2006/11/13/how-to-make-prototype-window-class-as-easy-as-lightbox-gone-wild</id>
   <content type="html">&lt;p&gt;I like the way that &lt;a href=&quot;http://particletree.com/features/lightbox-gone-wild/&quot; title=&quot;Lightbox Gone Wild&quot;&gt;Lightbox Gone Wild&lt;/a&gt; will automatically pickup any links with the &lt;code&gt;class=lbOn&lt;/code&gt;, but I wanted to use (at some point) &lt;a href=&quot;http://prototype-window.xilinus.com/&quot; title=&quot;Prototype Window Class&quot;&gt;Prototype Window Class&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;Luckily &lt;a href=&quot;http://prototype-window.xilinus.com/&quot; title=&quot;Prototype Window Class&quot;&gt;PWC&lt;/a&gt; is built on Prototype which means we've already loaded a helpful library.&lt;/p&gt;

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


&lt;p&gt;In order to take all &lt;code&gt;class=lbOn&lt;/code&gt; objects and run them through &lt;a href=&quot;http://prototype-window.xilinus.com/&quot; title=&quot;Prototype Window Class&quot;&gt;PWC&lt;/a&gt; we just write a simple loop and iterate.&lt;/p&gt;

&lt;p&gt;So here's the low-down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download &lt;a href=&quot;http://prototype-window.xilinus.com/&quot; title=&quot;Prototype Window Class&quot;&gt;PWC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Copy window.js&lt;/li&gt;
&lt;li&gt;Use the included prototype &amp;amp; script.aculo.us if you don't already include it in your page&lt;/li&gt;
&lt;li&gt;copy any themes you wish to use.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In your page add this bit of JavaScript:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;js&quot;&gt;
    var mylb = Class.create();
    
    mylb.prototype = {
        initialize: function(ctrl) {
            this.content = ctrl.href;
            Event.observe(ctrl, 'click', this.activate.bindAsEventListener(this), false);
            ctrl.onclick = function(){return false;};
            },
    
            // Turn everything on - mainly the IE fixes
            activate: function(){
                var win = new Window('window_id', {className: &quot;alphacube&quot;,title: &quot;Tour&quot;, url: this.content, width:700, height:500});
                win.setDestroyOnClose();
                win.showCenter(true);
            }
        }

        lbox = document.getElementsByClassName('lbOn');
        for(i = 0; i &lt; lbox.length; i++) {
            valid = new mylb(lbox[i]);
        }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;So, this code simply looks for all the anchor tags with &lt;code&gt;class=lbOn&lt;/code&gt; and then  creates a new &lt;code&gt;mylb&lt;/code&gt; instance for each anchor.  The end.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Cropping Images using DHTML (Prototype) and symfony</title>
   <link href="http://davedash.com/2006/09/16/cropping-images-using-dhtml-prototype-and-symfony/"/>
   <updated>2006-09-16T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/09/16/cropping-images-using-dhtml-prototype-and-symfony</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://demos.spindrop.us/image_cropper/&quot;&gt;&lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/screenshot.png&quot; style=&quot;float:left;margin-right:1em&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Like many of my tutorials, you don't &lt;em&gt;need&lt;/em&gt; &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, just &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt;.  However, I develop in &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; and take advantage of the &lt;acronym title=&quot;Model Viewer Controller&quot;&gt;MVC&lt;/acronym&gt;-support that it offers.&lt;/p&gt;

&lt;p&gt;Years ago when I was working on a photo gallery for &lt;a href=&quot;http://davedash.com/&quot;&gt;davedash.com&lt;/a&gt; I got the art of making tumbnails down fairly well.  It was automated and didn't allow for specifying how the thumbnail should be made.  With dozens of photos (which was a lot back then), when would I find that kind of time.&lt;/p&gt;

&lt;p&gt;Flashback to today, for &lt;a href=&quot;http://workface.com/&quot;&gt;my company&lt;/a&gt;... we want users with avatars... but nothing too large.  Maybe a nice 80x80 picture.  Well the coolest &lt;acronym title=&quot;User Interface&quot;&gt;UI&lt;/acronym&gt; I've seen was Apple's Address Book which let you use this slider mechanism to crop a fixed sized image from a larger image.&lt;/p&gt;

&lt;p&gt;Here's a &lt;a href=&quot;http://demos.spindrop.us/image_cropper/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;

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


&lt;h3&gt;Overview&lt;/h3&gt;

&lt;p&gt;The front-end &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; is based on code from &lt;a href=&quot;http://digg.com/&quot;&gt;digg&lt;/a&gt; which is based on the look and feel (as near as I can tell) from Apple.&lt;/p&gt;

&lt;p&gt;The &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; provides a clever visual way of telling the server how to chop the image.  The gist is this, sliding the image around and zooming in and out change a few form values that get passed to another script which uses this data to produce the image.&lt;/p&gt;

&lt;h3&gt;Frontend: What would you like to crop?&lt;/h3&gt;

&lt;p&gt;In this tutorial, we're going to be cropping an 80x80 avatar from an uploaded image.  The front-end requires the correct mix of Javascript, &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt;, &lt;acronym title=&quot;HypterText Markup Languag&quot;&gt;HTML&lt;/acronym&gt; and images.  The Javascript sets up the initial placements of the image and the controls.  The &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; presents some necessary styling.  The images makeup some of the controls.  The &lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt; glues everything together.&lt;/p&gt;

&lt;h4&gt;&lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt;&lt;/h4&gt;

&lt;p&gt;Let's work on our &lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt; first.  Since I used &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, I created a &lt;code&gt;crop&lt;/code&gt; action for a &lt;code&gt;userpics&lt;/code&gt; module.  So in our &lt;code&gt;cropSuccess.php&lt;/code&gt; template:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;ava&quot;&amp;gt;
    &amp;lt;?php echo form_tag(&quot;userpics/crop&quot;) ?&amp;gt;
        &amp;lt;div id=&quot;ava_img&quot;&amp;gt;
            &amp;lt;div id=&quot;ava_overlay&quot;&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;div id=&quot;ava_drager&quot;&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;img src=&quot;&amp;lt;?php echo $image ?&amp;gt;&quot; id=&quot;avatar&quot; /&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div id=&quot;ava_slider&quot;&amp;gt;&amp;lt;div id=&quot;ava_handle&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_width&quot; name=&quot;width&quot; value=&quot;80&quot; /&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_x&quot; name=&quot;x&quot; value=&quot;100&quot; /&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_y&quot; name=&quot;y&quot; value=&quot;100&quot; /&amp;gt;
        &amp;lt;input type=&quot;hidden&quot; id=&quot;ava_image&quot; name=&quot;file&quot; value=&quot;&amp;lt;?php echo $image ?&amp;gt;&quot; /&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;input type=&quot;submit&quot; name=&quot;submit&quot; id=&quot;ava_submit&quot; value=&quot;Crop&quot; style=&quot;width: auto; font-size: 105%; font-weight: bold; margin: 1em 0;&quot; /&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Right now a lot of this doesn't quite make sense.  If you attempt to render it, you will just see only the image.  As we add the corresponding &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; and images it will make some more sense.&lt;/p&gt;

&lt;h4&gt;&lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; and corresponding images&lt;/h4&gt;

&lt;p&gt;We'll go through each style individually and explain what purpose it serves in terms of the &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#ava&lt;/code&gt; is our container.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava {
    border: 1px solid gray;
     width: 200px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#ava_img&lt;/code&gt; is the area that contains our image.  Our window for editing this image 200x200 pixels.  If we drag out image out of bounds we just want the overflowing image to be clipped.  We also want our position to be relative so any child elements can be positioned absolutely with respect to &lt;code&gt;#ava_img&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_img {
    width: 200px;
    height: 200px;
    overflow: hidden;
    position: relative;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;div style=&quot;float:left;margin: 1em 1em 1em 0&quot;&gt;
    &lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/overlay.png&quot; alt=&quot;overlay&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;#ava_overlay&lt;/code&gt; is a window we use to see what exactly will be our avatar.  If it's in the small 80x80 window in the center of the image, then it's part of the avatar.  If it's in the fuzzy region, then it's getting cropped out.  This overlay of course needs to be positioned absolutely.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_overlay {
    width: 200px;
    height: 200px;
    position: absolute;
    top: 0px;
    left: 0px;
    background: url('/images/overlay.png');
    z-index: 50;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#ava_drager&lt;/code&gt; is probably the least intuitive element (Heck, I'm not even sure if I've even got it right).  In &lt;a href=&quot;http://demos.spindrop.us/image_cropper/&quot;&gt;our demo&lt;/a&gt; you're not actually dragging the image, because you can drag anywhere within the &lt;code&gt;#ava_img&lt;/code&gt; container and move the image around.  You're using dragging an invisible handle.  It's a 400x400 pixel square that can be dragged all over the container and thusly move the image as needed.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_drager {
    width: 400px;
    height: 400px;
    position: absolute;
    z-index: 100;
    color: #fff;
    cursor: move;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#avatar&lt;/code&gt; is our image, and since it will be moving all around the window, it requires absolute positioning.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#avatar {
    position: absolute;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;div style=&quot;float:left;margin: 1em 1em 1em 0&quot;&gt;
    &lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/slider_back.png&quot; alt=&quot;overlay&quot; /&gt;
&lt;/div&gt;


&lt;div style=&quot;float:left;margin-left: 1em 1em 1em 0;&quot;&gt;
    &lt;img src=&quot;http://demos.spindrop.us/image_cropper/images/handle.png&quot; alt=&quot;overlay&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;#ava_slider&lt;/code&gt; and &lt;code&gt;#ava_handle&lt;/code&gt; are our slider components.  They should be self-explanatory.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_slider {
    width: 200px;
    height: 27px;
    background: #eee;
    position: relative;
    border-top: 1px solid gray; 
    background: url('/images/slider_back.png');
}
#ava_handle {
    width: 19px;
    height: 20px;
    background: blue;
    position: absolute;
    background: url('/images/handle.png');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Internet Explorer&lt;/h5&gt;

&lt;p&gt;&lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt; do not work so well in Internet Explorer, but there is a small trick, adding these components into a style sheet that only IE can read will make things work:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#ava_overlay {
  background: none;
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/ui/cropper/overlay.png', sizingMethod='crop');
}

#ava_handle {
  background: none;
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/ui/cropper/handle.png', sizingMethod='crop');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;The Javascript&lt;/h4&gt;

&lt;p&gt;The Javascript is actually not as complicated as you'd expect thanks to the wonder of &lt;a href=&quot;http://prototype.conio.net/&quot;&gt;prototype&lt;/a&gt;.  This framework provides so much so easily.  You'll need to include &lt;a href=&quot;http://prototype.conio.net/&quot;&gt;prototype.js&lt;/a&gt; and &lt;a href=&quot;http://boring.youngpup.net/2001/domdrag/project&quot;&gt;dom-drag.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let's take a look.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; charset=&quot;utf-8&quot;&amp;gt;
// &amp;lt;![CDATA[
function setupAva() {
    if ($(&quot;avatar&quot;)) {
        var handle = $(&quot;ava_handle&quot;);
        var avatar = $(&quot;avatar&quot;);
        var drager = $(&quot;ava_drager&quot;);
        var slider = $(&quot;ava_slider&quot;);
        var ava_width = $(&quot;ava_width&quot;);
        var ava_x = $(&quot;ava_x&quot;);
        var ava_y = $(&quot;ava_y&quot;);
        // four numbers are minx, maxx, miny, maxy
        Drag.init(handle, null, 0, 134, 0, 0);
        Drag.init(drager, avatar, -100, 350, -100, 350);
        var start_w = avatar.width;
        var start_h = avatar.height;
        var ratio = (start_h / start_w);
        var new_h;
        var new_w;
        if (ratio &amp;gt; 1) {
            new_w = 80;
            new_h = (80*start_h)/start_w;
        } else {
            new_h = 80;
            new_w = (80*start_w)/start_h;
        }
        // these need to be set after we init
        avatar.style.top = '100px';
        avatar.style.left = '100px';
        avatar.style.width = new_w + 'px';
        avatar.style.height = new_h + 'px';
        avatar.style.margin = '-' + (new_h / 2) + 'px 0 0 -' + (new_w / 2) + 'px';
        handle.style.margin = '3px 0 0 20px';
        avatar.onDrag = function(x, y) {
            ava_x.value = x;
            ava_y.value = y;
        }
        handle.onDrag = function(x, y) {
            var n_width = (new_w + (x * 2));
            var n_height = (new_h + ((x * 2) * ratio));         
            avatar.style.width = n_width + 'px';
            avatar.style.height = n_height+ 'px';
            ava_width.value = n_width;  
            avatar.style.margin = '-' + (n_height / 2) + 'px 0 0 -' + (n_width / 2) + 'px';
        }
    }
}
Event.observe(window,'load',setupAva, false);
// ]]&amp;gt;
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If this isn't exactly crystal clear, I can explain.  If you're new to &lt;a href=&quot;http://prototype.conio.net/&quot;&gt;prototype&lt;/a&gt;, &lt;code&gt;$()&lt;/code&gt; is the same as &lt;code&gt;doucment.getElementByID()&lt;/code&gt; (at least for our purposes).&lt;/p&gt;

&lt;p&gt;We need to initialize two draggable elements, one is our slider for zooming and the other is our avatar itself.  We initialize the draggers using &lt;code&gt;Drag.init()&lt;/code&gt;.  We specify what to drag, if another element should be used as a handle and then the range of motion in xy coordinates.  In the second call we use that &lt;code&gt;#dragger&lt;/code&gt; to move around the image in this manner.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Drag.init(handle, null, 0, 134, 0, 0);
Drag.init(drager, avatar, -100, 350, -100, 350);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We want to initialize the the size and placement of the avatar.  We do that using maths.  First we want it in our 80x80 pixel box.  So it should be roughly 80x80.  I've set the math up so that the &lt;em&gt;smallest&lt;/em&gt; side is 80 pixels (there's reasons for doing this the other way around).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    if (ratio &amp;gt; 1) {
        new_w = 80;
        new_h = (80*start_h)/start_w;
    } else {
        new_h = 80;
        new_w = (80*start_w)/start_h;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We then place the avatar element.  We initialize it to be in the center of the screen (&lt;code&gt;top: 100px;left:100px&lt;/code&gt;) and then nudge the image using margins.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    avatar.style.top = '100px';
    avatar.style.left = '100px';
    avatar.style.width = new_w + 'px';
    avatar.style.height = new_h + 'px';
    avatar.style.margin = '-' + (new_h / 2) + 'px 0 0 -' + (new_w / 2) + 'px';
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We also use margins to place the handle.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    handle.style.margin = '3px 0 0 20px';
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;#ava_x&lt;/code&gt; and &lt;code&gt;#ava_y&lt;/code&gt; tell us where the center of the avatar is.  So when the avatar is moved we need to set these again:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    avatar.onDrag = function(x, y) {
        ava_x.value = x;
        ava_y.value = y;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That was easy.  Slighly more complicated is the zoomer function.  We are basically adjusting the width and the height proportionately based on roughly where the slider is.  Note that we're still using that &lt;code&gt;ratio&lt;/code&gt; variable that we calculated earlier.  We basically take the new x-coordinate of the handle and allow our image to get just slightly larger than the &lt;code&gt;#ava_image&lt;/code&gt; container.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    handle.onDrag = function(x, y) {
        var n_width = (new_w + (x * 2));
        var n_height = (new_h + ((x * 2) * ratio));         
        avatar.style.width = n_width + 'px';
        avatar.style.height = n_height+ 'px';
        ava_width.value = n_width;  
        avatar.style.margin = '-' + (n_height / 2) + 'px 0 0 -' + (n_width / 2) + 'px';
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We want to load initialize the slider right away when the page loads: &lt;code&gt;Event.observe(window,'load',setupAva, false);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Not terribly hard or complicated.  Once these elements are all in place you have a working functioning slider.  It returns the x and y coordinates of the center of the image with respect to our 200x200 pixel &lt;code&gt;#ava_image&lt;/code&gt;.  It also tells us the new width of our image.  We feed this information into a new script and out should pop a new image which matches &lt;em&gt;exactly&lt;/em&gt; what we see in our &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt;.&lt;/p&gt;

&lt;!--nextpage--&gt;


&lt;h3&gt;Processing the crop&lt;/h3&gt;

&lt;p&gt;Initially I was frustrated with the data that was being sent.  I knew the center of the image in relation to this 200x200 pixel canvas and its width... but what could I do with that.  Well I could just recreate what I saw in the &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt;.  I needed to create a 200x200 pixel image first, place my original avatar resized (and resampled) at the precise coordinates and &lt;em&gt;then&lt;/em&gt; cut out the center most 80x80 pixels to become the final avatar image.&lt;/p&gt;

&lt;p&gt;If you note in our template above for &lt;code&gt;cropSuccess.php&lt;/code&gt; we submit our form back to the &lt;code&gt;crop&lt;/code&gt; action.  Let's look at the action:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function executeCrop()
{
    if ($this-&amp;gt;getRequestParameter('file')&amp;amp;&amp;amp;$this-&amp;gt;getRequestParameter('width')) {          // we are saving our cropped image
        // Load the original avatar into a GD image so we can manipulate it with GD
        $o_filename = $this-&amp;gt;getRequestParameter('file');  // we'll use this to find the file on our system
        $o_filename = sfConfig::get('sf_root_dir').'/web' . $o_filename;
        $o_im = @imagecreatetruecolor(80, 80) or die(&quot;Cannot Initialize new GD image stream&quot;);
        $o_imagetype = exif_imagetype($o_filename); // is this gif/jpeg/png

        // appropriately create the GD image
        switch ($o_imagetype) {
            case 1: // gif
                $o_im = imagecreatefromgif($o_filename);
                break;
            case 2: // jpeg
                $o_im = imagecreatefromjpeg($o_filename);   
                break;
            case 3: // png
                $o_im = imagecreatefrompng($o_filename);
                break;
        }

        // Let's create our canvas
        $im = @imagecreatetruecolor(200, 200) or die(&quot;Cannot Initialize new GD image stream&quot;);
        imagecolortransparent ( $im, 127 ); // set the transparency color to 127
        imagefilledrectangle( $im, 0, 0, 200, 200, 127 ); // fill the canvas with a transparent rectangle

        // let's get the new dimension for our image

        $new_width = $this-&amp;gt;getRequestParameter('width');
        $o_width = imageSX($o_im);
        $o_height = imageSY($o_im);

        $new_height = $o_height/$o_width * $new_width;

        // we place the image at the xy coordinate and then shift it so that the image is now centered at the xy coordinate
        $x = $this-&amp;gt;getRequestParameter('x') - $new_width/2;
        $y = $this-&amp;gt;getRequestParameter('y') - $new_height/2;

        // copy the original image resized and resampled onto the canvas
        imagecopyresampled($im,$o_im,$x,$y,0,0,$new_width,$new_height,$o_width,$o_height); 
        imagedestroy($o_im);

        // $final will be our final image, we will chop $im and take out the 80x80 center
        $final = @imagecreatetruecolor(80, 80) or die(&quot;Cannot Initialize new GD image stream&quot;);
        imagecolortransparent ( $final, 127 ); // maintain transparency

        //copy the center of our original image and store it here
        imagecopyresampled ( $final, $im, 0, 0, 60, 60, 80, 80, 80, 80 );
        imagedestroy($im);

        //save our new user pic
        $p = new Userpic();
        $p-&amp;gt;setUser($this-&amp;gt;getUser()-&amp;gt;getUser());
        $p-&amp;gt;setGD2($final);
        $p-&amp;gt;save();
        imagedestroy($final);
        $this-&amp;gt;userpic = $p;
        return &quot;Finished&quot;;
    }


    $this-&amp;gt;getResponse()-&amp;gt;addJavascript(&quot;dom-drag&quot;);
    $this-&amp;gt;getResponse()-&amp;gt;addJavascript('/sf/js/prototype/prototype');
    $this-&amp;gt;getResponse()-&amp;gt;addJavascript('/sf/js/prototype/effects');
    $this-&amp;gt;image = '/images/userpics/originals/' . $this-&amp;gt;getRequestParameter('file');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's doing exactly what the paragraph above explains when the image dimensions are given.  The code is well commented so it should be easy enough to follow.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.php.net/manual/en/ref.image.php&quot;&gt;GD image functions in PHP&lt;/a&gt; are fairly robust and can help you do a lot of tricks with image data.  Note the code to save the image, we'll cover it in detail soon.&lt;/p&gt;

&lt;h3&gt;The Model&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;$p = new Userpic();
$p-&amp;gt;setUser($this-&amp;gt;getUser()-&amp;gt;getUser());
$p-&amp;gt;setGD2($final);
$p-&amp;gt;save();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;First some clarification the second line.  &lt;code&gt;myUser::getUser()&lt;/code&gt; gets the &lt;code&gt;User&lt;/code&gt; object associated with the currently logged in user.  The third line, however, is where the magic happens.  Before we look at it, let's have a quick look at our model:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;userpic:
 _attributes: { phpName: Userpic }
 id:
 user_id:
 image: blob
 thumb: blob
 created_at:
 updated_at:
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We have an &lt;code&gt;image&lt;/code&gt; attribute and a &lt;code&gt;thumb&lt;/code&gt; property to our &lt;code&gt;Userpic&lt;/code&gt; object.  This is where we store &lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt; versions of each icon and their 16x16 thumbnails respectively.  We do this in &lt;code&gt;Userpic::setGD2()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function setGD2($gd2_image)
{
    //convert to PNG
    ob_start();
    imagepng($gd2_image);
    $png = ob_get_clean();
    //save 16x16
    $gd2_tn = @imagecreatetruecolor(16, 16) or die(&quot;Cannot Initialize new GD image stream&quot;);
    imagealphablending( $gd2_tn, true );
    imagecolortransparent ( $gd2_tn, 127 );

    imagecopyresampled ( $gd2_tn, $gd2_image, 0, 0, 0, 0, 16, 16, 80, 80 );
    ob_start();
    imagepng($gd2_tn);
    $tn = ob_get_clean();

    $this-&amp;gt;setImage($png);
    $this-&amp;gt;setThumb($tn);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We capture the output of the full size &lt;acronym title=&quot;Portable Network Graphics&quot;&gt;PNG&lt;/acronym&gt;, then we scale it again and capture the output of the thumbnail and set them.&lt;/p&gt;

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

&lt;p&gt;When it comes to web apps, having a relatively simple &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; for people to resize images can go a long way in terms of adoption rate of avatars and custom user pictures by non technical users.&lt;/p&gt;

&lt;p&gt;Enjoy, and if you found this useful (or better implemented it) let me know.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using Zend Search Lucene in a symfony app</title>
   <link href="http://davedash.com/2006/08/25/using-zend-search-lucene-in-a-symfony-app/"/>
   <updated>2006-08-25T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/08/25/using-zend-search-lucene-in-a-symfony-app</id>
   <content type="html">&lt;p&gt;[tags]zend, search, lucene, zend search lucene, zsl, symfony,php[/tags]&lt;/p&gt;

&lt;p&gt;If you're like me you've probably followed the &lt;a href=&quot;http://symfony-project.com/askeet/21&quot;&gt;Askeet tutorial on Search&lt;/a&gt; in order to create a decent search engine for your web app.  It's fairly straight forward, but they hinted that when &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;Zend Search Lucene&lt;/a&gt; (&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;) is released, that might be the way to go.  Well we are in luck, &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt; is available, so let's just dive right in.&lt;/p&gt;

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


&lt;p&gt;If you aren't using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; have a look at &lt;a href=&quot;http://devzone.zend.com/node/view/id/91&quot; title=&quot;Roll Your Own Search Engine with Zend_Search_Lucene&quot;&gt;this article&lt;/a&gt; from the &lt;a href=&quot;http://devzone.zend.com/&quot;&gt;Zend Developer Zone&lt;/a&gt;.  It covers just enough to get you started.  If you are using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, just follow along and we'll get you where you need to go.&lt;/p&gt;

&lt;h3&gt;Obtaining Zend Search Lucene&lt;/h3&gt;

&lt;p&gt;First &lt;a href=&quot;http://framework.zend.com/download&quot; title=&quot;Zend Framework Download&quot;&gt;download&lt;/a&gt; the &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; (&lt;acronym title=&quot;Zend Developer Framework&quot;&gt;ZF&lt;/acronym&gt;).  The &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt;  is supposed to be fairly &quot;easy&quot; in terms of installation.  So let's put that to the test.  Open your &lt;a href=&quot;http://framework.zend.com/&quot;&gt;&lt;acronym title=&quot;Zend Developer Framework&quot;&gt;ZF&lt;/acronym&gt;&lt;/a&gt; archive.  Copy &lt;code&gt;Zend.php&lt;/code&gt; and &lt;code&gt;Zend/Search&lt;/code&gt; to your &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; project's library folder:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cp Zend.php $SF_PROJECT/lib              
mkdir $SF_PROJECT/lib/Zend
cp -r Zend/Search $SF_PROJECT/lib/Zend
cp Zend/Exception.php $SF_PROJECT/lib/Zend                 
chmod -R a+r $SF_PROJECT/lib/Zend*
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Index Something&lt;/h3&gt;

&lt;p&gt;We'll deviate slightly from &lt;a href=&quot;http://spindrop.us/category/reviewsbyus&quot; title=&quot;ReviewsBy.Us category of Spindrop&quot;&gt;food themed&lt;/a&gt; tutorials and do something generic.  Let's try a user search where we can find a user by their name or email address.  It's fairly simple to accomplish, and hardly requires the use of &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;&lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;&lt;/a&gt;, but by using &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt; we can easily extend it to do a full-text search of a user's profile or any other textual data.&lt;/p&gt;

&lt;p&gt;Each &quot;thing&quot; stored in the index is a &quot;document&quot; in &lt;acronym title=&quot;Zend Search Lucene&quot;&gt;ZSL&lt;/acronym&gt;, specifically a &lt;code&gt;Zend_Search_Lucene_Document&lt;/code&gt;.  Each document then consists of several &quot;fields&quot; (&lt;code&gt;Zend_Search_Lucene_Field&lt;/code&gt; objects).  In our example, our document will be an individual user and the fields will be relevant attributes of the user (username, first name, last name, email, the text of their profile).&lt;/p&gt;

&lt;p&gt;We're going to write a general re-indexing tool.  Something that will index all users.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;userActions&lt;/code&gt; class let's add the following action:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeReindex()
    {
        require_once 'Zend/Search/Lucene.php';
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
        
        $users = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            $doc = new Zend_Search_Lucene_Document();
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('id', $user-&gt;getId()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $user-&gt;getUsername()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $user-&gt;getEmail()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $user-&gt;getFirstname()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $user-&gt;getLastname()));
            $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$user-&gt;getEmail()} {$user-&gt;getFirstname()} {$user-&gt;getLastname()} {$user-&gt;getUsername()}&quot;));
            $index-&gt;addDocument($doc);
        }
        
        $index-&gt;commit();
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;The code should be fairly easy to follow.  First of all we're requiring the necessary libraries for Lucene.  The next line we are creating the index:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;app_search_user_index_file&lt;/code&gt; is a symfony configuration that you define in your &lt;code&gt;app.yml&lt;/code&gt;.  It defines which file you want to use for your index.  &lt;code&gt;/tmp/lucene.user.index&lt;/code&gt; works for our purposes.   The second parameter tells Lucene we are creating a new index.&lt;/p&gt;

&lt;p&gt;We then loop through all the users and for each user create a document.  For all the search relevant attributes that a user might have we add a field into the document.  Note the last field:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$user-&gt;getEmail()} {$user-&gt;getFirstname()} {$user-&gt;getLastname()} {$user-&gt;getUsername()}&quot;));
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;By default search is made for the &quot;contents&quot; field.  So in this example we want people to be able to type in someone's name, email, username without having to specify what field we're searching for.&lt;/p&gt;

&lt;h3&gt;Find those users&lt;/h3&gt;

&lt;p&gt;Finding the user's is equally as straight-forward.  We make a new action called &lt;code&gt;search&lt;/code&gt;:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeSearch()
    {
        require_once('Zend/Search/Lucene.php');
        $query = $this-&gt;getRequestParameter('q');
    
        $this-&gt;getResponse()-&gt;setTitle('Search for \'' . $query . '\' &amp;laquo; ' . sfConfig::get('app_title'), true);
    
        $hits = array();
    
        if ($query)
        {
            $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
            $hits = $index-&gt;find(strtolower($query));
        }
        $this-&gt;hits = $hits;
    }

The magic happens in our `if` statement:

    if ($query)
    {
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
        $hits = $index-&gt;find(strtolower($query));
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;If we have a query, open the &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;ZSL&lt;/a&gt; index (note that we only have one parameter here).  Run the &lt;code&gt;find&lt;/code&gt; method to find our query and store it to the &lt;code&gt;$hits&lt;/code&gt; array.  Note that our query was cleaned with &lt;code&gt;strtolower&lt;/code&gt;, since &lt;a href=&quot;http://framework.zend.com/manual/en/zend.search.html&quot;&gt;ZSL&lt;/a&gt; is case sensitive.&lt;/p&gt;

&lt;p&gt;The template takes care of the rest:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    &lt;?php use_helper('Form');?&gt;
    &lt;?php echo form_tag('@search_users') ?&gt;
    &lt;?php echo input_tag('q'); ?&gt;
    &lt;?php echo submit_tag() ?&gt;
    &lt;/form&gt;
    &lt;?php foreach ($hits as $hit): ?&gt;
      &lt;?php echo $hit-&gt;score ?&gt;
      &lt;?php echo $hit-&gt;firstname ?&gt;
      &lt;?php echo $hit-&gt;lastname ?&gt;
      &lt;?php echo $hit-&gt;email ?&gt;
    &lt;?php endforeach ?&gt;
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Fairly simple... but it could use some cleaning up (enjoy).&lt;/p&gt;

&lt;h3&gt;What about new users?&lt;/h3&gt;

&lt;p&gt;Regularly reindexing might be nice in terms of having an optimized search index, but its lousy if you want to be able to search the network immediately when new people join on.  So why not automatically re-index each user every time they are created or everytime one of their indexed components is summoned?&lt;/p&gt;

&lt;p&gt;This should be fairly simple by adding to the &lt;code&gt;User&lt;/code&gt; class:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    var $reindex = false;
    public function setUsername ( $v )
    {
        parent::setUsername($v);
        $this-&gt;reindex = true;
    }
    public function setFirstname ( $v )
    {
        parent::setFirstname($v);
        $this-&gt;reindex = true;
    }
    public function setLastname ( $v )
    {
        parent::setLastname($v);
        $this-&gt;reindex = true;
    }
    public function setEmail ( $v )
    {
        parent::setEmail($v);
        $this-&gt;reindex = true;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We have an attribute called &lt;code&gt;$reindex&lt;/code&gt;.  When it is false we don't need to worry about indexes.  When something significant changes, like an update to your name or email address, then we set &lt;code&gt;$reindex&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.  Then when we save:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function save ($con = null)
    {
        parent::save($con);
        if ($this-&gt;reindex) {
            require_once 'Zend/Search/Lucene.php';
            $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'));
            // first find any references to this user and delete them
            $hits = $index-&gt;find('id:'. $this-&gt;getId());
            foreach ($hits AS $hit) {
                $index-&gt;delete($hit-&gt;id);
            }
        
            $doc = $this-&gt;generateZSLDocument();
            $index-&gt;addDocument($doc);
            $index-&gt;commit();
        }
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;We're calling a new function called &lt;code&gt;generateZSLDocument&lt;/code&gt;.  It might look familiar:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function generateZSLDocument()
    {
    
        require_once 'Zend/Search/Lucene.php';
        $doc = new Zend_Search_Lucene_Document();
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('id', $this-&gt;getId()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('username', $this-&gt;getUsername()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Keyword('email', $this-&gt;getEmail()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('firstname', $this-&gt;getFirstname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Text('lastname', $this-&gt;getLastname()));
        $doc-&gt;addField(Zend_Search_Lucene_Field::Unstored('contents', &quot;{$this-&gt;getEmail()} {$this-&gt;getFirstname()} {$this-&gt;getLastname()} {$this-&gt;getUsername()}&quot;));
        return $doc;
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;Now, whenever a user is updated, so is our index.  Additionally we can modify our reindex action:&lt;/p&gt;

&lt;div&gt;&lt;textarea name=&quot;code&quot; class=&quot;php&quot;&gt;
    public function executeReindex()
    {
        require_once('Zend/Search/Lucene.php');
        $index = new Zend_Search_Lucene(sfConfig::get('app_search_user_index_file'),true);
        
        $users = UserPeer::doSelect(new Criteria());
        foreach ($users AS $user)
        {
            
            $index-&gt;addDocument($user-&gt;generateZSLDocument);
        }
        
        $index-&gt;commit();
    }
&lt;/textarea&gt;&lt;/div&gt;


&lt;p&gt;That's a &lt;strong&gt;lot&lt;/strong&gt; easier to deal with.&lt;/p&gt;

&lt;h3&gt;...and beyond&lt;/h3&gt;

&lt;p&gt;Hope this article helps some of you jumpstart your &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; apps.  Really cool, easy to implement search is here.  We no longer have to stick with shoddy solutions like HT://Dig or spend time rolling our own full text search, as the &lt;a href=&quot;http://symfony-project.com/askeet/21&quot;&gt;symfony team diligently showed us we could&lt;/a&gt;.  But there is a lot more ground to cover.  Including optimization techniques and best practices.&lt;/p&gt;

&lt;p&gt;Let me know what you think, and if you use this in any of your apps.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Digg-style AJAX comment editing in PHP/symfony</title>
   <link href="http://davedash.com/2006/08/13/digg-style-ajax-comment-editing-in-phpsymfony/"/>
   <updated>2006-08-13T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/08/13/digg-style-ajax-comment-editing-in-phpsymfony</id>
   <content type="html">&lt;p&gt;&quot;&lt;a href=&quot;http://digg.com/&quot;&gt;Digg&lt;/a&gt;&quot;-style anything can be pretty slick.  The &lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt;-interactions on that site make it very fun to use.  It's styles have been copied everywhere, and are definitely worth copying.  The latest feature that had caught my eye was the ability to edit your comments for a set time after posting them.  Of course, it wasn't just the ability to edit comments, it was &lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt; too and it has a timer.&lt;/p&gt;

&lt;p&gt;This is totally &lt;a href=&quot;http://spindrop.us/2006/07/02/comment-editing-in-reviewsbyus/&quot; title=&quot;Comment Editing in reviewsBy.us&quot;&gt;something I could use on a restaurant review site&lt;/a&gt;.  So I started on this project.  It's pretty straight forward.&lt;!--more--&gt;  For all of your posted comments you check if the owner of them is viewing them within 3 minutes of posting the commen.  3 minutes is usually enough time to notice you made a typo, but if you disagree I'll leave it to you to figure out how to adjust the code.&lt;/p&gt;

&lt;p&gt;For example, I make a comment, realize I spelled something wrong and then I can click on my comment to edit it.  Of course using &lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt; means this all happens without having to reload the web page.  Therefore the edits are seemingly quick.  So let's add it to any web site.&lt;/p&gt;

&lt;h3&gt;In Place Forms&lt;/h3&gt;

&lt;p&gt;First and foremost, the ability to edit a comment means you have a form that you can use to edit and submit your changes.  But rather than deal with creating a boring un&lt;acronym title=&quot;Asynchronus Java and XML&quot;&gt;AJAX&lt;/acronym&gt;y form, we'll enlist the help of &lt;a href=&quot;http://script.aculo.us/&quot;&gt;script.aculo.us&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, each comment is rendered using the following &lt;acronym title=&quot;HypterText Markup Language&quot;&gt;HTML&lt;/acronym&gt; and PHP:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;review_block&quot; id=&quot;comment_&amp;lt;?php echo $comment-&amp;gt;getId() ?&amp;gt;&quot;&amp;gt;  
    &amp;lt;p class=&quot;author&quot;&amp;gt;&amp;lt;?php echo link_to_user($comment-&amp;gt;getUser()) ?&amp;gt; - &amp;lt;?php echo $comment-&amp;gt;getCreatedAt('%d %B %Y') ?&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;div class=&quot;review_text&quot; id=&quot;review_text_&amp;lt;?php echo $comment-&amp;gt;getId()?&amp;gt;&quot;&amp;gt;&amp;lt;?php echo $comment-&amp;gt;getHtmlNote() ?&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that this &lt;code&gt;div&lt;/code&gt; and it's child &lt;code&gt;div&lt;/code&gt; have unique ids that we can refer back to (&lt;code&gt;comment_n&lt;/code&gt; and &lt;code&gt;review_text_n&lt;/code&gt; where &lt;code&gt;n&lt;/code&gt; is the id of the comment).  We can use this to interact with the &lt;acronym title=&quot;Document Object Model&quot;&gt;DOM&lt;/acronym&gt; via JavaScript.  What we do is for each comment, we check if it is owned by the current visitor &lt;em&gt;and&lt;/em&gt; if it's within our prescribed 3 minute window.  We can do that with some simple PHP:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php if ($comment-&amp;gt;getUser() &amp;amp;&amp;amp; $comment-&amp;gt;getUserId() == $sf_user-&amp;gt;getId() &amp;amp;&amp;amp; time() &amp;lt; 181 + $comment-&amp;gt;getCreatedAt(null) ): ?&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
    //&amp;lt;![CDATA[
        makeEditable('&amp;lt;?php echo $comment-&amp;gt;getId() ?&amp;gt;', &quot;&amp;lt;?php echo url_for($module . '/save?id=' . $comment-&amp;gt;getId()) ?&amp;gt;&quot;, &quot;&amp;lt;?php echo url_for('restaurantnote/show?id=' . $comment-&amp;gt;getId() . '&amp;amp;mode=raw') ?&amp;gt;&quot;, &amp;lt;?php echo 181-(time() - $comment-&amp;gt;getCreatedAt(null)) ?&amp;gt;);
    //]]&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;?php endif ?&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see we run the &lt;code&gt;makeEditable()&lt;/code&gt; function for each applicable comment.  As you can guess, &lt;code&gt;makeEditable()&lt;/code&gt; makes a comment editable.  For parameters it takes the comment's id (so it can refer to it in the &lt;acronym title=&quot;Document Object Model&quot;&gt;DOM&lt;/acronym&gt; and other background scripts).  It also takes as an argument the &quot;save&quot; &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; as well as a &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; from which it can load the raw comment.  The last argument is for the timer.&lt;/p&gt;

&lt;p&gt;Here is our function:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var editor;
var pe;
makeEditable = function(id, url, textUrl, time) {
    var div = $(&quot;review_text_&quot; + id);

    pe = new PeriodicalExecuter(function() { updateTime(id); }, 1);

    Element.addClassName($('comment_' + id), 'editable');
    new Insertion.Bottom(div, '&amp;lt;div class=&quot;edit_control&quot; id=&quot;edit_control_'+id+'&quot;&amp;gt;Edit Comment (&amp;lt;span id=&quot;time_'+id+'&quot;&amp;gt;'+time+' seconds&amp;lt;/span&amp;gt;)&amp;lt;/div&amp;gt;');

    editor = new Ajax.InPlaceEditor(div, url, { externalControl: 'edit_control_'+id, rows:6, okText: 'Save', cancelText: 'Cancel', 
    loadTextURL: textUrl, onComplete: function() { makeUneditable(id) } });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It does a couple things.  It runs a &lt;a href=&quot;http://www.sergiopereira.com/articles/prototype.js.html#Reference.PeriodicalExecuter&quot; title=&quot;PeriodicalExecutor&quot;&gt;PeriodicalExecuter&lt;/a&gt; to run the &lt;code&gt;updateTime&lt;/code&gt; function which updates our countdown timer.  It adds a &lt;acronym title=&quot;Cascading Style Sheets&quot;&gt;CSS&lt;/acronym&gt; class to our comment &lt;code&gt;div&lt;/code&gt;.  It adds a control button to edit a comment.  Lastly it uses the &lt;a href=&quot;http://script.aculo.us/&quot;&gt;script.aculo.us&lt;/a&gt; &lt;a href=&quot;http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor&quot;&gt;Ajax.InPlaceEditor&lt;/a&gt; to do most of the magic.  The hard part is done.&lt;/p&gt;

&lt;h3&gt;Periodic Execution Timer&lt;/h3&gt;

&lt;p&gt;So the &lt;code&gt;updateTime&lt;/code&gt; function is reasonably simple.  It finds the time written out in the &lt;acronym title=&quot;Document Object Model&quot;&gt;DOM&lt;/acronym&gt; and decrements it by 1 second each second.  Once it hits zero seconds it disables itself and the ability to edit the block.  Let's take a look:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;updateTime = function(id) {
  var div = $(&quot;time_&quot;+id);
  if (div) {
    var time =  parseInt(div.innerHTML) - 1;
    div.innerHTML = time;
  }
  if (time &amp;lt; 1) {
    pe.stop();
    var editLink = $('edit_control_'+id);
    if (Element.visible(editLink)) {
      makeUneditable(id);
      editLink.parentNode.removeChild(editLink);
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Call backs&lt;/h3&gt;

&lt;p&gt;We'll need a few call backs for the editor to work properly.  Since many content pieces are converted from something else to &lt;acronym title=&quot;HypterText Markup Language&quot;&gt;HTML&lt;/acronym&gt; and not directly written in &lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt; we'll need a callback that will load our text.  We'll also need a callback which will save our text (and then display it).&lt;/p&gt;

&lt;h4&gt;Load Text&lt;/h4&gt;

&lt;p&gt;The first call back we can see is referenced in the &lt;code&gt;makeEditable()&lt;/code&gt; function.  In our example it's:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;url_for('restaurantnote/show?id=' . $comment-&amp;gt;getId() . '&amp;amp;mode=raw');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which is a &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; route to the &lt;code&gt;restaurantnote&lt;/code&gt; module and the &lt;code&gt;show&lt;/code&gt; action with an argument &lt;code&gt;mode=raw&lt;/code&gt;.  Let's take a look at this action:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function executeShow ()
{
    $this-&amp;gt;restaurant_note = RestaurantNotePeer::retrieveByPk($this-&amp;gt;getRequestParameter('id'));
    $this-&amp;gt;forward404Unless($this-&amp;gt;restaurant_note instanceof RestaurantNote);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All this does is load the text (in our case the [markdown] formatting) into a template.&lt;/p&gt;

&lt;h4&gt;Save Text&lt;/h4&gt;

&lt;p&gt;The save text url in our example is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;url_for('restaurantnote/save?id=' . $comment-&amp;gt;getId());
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Using the &lt;a href=&quot;http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor&quot;&gt;Ajax.InPlaceEditor&lt;/a&gt; the value of the text-area is saved to the &lt;code&gt;value&lt;/code&gt; POST variable.  We consume it in our action like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function executeSave() 
{
    $note = RestaurantNotePeer::retrieveByPk($this-&amp;gt;getRequestParameter('id'));
    $this-&amp;gt;forward404Unless($note instanceof RestaurantNote);
    if ($note-&amp;gt;getUserId() == $this-&amp;gt;getUser()-&amp;gt;getId()) {
        $note-&amp;gt;setNote($this-&amp;gt;getRequestParameter('value'));
        $note-&amp;gt;save();
    }
    $this-&amp;gt;note = $note;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The note is also sent to a template that renders it, so when the save takes place, the edit form will be replaced with the new text.&lt;/p&gt;

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

&lt;p&gt;As you can see with some &lt;a href=&quot;http://script.aculo.us/&quot;&gt;script.aculo.us&lt;/a&gt; and &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, it's fairly easy to mimic &quot;Digg-style&quot; in-place comment editing.  You can test out a real example by visiting &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Syncing with symfony and clearing your cache in one shot</title>
   <link href="http://davedash.com/2006/07/17/syncing-with-symfony-and-clearing-your-cache-in-one-shot/"/>
   <updated>2006-07-17T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/17/syncing-with-symfony-and-clearing-your-cache-in-one-shot</id>
   <content type="html">&lt;p&gt;If you're using &lt;code&gt;symfony&lt;/code&gt;'s &lt;code&gt;sync&lt;/code&gt; command to synchronize files across environments (e.g. moving your development files to a staging server), it helps usually to clear the cache of the receiving server.&lt;/p&gt;

&lt;p&gt;The following line will help:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;symfony sync production go ; ssh user@production &quot;cd /var/www; symfony cc&quot;   
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Assuming you have &lt;acronym title=&quot;Secure SHell&quot;&gt;SSH&lt;/acronym&gt; keys defined and that you change &lt;code&gt;user@production&lt;/code&gt; to your username and server host as well as &lt;code&gt;/var/www&lt;/code&gt; switched to your website path.  Also the &lt;code&gt;symfony&lt;/code&gt; command needs to work on your &quot;production&quot; server (or whatever environment).&lt;/p&gt;

&lt;p&gt;There may be a cleaner way to take care of this by changing the &lt;code&gt;symfony&lt;/code&gt; command, but this works sufficiently well.&lt;/p&gt;

&lt;!--next page--&gt;


&lt;p&gt;One of the more frustrating elements of web development is synchronizing multiple sites: usually a development site, a staging site and a production site.  &lt;acronym title=&quot;SubVersioN&quot;&gt;SVN&lt;/acronym&gt; helps with keeping your code versioned, but usually you don't want to check out a copy of your web site onto your live server.&lt;/p&gt;

&lt;p&gt;Usually we use &lt;acronym title=&quot;Secure File Transfer Protocol&quot;&gt;SFTP&lt;/acronym&gt; or &lt;code&gt;rsync&lt;/code&gt;.  The former has lots of problems, because a lot of manual work is usually involved to make sure you don't over-write important files.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rsync&lt;/code&gt;, however, is a champ and &lt;a href=&quot;http://symfony-project.com/&quot; title=&quot;The symfony Project&quot;&gt;symfony&lt;/a&gt; takes advantage of this.  The key files you'll have to deal with are &lt;code&gt;$PROJECT_HOME/config/properties.ini&lt;/code&gt; and &lt;code&gt;$PROJECT_HOME/config/rsync_exclude.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;properties.ini&lt;/code&gt; should look roughly like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[symfony]
  name=reviewsby.us
[staging]
  host=staging.reviewsby.us
  port=22
  user=root
  dir=/var/www/staging/
[staging2]
  host=staging2.reviewsby.us
  port=22
  user=root
  dir=/var/www/staging
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each heading other than &quot;&lt;code&gt;[symfony]&lt;/code&gt;&quot; is a different environment.  In our example, we have two staging environments.  The values under each heading should be self-explanatory.  We can now run the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;symfony sync staging
symfony sync staging go
symfony sync staging2
symfony sync staging2 go
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The commands that lack &lt;code&gt;go&lt;/code&gt; are &quot;dry-runs&quot; which just show you what files will be transfered.  The other commands will run &lt;code&gt;rsync&lt;/code&gt; and transfer all the files not specified in the exclude file, &lt;code&gt;rsync_exclude.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See &lt;a href=&quot;http://www.symfony-project.com/askeet/22&quot;&gt;askeet&lt;/a&gt; or the &lt;a href=&quot;http://www.symfony-project.com/content/book/page/deployment.html&quot;&gt;symfony documentation&lt;/a&gt; for more details.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dynamic Linking to Syndication Feeds with symfony</title>
   <link href="http://davedash.com/2006/07/04/dynamic-linking-to-syndication-feeds-with-symfony/"/>
   <updated>2006-07-04T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/07/04/dynamic-linking-to-syndication-feeds-with-symfony</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.symfony-project.com/content/book/page/syndication.html&quot; title=&quot;How to build a syndication feed&quot;&gt;Adding a statically-linked syndication feed&lt;/a&gt;, a feed that is the same no matter where on the site you are, is a cinch with &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, but what about dynamically linked syndication feeds?  Let's say we're building the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;latest and greatest Web 2.0 app&lt;/a&gt;, there's going to be hundreds of &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feeds, not just the most recent items.  We'll want the latest comments to a post, the favorite things of a website member and it all has to be feed enabled.  Sure, we can slap a link to the &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feed and call it a day, but let's go a step further and stick it in the &lt;code&gt;&amp;lt;head/&amp;gt;&lt;/code&gt; area as well.  That way when someone clicks on the &lt;acronym title=&quot;Real Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; icon in their browser, or adds a web page to &lt;a href=&quot;http://bloglines.com&quot;&gt;Bloglines&lt;/a&gt; those extra feeds can be found.&lt;/p&gt;

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


&lt;h2&gt;Expanding your head&lt;/h2&gt;

&lt;p&gt;A typical &lt;code&gt;layout.php&lt;/code&gt; for a symfony app will have a &lt;code&gt;&amp;lt;head/&amp;gt;&lt;/code&gt; section like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;?php echo include_http_metas() ?&amp;gt;
    &amp;lt;?php echo include_metas() ?&amp;gt;
    &amp;lt;?php echo include_title() ?&amp;gt;
    &amp;lt;?php echo auto_discovery_link_tag('rss', 'feed/latest')?&amp;gt;  
    &amp;lt;?php echo auto_discovery_link_tag('rss', '@feed_latest_georss', 
    array('title' =&amp;gt; 'Latest Restaurants\' Locations (GeoRSS)' ))?&amp;gt;     
    &amp;lt;?php echo include_feeds() ?&amp;gt;&amp;lt;!-- this is the custom feed includer --&amp;gt;
    &amp;lt;link rel=&quot;shortcut icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since this is in the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; &lt;code&gt;layout.php&lt;/code&gt;, the latest feed and the latest GeoRSS feed (which we developed in &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot; title=&quot;Easy Yahoo Maps! with symfony&quot;&gt;this article&lt;/a&gt;) will show up on every page.  So for example, if you use &lt;a href=&quot;http://www.mozilla.com/firefox/&quot; title=&quot;Firefox&quot;&gt;FireFox&lt;/a&gt;, you can subscribe to either link when you click on the orange feed icon (&lt;img src=&quot;http://feedicons.com/images/layout/feed-icon-12x12.gif&quot; title=&quot;Feed Available&quot; alt=&quot;feed&quot; /&gt;) in the &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; bar no matter where you are in the web-application.&lt;/p&gt;

&lt;p&gt;To expand this to allow for multiple feeds, we need to include &lt;code&gt;&amp;lt;?php echo include_feeds() ?&amp;gt;&lt;/code&gt; (before or after the &lt;code&gt;auto_discovery_link_tag&lt;/code&gt; calls makes the most sense).&lt;/p&gt;

&lt;h2&gt;Making the Feed Helper&lt;/h2&gt;

&lt;p&gt;Let's created a &lt;code&gt;FeedHelper.php&lt;/code&gt; to put the &lt;code&gt;include_feeds()&lt;/code&gt; function (don't forget to add &lt;code&gt;use_helper('Feed')&lt;/code&gt; to your &lt;code&gt;layout.php&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The function looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function include_feeds()
{
    $type = 'rss';
    $already_seen = array();
    foreach (sfContext::getInstance()-&amp;gt;getRequest()-&amp;gt;getAttributeHolder()-&amp;gt;getAll('helper/asset/auto/feed') as $files)
    {
        if (!is_array($files))
        {
            $files = array($files);
        }
        foreach ($files as $file)
        {
            if (isset($already_seen[$file])) continue;
            $already_seen[$file] = 1;
            echo tag('link', array('rel' =&amp;gt; 'alternate', 'type' =&amp;gt; 'application/'.$type.'+xml', 'title' =&amp;gt; ucfirst($type), 'href' =&amp;gt; url_for($file, true)));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The function is doing what the deprecated &lt;code&gt;include_javascripts&lt;/code&gt; and &lt;code&gt;include_stylesheets&lt;/code&gt; functions did, just with syndication feeds.  Also note, I stuck to just using &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; feeds.  This function can no doubt be extended to Atom or other feed types, but for my purposes it was unnecessary&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;Dynamically setting the feeds&lt;/h2&gt;

&lt;p&gt;In the &lt;a href=&quot;http://reviewsby.us/&quot;&gt;reviewsby.us&lt;/a&gt; site, the menu items are tagged.  There's tags for &lt;a href=&quot;http://reviewsby.us/tag/chicken&quot;&gt;chicken&lt;/a&gt;, &lt;a href=&quot;http://reviewsby.us/tag/indian&quot;&gt;indian&lt;/a&gt; and &lt;a href=&quot;http://reviewsby.us/tag/bread&quot;&gt;bread&lt;/a&gt; for example.  Each of them are to have an associated GeoRSS feed as described in a &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot; title=&quot;Easy Yahoo Maps! with symfony&quot;&gt;previous tutorial&lt;/a&gt;.  I built our tagging system similar to &lt;a href=&quot;http://symfony-project.com/askeet&quot;&gt;Askeet&lt;/a&gt;.  So in our &lt;code&gt;tag&lt;/code&gt; module I created a function in the corresponding &lt;code&gt;actions.class.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function addFeed($feed)
{
    $this-&amp;gt;getRequest()-&amp;gt;setAttribute($feed, $feed, 'helper/asset/auto/feed');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This sets the attribute that &lt;code&gt;include_feeds()&lt;/code&gt; pulls from.  Here &lt;code&gt;$feed&lt;/code&gt; is simply the route to our feed.  So in our &lt;code&gt;executeShow()&lt;/code&gt; I just make a call to &lt;code&gt;$this-&amp;gt;addFeed('@feed_tag_georss?tag=' . $tag)&lt;/code&gt;.  We're done.&lt;/p&gt;

&lt;p&gt;We can now go to any of our tagged pages.  Let's try &lt;a href=&quot;http://reviewsby.us/tag/chicken&quot;&gt;chicken&lt;/a&gt; and see that we can subscribe to a &lt;a href=&quot;http://reviewsby.us/tag/chicken/geo.rss&quot;&gt;GeoRSS feed&lt;/a&gt; of restaurants serving &lt;a href=&quot;http://reviewsby.us/tag/chicken&quot;&gt;dishes tagged as chicken&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Slight problem.  The &lt;code&gt;title&lt;/code&gt; attribute of the generated &lt;code&gt;link&lt;/code&gt; tags are always &lt;code&gt;Rss&lt;/code&gt;.  That can be mildly unusable.&lt;/p&gt;

&lt;h2&gt;Throwing feed titles into the mix&lt;/h2&gt;

&lt;p&gt;Let's change our &lt;code&gt;addFeed()&lt;/code&gt; to allow for a second parameter, a title and have it store both the route and the title in the request attribute:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public function addFeed($feed, $title = null)
{
    $feedArray = array('url' =&amp;gt; $feed, 'title' =&amp;gt; $title);
    $this-&amp;gt;getRequest()-&amp;gt;setAttribute($feed, $feedArray, 'helper/asset/auto/feed');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We'll also need to adapt the &lt;code&gt;include_feeds&lt;/code&gt; to appropriately accommodate associative arrays:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function include_feeds()
{
    $type = 'rss';
    $already_seen = array();
    foreach (sfContext::getInstance()-&amp;gt;getRequest()-&amp;gt;getAttributeHolder()-&amp;gt;getAll('helper/asset/auto/feed') as $feeds)
    {
        if (!is_array($feeds) || is_associative($feeds))
        {
            $feeds = array($feeds);
        }

        foreach ($feeds as $feed)
        {
            if (is_array($feed)) {
                $file = $feed['url'];
                $title = empty($feed['title']) ? $type : $feed['title'];
            } else {
                $file = $feed;
                $title = $type;
            }

            if (isset($already_seen[$file])) continue;

            $already_seen[$file] = 1;
            echo tag('link', array('rel' =&amp;gt; 'alternate', 'type' =&amp;gt; 'application/'.$type.'+xml', 'title' =&amp;gt; $title, 'href' =&amp;gt; url_for($file, true)));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note, there's a function &lt;code&gt;is_associative()&lt;/code&gt;.  It's a custom function that we can place in another helper:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function is_associative($array)
{
  if (!is_array($array) || empty($array)) return false;
  $keys = array_keys($array);
  return array_keys($keys) !== $keys;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's a clever way of determining if a function is an associative array or not.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It looks like our GeoRSS feeds are on all our tag pages.  Now we can take our favorite items labeled as &lt;a href=&quot;http://reviewsby.us/tag/indian&quot;&gt;Indian food&lt;/a&gt; and easily add the &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt; to a service like &lt;a href=&quot;http://bloglines.com/&quot;&gt;Bloglines&lt;/a&gt; and have it &lt;a href=&quot;http://www.bloglines.com/sub/http://reviewsby.us/tag/indian&quot;&gt;keep us up to date on new Indian dishes&lt;/a&gt;.  This was simple, especially when much of the work was taken care of by &lt;a href=&quot;http://symfony-project.com/&quot;&gt;the framework&lt;/a&gt;.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;Most clients support &lt;acronym title=&quot;Really Simple Syndication&quot;&gt;RSS&lt;/acronym&gt; so unless there is a compelling need to use Atom or another format, then keeping it down to one choice is always your best bet. &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;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>ReviewsBy.Us bugfixes</title>
   <link href="http://davedash.com/2006/05/30/reviewsbyus-bugfixes/"/>
   <updated>2006-05-30T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/30/reviewsbyus-bugfixes</id>
   <content type="html">&lt;p&gt;A lot of bugs have popped up recently.&lt;/p&gt;

&lt;h3&gt;Logins&lt;/h3&gt;

&lt;p&gt;The logins weren't redirecting people to the correct place.  Unfortunately the login system still needs a lot of work.  I am probably going to rewrite it completely.  It doesn't consistantly remember where you are coming from or where you intend to go after logging in.  I'll be jotting down a clean system to log people in propperly.&lt;/p&gt;

&lt;h3&gt;Tags&lt;/h3&gt;

&lt;p&gt;Tags work a bit better.  They follow the flickr style of tagging, which is each word is a tag, unless surrounded in double quotes.  Previously they weren't producing a lot of empty tags.&lt;/p&gt;

&lt;h3&gt;Latitude and Longitude even more precise.&lt;/h3&gt;

&lt;p&gt;A small error in the database definitions resulted in lat/longitudes of restaurants that were greater than 100 (or rather &lt;code&gt;abs(x) &amp;gt; 100&lt;/code&gt; where &lt;code&gt;x&lt;/code&gt; is latitude or longitude were being truncated to &lt;code&gt;100&lt;/code&gt; or &lt;code&gt;-100&lt;/code&gt;.  This was easily fixed so things should look just right on the maps.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Quicksilver + TextMate = craZy delicious development environment</title>
   <link href="http://davedash.com/2006/05/28/quicksilver-textmate-crazy-delicious-development-environment/"/>
   <updated>2006-05-28T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/28/quicksilver-textmate-crazy-delicious-development-environment</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt;Cmd-T allows you to search for the files in your currently opened TextMate project.  I learned this shortly after writing this post, but forgot to mention it.  Thanks again to Tyson Tune for pointing that out.&lt;/p&gt;

&lt;div class=&quot;article_logo&quot;&gt;
    &lt;img src=&quot;http://spindrop.us/wp-content/uploads/2006/05/qs+tm.png&quot; alt=&quot;Quicksilver + Textmate&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;There's a number of tools for the &lt;a href=&quot;http://www.apple.com/macosx/&quot;&gt;OS X&lt;/a&gt; that help me with my productivity (and unfortunately have no equivalents on other platforms).  &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;, a launcher, and &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt;, a text editor work wonders and together work fairly well.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; is a the &lt;acronym title=&quot;Graphical User Interface&quot;&gt;GUI&lt;/acronym&gt; equivalent to the command line.  You can launch applications or files or perform any number of operations on those files or applications.  With its powerful collection of plugins you can have it do much more, for example you can take a music file and play it in iTunes within the iTunes party shuffle.  Or take an image file and have it submit to &lt;a href=&quot;http://flickr.com/&quot;&gt;flickr&lt;/a&gt; with a few simple keystrokes.  Initially, I couldn't get an idea of the application, other than a lot of people loved it.  Now, I'm barely using it to its potential and I love it.  Using a computer without it is quite a drag.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; is similarly feature rich and elegant.  Just using a small number of its features makes it worth its cost.  All my &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt; projects are written using &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; as are my articles for this web site.  It's strength for me is its automation.
Together Quicksilver and Textmate make a winning combination.&lt;/p&gt;

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


&lt;h3&gt;Projects in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt;&lt;/h3&gt;

&lt;div class=&quot;screenshot_thumb&quot;&gt;
    &lt;a href=&quot;http://static.flickr.com/49/154779362_e044c75c04_o.png&quot; title=&quot;Larger Photo&quot;&gt;
        &lt;img src=&quot;http://static.flickr.com/49/154779362_e044c75c04_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;reviewsby.us textmate project screenshot&quot; /&gt;
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I like the concept of &quot;projects&quot; in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; (its common to a lot of text editors).  You can drag files or folders into &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; and group them as you see fit.&lt;/p&gt;

&lt;p&gt;Many of my projects are written using &lt;a href=&quot;http://symfony-project.com/&quot;&gt;symfony&lt;/a&gt;, so I'll try to keep the entire project folder in my &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; project.  Additionally I'll keep the &lt;a href=&quot;http://www.symfony-project.com/trac/browser/trunk/lib&quot;&gt;symfony libraries&lt;/a&gt; if not the entire &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; libraries referenced in the project as well.  Now I have access to all of my files with relative ease.  I generally create a file group in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; of frequently accessed files to bypass the pain going through hierarchies of folders.&lt;/p&gt;

&lt;p&gt;If I have a &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; project open, anytime I open a file that belongs in that project, it will open in that project window.  That means if I use the &lt;code&gt;mate&lt;/code&gt; command line utility, Finder or even &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;, it'll still open in the project window.  This is useful.&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class=&quot;screenshot_thumb_alt&quot;&gt;
&lt;a href=&quot;http://static.flickr.com/51/154781466_ca0f67a703_o.png&quot; title=&quot;Zoom In&quot;&gt;&lt;img src=&quot;http://static.flickr.com/51/154781466_ca0f67a703_m.jpg&quot; width=&quot;240&quot; height=&quot;214&quot; alt=&quot;Quicksilver Catalogue&quot; /&gt;&lt;/a&gt;  
&lt;/div&gt;


&lt;h3&gt;Catalogues in &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;When you open up &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; and type in some letters, it searches some catalogues by default (e.g. Applications, Documents, Desktop, etc) in an attempt to figure out what the &quot;subject&quot; of your action to be.  These catalogues are fully customizable, so it's trivial to add the directories of your project and your libraries into &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;New Workflow&lt;/h3&gt;

&lt;div class=&quot;screenshot_thumb&quot;&gt;
&lt;a href=&quot;http://static.flickr.com/68/154785728_9e7a5a815d_o.png&quot; title=&quot;Zoom In&quot;&gt;&lt;img src=&quot;http://static.flickr.com/68/154785728_9e7a5a815d_m.jpg&quot; width=&quot;240&quot; height=&quot;82&quot; alt=&quot;Quicksilver finding reviewsby.us&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Now that you've setup your project in &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; and added the same directories to &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt;, you'll have a much improved workflow.  If you save your &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; project in your Documents directory, you need only open &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; (I use Command+Space as a shortcut) and type a few letters of the project (for example, I named my project file &lt;code&gt;reviewsby.us&lt;/code&gt; for my restaurant review site).&lt;/p&gt;

&lt;p&gt;When that's open, I can now open any file of that project in anyway I feel necessary.  Let's say I need to open a library file, like &lt;code&gt;sfFeed.class.php&lt;/code&gt;.  I need only type in a few letters and it opens inside my project.&lt;/p&gt;

&lt;p&gt;This process now saves me a ton of time in digging through hierarchies of folders upon folders.  It's many times quicker than Spotlight.  &lt;a href=&quot;http://quicksilver.blacktree.com/&quot;&gt;Give it a try&lt;/a&gt;, there's thousands of uses for it, this is just one way I use it.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;While debugging errors in a project, if PHP tells you a certain file gave you a certain error, you can highlight the filename in Safari (or any other Cocoa browser), hit Command Escape (or whatever custome keystroke you setup for sending selection to Quicksilver), hit Enter and have it show up in TextMate. &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;/li&gt;
    &lt;/ol&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Google Analytics</title>
   <link href="http://davedash.com/2006/05/26/google-analytics/"/>
   <updated>2006-05-26T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/26/google-analytics</id>
   <content type="html">&lt;div class=&quot;photo right&quot; style=&quot;float: right;&quot;&gt;
&lt;img src=&quot;http://static.flickr.com/51/153678327_1af6ace0c6.jpg&quot; width=&quot;500&quot; height=&quot;223&quot; alt=&quot;Google Analytics Screenshot&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;I've also jumped on the &lt;a href=&quot;http://www.google.com/analytics/&quot;&gt;Google Analytics&lt;/a&gt; bandwagon and added a lot of the sites in &quot;family.&quot;  If you're familiar with Urchin, it has a similar feel to it.  It almost feels a bit lighter feature-wise.  There's a few issues I will take with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When they list links to your site (e.g. /login) they don't hyperlink it.&lt;/li&gt;
&lt;li&gt;I'd like to drill down to raw data.  I like to make my own correlations.  Heck, I want to drill down to see if I should filter someone from the list.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Other than that, I like the potential that this will offer me.  This combined with &lt;a href=&quot;http://google.com/webmasters/sitemaps/&quot;&gt;Google Sitemaps&lt;/a&gt; will provide powerful analysis of the site.  As it stands, &lt;a href=&quot;http://katiebonn.com/&quot;&gt;Katie&lt;/a&gt; and I should be eating at the &lt;a href=&quot;http://reviewsby.us/restaurant/cheesecake-factory&quot;&gt;Cheesecake Factory&lt;/a&gt; and &lt;a href=&quot;http://reviewsby.us/restaurant/bad-waitress&quot;&gt;non-chain restaurants&lt;/a&gt; as we seem to do well in the ranks and I need to write more &lt;a href=&quot;http://spindrop.us/2006/04/26/easy_yahoo_maps_and_georss_with_symfony&quot;&gt;about maps&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>AJAX star rater for symfony</title>
   <link href="http://davedash.com/2006/05/23/ajax-star-rater-for-symfony/"/>
   <updated>2006-05-23T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/23/ajax-star-rater-for-symfony</id>
   <content type="html">&lt;p&gt;Francois from the &lt;a href=&quot;http://www.symfony-project.com/&quot;&gt;symfony project&lt;/a&gt; beat me to the punch.  I was going to post a detailed how-to on adding a star-rater to your web site (similar to the &lt;a href=&quot;http://spindrop.us/2006/05/17/rate-your-favorite-dishes&quot;&gt;one's I created for reviewsby.us&lt;/a&gt;), but for most of you &lt;a href=&quot;http://www.symfony-project.com/snippets/5&quot;&gt;this should do the trick&lt;/a&gt;.  Unless people request it sooner, I'll hold off on publishing the details on my star-rater for a while.  It only offers a few minor differences (&lt;acronym title=&quot;In My Opinion&quot;&gt;IMO&lt;/acronym&gt; advantages) to this snippet.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Migrating from Drupal (4.7) to Wordpress</title>
   <link href="http://davedash.com/2006/05/19/migrating-from-drupal-47-to-wordpress/"/>
   <updated>2006-05-19T00:00:00-07:00</updated>
   <id>http://davedash.com/2006/05/19/migrating-from-drupal-47-to-wordpress</id>
   <content type="html">&lt;p&gt;I helped &lt;a href=&quot;http://katiebonn.com/&quot;&gt;Katie&lt;/a&gt; setup her new blog this weekend and decided that &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; offers much of what I want out of this blog for a lot less effort than &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt;&lt;sup id=&quot;fnr1&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.  I decided it might be worth my time to now while this blog is in it's infancy to try converting from &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; to &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;.&lt;/p&gt;

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


&lt;p&gt;The way I start most of my projects is with a plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#discovery&quot;&gt;Discovery&lt;/a&gt;: Find any articles about converting and get a gist of what to do.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#implementation&quot;&gt;Implementation&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Create a temporary subdomain to install &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#copy_content&quot;&gt;Copy all content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Copy all static pages&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#urls&quot;&gt;Configure &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s to match the existing site&lt;/a&gt; &lt;sup id=&quot;fnr2&quot;&gt;&lt;a href=&quot;#fn2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#adjusting_templates&quot;&gt;adjust templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;split entries correctly (i.e. change &lt;code&gt;&amp;lt;!--break--&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#make_the_switch&quot;&gt;Make the switch when I'm satisfied&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I'm really confident that this will be easy.  I don't even have to worry about comments or anything, since this blog is pretty new, but I can demonstrate how to take care of the.&lt;/p&gt;

&lt;h3 id=&quot;discovery&quot;&gt;Discovery&lt;/h3&gt;


&lt;p&gt;&lt;a href=&quot;http://vrypan.net/log/archives/2005/03/10/migrating-from-drupal-to-wordpress/&quot;&gt;This post&lt;/a&gt; details a migration path from &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; to &lt;a href=&quot;http://wordpress.org/&quot;&gt;wordpress&lt;/a&gt;.  Some considerations had to be made since I'm using &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; 4.7.&lt;/p&gt;

&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;




&lt;h4 id=&quot;copy_content&quot;&gt;Copy content&lt;/h4&gt;


&lt;p&gt;I followed most of the instructions, with some alterations from &lt;a href=&quot;http://vrypan.net/log/archives/2005/03/10/migrating-from-drupal-to-wordpress/&quot;&gt;vrypan.net&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I installed &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; and in &lt;code&gt;mysql&lt;/code&gt; ran the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;use wordpress;
delete from wp_categories;
delete from wp_posts;     
delete from wp_post2cat;
delete from wp_comments
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I run my &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; site in the same database server, so the data copying was a snap.  If you aren't so fortunate, just copy the relevant drupal tables temporarily your wordpress database.&lt;/p&gt;

&lt;p&gt;First we get the &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; categories into &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;USE wordpress;

INSERT INTO 
    wp_categories (cat_ID, cat_name, category_nicename, category_description, category_parent)
SELECT term_data.tid, name, name, description, parent 
FROM drupal.term_data, drupal.term_hierarchy 
WHERE term_data.tid=term_hierarchy.tid;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again with the posts:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;INSERT INTO 
    wp_posts (id, post_date, post_content, post_title, 
    post_excerpt, post_name, post_modified)
SELECT DISTINCT
    n.nid, FROM_UNIXTIME(created), body, n.title, 
    teaser, 
    REPLACE(REPLACE(REPLACE(REPLACE(LOWER(n.title),' ', '_'),'.', '_'),',', '_'),'+', '_'),
    FROM_UNIXTIME(changed) 
FROM drupal.node n, drupal.node_revisions r
WHERE n.vid = r.vid
    AND type='story' OR type='page' ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And the relation between posts and categories:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;INSERT INTO wp_post2cat (post_id,category_id) SELECT nid,tid FROM drupal.term_node ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And finally comments:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;INSERT INTO 
    wp_comments 
    (comment_post_ID, comment_date, comment_content, comment_parent)
SELECT 
    nid, FROM_UNIXTIME(timestamp), 
    concat('',subject, '&amp;lt;br /&amp;gt;', comment), thread 
FROM drupal.comments ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I ended up moving the one static page I had into &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt;'s &quot;pages&quot; section manually.&lt;/p&gt;

&lt;p&gt;Since my pages are written in &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt;, I enabled the &lt;a href=&quot;http://www.michelf.com/projects/php-markdown/&quot;&gt;Markdown for WordPress plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;urls&quot;&gt;&lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s&lt;/h4&gt;


&lt;p&gt;Now for the real test.  I needed to go through each page on my site and see if I could get to it using the same &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s.  Since I had only 14 posts, I did this somewhat manually.  I used &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt;'s built in admin to do this from most popular to least popular.  Most &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s worked fine.  There were a small number that didn't for various reasons, I used custom &lt;code&gt;mod_rewrite&lt;/code&gt; rules to handle them.&lt;/p&gt;

&lt;h4 id=&quot;adjusting_templates&quot;&gt;Adjusting Templates&lt;/h4&gt;


&lt;p&gt;My &lt;a href=&quot;http://drupal.org/&quot;&gt;drupal&lt;/a&gt; template was fairly clean and simple.  So I adjusted the &lt;acronym title=&quot;Cascading Style Sheet&quot;&gt;CSS&lt;/acronym&gt; for the default theme in &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; until I got what I liked.  Very minimal changes had to be made to the actual &quot;&lt;acronym title=&quot;HyperText Markup Language&quot;&gt;HTML&lt;/acronym&gt;.&quot;&lt;/p&gt;

&lt;h3 id=&quot;make_the_switch&quot;&gt;Make the switch&lt;/h3&gt;


&lt;p&gt;Well, time to make the switch.  In the WordPress administration, I just had to tell it that it's now going to be located at &lt;code&gt;spindrop.us&lt;/code&gt;.  Then I moved my &lt;a href=&quot;http://wordpress.org/&quot;&gt;WordPress&lt;/a&gt; installation to the &lt;a href=&quot;http://spindrop.us/&quot;&gt;spindrop.us&lt;/a&gt; web root.  It was a snap.  Let me know if you have any troubles.&lt;/p&gt;

&lt;div id=&quot;footnotes&quot;&gt;
    &lt;hr/&gt;
    &lt;ol&gt;
        &lt;li id=&quot;fn1&quot;&gt;Taxonomy, legible &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s and trackback support all seemed quite difficult to master in Drupal.  In Wordpress they appeared to be available standard or with a minor change in the administration panels. &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;/li&gt;
        &lt;li id=&quot;fn2&quot;&gt;This is a prime reason why &lt;acronym title=&quot;Universal Resource Locator&quot;&gt;URL&lt;/acronym&gt;s should be clean and make sense to the end user, not the programmer of the publishing software. &lt;a href=&quot;#fnr2&quot; class=&quot;footnoteBackLink&quot;  title=&quot;Jump back to footnote 2 in the text.&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;
    &lt;/ol&gt;
&lt;/div&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>

