Dynamic Linking to Syndication Feeds with symfony

Adding a statically-linked syndication feed, a feed that is the same no matter where on the site you are, is a cinch with symfony, but what about dynamically linked syndication feeds? Let’s say we’re building the latest and greatest Web 2.0 app, there’s going to be hundreds of RSS 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 RSS feed and call it a day, but let’s go a step further and stick it in the <head/> area as well. That way when someone clicks on the RSS icon in their browser, or adds a web page to Bloglines those extra feeds can be found.

Expanding your head

A typical layout.php for a symfony app will have a <head/> section like this:

<head>
	<?php echo include_http_metas() ?>
	<?php echo include_metas() ?>
	<?php echo include_title() ?>
	<?php echo auto_discovery_link_tag('rss', 'feed/latest')?> 	
	<?php echo auto_discovery_link_tag('rss', '@feed_latest_georss', 
	array('title' => 'Latest Restaurants\' Locations (GeoRSS)' ))?> 	
	<?php echo include_feeds() ?><!-- this is the custom feed includer -->
	<link rel="shortcut icon" href="/favicon.ico" />
</head>

Since this is in the reviewsby.us layout.php, the latest feed and the latest GeoRSS feed (which we developed in this article) will show up on every page. So for example, if you use FireFox, you can subscribe to either link when you click on the orange feed icon (feed) in the URL bar no matter where you are in the web-application.

To expand this to allow for multiple feeds, we need to include <?php echo include_feeds() ?> (before or after the auto_discovery_link_tag calls makes the most sense).

Making the Feed Helper

Let’s created a FeedHelper.php to put the include_feeds() function (don’t forget to add use_helper('Feed') to your layout.php).

The function looks like this:

function include_feeds()
{
	$type = 'rss';
	$already_seen = array();
	foreach (sfContext::getInstance()->getRequest()->getAttributeHolder()->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' => 'alternate', 'type' => 'application/'.$type.'+xml', 'title' => ucfirst($type), 'href' => url_for($file, true)));
		}
	}
}

The function is doing what the deprecated include_javascripts and include_stylesheets functions did, just with syndication feeds. Also note, I stuck to just using RSS feeds. This function can no doubt be extended to Atom or other feed types, but for my purposes it was unnecessary1.

Dynamically setting the feeds

In the reviewsby.us site, the menu items are tagged. There’s tags for chicken, indian and bread for example. Each of them are to have an associated GeoRSS feed as described in a previous tutorial. I built our tagging system similar to Askeet. So in our tag module I created a function in the corresponding actions.class.php:

public function addFeed($feed)
{
	$this->getRequest()->setAttribute($feed, $feed, 'helper/asset/auto/feed');
}

This sets the attribute that include_feeds() pulls from. Here $feed is simply the route to our feed. So in our executeShow() I just make a call to $this->addFeed('@feed_tag_georss?tag=' . $tag). We’re done.

We can now go to any of our tagged pages. Let’s try chicken and see that we can subscribe to a GeoRSS feed of restaurants serving dishes tagged as chicken.

Slight problem. The title attribute of the generated link tags are always Rss. That can be mildly unusable.

Throwing feed titles into the mix

Let’s change our addFeed() to allow for a second parameter, a title and have it store both the route and the title in the request attribute:

public function addFeed($feed, $title = null)
{
	$feedArray = array('url' => $feed, 'title' => $title);
	$this->getRequest()->setAttribute($feed, $feedArray, 'helper/asset/auto/feed');
}

We’ll also need to adapt the include_feeds to appropriately accommodate associative arrays:

function include_feeds()
{
	$type = 'rss';
	$already_seen = array();
	foreach (sfContext::getInstance()->getRequest()->getAttributeHolder()->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' => 'alternate', 'type' => 'application/'.$type.'+xml', 'title' => $title, 'href' => url_for($file, true)));
		}
	}
}

Note, there’s a function is_associative(). It’s a custom function that we can place in another helper:

function is_associative($array)
{
  if (!is_array($array) || empty($array)) return false;
  $keys = array_keys($array);
  return array_keys($keys) !== $keys;
}

It’s a clever way of determining if a function is an associative array or not.

Conclusion

It looks like our GeoRSS feeds are on all our tag pages. Now we can take our favorite items labeled as Indian food and easily add the URL to a service like Bloglines and have it keep us up to date on new Indian dishes. This was simple, especially when much of the work was taken care of by the framework.


  1. Most clients support RSS 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.

Read full post
Comment editing in reviewsBy.us

Something that’s been heavily requested for reviewsBy.us is the ability to either preview or somehow edit comments after they are posted. I took a cue from digg and added some comment editing.

Since the last time we updated, the RSS feeds work better too thanks to a patch to the symfony. Also, the basis for a user profile is in place.

