What's new in symfony 1.3/1.4?
- Mailer
- Security
- Widgets
- Validators
- Forms
- Autoloaders
- Tests
- Tasks
sfTask::askAndValidate()
symfony:test
project:deploy
generate:project
sfFileSystem::execute()
task.test.filter_test_files
- Enhancements to
sfTask::run()
sfBaseTask::setConfiguration()
project:disable
andproject:enable
help
andlist
project:optimize
generate:app
- Sending an Email from a Task
- Using the Routing in a Task
- Exceptions
- Propel Integration
- Routing
- CLI
- I18N
- Plugins
- Settings
- Doctrine Integration
- Generating Form Classes
- Form Classes Inheritance
- New Tasks
- Date Setters and Getters
doctrine:migrate --down
doctrine:migrate --dry-run
- Output DQL Task as Table of Data
- Pass query parameters to
doctrine:dql
- Debugging queries in functional tests
sfFormFilterDoctrine
- Configuring Doctrine
doctrine:generate-module
,doctrine:generate-admin
,doctrine:generate-admin-for-route
- Magic method doc tags
- Using a different version of Doctrine
- Web Debug Toolbar
- Partials
- Pagers
- View cache
- Request
- Actions
- Helpers
- Context
This tutorial is a quick technical introduction for symfony 1.3/1.4. It is for developers who have already worked with symfony 1.2 and who want to quickly learn new features of symfony 1.3/1.4.
First, please note that symfony 1.3 is compatible with PHP 5.2.4 or later.
If you want to upgrade from 1.2, please read the UPGRADE file found in the symfony distribution. You will have there all the information needed to safely upgrade your projects to symfony 1.3.
Mailer
As of symfony 1.3/1.4, there is a new default mailer based on SwiftMailer 4.1.
Sending an email is as simple as using the composeAndSend()
method from an
action:
$this->getMailer()->composeAndSend('from@example.com', 'to@example.com', 'Subject', 'Body');
If you need to have more flexibility, you can also use the compose()
method
and send it afterwards. Here is for instance how to add an attachment to the
message:
$message = $this->getMailer()-> compose('from@example.com', 'to@example.com', 'Subject', 'Body')-> attach(Swift_Attachment::fromPath('/path/to/a/file.zip')) ; $this->getMailer()->send($message);
As the mailer is quite powerful, refer to the documentation for more information.
Security
When a new application is created with the generate:app
task, the security
settings are now enabled by default:
escaping_strategy
: The value is nowtrue
by default (can be disabled with the--escaping-strategy
option).csrf_secret
: A random password is generated by default, and thus, the CSRF protection is enabled by default (can be disabled with the--csrf-secret
option). It is highly recommended that you change the default generated password, by editing thesettings.yml
configuration file, or by using the--csrf-secret
option.
Widgets
Default Labels
When a label is auto-generated from the field name, _id
suffixes are now
removed:
first_name
=> First name (as before)author_id
=> Author (was "Author id" before)
sfWidgetFormInputText
The sfWidgetFormInput
class is now abstract. Text input fields are now
created with the sfWidgetFormInputText
class. This change was made to ease
introspection of form classes.
I18n widgets
The following widgets have been added:
sfWidgetFormI18nChoiceLanguage
sfWidgetFormI18nChoiceCurrency
sfWidgetFormI18nChoiceCountry
sfWidgetFormI18nChoiceTimezone
The first three of them replace the now deprecated
sfWidgetFormI18nSelectLanguage
, sfWidgetFormI18nSelectCurrency
, and
sfWidgetFormI18nSelectCountry
widgets.
Fluent Interface
The widgets now implement a fluid interface for the following methods:
sfWidgetForm
:setDefault()
,setLabel()
,setIdFormat()
,setHidden()
sfWidget
:addRequiredOption()
,addOption()
,setOption()
,setOptions()
,setAttribute()
,setAttributes()
sfWidgetFormSchema
:setDefault()
,setDefaults()
,addFormFormatter()
,setFormFormatterName()
,setNameFormat()
,setLabels()
,setLabel()
,setHelps()
,setHelp()
,setParent()
sfWidgetFormSchemaDecorator
:addFormFormatter()
,setFormFormatterName()
,setNameFormat()
,setLabels()
,setHelps()
,setHelp()
,setParent()
,setPositions()
Validators
sfValidatorRegex
The sfValidatorRegex
has a new must_match
option. If set to false
, the
regex must not match for the validator to pass.
The pattern
option of sfValidatorRegex
can now be an instance of
sfCallable
that returns a regex when called.
sfValidatorUrl
The sfValidatorUrl
has a new protocols
option. This allows you to specify
what protocols to allow:
$validator = new sfValidatorUrl(array('protocols' => array('http', 'https')));
The following protocols are allowed by default:
http
https
ftp
ftps
sfValidatorSchemaCompare
The sfValidatorSchemaCompare
class has two new comparators:
IDENTICAL
, which is equivalent to===
;NOT_IDENTICAL
, which is equivalent to!==
;
sfValidatorChoice
, sfValidatorPropelChoice
, sfValidatorDoctrineChoice
The sfValidatorChoice
, sfValidatorPropelChoice
,
sfValidatorDoctrineChoice
validators have two new options that are enabled
only if the multiple
option is true
:
min
The minimum number of values that need to be selectedmax
The maximum number of values that need to be selected
I18n validators
The following validators have been added:
sfValidatorI18nChoiceTimezone
Default Error Messages
You can now define default error messages globally by using the
sfValidatorBase::setDefaultMessage()
method:
sfValidatorBase::setDefaultMessage('required', 'This field is required.');
The previous code will override the default 'Required.' message for all validators. Note that the default messages must be defined before any validator is created (the configuration class is a good place).
note
The setRequiredMessage()
and setInvalidMessage()
methods are
deprecated and call the new setDefaultMessage()
method.
When symfony displays an error, the error message to use is determined as follows:
Symfony looks for a message passed when the validator was created (via the second argument of the validator constructor);
If it is not defined, it looks for a default message defined with the
setDefaultMessage()
method;If it is not defined, it falls back to the default message defined by the validator itself (when the message has been added with the
addMessage()
method).
Fluent Interface
The validators now implement a fluid interface for the following methods:
sfValidatorSchema
:setPreValidator()
,setPostValidator()
sfValidatorErrorSchema
:addError()
,addErrors()
sfValidatorBase
:addMessage()
,setMessage()
,setMessages()
,addOption()
,setOption()
,setOptions()
,addRequiredOption()
sfValidatorFile
An exception is thrown when creating an instance of sfValidatorFile if
file_uploads
is disabled in php.ini
.
Forms
sfForm::useFields()
The new sfForm::useFields()
method removes all non-hidden fields from a form
except the ones given as an argument. It is sometimes easier to explicitly
give the fields you want to keep in a form, instead of unsetting all unneeded
fields. For instance, when adding new fields to a base form, they won't
automagically appear in your form until explicitly added (think of a model
form where you add a new column to the related table).
class ArticleForm extends BaseArticleForm { public function configure() { $this->useFields(array('title', 'content')); } }
By default, the array of fields is also used to change the fields order. You
can pass false
as the second argument to useFields()
to disable the
automatic reordering.
sfForm::getEmbeddedForm($name)
You can now access a particular embedded form using the ->getEmbeddedForm()
method.
sfForm::renderHiddenFields()
The ->renderHiddenFields()
method now renders hidden fields from embedded
forms. An argument has been added to disable recursion, useful if you render
embedded forms using a formatter.
// render all hidden fields, including those from embedded forms echo $form->renderHiddenFields(); // render hidden fields without recurring echo $form->renderHiddenFields(false);
sfFormSymfony
The new sfFormSymfony
class introduces the event dispatcher to symfony
forms. You can access the dispatcher from inside your form classes as
self::$dispatcher
. The following form events are now notified by symfony:
form.post_configure
: This event is notified after every form is configuredform.filter_values
: This event filters the merged, tainted parameters and files array just prior to bindingform.validation_error
: This event is notified whenever form validation failsform.method_not_found
: This event is notified whenever an unknown method is called
BaseForm
Every new symfony 1.3/1.4 project includes a BaseForm
class that you can use to
extend the Form component or add project-specific functionality. The forms
generated by sfDoctrinePlugin
and sfPropelPlugin
automatically extend this
class. If you create additional form classes they should now extend BaseForm
rather than sfForm
.
sfForm::doBind()
The cleaning of tainted parameters has been isolated in a developer-friendly
method, ->doBind()
, which receives the merged array of parameters and files
from ->bind()
.
sfForm(Doctrine|Propel)::doUpdateObject()
The Doctrine and Propel form classes now include a developer-friendly
->doUpdateObject()
method. This method receives an array of values from
->updateObject()
that has already been processed by ->processValues()
.
sfForm::enableLocalCSRFProtection()
and sfForm::disableLocalCSRFProtection()
Using the sfForm::enableLocalCSRFProtection()
and
sfForm::disableLocalCSRFProtection()
methods, you can now easily configure
the CSRF protection from the configure()
method of your form classes.
To disable the CSRF protection for a form, add the following line in its
configure()
method:
$this->disableLocalCSRFProtection();
By calling the disableLocalCSRFProtection()
, the CSRF protection will be
disabled, even if you pass a CSRF secret when creating a form instance.
Fluent Interface
Some sfForm
methods now implement a fluent interface: addCSRFProtection()
,
setValidators()
, setValidator()
, setValidatorSchema()
, setWidgets()
,
setWidget()
, setWidgetSchema()
, setOption()
, setDefault()
, and
setDefaults()
.
Autoloaders
All symfony autoloaders are now case-insensitive. PHP is case-insensitive, now so is symfony.
sfAutoloadAgain
(EXPERIMENTAL)
A special autoloader has been added that is just for use in debug mode. The
new sfAutoloadAgain
class will reload the standard symfony autoloader and
search the filesystem for the class in question. The net effect is that you no
longer have to run symfony cc
after adding a new class to a project.
Tests
Speed up Testing
When you have a large suite of tests, it can be very time consuming to launch
all tests every time you make a change, especially if some tests fail. That's
because each time you fix a test, you should run the whole test suite again to
ensure that you have not break something else. But as long as the failed tests
are not fixed, there is no point in re-executing all other tests. As of
symfony 1.3/1.4, the test:all
and symfony:test
tasks have a --only-failed
(-f
as a shortcut) option that forces the task to only re-execute tests that
failed during the previous run:
$ php symfony test:all --only-failed
Here is how it works: the first time, all tests are run as usual. But for subsequent test runs, only tests that failed last time are executed. As you fix your code, some tests will pass, and will be removed from subsequent runs. When all tests pass again, the full test suite is run... you can then rinse and repeat.
Functional Tests
When a request generates an exception, the debug()
method of the response
tester now outputs a readable text representation of the exception, instead of
the normal HTML output. It makes debugging much easier.
sfTesterResponse
has a new matches()
method that runs a regex on the whole
response content. It is of great help on non XML-like responses, where
checkElement()
is not useable. It also replaces the less-powerful
contains()
method:
$browser->with('response')->begin()-> matches('/I have \d+ apples/')-> // it takes a regex as an argument matches('!/I have \d+ apples/')-> // a ! at the beginning means that the regex must not match matches('!/I have \d+ apples/i')-> // you can also add regex modifiers end();
JUnit Compatible XML Output
The test tasks are now able to output a JUnit compatible XML file by using the
--xml
option:
$ php symfony test:all --xml=log.xml
Easy Debugging
To ease the debugging when a test harness reports failed tests, you can now
pass the --trace
option to have a detailed output about the failures:
$ php symfony test:all -t
Lime Output Colorization
As of symfony 1.3/1.4, lime does the right thing as far as colorization is
concerned. It means, that you can almost always omit the second argument of
the lime constructor of lime_test
:
$t = new lime_test(1);
sfTesterResponse::checkForm()
The response tester now includes a method to easily verify that all fields in a form have been rendered to the response:
$browser->with('response')->begin()-> checkForm('ArticleForm')-> end();
Or, if you prefer, you can pass a form object:
$browser->with('response')->begin()-> checkForm($browser->getArticleForm())-> end();
If the response includes multiple forms you have the option of providing a CSS selector to pinpoint which portion of the DOM to test:
$browser->with('response')->begin()-> checkForm('ArticleForm', '#articleForm')-> end();
sfTesterResponse::isValid()
You can now check whether a response is well-formed XML with the response
tester's ->isValid()
method:
$browser->with('response')->begin()-> isValid()-> end();
You also validate the response against its document type be passing true
as an
argument:
$browser->with('response')->begin()-> isValid(true)-> end();
Alternatively, if you have a XSD or RelaxNG schema to validate against, you can provide the path to this file:
$browser->with('response')->begin()-> isValid('/path/to/schema.xsd')-> end();
Listen to context.load_factories
You can now add listeners for the context.load_factories
event to your
functional tests. This was not possible in previous versions of symfony.
$browser->addListener('context.load_factories', array($browser, 'listenForNewContext'));
A better ->click()
You can now pass any CSS selector to the ->click()
method, making it much
easier to target the element you want semantically.
$browser ->get('/login') ->click('form[action$="/login"] input[type="submit"]') ;
Tasks
The symfony CLI now attempts to detect the width of your terminal window and formats lines to fit. If detection is not possible the CLI defaults to 78 columns wide.
sfTask::askAndValidate()
There is a new sfTask::askAndValidate()
method to ask a question to the user
and validates its input:
$answer = $this->askAndValidate('What is you email?', new sfValidatorEmail());
The method also accepts an array of options (see the API doc for more information).
symfony:test
From time to time, developers need to run the symfony test suite to check that
symfony works well on their specific platform. Until now, they had to know the
prove.php
script bundled with symfony to do that. As of symfony 1.3/1.4, there
is a built-in task, symfony:test
that launches the symfony core test suite
from the command line, like any other task:
$ php symfony symfony:test
If you were used to run php test/bin/prove.php
, you should now run the
equivalent php data/bin/symfony symfony:test
command.
project:deploy
The project:deploy
task has been slightly improved. It now displays the
progress of the files transfer in real-time, but only if the -t
option is
passed. If not, the task is silent, except for errors of course. Speaking of
errors, if one occurs, the output is on a red background to ease problem
identification.
generate:project
As of symfony 1.3/1.4, Doctrine is the default configured ORM when executing the
generate:project
task:
$ php /path/to/symfony generate:project foo
To generate a project for Propel, use the --orm
option:
$ php /path/to/symfony generate:project foo --orm=Propel
If you don't want to use Propel or Doctrine, you can pass none
to the
--orm
option:
$ php /path/to/symfony generate:project foo --orm=none
The new --installer
option allows you to pass a PHP script that can further
customize the newly created project. The script is executed in the context of
the task, and so can use any of its methods. The more useful ones are the
following: installDir()
, runTask()
, ask()
, askConfirmation()
,
askAndValidate()
, reloadTasks()
, enablePlugin()
, and disablePlugin()
.
More information can be found in this post from the official symfony blog.
You can also include a second "author" argument when generating a project,
which specifies a value to use for the @author
doc tag when symfony
generates new classes.
$ php /path/to/symfony generate:project foo "Joe Schmo"
sfFileSystem::execute()
The sfFileSystem::execute()
methods replaces the sfFileSystem::sh()
method
with more powerful features. It takes callbacks for real-time processing of
the stdout
and stderr
outputs. It also returns both outputs as an array.
You can find one example of its usage in the sfProjectDeployTask
class.
task.test.filter_test_files
The test:*
tasks now filter test files through the
task.test.filter_test_files
event prior to running them. This event includes
arguments
and options
parameters.
Enhancements to sfTask::run()
You can now pass an associative array of arguments and options to
sfTask::run()
:
$task = new sfDoctrineConfigureDatabaseTask($this->dispatcher, $this->formatter); $task->run( array('dsn' => 'mysql:dbname=mydb;host=localhost'), array('name' => 'master') );
The previous version, which still works:
$task->run( array('mysql:dbname=mydb;host=localhost'), array('--name=master') );
sfBaseTask::setConfiguration()
When calling a task that extends sfBaseTask
from PHP, you no longer have to
pass --application
and --env
options to ->run()
. Instead, you can simply
set the configuration object directly by calling ->setConfiguration()
.
$task = new sfDoctrineLoadDataTask($this->dispatcher, $this->formatter); $task->setConfiguration($this->configuration); $task->run();
The previous version, which still works:
$task = new sfDoctrineLoadDataTask($this->dispatcher, $this->formatter); $task->run(array(), array( '--application='.$options['application'], '--env='.$options['env'], ));
project:disable
and project:enable
You can now wholesale disable or enable an entire environment using the
project:disable
and project:enable
tasks:
$ php symfony project:disable prod $ php symfony project:enable prod
You can also specify which applications to disable in that environment:
$ php symfony project:disable prod frontend backend $ php symfony project:enable prod frontend backend
These tasks are backward compatible with their previous signature:
$ php symfony project:disable frontend prod $ php symfony project:enable frontend prod
help
and list
The help
and list
tasks can now display their information as XML:
$ php symfony list --xml $ php symfony help test:all --xml
The output is based on the new sfTask::asXml()
method, which returns a XML
representation of a task object.
The XML output is mostly useful for third-party tools like IDEs.
project:optimize
Running this task reduces the number of disk reads performed during runtime by caching the location of your application's template files. This task should only be used on a production server. Don't forget to re-run the task each time the project changes.
$ php symfony project:optimize frontend
generate:app
The generate:app
task now checks for a skeleton directory in your project's
data/skeleton/app
directory before defaulting to the skeleton bundled in the
core.
Sending an Email from a Task
You can now easily send an email from a task by using the getMailer()
method.
Using the Routing in a Task
You can now easily get the routing object from a task by using the
getRouting()
method.
Exceptions
Autoloading
When an exception is thrown during autoloading, symfony now catches them and outputs an error to the user. That should solve some "White screen of death" pages.
Web Debug Toolbar
If possible, the web debug toolbar is now also displayed on exception pages in the development environment.
Propel Integration
Propel has been upgraded to version 1.4. Please visit Propel's site for more information on their upgrade (http://www.propelorm.org/wiki/Documentation/1.4).
Propel Behaviors
The custom builder classes symfony has relied on to extend Propel have been ported to Propel 1.4's new behaviors system.
propel:insert-sql
Before propel:insert-sql
removes all data from a database, it asks for a
confirmation. As this task can remove data from several databases, it now also
displays the name of the connections of the related databases.
propel:generate-module
, propel:generate-admin
, propel:generate-admin-for-route
The propel:generate-module
, propel:generate-admin
, and
propel:generate-admin-for-route
tasks now takes a --actions-base-class
option that allows
the configuration of the actions base class for the generated modules.
Propel Behaviors
Propel 1.4 introduced an implementation of behaviors in the Propel codebase. The custom symfony builders have been ported into this new system.
If you would like to add native behaviors to your Propel models, you can do so
in schema.yml
:
classes: Article: propel_behaviors: timestampable: ~
Or, if you use the old schema.yml
syntax:
propel: article: _propel_behaviors: timestampable: ~
Disabling form generation
You can now disable form generation on certain models by passing parameters to
the symfony
Propel behavior:
classes: UserGroup: propel_behaviors: symfony: form: false filter: false
Note that you have to rebuild the model before that setting is respected, because the behaviour is attached to the model and does only exist after rebuilding it.
Using a different version of Propel
Using a different version of propel is as easy as setting the
sf_propel_runtime_path
and sf_propel_generator_path
config variables in
ProjectConfiguration
:
// config/ProjectConfiguration.class.php public function setup() { $this->enablePlugins('sfPropelPlugin'); sfConfig::set('sf_propel_runtime_path', '/path/to/propel/runtime'); sfConfig::set('sf_propel_generator_path', '/path/to/propel/generator'); }
Routing
Default Requirements
The default \d+
requirement is now only applied to a
sfObjectRouteCollection
when the column
option is the default id
. This
means you no longer have to provide an alternate requirement when a
non-numeric column is specified (i.e. slug
).
sfObjectRouteCollection
options
A new default_params
option has been added to sfObjectRouteCollection
. It
allows for default parameters to be registered for each generated route:
forum_topic: class: sfDoctrineRouteCollection options: default_params: section: forum
CLI
Output Colorization
Symfony tries to guess if your console supports colors when you use the symfony CLI tool. But sometimes, symfony guesses wrong; for instance when you use Cygwin (because colorization is always turned off on the Windows platform).
As of symfony 1.3/1.4, you can force the use of colors for the output by passing
the global --color
option.
I18N
Data update
The data used for all I18N operations was updated from the ICU project
.
Symfony comes now with about 330 locale files, which is an increase of about 70
compared to Symfony 1.2. Please note that the updated data might be slightly
different from what has been in there before, so for example test cases checking
for the tenth item in a language list might fail.
Sorting according to user locale
All sorting on this locale dependent data is now also performed locale dependent.
sfCultureInfo->sortArray()
can be used for that.
Plugins
Before symfony 1.3/1.4, all plugins were enabled by default, except for the
sfDoctrinePlugin
and the sfCompat10Plugin
ones:
class ProjectConfiguration extends sfProjectConfiguration { public function setup() { // for compatibility / remove and enable only the plugins you want $this->enableAllPluginsExcept(array('sfDoctrinePlugin', 'sfCompat10Plugin')); } }
For freshly created projects with symfony 1.3/1.4, plugins must be explicitly
enabled in the ProjectConfiguration
class to be able to use them:
class ProjectConfiguration extends sfProjectConfiguration { public function setup() { $this->enablePlugins('sfDoctrinePlugin'); } }
The plugin:install
task automatically enables the plugin(s) it installs (and
plugin:uninstall
disable them). If you install a plugin via Subversion, you
still need to enable it by hand.
If you want to use a core-plugin, like sfProtoculousPlugin
or
sfCompat10Plugin
, you just need to add the corresponding enablePlugins()
statement in the ProjectConfiguration
class.
note
If you upgrade a project from 1.2, the old behavior will still be
active as the upgrade task does not change the ProjectConfiguration
file. The behavior change is only for new symfony 1.3/1.4 projects.
sfPluginConfiguration::connectTests()
You can connect a plugin's tests to the test:*
tasks by calling that plugin
configuration's ->connectTests()
method in the new setupPlugins()
method:
class ProjectConfiguration extends sfProjectConfiguration { public function setupPlugins() { $this->pluginConfigurations['sfExamplePlugin']->connectTests(); } }
Settings
sf_file_link_format
Symfony 1.3/1.4 formats file paths as clickable links whenever possible (i.e. the
debug exception template). The sf_file_link_format
is used for this purpose,
if set, otherwise symfony will look for the xdebug.file_link_format
PHP
configuration value.
For example, if you want to open files in TextMate, add the following to
settings.yml
:
all: .settings: file_link_format: txmt://open?url=file://%f&line=%l
The %f
placeholder will be replaced with file's absolute path and the %l
placeholder will be replaced with the line number.
Doctrine Integration
Doctrine has been upgraded to version 1.2. Please visit Doctrine's site for more information on their upgrade (http://www.doctrine-project.org/documentation/1_2/en).
Generating Form Classes
It is now possible to specify additional options for symfony in your Doctrine YAML schema files. We've added some options to disable the generation of form and filter classes.
For example in a typical many to many reference model, you don't need any form or filter form classes generated. So you can now do the following:
UserGroup: options: symfony: form: false filter: false columns: user_id: type: integer primary: true group_id: type: integer primary: true
Form Classes Inheritance
When you generate forms from your models, your models contain inheritance. The generated child classes will respect the inheritance and generate forms that follow the same inheritance structure.
New Tasks
We have introduced a few new tasks to help you when developing with Doctrine.
Create Model Tables
You can now individually create the tables for a specified array of models. It will drop the tables first then re-create them for you. This is useful if you are developing some new models in an existing project/database and you don't want to blow away the whole database and just want to rebuild a subset of tables.
$ php symfony doctrine:create-model-tables Model1 Model2 Model3
Delete Model Files
Often you will change your models, renaming things, remove unused models, etc.
in your YAML schema files. When you do this, you then have orphaned model, form
and filter classes. You can now manually clean out the generated files related
to a model by using the doctrine:delete-model-files
task.
$ php symfony doctrine:delete-model-files ModelName
The above task will find all the related generated files and report them to you before asking you to confirm whether you would like to delete the files or not.
Clean Model Files
You can automate the above process and find out what models exist on the disk
but do not exist in your YAML schema files by using the doctrine:clean-model-files
task.
$ php symfony doctrine:clean-model-files
The above command will compare your YAML schema files with the models and files
that have been generated and determine what should be removed. These models are
then passed on to the doctrine:delete-model-files
task. It will ask you to
confirm the removal of any files before actually deleting anything.
Build whatever
The new doctrine:build
task allows you to specify what exactly you would
like symfony and Doctrine to build. This task replicates the functionality in
many of the existing combination-tasks, which have all been deprecated in
favor of this more flexible solution.
Here are some possible uses of doctrine:build
:
$ php symfony doctrine:build --db --and-load
This will drop (:drop-db
) and create (:build-db
) the database, create the
tables configured in schema.yml
(:insert-sql
) and load the fixture data
(:data-load
).
$ php symfony doctrine:build --all-classes --and-migrate
This will build the model (:build-model
), forms (:build-forms
), form
filters (:build-filters
) and run any pending migrations (:migrate
).
$ php symfony doctrine:build --model --and-migrate --and-append=data/fixtures/categories.yml
This will build the model (:build-model
), migrate the database (:migrate
)
and append category fixtures data (:data-load --append --dir=data/fixtures/categories.yml
).
For more information see the doctrine:build
task's help page.
New option: --migrate
The following tasks now include a --migrate
option, which will replace the
nested doctrine:insert-sql
task with doctrine:migrate
.
doctrine:build-all
doctrine:build-all-load
doctrine:build-all-reload
doctrine:build-all-reload-test-all
doctrine:rebuild-db
doctrine:reload-data
doctrine:generate-migration --editor-cmd
The doctrine:generate-migration
task now includes a --editor-cmd
option
which will execute once the migration class is created for easy editing.
$ php symfony doctrine:generate-migration AddUserEmailColumn --editor-cmd=mate
This example will generate the new migration class and open the new file in TextMate.
doctrine:generate-migrations-diff
This new task will automatically generate complete migration classes for you, based on your old and new schemas.
Create or drop specific connections
You can now specify database connection names when running doctrine:build-db
and doctrine:drop-db
:
$ php symfony doctrine:drop-db master slave1 slave2
Date Setters and Getters
We've added two new methods for retrieving Doctrine date or timestamp values as PHP DateTime object instances.
echo $article->getDateTimeObject('created_at') ->format('m/d/Y');
You can also set a dates value by simply calling the setDateTimeObject
method
and passing a valid DateTime
instance.
$article->setDateTimeObject('created_at', new DateTime('09/01/1985'));
doctrine:migrate --down
The doctrine:migrate
now includes up
and down
options that will migrate
your schema one step in the requested direction.
$ php symfony doctrine:migrate --down
doctrine:migrate --dry-run
If your database supports rolling back DDL statements (MySQL does not), you
can take advantage of the new dry-run
option.
$ php symfony doctrine:migrate --dry-run
Output DQL Task as Table of Data
When you would previously run the doctrine:dql
command it will just output the
data as YAML. We have added a new --table
option. This option allows you to
output the data as a table, similar to how it ouputs in the MySQL command line.
So now the following is possible.
$ ./symfony doctrine:dql "FROM Article a" --table >> doctrine executing dql query DQL: FROM Article a +----+-----------+----------------+---------------------+---------------------+ | id | author_id | is_on_homepage | created_at | updated_at | +----+-----------+----------------+---------------------+---------------------+ | 1 | 1 | | 2009-07-07 18:02:24 | 2009-07-07 18:02:24 | | 2 | 2 | | 2009-07-07 18:02:24 | 2009-07-07 18:02:24 | +----+-----------+----------------+---------------------+---------------------+ (2 results)
Pass query parameters to doctrine:dql
The doctrine:dql
task has also been enhanced to accept query parameters as
arguments:
$ php symfony doctrine:dql "FROM Article a WHERE name LIKE ?" John%
Debugging queries in functional tests
The sfTesterDoctrine
class now includes a ->debug()
method. This method
will output information about that queries that have been run in the current
context.
$browser-> get('/articles')-> with('doctrine')->debug() ;
You can view only the last few queries executed by passing an integer to the method, or show only queries that contain a substring or match a regular expression by passing a string.
$browser-> get('/articles')-> with('doctrine')->debug('/from articles/i') ;
sfFormFilterDoctrine
The sfFormFilterDoctrine
class can now be seeded a Doctrine_Query
object
via the query
option:
$filter = new ArticleFormFilter(array(), array( 'query' => $table->createQuery()->select('title, body'), ));
The table method specified via ->setTableMethod()
(or now via the
table_method
option) is no longer required to return a query object. Any of
the following are valid sfFormFilterDoctrine
table methods:
// works in symfony >= 1.2 public function getQuery() { return $this->createQuery()->select('title, body'); } // works in symfony >= 1.2 public function filterQuery(Doctrine_Query $query) { return $query->select('title, body'); } // works in symfony >= 1.3 public function modifyQuery(Doctrine_Query $query) { $query->select('title, body'); }
Customizing a form filter is now easier. To add a filtering field, all you have to do is add the widget and a method to process it.
class UserFormFilter extends BaseUserFormFilter { public function configure() { $this->widgetSchema['name'] = new sfWidgetFormInputText(); $this->validatorSchema['name'] = new sfValidatorString(array('required' => false)); } public function addNameColumnQuery($query, $field, $value) { if (!empty($value)) { $query->andWhere(sprintf('CONCAT(%s.f_name, %1$s.l_name) LIKE ?', $query->getRootAlias()), $value); } } }
In earlier versions you would have need to extend getFields()
in addition to
creating a widget and method to get this to work.
Configuring Doctrine
You can now listen to the events doctrine.configure
and
doctrine.configure_connection
to configure Doctrine. This means the Doctrine
configuration can be easily customized from a plugin, as long as the plugin is
enabled prior to sfDoctrinePlugin
.
doctrine:generate-module
, doctrine:generate-admin
, doctrine:generate-admin-for-route
The doctrine:generate-module
, doctrine:generate-admin
, and
doctrine:generate-admin-for-route
tasks now takes a --actions-base-class
option that allows
the configuration of the actions base class for the generated modules.
Magic method doc tags
The magic getter and setter methods symfony adds to your Doctrine models are
now represented in the doc header of each generated base class. If your IDE
supports code completion, you should now see these getFooBar()
and
setFooBar()
methods show up on model objects, where FooBar
is a CamelCased
field name.
Using a different version of Doctrine
Using a different version of Doctrine is as easy as setting the
sf_doctrine_dir
setting in ProjectConfiguration
:
// config/ProjectConfiguration.class.php public function setup() { $this->enablePlugins('sfDoctrinePlugin'); sfConfig::set('sf_doctrine_dir', '/path/to/doctrine/lib'); }
Web Debug Toolbar
sfWebDebugPanel::setStatus()
Each panel in the web debug toolbar can specify a status that will affect its
title's background color. For example, the background color of the log panel's
title changes if any messages with a priority greater than sfLogger::INFO
are logged.
sfWebDebugPanel
request parameter
You can now specify a panel to be open on page load by appending a
sfWebDebugPanel
parameter to the URL. For example, appending
?sfWebDebugPanel=config
would cause the web debug toolbar to render with the
config panel open.
Panels can also inspect request parameters by accessing the web debug
request_parameters
option:
$requestParameters = $this->webDebug->getOption('request_parameters');
Partials
Slots improvements
The get_slot()
and include_slot()
helpers now accept a second parameter for
specifying the default slot content to return if none is provided by the slot:
<?php echo get_slot('foo', 'bar') // will output 'bar' if slot 'foo' is not defined ?> <?php include_slot('foo', 'bar') // will output 'bar' if slot 'foo' is not defined ?>
Pagers
The sfDoctrinePager
and sfPropelPager
methods now implement the Iterator
and Countable
interfaces.
<?php if (count($pager)): ?> <ul> <?php foreach ($pager as $article): ?> <li><?php echo link_to($article->getTitle(), 'article_show', $article) ?></li> <?php endforeach; ?> </ul> <?php else: ?> <p>No results.</p> <?php endif; ?>
View cache
The view cache manager nows accept params in factories.yml
. Generating the
cache key for a view has been refactored in different methods to ease
extending the class.
Two params are available in factories.yml
:
cache_key_use_vary_headers
(default:true
): specify if the cache keys should include the vary headers part. In practice, it says if the page cache should be http header dependent, as specified invary
cache parameter.cache_key_use_host_name
(default:true
): specify if the cache keys should include the host name part. In practice, it says if page cache should be hostname dependent.
Cache more
The view cache manager no longer refuses to cache based on whether there are
values in the $_GET
or $_POST
arrays. The logic now simply confirms the
current request is of the GET method before checking cache.yml
. This means
the following pages are now cacheable:
/js/my_compiled_javascript.js?cachebuster123
/users?page=3
Request
getContent()
The content of the request is now accessible via the getContent()
method.
PUT
and DELETE
parameters
When a request comes in with either a PUT
or a DELETE
HTTP method with a
content type set to application/x-www-form-urlencoded
, symfony now parses
the raw body and makes the parameters accessible like normal POST
parameters.
Actions
redirect()
The sfAction::redirect()
method family is now compatible with the url_for()
signature introduced in symfony 1.2:
// symfony 1.2 $this->redirect(array('sf_route' => 'article_show', 'sf_subject' => $article)); // symfony 1.3/1.4 $this->redirect('article_show', $article);
This enhancement was also applied to redirectIf()
and redirectUnless()
.
Helpers
link_to_if()
, link_to_unless()
The link_to_if()
and link_to_unless()
helpers are now compatible with the
link_to()
signature introduced in symfony 1.2:
// symfony 1.2 <?php echo link_to_unless($foo, '@article_show?id='.$article->getId()) ?> // symfony 1.3/1.4 <?php echo link_to_unless($foo, 'article_show', $article) ?>
Context
You can now listen to context.method_not_found
to dynamically add methods to
sfContext
. This is useful if you are added a lazy-loading factory, perhaps
from a plugin.
class myContextListener { protected $factory = null; public function listenForMethodNotFound(sfEvent $event) { $context = $event->getSubject(); if ('getLazyLoadingFactory' == $event['method']) { if (null === $this->factory) { $this->factory = new myLazyLoadingFactory($context->getEventDispatcher()); } $event->setReturnValue($this->factory); return true; } } }
This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.