Development

Documentation/ru_RU/my-first-project

You must first sign up to be able to contribute.

Version 1 (modified by Nikitin, 9 years ago)
Первая редакция

Мой первый проект на Symfony 1.1 ========================

Ну что ж, приступим? Давайте вместе создадим полнофунциональное веб-приложение за один час. Что будем делать? Интернет-магазин? Отлично, давайте что-нибудь другое… Блог? Вот это то, что нужно. Поехали…

Это руководство предполагает, что вы работаете с веб-сервером Apache, установленном на локальном компьютере. Также вам нужен PHP 5.1.3 или новее.

Установка Symfony и инициализация проекта


Чтобы быстро начать создание веб-приложения, используем Symfony SandBox?. Это пустой проект, в который уже включены все необходимые библиотеки и сделаны основные настройки. Самое большое преимущество Symfony SandBox? перед другими вариантами установки - это то, что вы можете немедленно начинать экспериментировать с Symfony.

Скачайте Symfony SandBox? здесь: [sf_sandbox_1_2.tgz](http://www.symfony-project.org/get/sf_sandbox_1_2.tgz) или здесь: [sf_sandbox_1_2.zip](http://www.symfony-project.org/get/sf_sandbox_1_2.zip), и распакуйте его в корневую директорию вашего веб-сервера. На Linux-системах рекомендуется сохранять права доступа, указанные в tar-файле (используя команду -p). См. файл README для получения более подробной информации. В результате этого файловая структура должна выглядеть таким образом:

www/

sf_sandbox/

apps/

frontend/

cache/ config/ data/ doc/ lib/ log/ plugins/ test/ web/

css/ images/ js/

По этой структуре видно, что **project** sf_sandbox содержит приложение (**application**) frontend. Протестируем sandbox набрав следующий URL:

http://localhost/sf_sandbox/web/index.php/

Перед вами появится страница с поздравлением.

![Congratulations](/images/tutorials/mfp_1_2/first-congrats.png)

Вы также можете установить symfony в любую директорию и настроить веб-сервер с помощью Virtual Host или Alias. В «The symfony book» содержится глава с подробной информацией об [установке Symfony](http://www.symfony-project.org/book/1_2/03-Running-Symfony) и [структуре директорий Symfony](http://www.symfony-project.org/book/1_2/02-Exploring-Symfony-s-Code).

Инициализация модели данных


Итак в блоге можно размещать сообщения и комментировать их. Создадим файл schema.yml в sf_sandbox/config/ и вставим следующий код модели данных:

[yml] propel:

blog_post:

id: ~ title: { type: varchar(255), required: true } excerpt: { type: longvarchar } body: { type: longvarchar } created_at: ~

blog_comment:

id: ~ blog_post_id: ~ author: { type: varchar(255) } email: { type: varchar(255) } body: { type: longvarchar } created_at: ~

Этот конфигурационный файл использует синтаксис YAML. Это очень простой язык, его структура очень напоминает древовидную структуру XML. В YAML вложенность обозначается отступами. YAML легче читается и на нём проще создавать конфигурационные файлы. Единственное ограничение - для оступов используются только пробелы, табуляция запрещена. Более подробную информацию о YAML вы сможете найти в главе, посвящённой [конфигурации Symfony](http://www.symfony-project.org/book/1_2/05-Configuring-Symfony).

Вышеприведённая схема описывает структуру двух таблиц, необходимых для блога. blog_post и blog_comment - имена соответствующих классов, которые будут сгенерированные. Сохраните файл, откройте командную строку, перейдите в директорию sf_sandbox/ и наберите:

$ php symfony propel:build-model

**Примечание**: Убедитесь, что когда вы вызываете команду symfony в командной строке, вы находитесь в корне вашего проекта (sf_sandbox/).

Эта команда создаёт несколько классов в директории sf_sandbox/lib/model/. Эти классы являются объектно-реляционной проекцией (ORM, object-relational mapping), которая позволяет нам получать доступ к реляционным данным из объектно-ориентированного кода без написания обычных SQL запросов. Для этих целей Symfony по умолчанию использует библиотеку Propel. Эти классы являются частью **модели** нашего приложения (подробно обсуждается в [главе посвящённой модели](http://www.symfony-project.org/book/1_2/08-Inside-the-Model-Layer))).

Теперь нам нужно конвертировать YAML-схему в SQL-запросы создания таблиц в базе данных. По умолчанию, the symfony sandbox сконфигурирован на работу с простейшей базой SQLite, для которой не нужно инициализации самой базы данных. Вам нужно убедиться, что расширение SQLite установлено (для этого проверьте файл php.ini - [rtfm](http://fr3.php.net/manual/en/ref.sqlite.php)).

По умолчанию, проект sf_sandbox использует файл базы данных sandbox.db, расположенный в директории sf_sandbox/data/.

Если же вы хотите использовать MySQL, то для этого необходимо внести изменения в конфигурацию configure:database:

$ php symfony configure:database mysql://root:pa$$word@localhost/symfony_project

Измените аргумент DSN в соответствии с вашими настройками (имя пользователя, пароль, хост, и имя базы данных). Далее вы можете создать базу данных либо через командную строку, либо с помощью веб-интерфейса (как описано в [главе о модели](http://www.symfony-project.org/book/1_2/08-Inside-the-Model-Layer)).

Затем откройте sf_sandbox/config/databases.yml и установите 'phptype' в 'mysql', и в поле 'database' укажите имя вашей базы данных MySQL.

**Внимание** Если вы используете SQLite под *nix системой, то вам нужно изменить права файла sandbox.db: $ chmod 777 data data/sandbox.db

Теперь наберите в командной строке:

$ php symfony propel:build-sql

Файл lib.model.schema.sql будет создан в директории sf_sandbox/data/sql/. SQL-запросы, находящиеся в этом файле, можно использовать для инициализации любой базы данных с такой же структурой таблиц.

Для создания таблиц на основе вышеупомянутого SQL-файла наберите в командной строке:

$ php symfony propel:insert-sql

**Примечание**: Не волнуйтесь, если в выдаче этого скрипта вы увидите множество предупреждений (warnings). Это нормально. Команда propel:insert-sql пытается удалить таблицы, перед их созданием из файла lib.model.schema.sql, а на данный момент в базе данных не было создано ни одной таблицы.

Так как мы хотим создавать и редактировать записи и комментарии, то нам потребуется создать несколько форм на основе написанной нами схемы БД:

$ php symfony propel:build-forms

Эта команда генерирует классы в директории sf_sandbox/lib/form/. Эти классы используются для управления объектами модели, как формами.

**Совет** Все вышеперечисленные команды можно выполнить за один запрос, с помощью команды propel:build-all.

Создание приложения


Основные возможности блога - это создание, получение, обновление и удаление (Create, Retrieve, Update, Delete - CRUD) сообщений и комментариев. Поскольку вы ещё новичок в symfony, вы пока не можете создавать код на symfony с нуля. Но у вас есть возможность сгенерировать код автоматически и в дальнейшем изменять его под ваши нужды. Symfony может автоматически генерировать CRUD-интерфейс на основе модели данных:

$ php symfony propel:generate-module --non-verbose-templates --with-show frontend post BlogPost? $ php symfony propel:generate-module --non-verbose-templates frontend comment BlogComment? $ php symfony cache:clear

**Совет** В команде propel:generate-module мы используем параметр --non-verbose-templates Если вы хотите узнать значение параметров этой команды, то вы можете вызвать справку следующим образом: $ php symfony help propel:generate-module

Итак, теперь у нас есть два модуля (post и comment), с помощью которых мы сможем манипулировать объектами от классов BlogPost и BlogComment. **Модуль** обычно предствляет из себя страницу или группу страниц со сходным функционалом. Новые модули располагаются в директории sf_sandbox/apps/frontend/modules/, и они доступны по ссылке:

http://localhost/sf_sandbox/web/frontend_dev.php/post http://localhost/sf_sandbox/web/frontend_dev.php/comment

Если вы попытаетесь создать комментарий, то вы получите ошибку, т.к. symfony ещё не знает как конвертировать объект post в строку. Откройте класс BlogPost (lib/model/BlogPost.php) и добавьте метод __toString():

[php] class BlogPost? extends BaseBlogPost? {

public function toString() {

return $this->getTitle();

}

}

И ещё, добавим css-свойства в файл sf_sandbox/web/css/main.css:

body, td {

font-family: Arial, Verdana, sans-serif; font-size: 12px;

}

td { margin: 4px; padding: 4px; }

Теперь вы можете создать несколько сообщений в блоге, чтобы он стал менее пустым.

![post CRUD](/images/tutorials/mfp_1_2/first-crud.png)

Мы рекомендуем более подробно узнать о [генераторах](http://www.symfony-project.org/book/1_2/14-Generators) и об [структуре проекта](http://www.symfony-project.org/book/1_2/04-The-Basics-of-Page-Creation) в symfony (проект, приложение, модуль).

**Примечание**: В ссылках приведённых выше, имя главного скрипта - называемого в symfony *front controller* - было изменено с index.php на frontend_dev.php. Эти два скрипта предоставляют доступ к одному и тому же приложению (frontend), но в различных средах. С помощью файла frontend_dev.php, вы получаете доступ к среде разработчика (**development environment**), в которой предусмотрены удобные средства отладки, вроде debug toolbar справа сверху странички и live configuration engine. В этом случае обработка каждой страницы занимает больше времени, чем при использовании index.php, фронт-контроллера среды **production environment**, оптимизированной по скорости. Если вы хотите переключиться в среду production, замените frontend_dev.php/ на index.php/ как показано ниже, но не забывайте очищать кэш, чтобы увидеть изменения: $ php symfony cache:clear http://localhost/sf_sandbox/web/index.php/

Мы рекомендуем более подробно узнать о различных средах ([environments](http://www.symfony-project.org/book/1_2/05-Configuring-Symfony#Environments)).

Изменение шаблона отображения


Для переключения между двумя модулями в блоге нужна глобальная навигация.

Откройте глобальный шаблон sf_sandbox/apps/frontend/templates/layout.php и измените содержимое тега <body> на следующее:

[php] <div id="container" style="width:700px;margin:0 auto;border:1px solid grey;padding:10px">

<div id="navigation" style="display:inline;float:right">

<ul>

<li><?php echo link_to('List of posts', 'post/index') ?></li> <li><?php echo link_to('List of comments', 'comment/index') ?></li>

</ul>

</div> <div id="title">

<h1><?php echo link_to('My first symfony project', '@homepage') ?></h1>

</div>

<div id="content" style="clear:right">

<?php echo $sf_data->getRaw('sf_content') ?>

</div>

</div>

Не обращайте внимания на плохой дизайн и использование встроенных свойств CSS. Время дорого, а в нашем распоряжении всего лишь час!

![post CRUD in layout](/images/tutorials/mfp_1_2/first-crud-layout.png)

Пока мы занимаемся шаблоном, заодно можем и поменять тэг title нашего блога. Откройте конфигурационный файл (sf_sandbox/apps/frontend/config/view.yml), найдите строку, определяющую тэг title и замените её на что-нибудь более подходящее. Обратите внимание, что некоторые лини закомментированы, вы можете их раскомментировать, если они вам необходимы

default:

http_metas:

content-type: text/html

metas:

title: The best blog ever #description: symfony project #keywords: symfony, project #language: en robots: index, follow

Теперь нужно изменить главную страницу сайта. Сейчас для неё используется шаблон по умолчанию из модуля default, который находится внутри фрэймворка, а не внутри директории приложения. Чтобы заменить главную страницу необходимо создать свой собственный основной (main) модуль:

$ php symfony generate:module frontend main

По умолчанию index action показывает экран поздравления с установкой. Для его удаления откройте файл sf_sandbox/apps/frontend/modules/main/actions/actions.class.php и удалите содержимое метода executeIndex(), как показано ниже:

[php] /**

  • Executes index action *
  • @param sfRequest $request A request object */

public function executeIndex($request) { }

Отредактируйте файл sf_sandbox/apps/frontend/modules/main/templates/indexSuccess.php, чтобы отобразить привествующее сообщение:

[php] <h1>Welcome to my new blog</h1> <p>You are the <?php echo rand(1000,5000) ?>th visitor today.</p>

Теперь мы должны сообщить symfony какое действие (action) запускать в случае запроса главной страницы сайта. Чтобы сделать это, откройте sf_sandbox/apps/frontend/config/routing.yml и отредактируйте правило главной страницы (homepage), как показано ниже:

[yml] homepage:

url: / param: { module: main, action: index }

Проверьте результат, снова открыв главную страницу:

http://localhost/sf_sandbox/web/frontend_dev.php/

![New home page](/images/tutorials/mfp_1_2/first-welcome.png)

Перед продолжением разработки приложения убедитесь, что вы создали тестовое сообщение в блоге и тестовый комментарий на это сообщение.

Более подробно о видах и шаблонах можно посмотреть в [соответствующей главе](http://www.symfony-project.org/book/1_2/07-Inside-the-View-Layer).

Pass data from the action to the template


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.

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 four last lines:

[php] public function executeShow($request) {

$this->blog_post = BlogPostPeer::retrieveByPk($request->getParameter('id')); $this->forward404Unless($this->blog_post);

$c = new Criteria(); $c->add(BlogCommentPeer::BLOG_POST_ID, $request->getParameter('id')); $c->addAscendingOrderByColumn(BlogCommentPeer::CREATED_AT); $this->comments = BlogCommentPeer::doSelect($c);

}

The Criteria and -Peer objects are part of Propel's object-relational mapping. Basically, these four lines will handle a SQL query to the blog_comment table to get the comments related to the current blog_post. Now, modify the post display template sf_sandbox/apps/frontend/modules/post/templates/showSuccess.php by adding at the end:

[php] // ... <?php use_helper('Text', 'Date') ?>

<hr /> <?php if ($comments) : ?>

<p><?php echo count($comments) ?> comments to this post.</p> <?php foreach ($comments as $comment): ?>

<p><em>posted by <?php echo $comment->getAuthor() ?> on <?php echo format_date($comment->getCreatedAt()) ?></em></p> <div class="comment" style="margin-bottom:10px;">

<?php echo simple_format_text($comment->getBody()) ?>

</div>

<?php endforeach; ?>

<?php endif; ?>

This page uses new PHP functions that are 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:

http://localhost/sf_sandbox/web/frontend_dev.php/post/show?id=1

![Comment under post](/images/tutorials/mfp_1_2/first-comments-under-post.png)

This is getting good.

Find more about the [naming conventions](http://www.symfony-project.org/book/1_2/07-Inside-the-View-Layer) linking an action to a template.

Add a record relative to another table


Currently you can't add comments to posts directly; if you edit a post, you have to go to the comments editing section, create a new one, then select the post you want to comment on using the drop-down menu. The screen looks like this:

![Related comment](/images/tutorials/mfp_1_2/related-comment-1.png)

It would be better if there was a link on each post editing page to go straight to the comment editing facility, so let's arrange that first. In the sf_sandbox/apps/frontend/modules/post/templates/showSuccess.php template, add this line at the bottom:

[php] <?php echo link_to('Add a comment', 'comment/edit?post_id='.$blog_post->getId()) ?>

The link_to() helper creates a hyperlink pointing to the edit action of the comment module, so you can add a comment directly from the post details page. At the moment, however, the comments edit page still offers a form element to select which post to relate a comment to. This would be best replaced by a hidden field (containing the post primary key) if the comments edit page URL is called specifying that key.

In symfony, forms are managed by classes. So, let's edit the BlogCommentForm class to make our changes. The file is located under the sf_sandbox/lib/form/ directory:

[php] class BlogCommentForm? extends BaseBlogCommentForm? {

/**

  • Configure method, called when the form is instantiated */

public function configure() {

$this->widgetSchemablog_post_id? = new sfWidgetFormInputHidden();

}

}

**Note**: For more details on the form framework, you are advised to read the

[Forms Book](http://www.symfony-project.org/book/forms/1_2/en/).

After you have made this change, you will now be able to add a comment directly to a post without having to explicitly specify the post to attach it to:

![Related comment](/images/tutorials/mfp_1_2/related-comment-2.png)

Next, after adding a comment, we want the user to come back to the post it relates to, rather that remaining on the comment editing page. To accomplish this, we need to edit executeEdit method. Find the following code in sf_sandbox/apps/frontend/modules/comment/actions/actions.class.php:

[php] if ($request->isMethod('post')) {

$this->form->bind($request->getParameter('blog_comment')); if ($this->form->isValid()) {

$blog_comment = $this->form->save();

$this->redirect('comment/edit?id='.$blog_comment->getId());

}

}

And change the redirect line so it reads thus:

[php] $this->redirect('post/show?id='.$blog_comment->getBlogPostId());

This will ensure that when a comment is saved, the user is returned to the post that the comment is related to. There are two things here that are worthy of note: firstly, the save is achieved simply by calling the save method on the form object (this is because the form is associated with the Propel model and therefore knows how to serialize the object back to the database). Secondly, we redirect immediately after the save, so that if the page is subsequently refreshed, the user is not asked if they wish to repeat the POST action again.

Okay, so that wraps up this part of the tutorial. You wanted a blog? You have a blog. Incidentally, since we've covered symfony actions a lot here, you may wish to find out more about them from [the manual](http://www.symfony-project.org/book/1_2/06-Inside-the-Controller-Layer).

Form Validation


Visitors can enter comments, but what if they submit the form without any data in it, or data that is obviously wrong? You would end up with a database containing invalid rows. To avoid that, we need to set up some validation rules to specify what data is allowed.

When symfony has created the form classes for us, if has generated the form elements to render on the screen, but it has also added some default validation rules by introspecting the schema. As the title is required in the blog_post table, you won't be able to submit a form without a title. You also won't be able to submit a post with a title longer than 255 character.

Let's override some of these rules now in the BlogCommentForm class. So, open the file, and add in the following PHP code at the end of the configure() method:

[php] $this->validatorSchemaemail? = new sfValidatorEmail(

array('required' => false), array('invalid' => 'The email address is not valid'));

By redefining the validator for the email column, we have overridden the default behavior.

Once this new rule is in place, try saving a comment with a bad email address - you now have a robust form! You will notice a number of things: first of all, where the form contains data, that data will automatically be preserved during the form submission. This saves the user having to type it back in (and normally is something in that the programmer has to arrange manually). Also, errors (in this case) are placed next to the fields that failed their associated validation tests.

Now would be a good time to explain a little about how the form save process works. It uses the following action code, which you edited earlier in /sf_sandbox/apps/frontend/modules/comment/actions/actions.class.php:

[php] $this->form = new BlogCommentForm?(BlogCommentPeer::retrieveByPk($request->getParameter('id')));

if ($request->isMethod('post')) {

$this->form->bind($request->getParameter('blog_comment')); if ($this->form->isValid()) {

$blog_comment = $this->form->save();

$this->redirect('post/show?id='.$blog_comment->getBlogPostId());

}

}

After the form object is instantiated, the following happens:

  • The code checks that the HTTP method is a POST
  • The parameter array blog_comment is retrieved. The getParameter() method detects

that this name is an array of values in the form, not a single value, and returns them as an associative array (e.g. form element blog_comment[author] is returned in an array having a key of author)

  • This associative array is then fed into the form using a process called **binding**,

in which the values are used to fill form elements in the form object. After this, the values are determined to have either passed or failed the validation checks

  • Only if the form is valid does the save go ahead, after which the page redirects

immediately to the show action.

![Form validation](/images/tutorials/mfp_1_2/first-form-validation.png)

Find more about [form validation](http://www.symfony-project.org/book/forms/1_2/en/02-Form-Validation).

Change the URL format


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 a URL for posts.

The problem is that post titles can contain special characters like spaces. If you just escape them, the URL will contain some ugly %20 strings, so the model needs to be extended with a new method in the BlogPost object, to get a clean, stripped title. To do that, edit the file BlogPost.php located in the sf_sandbox/lib/model/ directory, and add the following method:

[php] public function getStrippedTitle() {

$result = strtolower($this->getTitle());

// strip all non word chars $result = preg_replace('/\W/', ' ', $result);

// replace all white space sections with a dash $result = preg_replace('/\ +/', '-', $result);

// trim dashes $result = preg_replace('/\-$/', , $result); $result = preg_replace('/\-/', , $result);

return $result;

}

Now you can create a permalink action for the post module. Add the following method to the sf_sandbox/apps/frontend/modules/post/actions/actions.class.php:

[php] public function executePermalink($request) {

$posts = BlogPostPeer::doSelect(new Criteria()); $title = $request->getParameter('title'); foreach ($posts as $post) {

if ($post->getStrippedTitle() == $title) {

$request->setParameter('id', $post->getId());

return $this->forward('post', 'show');

}

}

$this->forward404();

}

The post list can call this permalink action instead of the show one for each post. In sf_sandbox/apps/frontend/modules/post/templates/indexSuccess.php, delete the id table header and cell, and change the Title cell from this:

[php] <td><?php echo $blog_post->getTitle() ?></td>

to this, which uses a named rule we will create in a second:

[php] <td><?php echo link_to($blog_post->getTitle(), '@post?title='.$blog_post->getStrippedTitle()) ?></td>

Just one more step: edit the routing.yml located in the sf_sandbox/apps/frontend/config/ directory and add these rules at the top:

list_of_posts:

url: /latest_posts param: { module: post, action: index }

post:

url: /blog/:title param: { module: post, action: permalink }

Now navigate again in your application to see your new URLs in action. If you get an error, it may be because the routing cache needs to be cleared. To do that, type the following at the command line while in your sf_sandbox folder:

$ php symfony cc

![Routed URLs](/images/tutorials/mfp_1_2/first-routing.png)

Find more about [smart URLs](http://www.symfony-project.org/book/1_2/09-Links-and-the-Routing-System).

Cleaning up the frontend


Well, if this is meant to be a blog, then it is perhaps a little strange that everybody is allowed to post! This isn't generally how blogs are meant to work, so let's clean up our templates a bit.

In the template sf_sandbox/apps/frontend/modules/post/templates/showSuccess.php, get rid of the 'edit' link by removing the line:

<a href="<?php echo url_for('post/edit?id='.$blog_post->getId()) ?>">Edit</a> &nbsp;

Do the same for the sf_sandbox/apps/frontend/modules/post/templates/indexSuccess.php template and remove:

<a href="<?php echo url_for('post/edit') ?>">Create</a>

You should also remove the following methods from sf_sandbox/apps/frontend/modules/post/actions/actions.class.php:

  • executeEdit
  • executeDelete

This means that readers cannot post anymore, which is what is required.

Generation of the backend


To allow you to write posts, let's create a backend application by typing in the command line (still from the sf_sandbox project directory):

$ php symfony generate:app backend $ php symfony propel:init-admin backend post BlogPost? $ php symfony propel:init-admin backend comment BlogComment?

This time, we use the [admin generator](http://www.symfony-project.org/book/1_2/14-Generators). It offers much more features and customization than the basic CRUD generator.

Just like you did for the frontend application, edit the layout (apps/backend/templates/layout.php) to add global navigation:

<div id="navigation">

<ul style="list-style:none;">

<li><?php echo link_to('Manage posts', 'post/index') ?></li> <li><?php echo link_to('Manage comments', 'comment/index') ?></li>

</ul>

</div> <div id="content">

<?php echo $sf_data->getRaw('sf_content') ?>

</div>

You can access your new back-office application in the development environment by calling:

http://localhost/sf_sandbox/web/backend_dev.php/post

![basic generated admin](/images/tutorials/mfp_1_2/first-basic-admin.png)

The great advantage of the generated admin is that you can easily customize it by editing a configuration file.

Change the sf_sandbox/apps/backend/modules/post/config/generator.yml to:

generator:

class: sfPropelAdminGenerator param:

model_class: BlogPost? theme: default fields:

title: { name: Title } excerpt: { name: Excerpt } body: { name: Body } nb_comments: { name: Comments } created_at: { name: Creation date }

list:

title: Post list layout: tabular display: [=title, excerpt, nb_comments, created_at] object_actions:

_edit: ~ _delete: ~

max_per_page: 5 filters: [title, created_at]

edit:

title: Post detail fields:

title: { type: input_tag, params: size=53 } excerpt: { type: textarea_tag, params: size=50x2 } body: { type: textarea_tag, params: size=50x10 } created_at: { type: input_date_tag, params: rich=on }

Note that among the existing columns of the blog_post table, the admin will look for a column (or getter method) for nb_comments. Since this is a generated value and not a column, we can add a getter to our model (sf_sandbox/lib/model/BlogPost.php):

[php] public function getNbComments() {

return count($this->getBlogComments());

}

Now refresh the Post administration screen to see the changes:

![customized generated admin](/images/tutorials/mfp_1_2/first-custom-admin.png)

Restrict access to the backend


Currently the backend application can be accessed by everybody. We therefore need to add some access restrictions. In apps/backend/config/, edit the file called security.yml and reset the content to:

all:

is_secure: on

Now you can no longer access these modules, unless you are logged in. However, currently the login action doesn't exist! Of course, we can easily add it. To do so, let's install a suitable plugin from the symfony website - sfGuardPlugin. Type the following at the command line:

$ php symfony plugin:install sfGuardPlugin

This will download the plugin from the symfony plugin repository. At this point the command line should give you an indication that the installation was successful:

$ php symfony plugin:install sfGuardPlugin >> plugin installing plugin "sfGuardPlugin" >> sfPearFrontendPlugin Attempting to discover channel "pear.symfony-project.com"... >> sfPearFrontendPlugin downloading channel.xml ... >> sfPearFrontendPlugin Starting to download channel.xml (663 bytes) >> sfPearFrontendPlugin . >> sfPearFrontendPlugin ...done: 663 bytes >> sfPearFrontendPlugin Auto-discovered channel "pear.symfony-project.com", alias >> sfPearFrontendPlugin "symfony", adding to registry >> sfPearFrontendPlugin Attempting to discover channel >> sfPearFrontendPlugin "plugins.symfony-project.org"... >> sfPearFrontendPlugin downloading channel.xml ... >> sfPearFrontendPlugin Starting to download channel.xml (639 bytes) >> sfPearFrontendPlugin ...done: 639 bytes >> sfPearFrontendPlugin Auto-discovered channel "plugins.symfony-project.org", alias >> sfPearFrontendPlugin "symfony-plugins", adding to registry >> sfPearFrontendPlugin downloading sfGuardPlugin-2.2.0.tgz ... >> sfPearFrontendPlugin Starting to download sfGuardPlugin-2.2.0.tgz (18,589 bytes) >> sfPearFrontendPlugin ...done: 18,589 bytes >> sfSymfonyPluginManager Installation successful for plugin "sfGuardPlugin"

Next, the plugin needs to be enabled. Edit sf_sandbox/apps/backend/config/settings.yml to enable the login system, as follows. Uncomment the all: key and add the following:

# (Some stuff here) all:

.actions:

login_module: sfGuardAuth # To be called when a non-authenticated user login_action: signin # Tries to access a secure page

secure_module: sfGuardAuth # To be called when a user doesn't have secure_action: secure # The credentials required for an action

.settings

enabled_modules: [default, sfGuardAuth, sfGuardGroup, sfGuardPermission, sfGuardUser]

Now, enable the new user system by editing sf_sandbox/apps/backend/lib/myUser.class.php, so that it reads:

[php] class myUser extends sfGuardSecurityUser { }

Now, the model, forms and filters need to be built, the new SQL needs to be generated, and the database tables need to be updated:

$ php symfony propel:build-model $ php symfony propel:build-forms $ php symfony propel:build-filters $ php symfony propel:build-sql $ php symfony propel:insert-sql

**Note** When launching the propel:insert-sql task, symfony will drop all tables to re-create them. As during the development this happens a lot, symfony can store initial data in fixtures (see [populating a database](http://www.symfony-project.org/book/1_2/16-Application-Management-Tools#Populating%20a%20Database) for more information).

Now, clear your cache again. Finally, create a user by running:

$ symfony guard:create-user frontend jon SoMePasSwOrD

Let's now make the post administration module the default one in our backend system. To do this, open up the apps/backend/config/routing.yml file and locate the homepage key. Change the module from default to post.

At that point, if you try to access the posts management (http://localhost/sf_sandbox/web/backend_dev.php/post), you will have to enter a valid username and password:

![login form](/images/tutorials/mfp_1_2/logon.png)

Find more about [security](http://www.symfony-project.org/book/1_2/06-Inside-the-Controller-Layer#Action%20Security).

Conclusion


Ok, the hour is out. You made it. Now you can use both applications in the production environment and play with them:

frontend: http://localhost/sf_sandbox/web/index.php/ backend: http://localhost/sf_sandbox/web/backend.php/

At this point, if you meet an error, it might be because you changed the model after some actions were put in cache (cache isn't activated in the development environment). To clear the cache, simply type:

$ php symfony cc

See, the application is fast and runs smoothly. Pretty cool, isn't it? Feel free to explore the code, add new modules, and change the design of pages.

And don't forget to mention your working symfony applications in the [symfony Wiki](http://trac.symfony-project.org/wiki/ApplicationsDevelopedWithSymfony)!