Read full post
Working Backwards as a Way to Prioritize Tasks

I handle multiple projects at once, and sometimes I get lost and forget what needs to get done. The biggest day of my life was a few weeks ago. I had a milesstone for a major project due for my 9-5 in this month. And I also have this blog, and a community-run restaurant review site to take care of. So figuring out what needs to happen and by when is extremely beneficial.

Sometimes a strategy that works to complete tasks is working backwards from your goal. So the pre-requisite here is to have a well-defined goal. Even though this is a programming blog, let’s take my wedding for an example. The end goal was that the ceremony and reception go off well enough that Katie and I are happy (it did). That’s pretty vague, so let’s look at a single aspect of the wedding. For example, one detail is having a wedding program. 500 wedding programs needed to be printed and delivered to the temple by Saturday morning (17 June 2006). This is a more defined goal, or component of the overall goal.

Let’s break it down:

  • What we need is to Deliver the programs
    • We’ll need to fold the programs
      • but before we can do that we need to print the programs
        • we’ll need to first get paper
        • and actually we need to know what we’re printing

Easy task, didn’t need to be broken down much, but it serves our purposes.

Prioritization - Get the Minimum Done First

Working backwards on it’s own is good for giving you a list of what to do and even when to do it by. But it doesn’t necessarily tell you what order to do it by. In fact, order might not matter, but if you’re in a slump and don’t feel like doing anything, having a prescribed order can help.

Now that we know the end goal, let’s see if we can extract what the minimum is. If we follow along with our wedding program example, the minimum would be a sheet of paper with a list of events. No details, no participants, just events. That’s easy, a cut-and-paste from an email that our officiant sent us and we’re done.

By getting the minimum done, we at least have something. Sure, we might want something fancier with an elegant design and good descriptions, but even if we botch that up for some reason we can rest easily knowing that we got our bases covered.

Finish Building an Onion

Finishing out the wedding program is a snap. We need to make some estimates first. We can improve on the program as much as we wanted, but we needed to stop by Thursday so we’d have enough time to print and fold them.

Timeline:

  • Delivery: half-hour
    • Folding: a couple days (it took only a few hours, but we can only print/fold so much at once)
      • Print: a couple days (see above)
        • get paper: :15 minutes
        • making the program better: remaining time

So in the remaining time, we came up with small revisions (essentially our onion skin layers) to our minimum. For example, finding descriptions of each event as a first revision. Our next revision can be rewriting Sanskrit headings in Devanagari script. Our final revision can be plopping this all in a nice layout design in Adobe Illustrator.

All these steps are gravy. We already had a product that was satisfactory, now we’re moving from satisfactory to pretty good to excellent.

Conclusion

So, no, this isn’t just applicable to wedding activities, but it’s a great way to prioritize tasks for any project. When I develop new web sites, like reviewsby.us, I first try to figure out what my goal is: to a have a list of the things I like to eat at various restaurants. All I need to do is build a simple on-line list. Beyond that, I can add features, and web 2.0 fluff that’ll make it more fun to use, but as long as I got the core, that’s all I need to have up to have this site be useful to me. Each additional step adds more value to myself and others.

Read full post
Version numbers need dates

What is the point of a version number? For the most part, it’s just a milestone. The difference between version 2.3 and 2.4 of a software is a given feature-set. Between 2.3 and 2.5 is an even larger feature-set that encompasses our previous feature-set.

So if I tell you, I have version 0.9.6.2 of svnX what does that tell you? For most, absolutely nothing. If I told you I was on I-35W at marker 7a you wouldn’t have a clue either… unless you looked at the map.

Version numbers tend to force us to look at “maps.” If my svn client acts up, I have to go to the website and see how old it is. Generally this isn’t a big deal. But some sites don’t publish when they made updates. Take Acquisition. It’s great software, but if I told you I was still using 128.3, you would have no clue how out of date I was.

On the otherhand if version numbers incorporated a date, we’d be set. If I told you I was using 1.0.20050405 of a piece of software and I was experiencing problems, you’d wonder why I hadn’t upgraded to something newer. If something newer didn’t exist, you’d question if development has stopped.

We can address versioning concerns by using this hybrid versioning. The developer’s own style of versioning (e.g. 1.0, 2.0, etc) can be combined with dates: 1.1.20060505. Now we know this is a 1.1 style release. Generally that means, the developer feels the software is complete enough to consider it at least 1.0, but with some improvements beyond that release. It tells the rest of the world, at a glance, that it was released in May of 2006.

Read full post
Breaking IE 6 with links on PNG backgrounds

In IE there’s a whole slew of troubles with PNG. One such trouble is links or anchors will not work in IE if you have a PNG image that has gone through the Microsoft [AlphaImageLoader], which is the only known way to render PNGs in IE6.

The solution, involves running the image filter on a separate element, and then positioning all the links within that element in a higher z-order. This is explained in better detail in Filter Flaws.

I’ve been running into too many designers who have been sending me too many designs that require translucent layers, and alpha-transparencies (and they should be able to, even though the trend is more simple these days). This of course subjects me to pretty much any strange quirk that IE6 can dish out with PNGs.

What’s frustrating is that 5 years ago I had PNGs problems. This is the Internet… very few problems on the Internet last that long. There’s certainly been enough people complaining about this malfunction in IE for years.

The problem with these problems is there is no “right” solution. Using [AlphaImageLoader] is a hack. As we can see with laying links upon backgrounds that use the loader, it’s prone to behavior we don’t normally expect. We shouldn’t have to raise the links above an invisible layer, in order to click on them.

Unfortunately as long as the majority of our users continue to use IE 6 (or older) we’ll be stuck with this problem. If we look at adoption rates it won’t be until a year after Internet Explorer 7 is out until it’s the top dog. Even then, if Firefox is still at 10% marketshare, that leaves another 40% with IE6 (or less) and the less popular browsers. We’re still stuck with this problem for some time.

Read full post
Goal Setting For Weight Loss

layout: post title: Goal-setting for weight loss tags: [food, desi, family, wedding, weight loss] — [wedding]: http://ktdd.org/

Well losing about 20-25 pounds (of mostly fat) in the year before [my wedding][wedding] definitely had the intended effect of receiving positive comments from friends and family. I secretly had the intent of not just being able to lose weight, but wanting to help others lose weight too. There’s no reason to suffer through anything if you can’t use it to better yourself or others later.

Since I’ve met my current goal (160 by wedding), I think I’m going to have a new goal, and I’ll use this opportunity to walk people through the way I lost weight — sort of. I took a lot of liberties with my daily diet, and I don’t recommend that others do the same. I do recommend that people write down any drastic changes that they might be contemplating and their desired goals and discuss them with their doctor or other qualified professional. Everything that I mention in this blog is purely anecdotal and should not be construed as best dietary practices.

Determining your motivation

Before you determine what a new goal is, you need to find a reason to even lose weight. If you don’t have a good enough reason, losing weight is tough. Reasons only need to be good enough for you. Here was my reasons for losing weight by my wedding:

  • Look thinner by the time of my wedding
  • Be able to say I’m in the medically recommended weight zone
  • No longer be subject to my brother teasing me
  • Feel better
  • Make it easier to do other physical activities that I enjoy
  • Not have to look at that big bulging gut.

Any of those reasons is sufficient. For example, who wouldn’t want to look their best for at [their wedding][wedding]. I had the benefit of receiving a lot of attention from friends and families. We had an Indian wedding, and Indian aunties really notice the weight changes — which in a way can be pretty nice when you are intending to lose weight.

In the week following my wedding, I’ve seen my weight rise out of that “recommended weight” zone. I want to be firmly entrenched in that “recommended weight” zone, and then I’d like to start gaining muscle mass. Once I’m at my new target weight, I’ll be firmly entrenched in that weight and if I can continue decent eating habits and do strength-training, I could reach somewhere in the 150s and have strength to show for. It goes back to the “making it easier to do physical activities” goal. I like being able to lift boxes, people, etc, without exerting too much effort.

Determining a weight

Having a reason to lose weight should be easy enough. Determining how much weight to lose is another proposition all together. This is an excellent time to get the opinion of a professional. However, if you are fairly certain that you are overweight, you can usually play it safe by saying a 10% weight loss. If you or anybody close to you has the slightest worry that this might be an unhealthy weight, check with a doctor.

I had been floating between 175-185 pounds for a number of years, I decided on 160 for a goal. At the time it seemed ambitious. I had tried losing weight before, but not to that extent. The idea of being in the BMI range for a healthy weight seemed like a fantasy.

It’s a fairly good idea to pick a goal that is significant. I could have set my sights low and said, I’d like to be 170 pounds, but that’s not much of a change. Physically it’s very little change, as I remember. The idea of hitting 160 pounds, however… I couldn’t even remember when I was last 160.

I decided that each time I hit a goal, I’d re-assess where I am, and see if it made sense to lose or gain more weight. At this time I feel like I could lose more, so I’m going to shoot for 15 more pounds. Putting me at 145 pounds. Beyond my occasional 14 miles I bike to and from work when I’m called in, I am planning on adding in at least some abdominal exercises.

Determining a date

Your goal date is probably more important than your determined weight (as long as the weight you pick is healthy). When I started, going from 185 to 160, seemed crazy and way faster than I expected, but over a year’s time, that’s only half a pound a week — which is quite reasonable.

