Book: Symfony 1.3 Web Application Development
November 4th, 2009
Few weeks ago Packt Publishing sent me their new book called Symfony 1.3 Web Application Development to review. Ironically (or not) I'm not doing any active symfony development in the last few months, but I'm trying to be up to date with the news around symfony.
As title says the book is based on the upcoming 1.3 version of the framework. While building an application called The Milkshake Shop readers are introduced to the features that the framework can offer including some of the latest (and may a little bit not so good documented) ones like forms, routes, creating and packaging plugins and so on. There is also a whole chapter dedicated to performance and optimization. Full table of contents can be found here.
It was a surprise (at lest for me) to see that the ORM choosen for The Milkshake Shop (and the book) was Propel instead of Doctrine which was choosen as the default ORM framework.
However this book is a good foundation for everyone that's new to symfony world and also gives some more information about the features introduced in both 1.2 and 1.3 to the developers which are still doing their job using 1.0 version.
At the end the one thing that I didn't like in this book -
"Test-driven development is the key to bug-free and well-written code. Symfony provides the ability for unit and functional testing. Unit tests enable the developer to test functions and methods for input and output. While functionality tests helps the developer to test for functional issues that would be executed in the browser, Symfony has its own testing framework called Lime. This testing framework is useful for both unit testing and functional testing. All test output can be saved in the xUnit format." -
Believe it or not, but that's the only place where testing is mentioned (correct me of I'm wrong)! Seriously, if "Test-driven development is the key to bug-free and well-written code" give it a chapter or two. Or even better, build the whole app in a test-driven way.
3 comments »
sfHoptoadNotifierPlugin - track your symfony project exceptions using Hoptoad
April 16th, 2009
sfHoptoadNotifier is symfony plugin that sends notifications to Hoptoad about your project
exceptions. It's based on rich/php-hoptoad-notifier.
You can check it's README for more info.
Requirements
rich/php-hoptoad-notifier uses the Horde_Yaml class. You can install this class using the commands below.
1 2 |
$ pear channel-discover pear.horde.org $ pear install horde/yaml |
$ pear install HTTP_Request |
$ git clone git://github.com/krasio/sfhoptoadnotifierplugin.git plugins/sfHoptoadNotifierPlugin |
1 2 3 |
all:
sf_hoptoad_notifier_plugin:
api_key: you_api_key_goes_here |
$ symfony cc |
0 comments »
sfRestfulAuthenticationPlugin released
November 11th, 2008
I've just released my first symfony plugin named sfRestfulAuthenticationPlugin.
As you can see from it's name it's something like a port of technoweenie's restful_authentication plugin. It uses some cool new features from symfony 1.2 like the form framework and RESTful routes.
Tickets can be submitted on http://sfrestfulauthenticationplugin.lighthouseapp.com.
0 comments »
symfony 1.2 will be RESTful
September 12th, 2008
As we can see from this presentation, symfony 1.2 (expected 10/2008) will be RESTful.
0 comments »
Deploying symfony project with Capistrano
May 12th, 2008
As you may know Capistrano is a great tool "originally written to ease the pain of deploying Rails applications". If you are not familiar with it take a look at it's getting started guide.
Even if Rails applications development is the primary way that people use Capistrano it can be used for a lot more than that.
Let me show you how you can use it for easy-and-with-no-pain deployment of symfony project.
First cd to your project and run (I assume that you have Capistrano installed allready)
$ capify . |
So this is how the modifed deploy.rb file that I use to deploy my symfony projects looks like :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
set :application, "mysfapp" set :repository, "http://path/to/the/version/control/repository" # If you aren't deploying to /u/apps/#{application} on the target # servers (which is the default), you can specify the actual location # via the :deploy_to variable: set :deploy_to, "/path/to/project/#{application}" # If there's no access to the repository from the production server, deploy via uploading tarball to the server #set :deploy_via, :copy # If you aren't using Subversion to manage your source code, specify # your SCM below: # set :scm, :subversion role :app, "codingspree.net" role :web, "codingspree.net" # role :db, "your db-server here", :primary => true set :user, "codingspree" # path to php executable set :php, "/usr/local/php5/bin/php5" # symfony application name (used for migrations) set :sf_app, "frontend" namespace (:deploy) do desc <<-DESC [internal] Overriding original task to fit to symfony project needs DESC task :finalize_update, :except => { :no_release => true } do run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true) run <<-CMD rm -rf #{latest_release}/log && ln -s #{shared_path}/log #{latest_release}/log CMD run <<-CMD rm -rf #{latest_release}/cache && ln -s #{shared_path}/cache #{latest_release}/cache CMD stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S") asset_paths = %w(images css js).map { |p| "#{latest_release}/web/#{p}" }.join(" ") run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" } end desc <<-DESC Overriding original task to exclude restart DESC task :default do update end desc <<-DESC Overriding original task to use symfoy migrations (via sfMigrationsLightPlugin) DESC task :migrations do update sf.migrate end after "deploy:update", 'deploy:customize' desc <<-DESC All custom tasks will be here DESC task :customize do # custmize it here sf.symlinks sf.remove_dev_environments # clear cache sf.cc end end namespace (:sf) do desc <<-DESC Run the "symfony migrate" task DESC task :migrate do run "cd #{current_path} && #{php} symfony migrate #{sf_app}" end desc <<-DESC Run the "symfony cc" task DESC task :cc do run "cd #{current_path} && rm -rf cache/*" end desc <<-DESC Create symlink to symfony specific targets DESC task :symlinks do # symlink to database.yml run "rm -rf #{current_path}/config/databases.yml" run "ln -s #{shared_path}/databases.yml #{current_path}/config/databases.yml" # symlink to config.php run "rm -rf #{current_path}/config/config.php" run "ln -s #{shared_path}/config.php #{current_path}/config/config.php" # symlink to sf data dir run "rm -rf #{current_path}/web/sf" run "ln -s /path/to/sf/data/dir #{current_path}/web/sf" # symlink to uploads run "rm -rf #{current_path}/web/uploads" run "ln -s #{shared_path}/uploads #{current_path}/web/uploads" end desc <<-DESC Remove DEV environments DESC task :remove_dev_environments do run "rm -rf #{current_path}/web/*_dev.php" end desc <<-DESC Disable symfony application DESC task :disable do run "cd #{current_path} && #{php} symfony disable #{sf_app} prod" end desc <<-DESC Enable symfony application DESC task :enable do run "cd #{current_path} && #{php} symfony enable #{sf_app} prod" end end |
1 2 |
# path to php executable set :php, "/usr/local/php5/bin/php5" |
1 2 |
# symfony application name (used for migrations) set :sf_app, "frontend" |
In the next section of the deploy.rb file I overrided few of the original tasks from deploy namespace to fit to my needs - finalize_update to set symlinks to symfony cache and log directories, the default task to exclude restart because I don't need it, migrations to set it to use symfony migrations and at the end there is one hook task that will be executed after deploy:update -it's called customize and I'll place all custom tasks there.
The last part of the file is sf namespace that includes some symfony related tasks as migrate, cc, symlinks, remove_dev_environments, disable, enable and so on. As you see you can wrap any symfony task that you have to execute on the production server with Capistrano task.
All you need now is to set a little bit the production server (run cap deploy:config, symlink document root to /path/to/project/mysfapp/current, place relevant database.yml and config.php in /path/to/project/mysfapp/shared, etc) and you are ready to run
$ cap deploy |
2 comments »
Adding callbacks for Propel models in symfony project
September 11th, 2007
This is a simple how-to that describes adding callbacks for Propel models in symfony project
(like those usefull methods in ActiveRecord::Callbacks module).
Step 1 : Enable Propel behaviors in config/propel.ini
propel.builder.AddBehaviors = true // Default value is false |
1 2 3 4 5 6 7 8 9 |
class PageTab extends BasePageTab
{
function preSave(BaseObject $object, $con)
{
}
}
sfMixer::register('BasePageTab:save:pre', array('PageTab', 'preSave')); |
$ symfony propel-build-model |
There is much more detailed article on this topic by François Zaninotto called "Understanding Behaviors"
0 comments »
"Skinny Controller, Fat Model" approach in symfony project
September 10th, 2007
A very common error when you first start to write an application in the MVC way is bypassing some of the components or just not using it as it should.
This problem and an example how to do it in the "right way" are described in a one of Jamis Buck's posts called Skinny Controller, Fat Model.
Here is a cover-up version of the code from that post ported to symfony/PHP...
Before (or how-NOT-to-do-it example) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!-- listSuccess.php -->
<?php
foreach ($path as $breadcrumb=>$name)
{
?>
<a href="<?=url_for($breadcrumb)?>"><?=__($name)?></a> /
<?
}
foreach ($people as $p)
{
?>
<div id="person-<?=$p->getId()?>">
<span class="name">
<?=$p->getLastName()?>, <?=$p->getFirstName()?>
</span>
<span class="age">
<?= (date('Y') - date('Y', strtotime($p->getBirthdate()))) ?>
</span>
</div>
<?
}
?> |
1 2 3 4 5 6 7 8 9 10 11 12 |
# actions.php
class personsActions extends sfActions
{
public function executeList()
{
$c = new Criteria;
$c->add(PersonPeer::ADDED_AT, date('Y-m-d'), Criteria::GREATER_EQUAL);
$c->add(PersonPeer::DELETED, false);
$this->people = Person::doSelect($c);
}
} |
1 2 3 4 |
# PersonPeer.php
class PersonPeer extends BasePersonPeer
{
} |
1 2 3 4 5 6 7 8 9 |
<!-- listSuccess.php -->
<?php echo breadcrumbs($path) ?>
<?php foreach ($people as $p): ?>
<div id="person-<?php echo $p->getId()?>">
<span class="name"><?php echo $p->getFullName()?></span>
<span class="age"><?php echo $p->getAge()?></span>
</div>
<? endforeach;?> |
1 2 3 4 5 6 7 8 |
# actions.php
class personsActions extends sfActions
{
public function executeList()
{
$this->people = PersonPeer::findRecent();
}
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# PersonPeer.php
class PersonPeer extends BasePersonPeer
{
public function findRecent()
{
$c = new Criteria;
$c->add(self::ADDED_AT, date('Y-m-d'), Criteria::GREATER_EQUAL);
$c->add(self::DELETED, false);
return self::doSelect($c);
}
}
# Person.php
class Person extends BasePerson
{
public function getFullName()
{
return $this->getLastName() . ', ' . $p->getFirstName();
}
public function getAge()
{
return (date('Y') - date('Y', strtotime($this->getBirthdate())));
}
} |
3 comments »

Hi, my name is Krasimir Angelov. I'm working as web developer.