[ Index ] |
|
Code source de Symfony 1.0.0 |
1 Chapter 14 - Generators 2 ======================= 3 4 Many applications are based on data stored in a database and offer an interface to access it. Symfony automates the repetitive task of creating a module providing data manipulation capabilities based on a Propel object. If your object model is properly defined, symfony can even generate an entire site administration automatically. This chapter will tell you of the two generators bundled in symfony: scaffolding and administration generator. The latter relies on a special configuration file with a complete syntax, so most of this chapter describes the various possibilities of the administration generator. 5 6 Code Generation Based on the Model 7 ---------------------------------- 8 9 In a web application, data access operations can be categorized as one of the following: 10 11 * Creation of a record 12 * Retrieval of records 13 * Update of a record (and modification of its columns) 14 * Deletion of a record 15 16 These operations are so common that they have a dedicated acronym: CRUD. Many pages can be reduced to one of them. For instance, in a forum application, the list of latest posts is a retrieve operation, and the reply to a post corresponds to a create operation. 17 18 The basic actions and templates that implement the CRUD operations for a given table are repeatedly created in web applications. In symfony, the model layer contains enough information to allow generating the CRUD operations code, so as to speed up the early part of the development or the back-end interfaces. 19 20 All the code generation tasks based on the model create an entire module, and result from a single call to the symfony command line in the shape of the following: 21 22 > symfony <TASK_NAME> <APP_NAME> <MODULE_NAME> <CLASS_NAME> 23 24 The code generation tasks are `propel-init-crud`, `propel-generate-crud`, and `propel-init-admin`. 25 26 ### Scaffolding and Administration 27 28 During application development, code generation can be used for two distinct purposes: 29 30 * A scaffolding is the basic structure (actions and templates) required to operate CRUD on a given table. The code is minimal, since it is meant to serve as a guideline for further development. It is a starting base that must be adapted to match your logic and presentation requirements. Scaffoldings are mostly used during the development phase, to provide a web access to a database, to build a prototype, or to bootstrap a module primarily based on a table. 31 * An administration is a sophisticated interface for data manipulation, dedicated to back-end administration. Administrations differ from scaffoldings because their code is not meant to be modified manually. They can be customized, extended, or assembled through configuration or inheritance. Their presentation is important, and they take advantage of additional features such as sorting, pagination, and filtering. An administration can be created and handed over to the client as a finished part of the software product. 32 33 The symfony command line uses the word crud to designate a scaffolding, and admin for an administration. 34 35 ### Initiating or Generating Code 36 37 Symfony offers two ways to generate code: either by inheritance (`init`) or by code generation (`generate`). 38 39 You can initiate a module, that is, create empty classes that inherit from the framework. This masks the PHP code of the actions and the templates to avoid them from being modified. This is useful if your data structure is not final, or if you just need a quick interface to a database to manipulate records. The code executed at runtime is not located in your application, but in the cache. The command-line tasks for this kind of generation start with `propel-init-`. 40 41 Initiated action code is empty. For instance, an initiated `article` module has actions looking like this: 42 43 [php] 44 class articleActions extends autoarticleActions 45 { 46 } 47 48 On the other hand, you can also generate the code of the actions and the templates so that it can be modified. The resulting module is therefore independent from the classes of the framework, and cannot be altered using configuration files. The command-line tasks for this kind of generation start with `propel-generate-`. 49 50 As the scaffoldings are built to serve as a base for further developments, it is often best to generate a scaffolding. On the other hand, an administration should be easy to update through a change in the configuration, and it should remain usable even if the data model changes. That's why administrations are initiated only. 51 52 ### Example Data Model 53 54 Throughout this chapter, the listings will demonstrate the capabilities of the symfony generators based on a simple example, which will remind you of Chapter 8. This is the well-known example of the weblog application, containing two `Article` and `Comment` classes. Listing 14-1 shows its schema, illustrated in Figure 14-1. 55 56 Listing 14-1 - `schema.yml` of the Example Weblog Application 57 58 propel: 59 blog_article: 60 _attributes: { phpName: Article } 61 id: 62 title: varchar(255) 63 content: longvarchar 64 created_at: 65 blog_comment: 66 _attributes: { phpName: Comment } 67 id: 68 article_id: 69 author: varchar(255) 70 content: longvarchar 71 created_at: 72 73 Figure 14-1 - Example data model 74 75  76 77 There is no particular rule to follow during the schema creation to allow code generation. Symfony will use the schema as is and interpret its attributes to generate a scaffolding or an administration. 78 79 >**TIP** 80 >To get the most out of this chapter, you need to actually do the examples. You will get a better understanding of what symfony generates and what can be done with the generated code if you have a view of every step described in the listings. So you are invited to create a data structure such as the one described previously, to create a database with a `blog_article` and a `blog_comment` table, and to populate this database with sample data. 81 82 Scaffolding 83 ----------- 84 85 Scaffolding is of great use in the early days of application development. With a single command, symfony creates an entire module based on the description of a given table. 86 87 ### Generating a Scaffolding 88 89 To generate the scaffolding for an `article` module based on the `Article` model class, type the following: 90 91 > symfony propel-generate-crud myapp article Article 92 93 Symfony reads the definition of the `Article` class in the `schema.yml` and creates a set of templates and actions based on it, in the `myapp/modules/article/` directory. 94 95 The generated module contains three views. The `list` view, which is the default view, displays the rows of the `blog_article` table when browsing to `http://localhost/myapp_dev.php/article` as reproduced in Figure 14-2. 96 97 Figure 14-2 - `list` view of the `article` module 98 99  100 101 Clicking an article identifier displays the `show` view. The details of one row appear in a single page, as in Figure 14-3. 102 103 Figure 14-3 - `show` view of the `article` module 104 105  106 107 Editing an article by clicking the edit link, or creating a new article by clicking the create link in the `list` view, displays the `edit` view, reproduced in Figure 14-4. 108 109 Using this module, you can create new articles, and modify or delete existing ones. The generated code is a good base for further developments. Listing 14-2 lists the generated actions and templates of the new module. 110 111 Figure 14-4 - `edit` view of the `article` module 112 113  114 115 Listing 14-2 - Generated CRUD Elements, in `myapp/modules/article/` 116 117 // In actions/actions.class.php 118 index // Forwards to the list action below 119 list // Displays the list of all the records of the table 120 show // Displays the lists of all columns of a record 121 edit // Displays a form to modify the columns of a record 122 update // Action called by the edit action form 123 delete // Deletes a record 124 create // Creates a new record 125 126 // In templates/ 127 editSuccess.php // Record edition form (edit view) 128 listSuccess.php // List of all records (list view) 129 showSuccess.php // Detail of one record (show view) 130 131 The logic of these actions and templates is quite simple and explicit, and so rather than reproduce and explain it all, Listing 14-3 gives just a sneak peek on the generated action class. 132 133 Listing 14-3 - Generated Action Class, in `myapp/modules/article/actions/actions.class.php` 134 135 [php] 136 class articleActions extends sfActions 137 { 138 public function executeIndex() 139 { 140 return $this->forward('article', 'list'); 141 } 142 143 public function executeList() 144 { 145 $this->articles = ArticlePeer::doSelect(new Criteria()); 146 } 147 148 public function executeShow() 149 { 150 $this->article = ArticlePeer::retrieveByPk($this->getRequestParameter('id')); 151 $this->forward404Unless($this->article); 152 } 153 ... 154 155 Modify the generated code to fit your requirements, repeat the CRUD generation for all the tables that you want to interact with, and you have a basic working application. Generating a scaffolding really bootstraps development; let symfony do the dirty job for you and focus on the interface and specifics. 156 157 ### Initiating a Scaffolding 158 159 Initiating a scaffolding is mostly useful when you need to check that you can access the data in the database. It is fast to build and also fast to delete once you're sure that everything works fine. 160 161 To initiate a Propel scaffolding that will create an `article` module to deal with the records of the `Article` model class name, type the following: 162 163 > symfony propel-init-crud myapp article Article 164 165 You can then access the `list` view using the default action: 166 167 http://localhost/myapp_dev.php/article 168 169 The resulting pages are exactly the same as for a generated scaffolding. You can use them as a simple web interface to the database. 170 171 If you check the newly created `actions.class.php` in the `article` module, you will see that it is empty: Everything is inherited from an auto-generated class. The same goes for the templates: There is no template file in the `templates/` directory. The code behind the initiated actions and templates is the same as for a generated scaffolding, but lies only in the application cache (`myproject/cache/myapp/prod/module/autoArticle/`). 172 173 During application development, developers initiate scaffoldings to interact with the data, regardless of interface. The code is not meant to be customized; an initiated scaffolding can be seen as a simple alternative to PHPmyadmin to manage data. 174 175 Administration 176 -------------- 177 178 Symfony can generate more advanced modules, still based on model class definitions from the `schema.yml` file, for the back-end of your applications. You can create an entire site administration with only generated administration modules. The examples of this section will describe administration modules added to a `backend` application. If your project doesn't have a `backend` application, create its skeleton now by calling the `init-app` task: 179 180 > symfony init-app backend 181 182 Administration modules interpret the model by way of a special configuration file called `generator.yml`, which can be altered to extend all the generated components and the module look and feel. Such modules benefit from the usual module mechanisms described in previous chapters (layout, validation, routing, custom configuration, autoloading, and so on). You can also override the generated action or templates, in order to integrate your own features into the generated administration, but `generator.yml` should take care of the most common requirements and restrict the use of PHP code only to the very specific. 183 184 ### Initiating an Administration Module 185 186 With symfony, you build an administration on a per-module basis. A module is generated based on a Propel object using the `propel-init-admin` task, which uses syntax similar to that used to initiate a scaffolding: 187 188 > symfony propel-init-admin backend article Article 189 190 This call is enough to create an `article` module in the `backend` application based on the `Article` class definition, and is accessible by the following: 191 192 http://localhost/backend.php/article 193 194 The look and feel of a generated module, illustrated in Figures 14-5 and 14-6, is sophisticated enough to make it usable out of the box for a commercial application. 195 196 Figure 14-5 - `list` view of the `article` module in the `backend` application 197 198  199 200 Figure 14-6 - `edit` view of the `article` module in the `backend` application 201 202  203 204 The difference between the interface of the scaffolding and the one of the administration may not look significant now, but the configurability of the administration will allow you to enhance the basic layout with many additional features without a line of PHP. 205 206 >**NOTE** 207 >Administration modules can only be initiated (not generated). 208 209 ### A Look at the Generated Code 210 211 The code of the Article administration module, in the `apps/backend/modules/article/` directory, is empty because it is only initiated. The best way to review the generated code of this module is to interact with it using the browser, and then check the contents of the `cache/` folder. Listing 14-4 lists the generated actions and the templates found in the cache. 212 213 Listing 14-4 - Generated Administration Elements, in `cache/backend/ENV/modules/article/` 214 215 // In actions/actions.class.php 216 create // Forwards to edit 217 delete // Deletes a record 218 edit // Displays a form to modify the fields of a record 219 // And handles the form submission 220 index // Forwards to list 221 list // Displays the list of all the records of the table 222 save // Forwards to edit 223 224 // In templates/ 225 _edit_actions.php 226 _edit_footer.php 227 _edit_form.php 228 _edit_header.php 229 _edit_messages.php 230 _filters.php 231 _list.php 232 _list_actions.php 233 _list_footer.php 234 _list_header.php 235 _list_messages.php 236 _list_td_actions.php 237 _list_td_stacked.php 238 _list_td_tabular.php 239 _list_th_stacked.php 240 _list_th_tabular.php 241 editSuccess.php 242 listSuccess.php 243 244 This shows that a generated administration module is composed mainly of two views, `edit` and `list`. If you have a look at the code, you will find it to be very modular, readable, and extensible. 245 246 ### Introducing the generator.yml Configuration File 247 248 The main difference between scaffoldings and administrations (apart from the fact that administration-generated modules don't have a `show` action) is that an administration relies on parameters found in the `generator.yml` YAML configuration file. To see the default configuration of a newly created administration module, open the `generator.yml` file, located in the `backend/modules/article/config/generator.yml` directory and reproduced in Listing 14-5. 249 250 Listing 14-5 - Default Generator Configuration, in `backend/modules/article/config/generator.yml` 251 252 generator: 253 class: sfPropelAdminGenerator 254 param: 255 model_class: Article 256 theme: default 257 258 This configuration is enough to generate the basic administration. Any customization is added under the `param` key, after the `theme` line (which means that all lines added at the bottom of the `generator.yml` file must at least start with four blank spaces to be properly indented). Listing 14-6 shows a typical customized `generator.yml`. 259 260 Listing 14-6 - Typical Complete Generator Configuration 261 262 generator: 263 class: sfPropelAdminGenerator 264 param: 265 model_class: Article 266 theme: default 267 268 fields: 269 author_id: { name: Article author } 270 271 list: 272 title: List of all articles 273 display: [title, author_id, category_id] 274 fields: 275 published_on: { params: date_format='dd/MM/yy' } 276 layout: stacked 277 params: | 278 %%is_published%%<strong>%%=title%%</strong><br /><em>by %%author%% 279 in %%category%% (%%published_on%%)</em><p>%%content_summary%%</p> 280 filters: [title, category_id, author_id, is_published] 281 max_per_page: 2 282 283 edit: 284 title: Editing article "%%title%%" 285 display: 286 "Post": [title, category_id, content] 287 "Workflow": [author_id, is_published, created_on] 288 fields: 289 category_id: { params: disabled=true } 290 is_published: { type: plain} 291 created_on: { type: plain, params: date_format='dd/MM/yy' } 292 author_id: { params: size=5 include_custom=>> Choose an author << } 293 published_on: { credentials: } 294 content: { params: rich=true tinymce_options=height:150 } 295 296 The following sections explain in detail all the parameters that can be used in this configuration file. 297 298 Generator Configuration 299 ----------------------- 300 301 The generator configuration file is very powerful, allowing you to alter the generated administration in many ways. But such capabilities come with a price: The overall syntax description is long to read and learn, making this chapter one of the longest in this book. The symfony website proposes an additional resource that will help you learn this syntax: the administration generator cheat sheet, reproduced in Figure 14-7. Download it from [http://www.symfony-project.com/uploads/assets/sfAdminGeneratorRefCard.pdf](http://www.symfony-project.com/uploads/assets/sfAdminGeneratorRefCard.pdf), and keep it close to you when you read the following examples of this chapter. 302 303 The examples of this section will tweak the `article` administration module, as well as the `comment` administration module, based on the `Comment` class definition. Create the latter with the `propel-init-admin` task: 304 305 > symfony propel-init-admin backend comment Comment 306 307 Figure 14-7 - The administration generator cheat sheet 308 309  310 311 ### Fields 312 313 By default, the columns of the `list` view and the fields of the `edit` view are the columns defined in `schema.yml`. With `generator.yml`, you can choose which fields are displayed, which ones are hidden, and add fields of your own--even if they don't have a direct correspondence in the object model. 314 315 #### Field Settings 316 317 The administration generator creates a `field` for each column in the `schema.yml` file. Under the `fields` key, you can modify the way each field is displayed, formatted, etc. For instance, the field settings shown in Listing 14-7 define a custom label class and input type for the `title` field, and a label and a tooltip for the `content` field. The following sections will describe in detail how each parameter works. 318 319 Listing 14-7 - Setting a Custom Label for a Column 320 321 generator: 322 class: sfPropelAdminGenerator 323 param: 324 model_class: Article 325 theme: default 326 327 fields: 328 title: { name: Article Title, type: textarea_tag, params: class=foo } 329 content: { name: Body, help: Fill in the article body } 330 331 In addition to this default definition for all the views, you can override the field settings for a given view (`list` and `edit`), as demonstrated in Listing 14-8. 332 333 Listing 14-8 - Overriding Global Settings View per View 334 335 generator: 336 class: sfPropelAdminGenerator 337 param: 338 model_class: Article 339 theme: default 340 341 fields: 342 title: { name: Article Title } 343 content: { name: Body } 344 345 list: 346 fields: 347 title: { name: Title } 348 349 edit: 350 fields: 351 content: { name: Body of the article } 352 353 This is a general principle: Any settings that are set for the whole module under the `fields` key can be overridden by view-specific (`list` and `edit`) areas that follow. 354 355 #### Adding Fields to the Display 356 357 The fields that you define in the `fields` section can be displayed, hidden, ordered, and grouped in various ways for each view. The `display` key is used for that purpose. For instance, to arrange the fields of the `comment` module, use the code of Listing 14-9. 358 359 Listing 14-9 - Choosing the Fields to Display, in `modules/comment/config/generator.yml` 360 361 generator: 362 class: sfPropelAdminGenerator 363 param: 364 model_class: Comment 365 theme: default 366 367 fields: 368 article_id: { name: Article } 369 created_at: { name: Published on } 370 content: { name: Body } 371 372 list: 373 display: [id, article_id, content] 374 375 edit: 376 display: 377 NONE: [article_id] 378 Editable: [author, content, created_at] 379 380 The `list` will then display three columns, as in Figure 14-8, and the `edit` form will display four fields, assembled in two groups, as in Figure 14-9. 381 382 Figure 14-8 - Custom column setting in the `list` view of the `comment` module 383 384  385 386 Figure 14-9 - Grouping fields in the `edit` view of the `comment` module 387 388  389 390 So you can use the `display` setting in two ways: 391 392 * To select the columns to display and the order in which they appear, put the fields in a simple array--as in the previous `list` view. 393 * To group fields, use an associative array with the group name as a key, or `NONE` for a group with no name. The value is still an array of ordered column names. 394 395 >**TIP** 396 >By default, the primary key columns never appear in either view. 397 398 #### Custom Fields 399 400 As a matter of fact, the fields configured in `generator.yml` don't even need to correspond to actual columns defined in the schema. If the related class offers a custom getter, it can be used as a field for the `list` view; if there is a getter and/or a setter, it can also be used in the `edit` view. For instance, you can extend the `Article` model with a `getNbComments()` method similar to the one in Listing 14-10. 401 402 Listing 14-10 - Adding a Custom Getter in the Model, in `lib/model/Article.class.php` 403 404 [php] 405 public function getNbComments() 406 { 407 return $this->countComments(); 408 } 409 410 Then `nb_comments` is available as a field in the generated module (notice that the getter uses a camelCase version of the field name), as in Listing 14-11. 411 412 Listing 14-11 - Custom Getters Provide Additional Columns for Administration Modules, in `backend/modules/article/config/generator.yml` 413 414 generator: 415 class: sfPropelAdminGenerator 416 param: 417 model_class: Article 418 theme: default 419 420 list: 421 display: [id, title, nb_comments, created_at] 422 423 The resulting `list` view of the `article` module is shown in Figure 14-10. 424 425 Figure 14-10 - Custom field in the `list` view of the `article` module 426 427  428 429 Custom fields can even return HTML code to display more than raw data. For instance, you can extend the `Comment` class with a `getArticleLink()` method as in Listing 14-12. 430 431 Listing 14-12 - Adding a Custom Getter Returning HTML, in `lib/model/Comment.class.php` 432 433 [php] 434 public function getArticleLink() 435 { 436 return link_to($this->getArticle()->getTitle(), 'article/edit?id='.$this->getArticleId()); 437 } 438 439 You can use this new getter as a custom field in the `comment/list` view with the same syntax as in Listing 14-11. See the example in Listing 14-13, and the result in Figure 14-11, where the HTML code output by the getter (a hyperlink to the article) appears in the second column instead of the article primary key. 440 441 Listing 14-13 - Custom Getters Returning HTML Can Also Be Used As Additional Columns, in `modules/comment/config/generator.yml` 442 443 generator: 444 class: sfPropelAdminGenerator 445 param: 446 model_class: Comment 447 theme: default 448 449 list: 450 display: [id, article_link, content] 451 452 Figure 14-11 - Custom field in the `list` view of the `comment` module 453 454  455 456 #### Partial Fields 457 458 The code located in the model must be independent from the presentation. The example of the `getArticleLink()` method earlier doesn't respect this principle of layer separation, because some view code appears in the model layer. To achieve the same goal in a correct way, you'd better put the code that outputs HTML for a custom field in a partial. Fortunately, the administration generator allows it if you declare a field name prefixed by an underscore. In that case, the `generator.yml` file of Listing 14-13 is to be modified as in Listing 14-14. 459 460 Listing 14-14 - Partials Can Be Used As Additional Columns--Use the `_` Prefix 461 462 list: 463 display: [id, _article_link, created_at] 464 465 For this to work, an `_article_link.php` partial must be created in the `modules/comment/templates/` directory, as in Listing 14-15. 466 467 Listing 14-15 - Example Partial for the `list` View, in `modules/comment/templates/_article_link.php` 468 469 <?php echo link_to($comment->getArticle()->getTitle(), 'article/edit?id='.$comment->getArticleId()) ?> 470 471 Notice that the partial template of a partial field has access to the current object through a variable named by the class (`$comment` in this example). For instance, for a module built for a class called `UserGroup`, the partial will have access to the current object through the `$user_group` variable. 472 473 The result is the same as in Figure 14-11, except that the layer separation is respected. If you get used to respecting the layer separation, you will end up with more maintainable applications. 474 475 If you need to customize the parameters of a partial field, do the same as for a normal field, under the `field` key. Just don't include the leading underscore (`_`) in the key--see an example in Listing 14-16. 476 477 Listing 14-16 - Partial Field Properties Can Be Customized Under the `fields` Key 478 479 fields: 480 article_link: { name: Article } 481 482 If your partial becomes crowded with logic, you'll probably want to replace it with a component. Change the `_` prefix to `~` and you can define a component field, as you can see in Listing 14-17. 483 484 Listing 14-17 - Components Can Be Used As Additional Columns--Use the `~` Prefix 485 486 ... 487 list: 488 display: [id, ~article_link, created_at] 489 490 In the generated template, this will result by a call to the `articleLink` component of the current module. 491 492 >**NOTE** 493 >Custom and partial fields can be used in the `list` view, the `edit` view, and for filters. If you use the same partial for several views, the context (`'list'`, `'edit'`, or `'filter'`) is stored in the `$type` variable. 494 495 ### View Customization 496 497 To change the `edit` and `list` views' appearance, you could be tempted to alter the templates. But because they are automatically generated, doing so isn't a very good idea. Instead, you should use the `generator.yml` configuration file, because it can do almost everything that you need without sacrificing modularity. 498 499 #### Changing the View Title 500 501 In addition to a custom set of fields, the `list` and `edit` pages can have a custom page title. For instance, if you want to customize the title of the `article` views, do as in Listing 14-18. The resulting `edit` view is illustrated in Figure 14-12. 502 503 Listing 14-18 - Setting a Custom Title for Each View, in `backend/modules/article/config/generator.yml` 504 505 list: 506 title: List of Articles 507 ... 508 509 edit: 510 title: Body of article %%title%% 511 display: [content] 512 513 Figure 14-12 - Custom title in the `edit` view of the `article` module 514 515  516 517 As the default titles use the class name, they are often good enough--provided that your model uses explicit class names. 518 519 >**TIP** 520 >In the string values of `generator.yml`, the value of a field can be accessed via the name of the field surrounded by `%%`. 521 522 #### Adding Tooltips 523 524 In the `list` and `edit` views, you can add tooltips to help describe the fields that are displayed. For instance, to add a tooltip to the `article_id` field of the `edit` view of the `comment` module, add a `help` property in the `fields` definition as in Listing 14-19. The result is shown in Figure 14-13. 525 526 Listing 14-19 - Setting a Tooltip in the `edit` View, in `modules/comment/config/generator.yml` 527 528 edit: 529 fields: 530 ... 531 article_id: { help: The current comment relates to this article } 532 533 Figure 14-13 - Tooltip in the `edit` view of the `comment` module 534 535  536 537 In the `list` view, tooltips are displayed in the column header; in the `edit` view, they appear under the input. 538 539 #### Modifying the Date Format 540 541 Dates can be displayed using a custom format as soon as you use the `date_format` param, as demonstrated in Listing 14-20. 542 543 Listing 14-20 - Formatting a Date in the `list` View 544 545 list: 546 fields: 547 created_at: { name: Published, params: date_format='dd/MM' } 548 549 It takes the same format parameter as the `format_date()` helper described in the previous chapter. 550 551 >**SIDEBAR** 552 >Administration templates are i18N ready 553 > 554 >All of the text found in the generated templates is automatically internationalized (i.e., enclosed in a call to the `__()` helper). This means that you can easily translate a generated administration by adding the translations of the phrases in an XLIFF file, in your `apps/myapp/i18n/` directory, as explained in the previous chapter. 555 556 ### List View-Specific Customization 557 558 The `list` view can display the details of a record in a tabular way, or with all the details stacked in one line. It also contains filters, pagination, and sorting features. These features can be altered by configuration, as described in the next sections. 559 560 #### Changing the Layout 561 562 By default, the hyperlink between the `list` view and the `edit` view is borne by the primary key column. If you refer back to Figure 14-11, you will see that the `id` column in the comment list not only shows the primary key of each comment, but also provides a hyperlink allowing users to access the `edit` view. 563 564 If you prefer the hyperlink to the detail of the record to appear on another column, prefix the column name by an equal sign (`=`) in the `display` key. Listing 14-21 shows how to remove the `id` from the displayed fields of the comment `list` and to put the hyperlink on the `content` field instead. Check Figure 14-14 for a screenshot. 565 566 Listing 14-21 - Moving the Hyperlink for the `edit` View in the `list` View, in `modules/comment/config/generator.yml` 567 568 list: 569 display: [article_link, =content] 570 571 Figure 14-14 - Moving the link to the `edit` view on another column, in the `list` view of the `comment` module 572 573  574 575 By default, the `list` view uses the `tabular` layout, where the fields appear as columns, as shown previously. But you can also use the `stacked` layout and concatenate the fields into a single string that expands on the full length of the table. If you choose the `stacked` layout, you must set in the `params` key the pattern defining the value of each line of the list. For instance, Listing 14-22 defines a stacked layout for the list view of the comment module. The result appears in Figure 14-15. 576 577 Listing 14-22 - Using a `stacked` Layout in the `list` View, in `modules/comment/config/generator.yml` 578 579 list: 580 layout: stacked 581 params: | 582 %%=content%% <br /> 583 (sent by %%author%% on %%created_at%% about %%article_link%%) 584 display: [created_at, author, content] 585 586 Figure 14-15 - Stacked layout in the `list` view of the `comment` module 587 588  589 590 Notice that a `tabular` layout expects an array of fields under the `display` key, but a `stacked` layout uses the `params` key for the HTML code generated for each record. However, the `display` array is still used in a `stacked` layout to determine which column headers are available for the interactive sorting. 591 592 #### Filtering the Results 593 594 In a `list` view, you can add a set of filter interactions. With these filters, users can both display fewer results and get to the ones they want faster. Configure the filters under the `filters` key, with an array of field names. For instance, add a filter on the `article_id`, `author`, and `created_at` fields to the comment `list` view, as in Listing 14-23, to display a filter box similar to the one in Figure 14-16. You will need to add a `__toString()` method to the `Article` class (returning, for instance, the article `title`) for this to work. 595 596 Listing 14-23 - Setting the Filters in the `list` View, in `modules/comment/config/generator.yml` 597 598 list: 599 filters: [article_id, author, created_at] 600 layout: stacked 601 params: | 602 %%=content%% <br /> 603 (sent by %%author%% on %%created_at%% about %%article_link%%) 604 display: [created_at, author, content] 605 606 Figure 14-16 - Filters in the `list` view of the `comment` module 607 608  609 610 The filters displayed by symfony depend on the column type: 611 612 * For text columns (like the `author` field in the `comment` module), the filter is a text input allowing text-based search with wildcards (`*`). 613 * For foreign keys (like the `article_id` field in the `comment` module), the filter is a drop-down list of the records of the related table. As for the regular `object_select_tag()`, the options of the drop-down list are the ones returned by the `__toString()` method of the related class. 614 * For date columns (like the `created_at` field in the `comment` module), the filter is a pair of rich date tags (text fields filled by calendar widgets), allowing the selection of a time interval. 615 * For Boolean columns, the filter is a drop-down list having `true`, `false`, and `true or false` options--the last value reinitializes the filter. 616 617 Just like you use partial fields in lists, you can also use partial filters to create a filter that symfony doesn't handle on its own. For instance, imagine a `state` field that may contain only two values (`open` and `closed`), but for some reason you store those values directly in the field instead of using a table relation. A simple filter on this field (of type `string`) would be a text-based search, but what you want is probably a drop-down list of values. That's easy to achieve with a partial filter. See Listing 14-24 for an example implementation. 618 619 Listing 14-24 - Using a Partial Filter 620 621 [php] 622 // Define the partial, in templates/_state.php 623 <?php echo select_tag('filters[state]', options_for_select(array( 624 '' => '', 625 'open' => 'open', 626 'closed' => 'closed', 627 ), isset($filters['state']) ? $filters['state'] : '')) ?> 628 629 // Add the partial filter in the filter list, in config/generator.yml 630 list: 631 filters: [date, _state] 632 633 Notice that the partial has access to a `$filters` variable, which is useful to get the current value of the filter. 634 635 There is one last option that can be very useful for looking for empty values. Imagine that you want to filter the list of comments to display only the ones that have no author. The problem is that if you leave the author filter empty, it will be ignored. The solution is to set the `filter_is_empty` field setting to true, as in Listing 14-25, and the filter will display an additional check box, which will allow you to look for empty values, as illustrated in Figure 14-17. 636 637 Listing 14-25 - Adding Filtering of Empty Values on the `author` Field in the `list` View 638 639 list: 640 fields: 641 author: { filter_is_empty: true } 642 filters: [article_id, author, created_at] 643 644 Figure 14-17 - Allowing the filtering of empty `author` values 645 646  647 648 #### Sorting the List 649 650 In a `list` view, the table headers are hyperlinks that can be used to reorder the list, as shown in Figure 14-18. These headers are displayed both in the `tabular` and `stacked` layouts. Clicking these links reloads the page with a `sort` parameter that rearranges the list order accordingly. 651 652 Figure 14-18 - Table headers of the `list` view are sort controls 653 654  655 656 You can reuse the syntax to point to a list directly sorted according to a column: 657 658 [php] 659 <?php echo link_to('Comment list by date', 'comment/list?sort=created_at&type=desc' ) ?> 660 661 You can also define a default `sort` order for the `list` view directly in the `generator.yml` file. The syntax follows the example given in Listing 14-26. 662 663 Listing 14-26 - Setting a Default Sort Field in the `list` View 664 665 list: 666 sort: created_at 667 # Alternative syntax, to specify a sort order 668 sort: [created_at, desc] 669 670 >**NOTE** 671 >Only the fields that correspond to an actual column are transformed into sort controls--not the custom or partial fields. 672 673 #### Customizing the Pagination 674 675 The generated administration effectively deals with even large tables, because the `list` view uses pagination by default. When the actual number of rows in a table exceeds the number of maximum rows per page, pagination controls appear at the bottom of the list. For instance, Figure 14-19 shows the list of comments with six test comments in the table but a limit of five comments displayed per page. Pagination ensures a good performance, because only the displayed rows are effectively retrieved from the database, and a good usability, because even tables with millions of rows can be managed by an administration module. 676 677 Figure 14-19 - Pagination controls appear on long lists 678 679  680 681 You can customize the number of records to be displayed in each page with the `max_per_page` parameter: 682 683 list: 684 max_per_page: 5 685 686 #### Using a Join to Speed Up Page Delivery 687 688 By default, the administration generator uses a simple `doSelect()` to retrieve a list of records. But, if you use related objects in the list, the number of database queries required to display the list may rapidly increase. For instance, if you want to display the name of the article in a list of comments, an additional query is required for each post in the list to retrieve the related `Article` object. So you may want to force the pager to use a `doSelectJoinXXX()` method to optimize the number of queries. This can be specified with the `peer_method` parameter. 689 690 list: 691 peer_method: doSelectJoinArticle 692 693 Chapter 18 explains the concept of Join more extensively. 694 695 ### Edit View-Specific Customization 696 697 In an `edit` view, the user can modify the value of each column for a given record. Symfony determines the type of input to display according to the data type of the column. It then generates an `object_*_tag()` helper, and passes that helper the object and the property to edit. For instance, if the article `edit` view configuration stipulates that the user can edit the `title` field: 698 699 edit: 700 display: [title, ...] 701 702 then the `edit` page will display a regular text input tag to edit the `title` because this column is defined as a `varchar` type in the schema. 703 704 [php] 705 <?php echo object_input_tag($article, 'getTitle') ?> 706 707 #### Changing the Input Type 708 709 The default type-to-field conversion rules are as follows: 710 711 * A column defined as `integer`, `float`, `char`, `varchar(size)` appears in the `edit` view as an `object_input_tag()`. 712 * A column defined as `longvarchar` appears as an `object_textarea_tag()`. 713 * A foreign key column appears as an `object_select_tag()`. 714 * A column defined as `boolean` appears as an `object_checkbox_tag()`. 715 * A column defined as a `timestamp` or `date` appears as an `object_input_date_tag()`. 716 717 You may want to override these rules to specify a custom input type for a given field. To that extent, set the `type` parameter in the `fields` definition to a specific form helper name. As for the options of the generated `object_*_tag()`, you can change them with the params parameter. See an example in Listing 14-27. 718 719 Listing 14-27 - Setting a Custom Input Type and Params for the `edit` View 720 721 generator: 722 class: sfPropelAdminGenerator 723 param: 724 model_class: Comment 725 theme: default 726 727 edit: 728 fields: 729 ## Drop the input, just display plain text 730 id: { type: plain } 731 ## The input is not editable 732 author: { params: disabled=true } 733 ## The input is a textarea (object_textarea_tag) 734 content: { type: textarea_tag, params: rich=true css=user.css tinymce_options=width:330 } 735 ## The input is a select (object_select_tag) 736 article_id: { params: include_custom=Choose an article } 737 ... 738 739 The params parameters are passed as options to the generated `object_*_tag()`. For instance, the `params` definition for the preceding `article_id` will produce in the template the following: 740 741 <?php echo object_select_tag($comment, 'getArticleId', 'related_class=Article', 'include_custom=Choose an article') ?> 742 743 This means that all the options usually available in the form helpers can be customized in an `edit` view. 744 745 #### Handling Partial Fields 746 747 Partial fields can be used in `edit` views just like in `list` views. The difference is that you have to handle by hand, in the action, the update of the column according to the value of the request parameter sent by the partial field. Symfony knows how to handle the normal fields (corresponding to actual columns), but can't guess how to handle the inputs you may include in partial fields. 748 749 For instance, imagine an administration module for a `User` class where the available fields are `id`, `nickname`, and `password`. The site administrator must be able to change the password of a user upon request, but the `edit` view must not display the value of the password field for security reasons. Instead, the form should display an empty password input that the site administrator can fill to change the value. The generator settings for such an `edit` view are then similar to Listing 14-28. 750 751 Listing 14-28 - Including a Partial Field in the `edit` View 752 753 edit: 754 display: [id, nickname, _newpassword] 755 fields: 756 newpassword: { name: Password, help: Enter a password to change it, leave the field blank to keep the current one } 757 758 The `templates/_newpassword.php` partial contains something like this: 759 760 [php] 761 <?php echo input_password_tag('newpassword', '') ?> 762 763 Notice that this partial uses a simple form helper, not an object form helper, since it is not desirable to retrieve the password value from the current `User` object to populate the form input--which could disclose the user password. 764 765 Now, in order to use the value from this control to update the object in the action, you need to extend the `updateUserFromRequest()` method in the action. To do that, create a method with the same name in the action class file with the custom behavior for the input of the partial field, as in Listing 14-29. 766 767 Listing 14-29 - Handling a Partial Field in the Action, in `modules/user/actions/actions.class.php` 768 769 [php] 770 class userActions extends sfActions 771 { 772 protected function updateUserFromRequest() 773 { 774 // Handle the input of the partial field 775 $password = $this->getRequestParameter('newpassword'); 776 777 if ($password) 778 { 779 $this->user->setPassword($password); 780 } 781 782 // Let symfony handle the other fields 783 parent::updateUserFromRequest(); 784 } 785 } 786 787 >**NOTE** 788 >In the real world, a `user/edit` view usually contains two password fields, the second having to match the first one to avoid typing mistakes. In practice, as you saw in Chapter 10, this is done via a validator. The administration-generated modules benefit from this mechanism just like regular modules. 789 790 ### Dealing with Foreign Keys 791 792 If your schema defines table relationships, the generated administration modules take advantage of it and offer even more automated controls, thus greatly simplifying the relationship management. 793 794 #### One-to-Many Relationships 795 796 The 1-n table relationships are taken care of by the administration generator. As is depicted by Figure 14-1 earlier, the `blog_comment` table is related to the `blog_article` table through the `article_id` field. If you initiate the module of the `Comment` class with the administration generator, the `comment/edit` action will automatically display the `article_id` as a drop-down list showing the IDs of the available records of the `blog_article` table (check again Figure 14-9 for an illustration). 797 798 In addition, if you define a `__toString()` method in the `Article` object, the text of the drop-down options use it instead of the primary keys. 799 800 If you need to display the list of comments related to an article in the `article` module (n-1 relationship), you will need to customize the module a little by way of a partial field. 801 802 #### Many-to-Many Relationships 803 804 Symfony also takes care of n-n table relationships, but since you can't define them in the schema, you need to add a few parameters to the `generator.yml` file. 805 806 The implementation of many-to-many relationships requires an intermediate table. For instance, if there is an n-n relation between a `blog_article` and a `blog_author` table (an article can be written by more than one author and, obviously, an author can write more than one article), your database will always end up with a table called `blog_article_author` or similar, as in Figure 14-20. 807 808 Figure 14-20 - Using a "through class" to implement many-to-many relationships 809 810  811 812 The model then has a class called `ArticleAuthor`, and this is the only thing that the administration generator needs--but you have to pass it as a `through_class` parameter of the field. 813 814 For instance, in a generated module based on the `Article` class, you can add a field to create new n-n associations with the `Author` class if you write `generator.yml` as in Listing 14-30. 815 816 Listing 14-30 - Handling Many-to-Many Relationships with a `through_class` Parameter 817 818 edit: 819 fields: 820 article_author: { type: admin_double_list, params: through_class=ArticleAuthor } 821 822 Such a field handles links between existing objects, so a regular drop-down list is not enough. You must use a special type of input for that. Symfony offers three widgets to help relate members of two lists (illustrated in Figure 14-21): 823 824 * An `admin_double_list` is a set of two expanded select controls, together with buttons to switch elements from the first list (available elements) to the second (selected elements). 825 * An `admin_select_list` is an expanded select control in which you can select many elements. 826 * An `admin_check_list` is a list of check box tags. 827 828 Figure 14-21 - Available controls for many-to-many relationships 829 830  831 832 ### Adding Interactions 833 834 Administration modules allow users to perform the usual CRUD operations, but you can also add your own interactions or restrict the possible interactions for a view. For instance, the interaction definition shown in Listing 14-31 gives access to all the default CRUD actions on the `article` module. 835 836 Listing 14-31 - Defining Interactions for Each View, in `backend/modules/article/config/generator.yml` 837 838 list: 839 title: List of Articles 840 object_actions: 841 _edit: ~ 842 _delete: ~ 843 actions: 844 _create: ~ 845 846 edit: 847 title: Body of article %%title%% 848 actions: 849 _list: ~ 850 _save: ~ 851 _save_and_add: ~ 852 _delete: ~ 853 854 In a `list` view, there are two action settings: the list of actions available for every object, and the list of actions available for the whole page. The list interactions defined in Listing 14-31 render like in Figure 14-22. Each line shows one button to edit the record and one to delete it. At the bottom of the list, a button allows the creation of a new record. 855 856 Figure 14-22 - Interactions in the `list` view 857 858  859 860 In an `edit` view, as there is only one record edited at a time, there is only one set of actions to define. The `edit` interactions defined in Listing 14-31 render like in Figure 14-23. Both the `save` and the `save_and_add` actions save the current edits in the records, the difference being that the `save` action displays the `edit` view on the current record after saving, while the `save_and_add` action displays an empty `edit` view to add another record. The `save_and_add` action is a shortcut that you will find very useful when adding many records in rapid succession. As for the position of the `delete` action, it is separated from the other buttons so that users don't click it by mistake. 861 862 The interaction names starting with an underscore (`_`) tell symfony to use the default icon and action corresponding to these interactions. The administration generator understands `_edit`, `_delete`, `_create`, `_list`, `_save`, `_save_and_add`, and `_create`. 863 864 Figure 14-23 - Interactions in the `edit` view 865 866  867 868 But you can also add a custom interaction, in which case you must specify a name starting with no underscore, as in Listing 14-32. 869 870 Listing 14-32 - Defining a Custom Interaction 871 872 list: 873 title: List of Articles 874 object_actions: 875 _edit: - 876 _delete: - 877 addcomment: { name: Add a comment, action: addComment, icon: backend/addcomment.png } 878 879 Each article in the list will now show the `addcomment.png` button, as shown in Figure 14-24. Clicking it triggers a call to the `addComment` action in the current module. The primary key of the current object is automatically added to the request parameters. 880 881 Figure 14-24 - Custom interaction in the `list` view 882 883  884 885 The `addComment` action can be implemented as in Listing 14-33. 886 887 Listing 14-33 - Implementing the Custom Interaction Action, in `actions/actions.class.php` 888 889 [php] 890 public function executeAddComment() 891 { 892 $comment = new Comment(); 893 $comment->setArticleId($this->getRequestParameter('id')); 894 $comment->save(); 895 896 $this->redirect('comment/edit?id='.$comment->getId()); 897 } 898 899 One last word about actions: If you want to suppress completely the actions for one category, use an empty list, as in Listing 14-34. 900 901 Listing 14-34 - Removing All Actions in the `list` View 902 903 list: 904 title: List of Articles 905 actions: {} 906 907 ### Form Validation 908 909 If you take a look at the generated `_edit_form.php` template in your project `cache/` directory, you will see that the form fields use a special naming convention. In a generated `edit` view, the input names result from the concatenation of the module name and the field name between angle brackets. 910 911 For instance, if the `edit` view for the `article` module has a `title` field, the template will look like Listing 14-35 and the field will be identified as `article[title]`. 912 913 Listing 14-35 - Syntax of the Generated Input Names 914 915 // generator.yml 916 generator: 917 class: sfPropelAdminGenerator 918 param: 919 model_class: Article 920 theme: default 921 edit: 922 display: [title] 923 924 // Resulting _edit_form.php template 925 <?php echo object_input_tag($article, 'getTitle', array('control_name' => 'article[title]')) ?> 926 927 // Resulting HTML 928 <input type="text" name="article[title]" id="article[title]" value="My Title" /> 929 930 This has plenty of advantages during the internal form-handling process. However, as explained in Chapter 10, it makes the form validation configuration a bit trickier, so you have to change square brackets, `[` `]`, to curly braces, `{` `}`, in the `fields` definition. Also, when using a field name as a parameter for a validator, you should use the name as it appears in the generated HTML code (that is, with the square brackets, but between quotes). Refer to Listing 14-36 for a detail of the special validator syntax for generated forms. 931 932 Listing 14-36 - Validator File Syntax for Administration-Generated Forms 933 934 ## Replace square brackets by curly brackets in the fields list 935 fields: 936 article{title}: 937 required: 938 msg: You must provide a title 939 ## For validator parameters, use the original field name between quotes 940 sfCompareValidator: 941 check: "user[newpassword]" 942 compare_error: The password confirmation does not match the password. 943 944 ### Restricting User Actions Using Credentials 945 946 For a given administration module, the available fields and interactions can vary according to the credentials of the logged user (refer to Chapter 6 for a description of symfony's security features). 947 948 The fields in the generator can take a `credentials` parameter into account so as to appear only to users who have the proper credential. This works for the `list` view and the `edit` view. Additionally, the generator can also hide interactions according to credentials. Listing 14-37 demonstrates these features. 949 950 Listing 14-37 - Using Credentials in `generator.yml` 951 952 ## The id column is displayed only for users with the admin credential 953 list: 954 title: List of Articles 955 layout: tabular 956 display: [id, =title, content, nb_comments] 957 fields: 958 id: { credentials: [admin] } 959 960 ## The addcomment interaction is restricted to the users with the admin credential 961 list: 962 title: List of Articles 963 object_actions: 964 _edit: - 965 _delete: - 966 addcomment: { credentials: [admin], name: Add a comment, action: addComment, icon: backend/addcomment.png } 967 968 Modifying the Presentation of Generated Modules 969 ----------------------------------------------- 970 971 You can modify the presentation of the generated modules so that it matches any existing graphical charter, not only by applying your own style sheet, but also by overriding the default templates. 972 973 ### Using a Custom Style Sheet 974 975 Since the generated HTML is structured content, you can do pretty much anything you like with the presentation. 976 977 You can define an alternative CSS to be used for an administration module instead of a default one by adding a `css` parameter to the generator configuration, as in Listing 14-38. 978 979 Listing 14-38 - Using a Custom Style Sheet Instead of the Default One 980 981 generator: 982 class: sfPropelAdminGenerator 983 param: 984 model_class: Comment 985 theme: default 986 css: mystylesheet 987 988 Alternatively, you can also use the mechanisms provided by the module `view.yml` to override the styles on a per-view basis. 989 990 ### Creating a Custom Header and Footer 991 992 The `list` and `edit` views systematically include a header and footer partial. There is no such partial by default in the `templates/` directory of an administration module, but you just need to add one with one of the following names to have it included automatically: 993 994 _list_header.php 995 _list_footer.php 996 _edit_header.php 997 _edit_footer.php 998 999 For instance, if you want to add a custom header to the `article/edit` view, create a file called `_edit_header.php` as in Listing 14-39. It will work with no further configuration. 1000 1001 Listing 14-39 - Example `edit` Header Partial, in `modules/articles/template/_edit_header.php` 1002 1003 [php] 1004 <?php if ($article->getNbComments() > 0): ?> 1005 <h2>This article has <?php echo $article->getNbComments() ?> comments.</h2> 1006 <?php endif; ?> 1007 1008 Notice that an edit partial always has access to the current object through a variable having the same name as the module, and that a `list` partial always has access to the current pager through the `$pager` variable. 1009 1010 >**SIDEBAR** 1011 >Calling the Administration Actions with Custom Parameters 1012 > 1013 >The administration module actions can receive custom parameters using the `query_string` argument in a `link_to()` helper. For example, to extend the previous `_edit_header` partial with a link to the comments for the article, write this: 1014 > 1015 > [php] 1016 > <?php if ($article->getNbComments() > 0): ?> 1017 > <h2>This article has <?php echo link_to($article->getNbComments().' comments', 'comment/list', array('query_string' => 'filter=filter&filters%5Barticle_id%5D='.$article->getId())) ?></h2> 1018 > <?php endif; ?> 1019 > 1020 >This query string parameter is an encoded version of the more legible 1021 > 1022 > 'filter=filter&filters[article_id]='.$article->getId() 1023 > 1024 >It filters the comments to display only the ones related to `$article`. Using the query_string argument, you can specify a sorting order and/or a filter to display a custom list view. This can also be useful for custom interactions. 1025 1026 ### Customizing the Theme 1027 1028 There are other partials inherited from the framework that can be overridden in the module `templates/` folder to match your custom requirements. 1029 1030 The generator templates are cut into small parts that can be overridden independently, and the actions can also be changed one by one. 1031 1032 However, if you want to override those for several modules in the same way, you should probably create a reusable theme. A theme is a complete set of templates and actions that can be used by an administration module if specified in the theme value at the beginning of `generator.yml`. With the default theme, symfony uses the files defined in `$sf_symfony_data_dir/generator/sfPropelAdmin/default/`. 1033 1034 The theme files must be located in a project tree structure, in a `data/generator/sfPropelAdmin/[theme_name]/template/` directory, and you can bootstrap a new theme by copying the files from the default theme (located in `$sf_symfony_data_dir/generator/sfPropelAdmin/default/template/` directory). This way, you are sure that all the files required for a theme will be present in your custom theme: 1035 1036 // Partials, in [theme_name]/template/templates/ 1037 _edit_actions.php 1038 _edit_footer.php 1039 _edit_form.php 1040 _edit_header.php 1041 _edit_messages.php 1042 _filters.php 1043 _list.php 1044 _list_actions.php 1045 _list_footer.php 1046 _list_header.php 1047 _list_messages.php 1048 _list_td_actions.php 1049 _list_td_stacked.php 1050 _list_td_tabular.php 1051 _list_th_stacked.php 1052 _list_th_tabular.php 1053 1054 // Actions, in [theme_name]/template/actions/actions.class.php 1055 processFilters() // Process the request filters 1056 addFiltersCriteria() // Adds a filter to the Criteria object 1057 processSort() 1058 addSortCriteria() 1059 1060 Be aware that the template files are actually templates of templates, that is, PHP files that will be parsed by a special utility to generate templates based on generator settings (this is called the compilation phase). The generated templates must still contain PHP code to be executed during actual browsing, so the templates of templates use an alternative syntax to keep PHP code unexecuted for the first pass. Listing 14-40 shows an extract of a default template of template. 1061 1062 Listing 14-40 - Syntax of Templates of Templates 1063 1064 [php] 1065 <?php foreach ($this->getPrimaryKey() as $pk): ?> 1066 [?php echo object_input_hidden_tag($<?php echo $this->getSingularName() ?>,'get<?php echo $pk->getPhpName() ?>') ?] 1067 <?php endforeach; ?> 1068 1069 In this listing, the PHP code introduced by `<?` is executed immediately (at compilation), the one introduced by `[?` is only executed at execution, but the templating engine finally transforms the `[?` tags into `<?` tags so that the resulting template looks like this: 1070 1071 [php] 1072 <?php echo object_input_hidden_tag($article, 'getId') ?> 1073 1074 Dealing with templates of templates is tricky, so the best advice if you want to create your own theme is to start from the `default` theme, modify it step by step, and test it extensively. 1075 1076 >**TIP** 1077 >You can also package a generator theme in a plug-in, which makes it even more reusable and easy to deploy across multiple applications. Refer to Chapter 17 for more information. 1078 1079 - 1080 1081 >**SIDEBAR** 1082 >Building Your Own Generator 1083 > 1084 >The scaffolding and administration generators both use a set of symfony internal components that automate the creation of generated actions and templates in the cache, the use of themes, and the parsing of templates of templates. 1085 > 1086 >This means that symfony provides all the tools to build your own generator, which can look like the existing ones or be completely different. The generation of a module is managed by the `generate()` method of the `sfGeneratorManager` class. For instance, to generate an administration, symfony calls the following internally: 1087 > 1088 > [php] 1089 > $generator_manager = new sfGeneratorManager(); 1090 > $data = $generator_manager->generate('sfPropelAdminGenerator', $parameters); 1091 > 1092 >If you want to build your own generator, you should look at the API documentation of the `sfGeneratorManager` and the `sfGenerator` classes, and take as examples the `sfAdminGenerator` and `sfCRUDGenerator` classes. 1093 1094 Summary 1095 ------- 1096 1097 To bootstrap your modules or automatically generate your back-end applications, the basis is a well-defined schema and object model. You can modify the PHP code of scaffoldings, but administration-generated modules are to be modified mostly through configuration. 1098 1099 The `generator.yml` file is the heart of the programming of generated back-ends. It allows for the complete customization of content, features, and the look and feel of the `list` and `edit` views. You can manage field labels, tooltips, filters, sort order, page size, input type, foreign relationships, custom interactions, and credentials directly in YAML, without a single line of PHP code. 1100 1101 If the administration generator doesn't natively support the feature you need, the partial fields and the ability to override actions provide complete extensibility. Plus, you can reuse your adaptations to the administration generator mechanisms thanks to the theme mechanisms.
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |