| 1 |
Upgrading Projects from 1.1 to 1.2 |
|---|
| 2 |
================================== |
|---|
| 3 |
|
|---|
| 4 |
This document describes the changes made in symfony 1.2 and what need |
|---|
| 5 |
to be done to upgrade your symfony 1.1 projects. |
|---|
| 6 |
|
|---|
| 7 |
If you want more detailed information on what has been changed/added in symfony 1.2, |
|---|
| 8 |
you can read the [What's new?](http://www.symfony-project.org/tutorial/1_2/whats-new) tutorial. |
|---|
| 9 |
|
|---|
| 10 |
>**CAUTION** |
|---|
| 11 |
>symfony 1.2 is compatible with PHP 5.2.4 or later. |
|---|
| 12 |
>It might also work with PHP 5.2.0 to 5.2.3 but there is no guarantee. |
|---|
| 13 |
|
|---|
| 14 |
How to upgrade? |
|---|
| 15 |
--------------- |
|---|
| 16 |
|
|---|
| 17 |
To upgrade a project: |
|---|
| 18 |
|
|---|
| 19 |
* Check that all plugins used by your project are compatible with symfony |
|---|
| 20 |
1.2 |
|---|
| 21 |
|
|---|
| 22 |
* If you don't use a SCM tool, please make a backup of your project. |
|---|
| 23 |
|
|---|
| 24 |
* Upgrade symfony to 1.2 |
|---|
| 25 |
|
|---|
| 26 |
* Launch the `project:upgrade1.2` task from your project directory |
|---|
| 27 |
to perform an automatic upgrade: |
|---|
| 28 |
|
|---|
| 29 |
$ php symfony project:upgrade1.2 |
|---|
| 30 |
|
|---|
| 31 |
This task can be launched several times without any side effect. Each time |
|---|
| 32 |
you upgrade to a new symfony 1.2 beta / RC or the final symfony 1.2, you |
|---|
| 33 |
have to launch this task. |
|---|
| 34 |
|
|---|
| 35 |
* Upgrade the plugins to their 1.2 version |
|---|
| 36 |
|
|---|
| 37 |
* You need to rebuild your models and forms due to some changes described |
|---|
| 38 |
below (read below how to upgrade to Propel 1.3 first): |
|---|
| 39 |
|
|---|
| 40 |
$ php symfony propel:build-model |
|---|
| 41 |
$ php symfony propel:build-forms |
|---|
| 42 |
$ php symfony propel:build-filters |
|---|
| 43 |
|
|---|
| 44 |
* Clear the cache: |
|---|
| 45 |
|
|---|
| 46 |
$ php symfony cc |
|---|
| 47 |
|
|---|
| 48 |
The remaining sections explain the main changes made in symfony 1.2 that need |
|---|
| 49 |
some kind of upgrade (automatic or not). |
|---|
| 50 |
|
|---|
| 51 |
Upgrade to 1.2 final |
|---|
| 52 |
-------------------- |
|---|
| 53 |
|
|---|
| 54 |
If you have upgraded your 1.1 project before 1.2 final, you need to check the |
|---|
| 55 |
following things: |
|---|
| 56 |
|
|---|
| 57 |
* If you have generated CRUD modules with the `--non-verbose-templates` options, |
|---|
| 58 |
you need to remove the extra `$form->renderHiddenFields()` statement in the `_form.php` |
|---|
| 59 |
template. If not, you can have erroneous "CSRF attack detected" error messages. |
|---|
| 60 |
|
|---|
| 61 |
* For all generated CRUD modules, and if you have enabled CSRF protection, you need |
|---|
| 62 |
to insert `$request->checkCSRFProtection()` at the beginning of the generated |
|---|
| 63 |
`executeDelete()` method to be protected from CSRF attacks. |
|---|
| 64 |
|
|---|
| 65 |
Propel |
|---|
| 66 |
------ |
|---|
| 67 |
|
|---|
| 68 |
Propel has been upgraded to version 1.3, which replaces support for Creole |
|---|
| 69 |
with PDO. |
|---|
| 70 |
|
|---|
| 71 |
Due to the removal of Creole, the following classes are removed: |
|---|
| 72 |
|
|---|
| 73 |
*class name* | *equivalent* |
|---|
| 74 |
------------------------ | ------------------------------- |
|---|
| 75 |
`sfCreoleDatabase` | `sfPropelDatabase` |
|---|
| 76 |
`sfDebugConnection` | `DebugPDO` |
|---|
| 77 |
`sfMessageSource_Creole` | `sfMessageSource_PDO` |
|---|
| 78 |
`sfCreoleSessionStorage` | `sfPDOSessionStorage` |
|---|
| 79 |
|
|---|
| 80 |
The `propel:build-db` task has been removed as this functionality is not yet |
|---|
| 81 |
provided by Propel 1.3. |
|---|
| 82 |
|
|---|
| 83 |
The first step to upgrading is changing from Creole to PDO syntax in the |
|---|
| 84 |
database configuration from the project `databases.yml` file. |
|---|
| 85 |
|
|---|
| 86 |
Locate the following: |
|---|
| 87 |
|
|---|
| 88 |
[yml] |
|---|
| 89 |
all: |
|---|
| 90 |
propel: |
|---|
| 91 |
class: sfPropelDatabase |
|---|
| 92 |
param: |
|---|
| 93 |
dsn: mysql://username:password@localhost/example |
|---|
| 94 |
|
|---|
| 95 |
Replace with the following: |
|---|
| 96 |
|
|---|
| 97 |
[yml] |
|---|
| 98 |
dev: |
|---|
| 99 |
propel: |
|---|
| 100 |
param: |
|---|
| 101 |
classname: DebugPDO |
|---|
| 102 |
|
|---|
| 103 |
test: |
|---|
| 104 |
propel: |
|---|
| 105 |
param: |
|---|
| 106 |
classname: DebugPDO |
|---|
| 107 |
|
|---|
| 108 |
all: |
|---|
| 109 |
propel: |
|---|
| 110 |
class: sfPropelDatabase |
|---|
| 111 |
param: |
|---|
| 112 |
dsn: mysql:dbname=example;host=localhost |
|---|
| 113 |
username: username |
|---|
| 114 |
password: password |
|---|
| 115 |
encoding: utf8 |
|---|
| 116 |
persistent: true |
|---|
| 117 |
pooling: true |
|---|
| 118 |
classname: PropelPDO |
|---|
| 119 |
|
|---|
| 120 |
Next, you must also upgrade the `propel.ini` with the PDO format DSN and updated |
|---|
| 121 |
configuration options. |
|---|
| 122 |
|
|---|
| 123 |
Locate the following: |
|---|
| 124 |
|
|---|
| 125 |
[ini] |
|---|
| 126 |
propel.database = mysql |
|---|
| 127 |
propel.database.createUrl = mysql://username:password@localhost/ |
|---|
| 128 |
propel.database.url = mysql://username:password@localhost/example |
|---|
| 129 |
|
|---|
| 130 |
Replace with the following: |
|---|
| 131 |
|
|---|
| 132 |
[ini] |
|---|
| 133 |
propel.database = mysql |
|---|
| 134 |
propel.database.driver = mysql |
|---|
| 135 |
propel.database.url = mysql:dbname=example;host=localhost |
|---|
| 136 |
propel.database.user = username |
|---|
| 137 |
propel.database.password = password |
|---|
| 138 |
propel.database.encoding = utf8 |
|---|
| 139 |
|
|---|
| 140 |
Since the underlying api has changed quite a bit, you need to rebuild the |
|---|
| 141 |
object model: |
|---|
| 142 |
|
|---|
| 143 |
$ php symfony propel:build-model |
|---|
| 144 |
|
|---|
| 145 |
In most cases, this will be all that is required. If you have customized |
|---|
| 146 |
object model classes, you may need to manually upgrade for the changes in |
|---|
| 147 |
API from Creole to PDO. The upgrade task will attempt to change method |
|---|
| 148 |
signatures to match the `Persistent` interface, by adding type hinting for |
|---|
| 149 |
PropelPDO in `->save($con = null)` and `->delete($con = null)`. |
|---|
| 150 |
|
|---|
| 151 |
Change instances of: |
|---|
| 152 |
|
|---|
| 153 |
[php] |
|---|
| 154 |
public function save($con = null) |
|---|
| 155 |
public function delete($con = null) |
|---|
| 156 |
|
|---|
| 157 |
To add PropelPDO type hint: |
|---|
| 158 |
|
|---|
| 159 |
[php] |
|---|
| 160 |
public function save(PropelPDO $con = null) |
|---|
| 161 |
public function delete(PropelPDO $con = null) |
|---|
| 162 |
|
|---|
| 163 |
The transaction api has change slightly: `->begin` has been renamed `->beginTransaction()` |
|---|
| 164 |
and `->rollback()` has been renamed `->rollBack()`. Here are the differences: |
|---|
| 165 |
|
|---|
| 166 |
`Creole`: |
|---|
| 167 |
|
|---|
| 168 |
[php] |
|---|
| 169 |
$con->begin(); |
|---|
| 170 |
try { |
|---|
| 171 |
/* db logic */ |
|---|
| 172 |
$con->commit(); |
|---|
| 173 |
} catch (SQLException $sqle) { |
|---|
| 174 |
$con->rollback(); |
|---|
| 175 |
throw $sqle; |
|---|
| 176 |
} |
|---|
| 177 |
|
|---|
| 178 |
`PDO`: |
|---|
| 179 |
|
|---|
| 180 |
[php] |
|---|
| 181 |
$con->beginTransaction(); |
|---|
| 182 |
try { |
|---|
| 183 |
/* db logic */ |
|---|
| 184 |
$con->commit(); |
|---|
| 185 |
} catch (PDOException $sqle) { |
|---|
| 186 |
$con->rollBack(); |
|---|
| 187 |
throw $sqle; |
|---|
| 188 |
} |
|---|
| 189 |
|
|---|
| 190 |
The `::doSelectRS` method has been renamed to `::doSelectStmt`. Here are the differences: |
|---|
| 191 |
|
|---|
| 192 |
`Creole`: |
|---|
| 193 |
|
|---|
| 194 |
[php] |
|---|
| 195 |
// example of how to manually hydrate objects |
|---|
| 196 |
$rs = AuthorPeer::doSelectRS(new Criteria()); |
|---|
| 197 |
while($rs->next()) { |
|---|
| 198 |
$a = new Author(); |
|---|
| 199 |
$a->hydrate($rs); |
|---|
| 200 |
} |
|---|
| 201 |
|
|---|
| 202 |
// example of how to create array of single column |
|---|
| 203 |
$rs = AuthorPeer::doSelectRS(new Criteria()); |
|---|
| 204 |
$names = array(); |
|---|
| 205 |
while($rs->next()) { |
|---|
| 206 |
$names[] = $rs->getString(2); |
|---|
| 207 |
} |
|---|
| 208 |
|
|---|
| 209 |
$con = Propel::getConnection(SomeTablePeer::DATABASE_NAME); |
|---|
| 210 |
$stmt = $con->prepareStatement("SELECT * FROM some_table WHERE name = ?"); |
|---|
| 211 |
$stmt->setString(1, $name); |
|---|
| 212 |
$rs = $stmt->executeQuery(); |
|---|
| 213 |
while($rs->next()) { |
|---|
| 214 |
print "Name: " . $rs->getString("name") . "\n"; |
|---|
| 215 |
} |
|---|
| 216 |
|
|---|
| 217 |
|
|---|
| 218 |
`PDO`: |
|---|
| 219 |
|
|---|
| 220 |
[php] |
|---|
| 221 |
// example of how to manually hydrate objects |
|---|
| 222 |
$stmt = AuthorPeer::doSelectStmt(new Criteria()); |
|---|
| 223 |
while($row = $stmt->fetch(PDO::FETCH_NUM)) { |
|---|
| 224 |
$a = new Author(); |
|---|
| 225 |
$a->hydrate($row); |
|---|
| 226 |
} |
|---|
| 227 |
|
|---|
| 228 |
// example of how to create array of single column |
|---|
| 229 |
$stmt = AuthorPeer::doSelectStmt(new Criteria()); |
|---|
| 230 |
$names = array(); |
|---|
| 231 |
while($res = $stmt->fetchColumn(1)) { |
|---|
| 232 |
$names[] = $res; |
|---|
| 233 |
} |
|---|
| 234 |
|
|---|
| 235 |
$con = Propel::getConnection(SomeTablePeer::DATABASE_NAME); |
|---|
| 236 |
$stmt = $con->prepare("SELECT * FROM some_table WHERE name = ?"); |
|---|
| 237 |
$stmt->bindValue(1, $name); |
|---|
| 238 |
$stmt->execute(); |
|---|
| 239 |
while($row = $stmt->fetch()) { |
|---|
| 240 |
print "Name: " . $row['name'] . "\n"; |
|---|
| 241 |
} |
|---|
| 242 |
|
|---|
| 243 |
The `Clob` and `Lob` classes from Creole are not used anymore. It means you |
|---|
| 244 |
need to change your code when using these objects: |
|---|
| 245 |
|
|---|
| 246 |
[php] |
|---|
| 247 |
// Propel 1.1 |
|---|
| 248 |
$object->getClobColumn()->getContents(); |
|---|
| 249 |
|
|---|
| 250 |
// Propel 1.2 |
|---|
| 251 |
$object->getClobColumn(); |
|---|
| 252 |
|
|---|
| 253 |
See http://propel.phpdb.org/trac/wiki/Users/Documentation/1.3/Upgrading for more details |
|---|
| 254 |
on upgrading. |
|---|
| 255 |
|
|---|
| 256 |
All the Propel library have been moved from `lib/propel` to `lib`. The upgrade task |
|---|
| 257 |
upgrades the `propel.ini` file to reflect these changes. |
|---|
| 258 |
|
|---|
| 259 |
Request |
|---|
| 260 |
------- |
|---|
| 261 |
|
|---|
| 262 |
The `path_info_array`, `path_info_key`, and `relative_url_root` settings have |
|---|
| 263 |
been moved from `settings.yml` to `factories.yml` (in the `param` section of the |
|---|
| 264 |
`request` factory configuration). |
|---|
| 265 |
|
|---|
| 266 |
This change removes the dependency between `sfRequest` and `sfConfig`. |
|---|
| 267 |
|
|---|
| 268 |
These three request options are now passed to the request constructor as a |
|---|
| 269 |
fourth argument. The formats are also passed as an option, |
|---|
| 270 |
instead of being passed as an attribute. |
|---|
| 271 |
|
|---|
| 272 |
The request method constants from `sfRequest` values have changed from integers |
|---|
| 273 |
to strings and the `sfRequest::NONE` method has been removed: |
|---|
| 274 |
|
|---|
| 275 |
**Constant** | **Old value** | **New value** |
|---|
| 276 |
------------ | ------------- | ------------- |
|---|
| 277 |
GET | 2 | GET |
|---|
| 278 |
POST | 4 | POST |
|---|
| 279 |
PUT | 5 | PUT |
|---|
| 280 |
DELETE | 6 | DELETE |
|---|
| 281 |
HEAD | 7 | HEAD |
|---|
| 282 |
NONE | 1 | - |
|---|
| 283 |
|
|---|
| 284 |
The `getMethod()` and `getMethodName()` methods now returns the same value, |
|---|
| 285 |
so `getMethodName()` is deprecated. |
|---|
| 286 |
|
|---|
| 287 |
The `sfAction::getMethodNames()` and the corresponding code in |
|---|
| 288 |
`sfValidationExecutionFilter` from `sfCompat10Plugin` have been removed. |
|---|
| 289 |
This method was deprecated in 1.1 and was not really useable in 1.0. |
|---|
| 290 |
|
|---|
| 291 |
Validators |
|---|
| 292 |
---------- |
|---|
| 293 |
|
|---|
| 294 |
The `sfValidatorSchemaCompare` constant values have been changed. No change to |
|---|
| 295 |
your code need to be done, but now, you can use nice shortcuts. |
|---|
| 296 |
The following two examples are equivalent: |
|---|
| 297 |
|
|---|
| 298 |
[php] |
|---|
| 299 |
// symfony 1.1 and 1.2 |
|---|
| 300 |
$v = new sfValidatorSchemaCompare('left', sfValidatorSchemaCompare::EQUAL, 'right'); |
|---|
| 301 |
|
|---|
| 302 |
// symfony 1.2 only |
|---|
| 303 |
$v = new sfValidatorSchemaCompare('left', '==', 'right'); |
|---|
| 304 |
|
|---|
| 305 |
The `sfValidatorI18nChoiceCountry` and `sfValidatorI18nChoiceLanguage` validators |
|---|
| 306 |
had a required `culture` option in symfony 1.1. As the culture is not used in those |
|---|
| 307 |
validators, the `culture` option is now deprecated. It is still there to maintain |
|---|
| 308 |
backward compatibility but you don't need to provide it anymore. |
|---|
| 309 |
|
|---|
| 310 |
Forms |
|---|
| 311 |
----- |
|---|
| 312 |
|
|---|
| 313 |
In symfony 1.1, the `BaseFormPropel` was generated in the wrong place (under the |
|---|
| 314 |
`lib/form/base/` directory). You need to move it to the `lib/form/` directory. |
|---|
| 315 |
|
|---|
| 316 |
Widgets |
|---|
| 317 |
------- |
|---|
| 318 |
|
|---|
| 319 |
The new `sfWidgetFormChoice` widgets are now used by default by the Propel generated forms |
|---|
| 320 |
instead of `sfWidgetFormSelect`. To take advantage of this more powerful widgets, you will |
|---|
| 321 |
need to rebuilt your forms: |
|---|
| 322 |
|
|---|
| 323 |
$ php symfony propel:build-forms |
|---|
| 324 |
|
|---|
| 325 |
Response |
|---|
| 326 |
-------- |
|---|
| 327 |
|
|---|
| 328 |
There is a new setting for the `response` factory: `send_http_headers`. |
|---|
| 329 |
This setting is `true` by default, except for the `test` environment where |
|---|
| 330 |
the headers must not be sent by PHP (the same goal was achieved by using |
|---|
| 331 |
the `sf_test` setting in symfony 1.1). |
|---|
| 332 |
|
|---|
| 333 |
This change removes the dependency between `sfResponse` and `sfConfig`. |
|---|
| 334 |
|
|---|
| 335 |
The `getStylesheets()` and `getJavascripts()` methods can now return all the |
|---|
| 336 |
stylesheets and javascripts ordered by position if you pass `sfWebResponse::ALL` |
|---|
| 337 |
as their first argument: |
|---|
| 338 |
|
|---|
| 339 |
[php] |
|---|
| 340 |
$response = new sfWebResponse(new sfEventDispatcher()); |
|---|
| 341 |
$response->addStylesheet('foo.css'); |
|---|
| 342 |
$response->addStylesheet('bar.css', 'first'); |
|---|
| 343 |
|
|---|
| 344 |
var_export($response->getStylesheets()); |
|---|
| 345 |
|
|---|
| 346 |
// outputs |
|---|
| 347 |
array( |
|---|
| 348 |
'bar.css' => array(), |
|---|
| 349 |
'foo.css' => array(), |
|---|
| 350 |
) |
|---|
| 351 |
|
|---|
| 352 |
The `sfWebResponse::ALL` is also now the default value for the position argument. |
|---|
| 353 |
In symfony 1.1, as the default value is the empty string, the methods only return |
|---|
| 354 |
the files registered for the default position by default, which is not very intuitive. |
|---|
| 355 |
|
|---|
| 356 |
In symfony 1.1, you was able to get all the files by passing the `'ALL'` string |
|---|
| 357 |
as the position, and this behavior is still available by passing |
|---|
| 358 |
`sfWebResponse::RAW`: |
|---|
| 359 |
|
|---|
| 360 |
[php] |
|---|
| 361 |
var_export($response->getStylesheets(sfWebResponse::RAW)); |
|---|
| 362 |
|
|---|
| 363 |
// outputs |
|---|
| 364 |
array( |
|---|
| 365 |
'first' => |
|---|
| 366 |
array( |
|---|
| 367 |
'bar.css' => array (), |
|---|
| 368 |
), |
|---|
| 369 |
'' => |
|---|
| 370 |
array( |
|---|
| 371 |
'foo.css' => array(), |
|---|
| 372 |
), |
|---|
| 373 |
'last' => array(), |
|---|
| 374 |
) |
|---|
| 375 |
|
|---|
| 376 |
All the positions (first, '', and last) are now also available as constants: |
|---|
| 377 |
|
|---|
| 378 |
[php] |
|---|
| 379 |
sfWebResponse::FIRST === 'first' |
|---|
| 380 |
sfWebResponse::MIDDLE === '' |
|---|
| 381 |
sfWebResponse::LAST === 'last' |
|---|
| 382 |
|
|---|
| 383 |
The `removeStylesheet()` and `removeJavascript()` methods now only take one argument, |
|---|
| 384 |
the file to remove from the response. It will remove the file in all the available |
|---|
| 385 |
positions. In symfony 1.1, they take the position as a second argument. |
|---|
| 386 |
|
|---|
| 387 |
Prototype and Scriptaculous |
|---|
| 388 |
--------------------------- |
|---|
| 389 |
|
|---|
| 390 |
symfony continues to decouple its bundled software. In 1.2 the bundled Prototype |
|---|
| 391 |
and Scriptaculous libraries and helpers (`JavascriptHelper`) have been moved to a |
|---|
| 392 |
core-plugin. Core plugins behave like any plugin but are shipped with symfony. |
|---|
| 393 |
This will make the JavaScript and CSS files of the new `sfProtoculousPlugin` |
|---|
| 394 |
(the more or less unofficial name of the often featured duo) behave like real plugin |
|---|
| 395 |
assets. They will be now in `web/sfProtoculousPlugin` rather than in `web/sf` |
|---|
| 396 |
(as it has been in 1.0 and 1.1). The `prototype_web_dir` setting will also now point |
|---|
| 397 |
to the new directory. |
|---|
| 398 |
|
|---|
| 399 |
In addition some very basic javascript helpers that are reusable by any JS framework, have |
|---|
| 400 |
been extracted to a `JavascriptBaseHelper` which stays in core. |
|---|
| 401 |
|
|---|
| 402 |
As a new addition, `javascript_tag()` now can behave as `slot()`. This allows such usage |
|---|
| 403 |
|
|---|
| 404 |
[php] |
|---|
| 405 |
<?php javascript_tag() ?> |
|---|
| 406 |
alert('All is good') |
|---|
| 407 |
<?php end_javascript_tag() ?> |
|---|
| 408 |
|
|---|
| 409 |
Assets from built-in plugins |
|---|
| 410 |
---------------------------- |
|---|
| 411 |
|
|---|
| 412 |
Some built-in plugins come with some stylesheet and JavaScript files. To make them |
|---|
| 413 |
accessible to your project, you need to run the `plugin:publish-assets` task: |
|---|
| 414 |
|
|---|
| 415 |
$ php symfony plugin:publish-assets |
|---|
| 416 |
|
|---|
| 417 |
The `project:upgrade1.2` task do this for you. |
|---|
| 418 |
|
|---|
| 419 |
Browser |
|---|
| 420 |
------- |
|---|
| 421 |
|
|---|
| 422 |
The `sfBrowser` and `sfTestBrowser` classes have been refactored in four classes: |
|---|
| 423 |
|
|---|
| 424 |
* `sfBrowserBase`: The base browser class. It knows nothing about symfony, |
|---|
| 425 |
except classes from the symfony platform. |
|---|
| 426 |
|
|---|
| 427 |
* `sfBrowser`: It inherits from `sfBrowserBase` and implements the |
|---|
| 428 |
methods specific to symfony. |
|---|
| 429 |
|
|---|
| 430 |
* `sfTestFunctionalBase`: The base functional test class. It implements test |
|---|
| 431 |
methods that are independant from symfony. |
|---|
| 432 |
|
|---|
| 433 |
* `sfTestFunctional`: It inherits from `sfTestFunctionalBase` and implements |
|---|
| 434 |
the test methods specific to symfony. |
|---|
| 435 |
|
|---|
| 436 |
* `sfTestBrowser`: A BC class which is the same as the `sfTestFunctional` |
|---|
| 437 |
class with a constructor signature compatible with symfony 1.1 |
|---|
| 438 |
|
|---|
| 439 |
The idea behind the refactor is that the `sfTestFunctional` class is a test class, |
|---|
| 440 |
not a browser. So, it takes a browser and a test object as its arguments: |
|---|
| 441 |
|
|---|
| 442 |
[php] |
|---|
| 443 |
$testBrowser = new sfTestBrowser('localhost'); |
|---|
| 444 |
|
|---|
| 445 |
$tester = new sfTestFunctional(new sfBrowser('localhost'), new lime_test()); |
|---|
| 446 |
|
|---|
| 447 |
The `sfTestFunctional` class acts as a proxy for the browser class which means |
|---|
| 448 |
that all methods from the browser are accessible directly from the functional |
|---|
| 449 |
tester object. |
|---|
| 450 |
|
|---|
| 451 |
This refactor must not introduce backward incompatibility with symfony 1.1. |
|---|
| 452 |
|
|---|
| 453 |
The browser classes now add the `HTTP_REFERER` header for each request. |
|---|
| 454 |
|
|---|
| 455 |
Tests |
|---|
| 456 |
----- |
|---|
| 457 |
|
|---|
| 458 |
### Testers |
|---|
| 459 |
|
|---|
| 460 |
The `sfTestFunctionalBase` class now delegates the actual tests to `sfTester` |
|---|
| 461 |
classes. symfony 1.2 has several built-in tester classes: |
|---|
| 462 |
|
|---|
| 463 |
* `request`: `sfTesterRequest` |
|---|
| 464 |
* `response`: `sfTesterResponse` |
|---|
| 465 |
* `user`: `sfTesterUser` |
|---|
| 466 |
* `view_cache`: `sfTesterViewCache` |
|---|
| 467 |
|
|---|
| 468 |
All the old methods from the test browser have been moved to one of the tester |
|---|
| 469 |
class: |
|---|
| 470 |
|
|---|
| 471 |
*method name* | *tester class* | *new method name* |
|---|
| 472 |
---------------------- | ------------------- | ----------------- |
|---|
| 473 |
`isRequestParameter` | `sfTesterRequest` | `isParameter` |
|---|
| 474 |
`isRequestFormat` | `sfTesterRequest` | `isFormat` |
|---|
| 475 |
| | |
|---|
| 476 |
`isStatusCode` | `sfTesterResponse` | `isStatusCode` |
|---|
| 477 |
`responseContains` | `sfTesterResponse` | `contains` |
|---|
| 478 |
`isResponseHeader` | `sfTesterResponse` | `isHeader` |
|---|
| 479 |
`checkResponseElement` | `sfTesterResponse` | `checkElement` |
|---|
| 480 |
| | |
|---|
| 481 |
`isUserCulture` | `sfTesterUser` | `isCulture` |
|---|
| 482 |
| | |
|---|
| 483 |
`isCached` | `sfTesterViewCache` | `isCached` |
|---|
| 484 |
`isUriCached` | `sfTesterViewCache` | `isUriCached` |
|---|
| 485 |
|
|---|
| 486 |
The tester classes also comes with new test methods: |
|---|
| 487 |
|
|---|
| 488 |
*tester class* | *new method name* |
|---|
| 489 |
------------------- | ----------------- |
|---|
| 490 |
`sfTesterRequest` | `hasCookie` |
|---|
| 491 |
`sfTesterRequest` | `isCookie` |
|---|
| 492 |
`sfTesterRequest` | `isMethod` |
|---|
| 493 |
| |
|---|
| 494 |
`sfTesterUser` | `isAuthenticated` |
|---|
| 495 |
`sfTesterUser` | `hasCredential` |
|---|
| 496 |
`sfTesterUser` | `isAttribute` |
|---|
| 497 |
`sfTesterUser` | `isFlash` |
|---|
| 498 |
|
|---|
| 499 |
Even if the old methods are deprecated, they do not send any warning and will |
|---|
| 500 |
not be removed to maintain backward compatibility with symfony 1.0 and 1.1. |
|---|
| 501 |
|
|---|
| 502 |
### Links |
|---|
| 503 |
|
|---|
| 504 |
When you simulate a click on a button or on a link, you give the name to the `click()` |
|---|
| 505 |
method. But you don't have the possibility to differentiate two different links or buttons |
|---|
| 506 |
with the same name. |
|---|
| 507 |
|
|---|
| 508 |
As of symfony 1.2, the `click()` method takes a third argument to pass some options. |
|---|
| 509 |
|
|---|
| 510 |
You can pass a `position` option to change the link you want to click on: |
|---|
| 511 |
|
|---|
| 512 |
[php] |
|---|
| 513 |
$b-> |
|---|
| 514 |
click('/', array(), array('position' => 1))-> |
|---|
| 515 |
// ... |
|---|
| 516 |
; |
|---|
| 517 |
|
|---|
| 518 |
By default, symfony clicks on the first link it finds in the page. |
|---|
| 519 |
|
|---|
| 520 |
You can also pass a `method` option to change the method of the link or the form |
|---|
| 521 |
you are clicking on: |
|---|
| 522 |
|
|---|
| 523 |
[php] |
|---|
| 524 |
$b-> |
|---|
| 525 |
click('/delete', array(), array('method' => 'delete'))-> |
|---|
| 526 |
// ... |
|---|
| 527 |
; |
|---|
| 528 |
|
|---|
| 529 |
This is very useful when a link is converted to a dynamic form generated |
|---|
| 530 |
with JavaScript. |
|---|
| 531 |
|
|---|
| 532 |
Actions |
|---|
| 533 |
------- |
|---|
| 534 |
|
|---|
| 535 |
By default, when you use the `redirectIf()` or `redirectUnless()` methods |
|---|
| 536 |
in your actions, symfony automatically changes the response HTTP status |
|---|
| 537 |
code to 302. |
|---|
| 538 |
|
|---|
| 539 |
These two methods now have an additional optional argument to change this |
|---|
| 540 |
default status code: |
|---|
| 541 |
|
|---|
| 542 |
[php] |
|---|
| 543 |
$this->redirectIf($condition, '@homepage', 301); |
|---|
| 544 |
$this->redirectUnless($condition, '@homepage', 301); |
|---|
| 545 |
|
|---|
| 546 |
The `redirect()` method already have this feature. |
|---|
| 547 |
|
|---|
| 548 |
Components |
|---|
| 549 |
---------- |
|---|
| 550 |
|
|---|
| 551 |
If you have the output escaper enabled, there was a bug in symfony 1.0 and 1.1. |
|---|
| 552 |
When you passed some variables from an action to a component, these were escaped |
|---|
| 553 |
in the component. So, in some circumstances, you had to unescaped them: |
|---|
| 554 |
|
|---|
| 555 |
[php] |
|---|
| 556 |
public function executeInAComponent($request) |
|---|
| 557 |
{ |
|---|
| 558 |
$this->foo = $this->foo->getRawValue(); |
|---|
| 559 |
} |
|---|
| 560 |
|
|---|
| 561 |
As of symfony 1.2, this has been fixed and all variables are now unescaped |
|---|
| 562 |
by default in a component method. |
|---|
| 563 |
|
|---|
| 564 |
For the above example, you will need to remove the `getRawValue()` step as |
|---|
| 565 |
the framework does this automatically for you. |
|---|
| 566 |
|
|---|
| 567 |
sfParameterHolder |
|---|
| 568 |
----------------- |
|---|
| 569 |
|
|---|
| 570 |
The `has()` method of `sfParameterHolder` has been changed to be more |
|---|
| 571 |
semantically correct. |
|---|
| 572 |
|
|---|
| 573 |
It now returns `true` even if the value is `null`: |
|---|
| 574 |
|
|---|
| 575 |
[php] |
|---|
| 576 |
$ph = new sfParameterHolder(); |
|---|
| 577 |
$ph->set('foo', 'bar'); |
|---|
| 578 |
$ph->set('bar', null); |
|---|
| 579 |
|
|---|
| 580 |
$ph->has('foo') === true; |
|---|
| 581 |
$ph->has('bar') === true; // returns false under symfony 1.0 or 1.1 |
|---|
| 582 |
|
|---|
| 583 |
The `sfParameterHolder::has()` method is used by the `hasParameter()` and |
|---|
| 584 |
`hasAttribute()` methods available for a large number of core classes. |
|---|
| 585 |
|
|---|
| 586 |
Tasks |
|---|
| 587 |
----- |
|---|
| 588 |
|
|---|
| 589 |
The Propel tasks relying on Phing now output a clear error message if the embed |
|---|
| 590 |
Phing task fails. |
|---|
| 591 |
|
|---|
| 592 |
Some CLI tasks takes an application name as an argument because they need to connect |
|---|
| 593 |
to the database. We need an application because the configuration can be customized |
|---|
| 594 |
on an application basis and all the symfony configuration system is based on the |
|---|
| 595 |
application level. |
|---|
| 596 |
|
|---|
| 597 |
For these tasks, this argument has been removed in favor of an optional "application" |
|---|
| 598 |
option. If you don't provide the "application" option, symfony will take the database |
|---|
| 599 |
configuration from the project `databases.yml` file. |
|---|
| 600 |
|
|---|
| 601 |
The following task signatures have been changed accordingly: |
|---|
| 602 |
|
|---|
| 603 |
* `propel:build-all-load` |
|---|
| 604 |
* `propel:data-dump` |
|---|
| 605 |
* `propel:data-load` |
|---|
| 606 |
|
|---|
| 607 |
>**Note** |
|---|
| 608 |
>This is possible because `sfDatabaseManager` now takes a project configuration or an |
|---|
| 609 |
application configuration. For the curious one, this works because |
|---|
| 610 |
`sfDatabaseConfigHandler` now returns a static or a dynamic configuration based |
|---|
| 611 |
on an array of configuration files (see the `execute()` and `evaluate()` methods). |
|---|
| 612 |
|
|---|
| 613 |
The `propel:insert-sql` task removes all the data from the database. |
|---|
| 614 |
As it destroys information, it now asks the user to confirm the execution of |
|---|
| 615 |
the task. The same goes for the `propel:build-all` and `propel:build-all-load` tasks, |
|---|
| 616 |
as they call the `propel:insert-sql` task. |
|---|
| 617 |
|
|---|
| 618 |
If you want to use these tasks in a batch and want to avoid the confirmation question, |
|---|
| 619 |
pass the `no-confirmation` option: |
|---|
| 620 |
|
|---|
| 621 |
$ php symfony propel:insert-sql --no-confirmation |
|---|
| 622 |
|
|---|
| 623 |
Before symfony 1.2, the `propel:insert-sql` task was the only task to read its |
|---|
| 624 |
database configuration information from `propel.ini`. As of symfony 1.2, it |
|---|
| 625 |
reads its information from `databases.yml`. So, if you use several different |
|---|
| 626 |
connections in your model, the task will take those into account. |
|---|
| 627 |
Thanks to this new feature, you can now use the `--connection` option |
|---|
| 628 |
if you want to only load SQL statements for a given connection: |
|---|
| 629 |
|
|---|
| 630 |
$ php symfony propel:insert-sql --connection=propel |
|---|
| 631 |
|
|---|
| 632 |
You can also use the `--env` and the `--application` options to select a |
|---|
| 633 |
specific configuration to use: |
|---|
| 634 |
|
|---|
| 635 |
$ php symfony propel:insert-sql --env=prod --application=frontend |
|---|
| 636 |
|
|---|
| 637 |
The `propel:generate-crud` has been renamed to `propel:generate-module`. The old |
|---|
| 638 |
task name is still available as an alias. |
|---|
| 639 |
|
|---|
| 640 |
The `non-atomic-actions` option of `propel:generate-module` has been removed |
|---|
| 641 |
and some new options have been added: |
|---|
| 642 |
|
|---|
| 643 |
* singular: The singular name for the actions and templates |
|---|
| 644 |
* plural: The plural name for the actions and templates |
|---|
| 645 |
* route-prefix: The route prefix to use |
|---|
| 646 |
* with-propel-route: Whether the related routes are Propel aware |
|---|
| 647 |
|
|---|
| 648 |
To ease the debugging, the `propel:build-model`, `propel:build-all`, and |
|---|
| 649 |
`propel:build-all-load` tasks do not remove the generated XML schemas anymore |
|---|
| 650 |
if you pass the `--trace` option. |
|---|
| 651 |
|
|---|
| 652 |
URL helpers |
|---|
| 653 |
----------- |
|---|
| 654 |
|
|---|
| 655 |
The old `post` option of `link_to` is still valid but deprecated: |
|---|
| 656 |
|
|---|
| 657 |
[php] |
|---|
| 658 |
// is deprecated |
|---|
| 659 |
<?php echo link_to('@some_route', array('post' => true)) ?> |
|---|
| 660 |
|
|---|
| 661 |
// and equivalent to |
|---|
| 662 |
<?php echo link_to('@some_route', array('method' => 'post')) ?> |
|---|
| 663 |
|
|---|
| 664 |
The `url_for()` and `link_to()` helpers support new signatures. |
|---|
| 665 |
Instead of an internal URI, they can now also take the route name and |
|---|
| 666 |
an array of parameters: |
|---|
| 667 |
|
|---|
| 668 |
[php] |
|---|
| 669 |
echo url_for('@article', array('id' => 1)); |
|---|
| 670 |
echo link_to('Link to article', '@article', array('id' => 1)); |
|---|
| 671 |
|
|---|
| 672 |
The old behavior still works without changing anything to your code. |
|---|
| 673 |
|
|---|
| 674 |
Image helper |
|---|
| 675 |
------------ |
|---|
| 676 |
|
|---|
| 677 |
In symfony 1.0 and 1.1 the `image_tag` helper would generate the `alt` |
|---|
| 678 |
attribute of the img-tag from the filename. This now only happens if |
|---|
| 679 |
`sf_compat_10` is on. The new behaviour eases finding of unset alt attributes |
|---|
| 680 |
using a (x)html validator. As a bonus, there is now a `alt_title` option that |
|---|
| 681 |
will set alt and title attribute to the same value, which is useful for cross |
|---|
| 682 |
browser tooltips. |
|---|
| 683 |
|
|---|
| 684 |
View |
|---|
| 685 |
---- |
|---|
| 686 |
|
|---|
| 687 |
You can override `sfViewCacheManager::generateCacheKey()` by defining a |
|---|
| 688 |
`sf_cache_namespace_callable` setting. As of symfony 1.2, the callable |
|---|
| 689 |
is now called with an additional argument, the view cache manager instance. |
|---|
| 690 |
|
|---|
| 691 |
Configuration |
|---|
| 692 |
------------- |
|---|
| 693 |
|
|---|
| 694 |
Before symfony 1.2, all the plugins installed under the `plugins` directory, |
|---|
| 695 |
and all built-in plugins were automatically loaded. |
|---|
| 696 |
|
|---|
| 697 |
As of symfony 1.2, you need to enable the plugins you want to use in your projects. |
|---|
| 698 |
You can do this in your `ProjectConfiguration` class. Here is how to enable the |
|---|
| 699 |
Doctrine plugin and disable the Propel one: |
|---|
| 700 |
|
|---|
| 701 |
[php] |
|---|
| 702 |
public function setup() |
|---|
| 703 |
{ |
|---|
| 704 |
$this->enablePlugins('sfDoctrinePlugin'); |
|---|
| 705 |
$this->disablePlugins('sfPropelPlugin'); |
|---|
| 706 |
} |
|---|
| 707 |
|
|---|
| 708 |
You can add several plugins in one call by passing an array of plugin names: |
|---|
| 709 |
|
|---|
| 710 |
[php] |
|---|
| 711 |
public function setup() |
|---|
| 712 |
{ |
|---|
| 713 |
$this->enablePlugins(array('sfDoctrinePlugin', 'sfGuardPlugin')); |
|---|
| 714 |
} |
|---|
| 715 |
|
|---|
| 716 |
You can also change the order in which plugins are loaded by using the `setPlugins` |
|---|
| 717 |
method: |
|---|
| 718 |
|
|---|
| 719 |
[php] |
|---|
| 720 |
public function setup() |
|---|
| 721 |
{ |
|---|
| 722 |
$this->setPlugins(array('sfDoctrinePlugin', 'sfCompat10Plugin')); |
|---|
| 723 |
} |
|---|
| 724 |
|
|---|
| 725 |
The `orm` setting is deprecated in `settings.yml` as it is now automatically |
|---|
| 726 |
set when the ORM plugin is loaded. |
|---|
| 727 |
|
|---|
| 728 |
The `compat_10` setting is also deprecated in `settings.yml` as it is now |
|---|
| 729 |
automatically set when the `sfCompat10Plugin` is loaded. |
|---|
| 730 |
|
|---|
| 731 |
So, to enable the 1.0 compatibility plugin, you need to enable it in your |
|---|
| 732 |
configuration: |
|---|
| 733 |
|
|---|
| 734 |
[php] |
|---|
| 735 |
public function setup() |
|---|
| 736 |
{ |
|---|
| 737 |
$this->enablePlugins('sfCompat10Plugin'); |
|---|
| 738 |
} |
|---|
| 739 |
|
|---|
| 740 |
By default, symfony only enables the Propel plugin. |
|---|
| 741 |
|
|---|
| 742 |
You can also enable all installed plugins: |
|---|
| 743 |
|
|---|
| 744 |
[php] |
|---|
| 745 |
public function setup() |
|---|
| 746 |
{ |
|---|
| 747 |
$this->enableAllPluginsExcept('sfDoctrinePlugin'); |
|---|
| 748 |
} |
|---|
| 749 |
|
|---|
| 750 |
The previous example allows you to enable all plugins except the Doctrine one. |
|---|
| 751 |
If you upgrade from 1.0 or 1.1, this line will make symfony behave like in |
|---|
| 752 |
symfony 1.0 and 1.1. |
|---|
| 753 |
|
|---|
| 754 |
The `sfLoader` class is deprecated as the `getHelperDirs()` and `loadHelpers()` |
|---|
| 755 |
methods are now part of the `sfApplicationConfiguration` class. The `sfLoader` |
|---|
| 756 |
methods now generate a deprecated log message and then call the new methods from |
|---|
| 757 |
the current active configuration. |
|---|
| 758 |
|
|---|
| 759 |
### Plugin configuration |
|---|
| 760 |
|
|---|
| 761 |
Plugins now have the option of providing a plugin configuration class. These |
|---|
| 762 |
plugin configuration classes setup autoloading for the plugin, and are |
|---|
| 763 |
instantiated in `sfProjectConfiguration`. This means symfony 1.2 tasks no |
|---|
| 764 |
longer require an application argument for plugin classes to be used. This |
|---|
| 765 |
allows plugins to connect to `command.*` events, something that was not |
|---|
| 766 |
previously possible. |
|---|
| 767 |
|
|---|
| 768 |
I18N |
|---|
| 769 |
---- |
|---|
| 770 |
|
|---|
| 771 |
Internally, the `sfCultureInfo` class is now only used as a singleton. |
|---|
| 772 |
Even if it is still possible to bypass the `getInstance()` method and instantiate |
|---|
| 773 |
a new object directly, it is deprecated. By using the singleton, the performance |
|---|
| 774 |
are better as we only instantiate one culture info object per request and it |
|---|
| 775 |
also means that you can now override some culture information globally in your |
|---|
| 776 |
configuration classes. |
|---|
| 777 |
|
|---|
| 778 |
The `sfCultureInfo::getCountries()`, `sfCultureInfo::getCurrencies()`, and |
|---|
| 779 |
`sfCultureInfo::getLanguages()` methods now take an optional argument which allows |
|---|
| 780 |
to restrict the return value: |
|---|
| 781 |
|
|---|
| 782 |
[php] |
|---|
| 783 |
// will only return the FR and ES countries in english |
|---|
| 784 |
$countries = sfCultureInfo::getInstance('en')->getCountries(array('FR', 'ES')); |
|---|
| 785 |
|
|---|
| 786 |
The `sfCultureInfo::getCurrencies()` method now returns an array of currency names. |
|---|
| 787 |
In previous symfony versions, it returned an array with the symbol and the name. |
|---|
| 788 |
To get the old behavior, just pass `true` as the second argument: |
|---|
| 789 |
|
|---|
| 790 |
[php] |
|---|
| 791 |
$currencies = sfCultureInfo::getInstance('en')->getCurrencies(null, true); |
|---|
| 792 |
|
|---|
| 793 |
To get the translation of a single country, language, or currency, you can now |
|---|
| 794 |
use the `getCountry()`, `getCurrency()`, and `getLanguage()` methods from the |
|---|
| 795 |
`sfCultureInfo` class. |
|---|
| 796 |
|
|---|
| 797 |
Exception Templates |
|---|
| 798 |
------------------- |
|---|
| 799 |
|
|---|
| 800 |
symfony now respects the current request format when rendering any uncaught |
|---|
| 801 |
exceptions. You can customize each format's output by adding a template to |
|---|
| 802 |
your project or application `config/error` directory. |
|---|
| 803 |
|
|---|
| 804 |
For example, an uncaught exception during an XML request could render |
|---|
| 805 |
`config/error/exception.xml.php` when your application is in debug mode, or |
|---|
| 806 |
`config/error/error.xml.php` when your application is not in debug mode. |
|---|
| 807 |
|
|---|
| 808 |
If you had customized the 500 error template in your project, you will |
|---|
| 809 |
need to manually move it to the new directory: |
|---|
| 810 |
|
|---|
| 811 |
* For symfony 1.1: from `config/error500.php` to `config/error/error.html.php` |
|---|
| 812 |
* For symfony 1.0: from `web/errors/error500.php` to `config/error/error.html.php` |
|---|