An arbitrary date to reach a goal might not mean much to you. For me, I picked my wedding day — and it had the intended effect of getting a lot of attention. Luckily for me, I can determine if I kept the same weight-loss pace as I have, I’d be around 145 pounds by Christmas. The nice thing about this being my second goal, is I can determine whether or not I liked that pace. If the pace seemed too relaxed I could just adjust the date or weight to suit me.

I’m picking New Years Day, 2007 as my new goal date. I’ll be 145 pounds by then. It’ll be a good time, because this is when many people decide they should start losing weight. At that point I can serve as an example.

Determine what you are willing to do to achieve the goal

Setting a date and a measurement for a goal is a huge step. In fact, you could probably achieve your goals with just that information alone. The other necessary steps will just come into play. However, it is helpful to know what you’ll do and won’t do to get to where you want to be.

Let’s be clear, dieting is simply burning more calories than you consume. What will you do to achieve that? You can either burn more or consume less or both. The bulk of my weight loss was from consuming less. Burning more, for me, did help, but not as much as people would like to believe1.

Do both if you can, but don’t get hung up that you can’t bike to work (even though you can) or that you have no time to use that treadmill. With some dietary adjustments it should be easy to lower your calorie intake to the point where your body starts wearing down on its reserves.

Personally I also picked a few specific things I was willing to give up. I was willing to give up sugar (not everyday, but regularly), fried foods if I had to, and even dairy products. Omitting foods didn’t make me lose weight, but it did prevent me from snacking. Snacking for me made having a calorie deficit nearly impossible.

I also had to accept that I couldn’t stuff myself at mealtime. Late night pizzas, all-you-can-eat buffets — none of that. My fianceé-at-the-time (now my lovely wife) was very good at convincing me that I only needed to eat until I was no longer hungry. That alone, helped quite a bit.

There will be bouts of hunger. You are starving your body. You are telling it, hey, I’m not eating enough to maintain this weight, so please chew on my reserves. The danger of course, is that your reserves are fat and muscle. Some dietary, and exercise changes can make that loss appear to be mostly fat. This time around, I’m willing to stick to the same course. It worked before, it should work again.

Determine what you won’t do to achieve this goal

Equally as important to what you’ll try to do is what you won’t try to do. It might be important to establish that you will not exceed a certain calorie deficiency/day. It might also be important to you to not jump on fad diets. Whatever it is, you should find out what you don’t want to do, make sure you are justified in feeling that way, and then stick to your guns, unless you’ve been scientifically proven wrong. It’s far too easy to get caught up in a numbers game and start sacrificing your health just to meet the right number on the scale.

Conclusion

If you know what you want, when you want it and are willing to take steps to achieve it, you’ll be fine. Goal setting is important in just about every facet of life, not just weight loss. Realistic goals can get you where you need to be. Make sure you’re setting up some realistic goals.

So to recap my own goals: New Years Day 2007 I will be 145 pounds. I’d like to hear if other people have some weight related goals in the comments and their strategies.


  1. Last summer I biked about 70 miles a week, but the bulk of my weight was in the winter when I started working more and more from home.

Read full post
Random Selections using Propel

Propel is a handy way to deal with ORM. Rather than figuring out the correct SQL statement to select your elements you just use a -Peer object to select it.

The one drawback is there’s no way of choosing an object at random. You can select the first element of a result set, but not a random one without some changes to your -Peer class.

The quick and dirty fix that I did is to use custom SQL to populate a propel object. It’s a rather suitable approach for more complicated selects. So here’s how we randomly select things:

$con = Propel::getConnection('propel');
$sql = 'SELECT %s.* FROM %s ORDER BY RAND()';
$sql = sprintf($sql,MyObjectPeer::TABLE_NAME,MyObjectPeer::TABLE_NAME);
$stmt = $con->createStatement();
$rs = $stmt->executeQuery($sql, ResultSet::FETCHMODE_NUM);
$objects =  MyObjectPeer::populateObjects($rs);
$object = $objects[0];

If you know you’re going to only use one object, SELECT %s.* FROM %s ORDER BY RAND() LIMIT 1 will work as well.

Read full post
Goal met, now it's time to get married

160 pounds.

That was my goal weight for my wedding date, and I made it. There’s been a lot of interested parties as to how I lost my weight, apparently it’s visibly noticable. My pants are much looser too. And of course, I feel lighter.

As to what I did, it’s not all to different than Jeremy’s approach. Although I knew I didn’t want to rigidly count calories. After the wedding, I’ll try to concisely reveal what it is I did to lose weight, since a lot of friends and family have asked.

As for the wedding, things are coming together, and I am very excited. It’ll be a blast. A lavish blast, but still a blast. Katie and I can’t wait to see all of you who are coming in town.

Read full post