| 4 | | So, you want to try it on? Let's build together a fully-functional web app in one hour. You name it. A bookseller application? Ok, another idea. A weblog! That's a good one. Let's go. |
|---|
| 5 | | |
|---|
| 6 | | We'll assume that you are working with apache/PHP5 installed and launched on your localhost. You will also need the SQLite extension, which is bundled and compiled by default in PHP5. However, since PHP 5.1.0 you need to manually activate the extension in `php.ini` (see how [here](http://fr3.php.net/manual/en/ref.sqlite.php)). |
|---|
| | 4 | So, you want to try it on? Let's build together a fully-functional web app in |
|---|
| | 5 | one hour. You name it. A bookseller application? Ok, another idea. A blog! |
|---|
| | 6 | That's a good one. Let's go. |
|---|
| | 7 | |
|---|
| | 8 | We'll assume that you are working with Apache installed and launched on your |
|---|
| | 9 | localhost. You will also need PHP 5.1.3 or newer with the SQLite extension |
|---|
| | 10 | installed and enabled (you can check this in `php.ini` - see how |
|---|
| | 11 | [in the PHP documentation](http://fr3.php.net/manual/en/ref.sqlite.php). |
|---|
| 11 | | To go fast, we will use the symfony sandbox (you can also download the [final source code](http://www.symfony-project.com/downloads/my_first_project.tgz)). It is an empty symfony project where all the required libraries are already included, and where the basic configuration is already done. The great advantage of the sandbox over other types of installation is that you can start experimenting with symfony immediately. |
|---|
| 12 | | |
|---|
| 13 | | Get it here: [sf_sandbox.tgz](http://www.symfony-project.com/get/sf_sandbox.tgz), and unpack it in your root web directory. Refer to the included readme file for more information. The resulting file structure should look like: |
|---|
| | 16 | To go fast, we will use the symfony sandbox (you can also download the |
|---|
| | 17 | [final source code](http://www.symfony-project.com/downloads/my_first_project.tgz)). |
|---|
| | 18 | It is an empty symfony project where all the required libraries are already included, |
|---|
| | 19 | and where the basic configuration is already done. The great advantage of the sandbox |
|---|
| | 20 | over other types of installation is that you can start experimenting with symfony |
|---|
| | 21 | immediately. |
|---|
| | 22 | |
|---|
| | 23 | Get it here: [sf_sandbox.tgz](http://www.symfony-project.com/get/sf_sandbox.tgz), |
|---|
| | 24 | and unpack it in your root web directory. Refer to the included readme file for more |
|---|
| | 25 | information. The resulting file structure should look like: |
|---|
| 65 | | created_at: |
|---|
| 66 | | |
|---|
| 67 | | This configuration file uses the YAML syntax. It's a very simple language that allows XML-like tree structures described by indentation. Furthermore, it is faster to read and write than XML. The only thing is, the indentation has a meaning and tabulations are forbidden, so remember to use spaces for indentation. You will find more about YAML and the symfony configuration in the [configuration chapter](http://www.symfony-project.com/book/trunk/05-Configuring-Symfony). |
|---|
| 68 | | |
|---|
| 69 | | This schema describes the structure of two the tables needed for the weblog. `Post` and `Comment` are the names of the related classes to be generated. Save the file, open a command line, browse to the `sf_sandbox/` directory and type: |
|---|
| | 80 | created_at: ~ |
|---|
| | 81 | |
|---|
| | 82 | This configuration file uses the YAML syntax. It's a very simple language that |
|---|
| | 83 | allows XML-like tree structures described by indentation. Furthermore, it is |
|---|
| | 84 | faster to read and write than XML. The only thing is, the indentation has a |
|---|
| | 85 | meaning and tabulations are forbidden, so remember to use spaces for indentation. |
|---|
| | 86 | You will find more about YAML and the symfony configuration in the |
|---|
| | 87 | [configuration chapter](http://www.symfony-project.com/book/1.1/05-Configuring-Symfony). |
|---|
| | 88 | |
|---|
| | 89 | This schema describes the structure of two the tables needed for the blog. |
|---|
| | 90 | `Post` and `Comment` are the names of the related classes to be generated. |
|---|
| | 91 | Save the file, open a command line, browse to the `sf_sandbox/` directory and type: |
|---|
| 73 | | >**Note**: Make sure to be at the root of your project (`sf_sandbox/`) when you call the `symfony` command. |
|---|
| 74 | | |
|---|
| 75 | | A few classes are created in the `sf_sandbox/lib/model/` directory. These are the classes of the object-relational mapping, who allow us to have access to a relational database from within an object-oriented code without writing a single SQL query. Symfony uses the Propel library for this purpose. We will call theses objects the **model** (find more in the [model chapter](http://www.symfony-project.com/book/trunk/08-Inside-the-Model-Layer)). |
|---|
| | 95 | >**Note**: Make sure to be at the root of your project (`sf_sandbox/`) when you |
|---|
| | 96 | call the `symfony` command. |
|---|
| | 97 | |
|---|
| | 98 | A few classes are created in the `sf_sandbox/lib/model/` directory. These are the |
|---|
| | 99 | classes of the object-relational mapping, who allow us to have access to a relational |
|---|
| | 100 | database from within an object-oriented code without writing a single SQL query. |
|---|
| | 101 | By default, symfony uses the Propel library for this purpose. Theses classes are |
|---|
| | 102 | part of the **model** of our application (find more in the [model chapter](http://www.symfony-project.com/book/1.1/08-Inside-the-Model-Layer)). |
|---|
| 81 | | A `lib.model.schema.sql` file is created in `sf_sandbox/data/sql/`. This SQL query can be used to initialize a database with the same table structure. You could create a database in MySQL with the command line or a web interface (as described in the [model chapter](http://www.symfony-project.com/book/trunk/08-Inside-the-Model-Layer)). Luckily, the symfony sandbox is configured to work out of the box with a simple SQLite file, so no database initialization is required. By default, the `sf_sandbox` project will use a database called `sandbox.db` located in `sf_sandbox/data/`. To build the table structure based on the the SQL file, type: |
|---|
| | 108 | A `lib.model.schema.sql` file is created in `sf_sandbox/data/sql/`. |
|---|
| | 109 | The SQL statements found is this file can be used to initialize a database with |
|---|
| | 110 | the same table structure. You could create a database in MySQL with the command |
|---|
| | 111 | line or a web interface (as described in the |
|---|
| | 112 | [model chapter](http://www.symfony-project.com/book/1.1/08-Inside-the-Model-Layer)). |
|---|
| | 113 | Luckily, the symfony sandbox is configured to work out of the box with a simple |
|---|
| | 114 | SQLite file, so no database initialization is required. By default, the `sf_sandbox` |
|---|
| | 115 | project will use a database called `sandbox.db` located in `sf_sandbox/data/`. |
|---|
| | 116 | To build the table structure based on the the SQL file, type: |
|---|
| 85 | | >**Note**: Don't worry if there is a warning at that point, it is normal. The `propel:insert-sql` command removes existing tables before adding the ones of your `lib.model.schema.sql`, and there is no table to remove at that time. |
|---|
| 86 | | |
|---|
| 87 | | Create the application scaffolding |
|---|
| 88 | | ---------------------------------- |
|---|
| 89 | | |
|---|
| 90 | | The basic features of a weblog are to be able to Create, Retrieve, Update and Delete (CRUD) posts and comments. As you are new to symfony, you will not create symfony code from scratch, but rather let it create a scaffolding that you may use and modify as needed. Symfony can interpret the data model to generate the CRUD interface automatically: |
|---|
| | 120 | >**Note**: Don't worry if there is a warning at that point, it is normal. |
|---|
| | 121 | The `propel:insert-sql` command removes existing tables before adding the ones |
|---|
| | 122 | of your `lib.model.schema.sql`, and there is no table to remove at that time. |
|---|
| | 123 | |
|---|
| | 124 | Create the application |
|---|
| | 125 | ---------------------- |
|---|
| | 126 | |
|---|
| | 127 | The basic features of a blog are to be able to Create, Retrieve, Update and |
|---|
| | 128 | Delete (CRUD) posts and comments. As you are new to symfony, you will not create |
|---|
| | 129 | symfony code from scratch, but rather let it generate the code that you may use |
|---|
| | 130 | and modify as needed. Symfony can interpret the data model to generate the CRUD |
|---|
| | 131 | interface automatically: |
|---|
| 109 | | Find more about [scaffolding](http://www.symfony-project.com/book/trunk/14-Generators) and the explanation of symfony projects [structure](http://www.symfony-project.com/book/trunk/04-The-Basics-of-Page-Creation) (project, application, module). |
|---|
| 110 | | |
|---|
| 111 | | >**Note**: In the URLs above, the name of the main script - called *front controller* in symfony - was changed from `index.php` to `frontend_dev.php`. The two scripts access the same application (`frontend`), but in different environments. With `frontend_dev.php`, you access the application in the **development environment**, which provides handy development tools like the debug toolbar on the top right of the screen and the live configuration engine. That's why the processing of each page is slower than when using `index.php`, which is the front controller of the **production environment**, optimized for speed. If you want to keep on using the production environment, replace `frontend_dev.php/` by `index.php/` in the following URLs, but don't forget to clear the cache before watching the changes: |
|---|
| | 154 | Find more about [generators](http://www.symfony-project.com/book/1.1/14-Generators) |
|---|
| | 155 | and the explanation of symfony projects |
|---|
| | 156 | [structure](http://www.symfony-project.com/book/1.1/04-The-Basics-of-Page-Creation) |
|---|
| | 157 | (project, application, module). |
|---|
| | 158 | |
|---|
| | 159 | >**Note**: In the URLs above, the name of the main script - called |
|---|
| | 160 | *front controller* in symfony - was changed from `index.php` to `frontend_dev.php`. |
|---|
| | 161 | The two scripts access the same application (`frontend`), but in different environments. |
|---|
| | 162 | With `frontend_dev.php`, you access the application in the **development environment**, |
|---|
| | 163 | which provides handy development tools like the debug toolbar on the top right |
|---|
| | 164 | of the screen and the live configuration engine. That's why the processing of |
|---|
| | 165 | each page is slower than when using `index.php`, which is the front controller |
|---|
| | 166 | of the **production environment**, optimized for speed. If you want to keep on |
|---|
| | 167 | using the production environment, replace `frontend_dev.php/` by `index.php/` |
|---|
| | 168 | in the following URLs, but don't forget to clear the cache before watching the |
|---|
| | 169 | changes: |
|---|
| 197 | | That was fast, wasn't it? Now it is time to mix the `comment` module into the `post` one to get comments displayed below posts. |
|---|
| 198 | | |
|---|
| 199 | | First, you need to make the post comments available for the post display template. In symfony, this kind of logic is kept in **actions**. Edit the actions file `sf_sandbox/apps/frontend/modules/post/actions/actions.class.php` and change the `executeShow()` method by adding the 4 last lines: |
|---|
| | 267 | That was fast, wasn't it? Now it is time to mix the `comment` module into the |
|---|
| | 268 | `post` one to get comments displayed below posts. |
|---|
| | 269 | |
|---|
| | 270 | First, you need to make the post comments available for the post display template. |
|---|
| | 271 | In symfony, this kind of logic is kept in **actions**. Edit the actions file |
|---|
| | 272 | `sf_sandbox/apps/frontend/modules/post/actions/actions.class.php` and change |
|---|
| | 273 | the `executeShow()` method by adding the 4 last lines: |
|---|
| 213 | | The `Criteria` and `-Peer` objects are part of Propel's object-relational mapping. Basically, these four lines will handle a SQL query to the `Comment` table to get the comments related to the current `Post` (the one designated by the URL parameter `id`). The `$this->comments` line in the action will give access to a `$comments` variable in the corresponding template. Now, modify the post display template `sf_sandbox/apps/frontend/modules/post/templates/showSuccess.php` by adding at the end: |
|---|
| | 287 | The `Criteria` and `-Peer` objects are part of Propel's object-relational mapping. |
|---|
| | 288 | Basically, these four lines will handle a SQL query to the `Comment` table to get |
|---|
| | 289 | the comments related to the current `Post` (the one designated by the URL parameter `id`). |
|---|
| | 290 | The `$this->comments` line in the action will give access to a `$comments` variable |
|---|
| | 291 | in the corresponding template. Now, modify the post display template |
|---|
| | 292 | `sf_sandbox/apps/frontend/modules/post/templates/showSuccess.php` by adding at the end: |
|---|
| 230 | | This page uses new PHP functions (`format_date()` and `simple_format_text()`) provided by symfony, and called 'helpers' because they do some tasks for you that would normally require more time and code. Create a new comment for your first post, then check again the first post, either by clicking on its number in the list, or by typing directly: |
|---|
| | 309 | This page uses new PHP functions (`format_date()` and `simple_format_text()`) |
|---|
| | 310 | provided by symfony, and called 'helpers' because they do some tasks for you |
|---|
| | 311 | that would normally require more time and code. Create a new comment for your |
|---|
| | 312 | first post, then check again the first post, either by clicking on its number |
|---|
| | 313 | in the list, or by typing directly: |
|---|
| 243 | | When adding a comment, you can choose the `id` of the related post. That's not very user-friendly. Let's change this, and make sure that the user comes back to the post he was looking at after adding a comment. |
|---|
| 244 | | |
|---|
| 245 | | First, in the still fresh `modules/post/templates/showSuccess.php` template, add a line at the bottom: |
|---|
| | 327 | When adding a comment, you can choose the `id` of the related post. |
|---|
| | 328 | That's not very user-friendly. Let's change this, and make sure that the user |
|---|
| | 329 | comes back to the post he was looking at after adding a comment. |
|---|
| | 330 | |
|---|
| | 331 | First, in the still fresh `modules/post/templates/showSuccess.php` template, |
|---|
| | 332 | add a line at the bottom: |
|---|
| 272 | | The form in the `comment/create` page points to a `comment/update` action, which redirects to `comment/show` when submitted (this is the default behaviour in generated CRUDs). For the weblog, that means that after adding a comment to a post, the detail of the comment is displayed. It is better to display the post with the comments at that point. So open the `modules/comment/actions/actions.class.php` and look for the `executeUpdate()` method. Note that the `created_at` field is not defined by the action: symfony knows that a field named `created_at` has to be set to the system time when a record is created. The final redirect of the action has to be modified to point to the correct action. Change it to: |
|---|
| | 362 | The form in the `comment/create` page points to a `comment/update` action, which |
|---|
| | 363 | redirects to `comment/show` when submitted (this is the default behaviour in generated CRUDs). |
|---|
| | 364 | For the blog, that means that after adding a comment to a post, the detail of the |
|---|
| | 365 | comment is displayed. It is better to display the post with the comments at that point. |
|---|
| | 366 | So open the `modules/comment/actions/actions.class.php` and look for the `executeUpdate()` |
|---|
| | 367 | method. Note that the `created_at` field is not defined by the action: symfony knows |
|---|
| | 368 | that a field named `created_at` has to be set to the system time when a record is created. |
|---|
| | 369 | The final redirect of the action has to be modified to point to the correct action. |
|---|
| | 370 | Change it to: |
|---|
| 332 | | >**Note**: Beware that you don't copy 4 extra spaces at the beginning of each line, since the YAML parser would fail in that case. The first letter of this file must be the 'm' of 'methods'. |
|---|
| 333 | | |
|---|
| 334 | | The `fillin` activation enables the repopulation of the form with the value previously entered by the user in case of failed validation. The `names` declarations set the validation rules for each input of the form. |
|---|
| 335 | | |
|---|
| 336 | | By itself, the controller will redirect the user to a `updateError.php` template if an error is detected. It would be better to display the form again with an error message. To do that, add a `handleErrorUpdate` method to the action class of the `modules/comment/actions/actions.class.php` file: |
|---|
| | 434 | >**Note**: Beware that you don't copy 4 extra spaces at the beginning of each |
|---|
| | 435 | line, since the YAML parser would fail in that case. The first letter of this |
|---|
| | 436 | file must be the 'm' of 'methods'. |
|---|
| | 437 | |
|---|
| | 438 | The `fillin` activation enables the repopulation of the form with the value |
|---|
| | 439 | previously entered by the user in case of failed validation. The `names` declarations |
|---|
| | 440 | set the validation rules for each input of the form. |
|---|
| | 441 | |
|---|
| | 442 | By itself, the controller will redirect the user to a `updateError.php` template |
|---|
| | 443 | if an error is detected. It would be better to display the form again with an |
|---|
| | 444 | error message. To do that, add a `handleErrorUpdate` method to the action class |
|---|
| | 445 | of the `modules/comment/actions/actions.class.php` file: |
|---|
| 367 | | Did you notice the way the URLs are rendered? You can make them more user and search engine-friendly. Let's use the post title as an URL for posts. |
|---|
| 368 | | |
|---|
| 369 | | The problem is that post titles can contain special characters like spaces. If you just escape them, the URL will show some ugly `%20` kind of things, so you'd better extend the model to add a new method to the `Post` object to get a clean, stripped title. To do that, edit the file `Post.php` located in the `sf_sandbox/lib/model/` directory, and add the following method: |
|---|
| | 477 | Did you notice the way the URLs are rendered? You can make them more user and |
|---|
| | 478 | search engine-friendly. Let's use the post title as an URL for posts. |
|---|
| | 479 | |
|---|
| | 480 | The problem is that post titles can contain special characters like spaces. |
|---|
| | 481 | If you just escape them, the URL will show some ugly `%20` kind of things, |
|---|
| | 482 | so you'd better extend the model to add a new method to the `Post` object |
|---|
| | 483 | to get a clean, stripped title. To do that, edit the file `Post.php` located |
|---|
| | 484 | in the `sf_sandbox/lib/model/` directory, and add the following method: |
|---|
| 438 | | Well, if this is a weblog, then everybody has the right to post. This isn't exactly what you thought about, right? Ok, let's clean up our templates a bit. |
|---|
| 439 | | |
|---|
| 440 | | In the template `modules/post/templates/showSuccess.php`, get rid of the 'edit' link by removing the line: |
|---|
| | 557 | Well, if this is a blog, then everybody has the right to post. |
|---|
| | 558 | This isn't exactly what you thought about, right? Ok, let's clean up our |
|---|
| | 559 | templates a bit. |
|---|
| | 560 | |
|---|
| | 561 | In the template `modules/post/templates/showSuccess.php`, get rid of the |
|---|
| | 562 | 'edit' link by removing the line: |
|---|
| 468 | | This time, we use the [admin generator](http://www.symfony-project.com/book/trunk/14-Generators). It offers much more features and customization than the very basic CRUD generator. |
|---|
| 469 | | |
|---|
| 470 | | Just like you did for the `frontend` application, edit the layout (`apps/backend/templates/layout.php`) to add global navigation: |
|---|
| | 591 | This time, we use the [admin generator](http://www.symfony-project.com/book/1.1/14-Generators). |
|---|
| | 592 | It offers much more features and customization than the basic CRUD generator. |
|---|
| | 593 | |
|---|
| | 594 | Just like you did for the `frontend` application, edit the layout (`apps/backend/templates/layout.php`) |
|---|
| | 595 | to add global navigation: |
|---|
| 542 | | Repeat the operation for the `comment` module. Now you can't access these modules anymore unless you are logged. |
|---|
| 543 | | |
|---|
| 544 | | But the logging action doesn't exist! Ok, so you can easily add it. First, create the `security` module skeleton: |
|---|
| | 670 | Repeat the operation for the `comment` module. Now you can't access these modules |
|---|
| | 671 | anymore unless you are logged. |
|---|
| | 672 | |
|---|
| | 673 | But the logging action doesn't exist! Ok, so you can easily add it. |
|---|
| | 674 | First, create the `security` module skeleton: |
|---|
| 618 | | See, the application is fast and runs smoothly. Pretty darn cool, isn't it? Feel free to explore the code, add new modules, and change the design of pages. |
|---|
| 619 | | |
|---|
| 620 | | And don't forget to mention your working symfony applications in the symfony Wiki! |
|---|
| | 757 | See, the application is fast and runs smoothly. Pretty darn cool, isn't it? |
|---|
| | 758 | Feel free to explore the code, add new modules, and change the design of pages. |
|---|
| | 759 | |
|---|
| | 760 | And don't forget to mention your working symfony applications in the |
|---|
| | 761 | [symfony Wiki](http://trac.symfony-project.com/wiki/ApplicationsDevelopedWithSymfony)! |
|---|