Loading Different Javascript/CSS Files in Different Environments

Today we were working on minifying our Javascript and CSS files for a site we are launching tomorrow.  We came across that we didn’t want to have to get the repository out of sync and update view.yml to only include our single CSS style sheet and one javascript file when working between developement environment and production.  What we wanted was the following in view.yml the javascripts: and stylesheets: parameters to change depending on what environment you are loading, however using prod: and dev: in this didn’t work like app.yml does.  An example of this would be:

all:
  title: My site
dev:
  stylesheets:[styleone.css,styletwo.css,stylethree.css]
  javascripts:[one.js,two.js,three.js]
prod:
  stylesheets:[style.min.css]
  javascripts:[main.min.js]

This way we could have them load in production much quicker and reduce load time, however still easily debug the files in development environments.  After looking around a bit we couldn’t find any standard current solution so we came up with the following.  In app.yml we defined two variables for the dev and production environments – javascript_files and css_files.  Here you put the list of the files you wanted to loaded in each environment.  Then in view.yml it now looks like:

all:
  title: My Title
  stylesheets: [<?php echo sfConfig::get('app_css_files');?>]
  javascripts: [<?php echo sfConfig::get('app_javascript_files');?>]

The configuration variables get their value depending on the environment you are running in so it loads the proper files.  With this configuration we can easily debug in development environment and still have minified versions of the CSS and Javascript in production environement.   Hopefully this will save some of you time and make your life easier.

Taking Your Application International

Many clients develop their applications with only one language in mind.  Recently one of our clients after we had developed two different applications for them decided that the applications needed to be translated into seven different languages.  At first the client said, “We’ll just make seven copies of it, and update each one separately.”  While this may at first seem the be a quick simple solution, think of the long term affects of this.  First, if the application is large, you are going to be wasting much space.  Second, and most important, using this approach you are going to be stuck trying to maintain seven different copies of the same application; every update for each each application will have to be made seven times.  Not only is this error prone, but it is inefficient.

Our solution?  Use a common practice called internationalization (i18n) and localization (l10n).  i18n and l10n is text translation (from page content to form labels to error message) and localizing of content ( displaying dates, currency, numbers, etc. in a specific format).  For many applications this is not an easy process, and often could require one to go back and rewrite much of the code.  However, we use Symfony which makes the task much easier.  Symfony allows you to use dictionary files and the database to handle this.  Symfony can scan your entire project looking for specific markup(__(“Text here”)) and pull the strings out into a simple XML file which you can enter the translations for.  The file looks like the following:

With file all you need to do is to modify the <target /> to <target>My Translated Text</target>  When the string “My String To Be Converted”  is output in the application it will be converted to “My Translated Text”.  You’d save this file for example as messages.es.xml if it was the Spanish translation file.

To read more on internationalization with Symfony visit http://www.symfony-project.org/book/1_2/13-I18n-and-L10n.  We use Symfony because of situations like this it allows us to quickly adapt our products to our customer needs.

Always think ahead when developing your applications.  Planning for the future work on an application can save you hours of rewriting.

sfWidgetjQueryTimepickr – Symfony timepickr widget

A couple of months ago John Resig posted on his blog about a “new” way for users to pick time.

The component is a jQuery plugin called timepickr and I thought it was particularly neat. Anyway, I finally needed to write a form which had a time component so I figured I’d drop in the timepickr jQuery plugin.

It didn’t look like there was a Symfony Forms widget for it so I whipped one up. You can grab it here.

The only issue with timepickr is that it introduces a ton of dependencies. It requires jQuery, jQuery-ui, jQuery.utils, jQuery.string, and ui.dropslide. Additionally, it needs the dropslide css as well as the timepickr css.

The form widget assumes that you will include jquery-ui by yourself since everyone usually has different naming conventions. It will include the other JS files and CSS files for you.

You can download a package with all of the JS and CSS you need here. Again this DOES NOT include the jQuery-ui stuff. YOU have to include that yourself in your Symfony project as well as on the page that you deploy this widget.

To use it, just drop it in your Symfony project somewhere where classes get autoloaded (projectdir/lib works) and then instantiate a widget with new sfWidgetjQueryTimepickr() . It currently will support all of the timepickr options passed in as widget options (on the constructor). Full documentation for timepickr is here.

Have fun picking time.

GIT Ignores and Symfony

We use GIT for version control at Setfive.  However often with our Symfony projects creating all the ignore files in all the plugins model/base’s and other locations throughout the project get very tiresome.  Since on some larger projects you can end up with 10-20 plugins, having to create an ignore for the autogenerated model, forms, and filters takes a long time.  Today we really quickly just wrote a plugin that allows you to quickly just run symfony util:generate-ignores git --add-ignores and it will automatically place the .gitignore file throughout your project in the correct locations and add them into your next commit.  You can also just have it place them throughout the project, but not add them to the next commit if you drop the --add-ignores option.  The plugin also accepts “cvs” instead of git for cvs based projects.

The reason you do not want the base, om, etc. directories in your repository is because every time a person rebuilds the model they will be updating those files(many times just changing the timestamp at the top of the autogenerated files), which causes uncessarily large commits.

You can get the plugin via http://www.symfony-project.org/plugins/sfSCMIgnoresTaskPlugin or install it via symfony plugin:install sfSCMIgnoresTaskPlugin.

If you have any questions/requests shoot us an email.

sfPropelPager and GROUP BY criteria

So for one reason or another (actually a few bad ones) I ended up having to use a Criteria object looking like this:

It makes SQL that looks something like this:

“SELECT tag_id, tag_model, id, COUNT(*) AS the_count FROM sf_tagging WHERE tag_id IN (1,2,3) GROUP BY taggable_model, taggable_id HAVING the_count > int”

The query sucks but whatever it works.

My issue came when I tried to use it with a sfPropelPager. I set up the pager per usual but for some reason the results that were coming back weren’t correct. For some reason, the COUNT being returned by the sfPropelPager was completely wrong. It turns out the offending lines are here in sfPropelPager.class.php :

For whatever reason, sfPropelPager clears the GROUP BY clauses before it calculates the COUNT for a criteria object. I’m not sure why it does this – but it certainly is unexpected and breaks my query in particular.

There are a handful of posts about this on the Symfony forums and it looks like the Propel people know about the issue to.

The solution to this is to use the setPeerCountMethod() from sfPropelPager. The setPeerCountMethod() function allows you to specify a custom COUNT() method inside the peer for your Criteria. I went ahead and added a new function to put the GROUP BY columns back in:

This solution works but it is extremely rigid. Since the custom count function has to be static you’d really be out of luck if you had variable columns or other dynamic requirements.

I’d love to know if someone has a cleaner/better/more elegant solution for this.