This is the second part of the translation (
first part ) of the article on how to make a simple project in a Symphony in 1 hour. In it we will adjust the validation of the forms, change the format of the URL, make the admin panel and close access to it.
Validation of forms
In the last series, we made a form for adding comments (and incidentally noticed that the author of the original article nakosyachil in paragraph attachment comments to the post). Now we need to follow the fundamental principle of security: do not trust the data entered by the user. Therefore, we organize the verification of what the user will send through the form.
When we ask the Symphony to generate classes, it generates not only them, but also form elements. And she adds to these elements a few checks that she deems necessary, based on the data model scheme. Since it is written in the diagram that the
title field is required (required) for the
blog_post table, the user will fail if he tries to submit the form without filling this field. In addition, the Symphony will understand that it is impossible to send a string longer than 255 characters in this field (again, it draws this knowledge from the data model schema).
Let's replace (overwrite, override) some of these checks in the
BlogCommentForm class. Open the file (find it yourself?) And add the following lines to the configure () method:
$ this-> validatorSchema ['email'] = new sfValidatorEmail (
array ('required' => false),
array ('invalid' => 'The email address is not valid'));
')
By overriding the method for the
email field, we override the default behavior.
Now we have everything to explain to the user that he is wrong when he enters an invalid email address. The form has become more foolproof! Here we must pay attention to the following: firstly, when the form contains some data, then when the form is submitted, this data is stored in a special way. Therefore, if a user submits a form with partially incorrect data, he will not have to fill out the form again. Secondly, errors that occurred while filling in certain fields are displayed next to these fields, and not somewhere in the general heap of errors over the form.
Now is the time to clarify how the form is processed and the data is stored. In this difficult task are used action games that we ruled in the last series. They are contained in this file:
/sf_sandbox/apps/frontend/modules/comment/actions/actions.class.php:
Here is what we need:
$ 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 creating an instance of a form class, the following happens:
- There is a check that the form was submitted by the POST method
- Indeed, the blog_comment array parameter was received. The getParameter () method will be able to determine that this parameter is an array of values, not a single value, and returns an associative array (for example, the value of the blog_comment [author] field will be written in this array under the author key)
- The resulting associative array will be hooked up to the form, and this process is called binding ( binding ), as a result of which the values from this array will fill the corresponding fields in the form class instance. After that, a form check is run, and field by field a check is made whether the data is correct in the form or not.
- Only if all the checks have passed, data is saved and the page is redirected to a show action.
Attention! In Symphony 1.2, something changed (the original article was written for Symphony 1.1). Firstly, the creation of the form and the check for the POST method are included in the executeCreate action:
public function executeCreate (sfWebRequest $ request)
{
$ this-> forward404Unless ($ request-> isMethod ('post'));
$ this-> form = new BlogCommentForm ();
$ this-> processForm ($ request, $ this-> form);
$ this-> setTemplate ('new');
}
Perhaps there will be some confusion about what action is needed. For example,
executeNew is an action that
displays a form when a new comment is added. And
executeCreate is a data processing action when sending a form for adding a new comment.
To make a field mandatory, it suffices to specify required: in the data schema file (schema.yml) for this field. For example,
author: {type: varchar (255), required: true}
We read about data validation in more detail in the
form validation chapter.
Change URL format
Have you understood the principle by which the URL for modules and action games is formed? All this is customizable: and in order to make the user more pleasant to look at these addresses, and to search engines too. I suggest using the post title when creating a URL.
The problem is that the name may contain special characters, such as spaces. If you simply convert them to escape sequence, then the URL will contain ugly% 20, so we need to expand the model with the new BlogPost class method so that it makes something URL-pretty from the post header. To do this, open the
BlogPost.php file from the
sf_sandbox / lib / model / folder and add the following to it:
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;
}
I think it will be easy for you to add a couple of substitutions to make a good transliteration out of the Russian post title.
And now you need to make
permalink action for the
post module. Add a new method to sf_sandbox / apps / frontend / modules / post / actions / actions.class.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 ();
}
Question! guys, I understand that this is just a hard programming! For each entry, select the remaining stopitsot records and look for: is there anything among them that we need? Is there no way to more elegantly pick up an ID for a given title? Or am I wasting my time in vain, and the Symphony caching subsystem will do everything right?
The list of posts can now trigger
permalink action instead of
show for each post. In the
sf_sandbox / apps / frontend / modules / post / templates / indexSuccess.php file, remove the column with the post ID, and replace the contents of the cell where the
Title header is displayed with this:
<td> <? php echo $ blog_post-> getTitle ()?> </ td>
on this:
<td> <? php echo link_to ($ blog_post-> getTitle (), '@post? title ='. $ blog_post-> getStrippedTitle ())?> </ td>
There is only one step left:
edit routing.yml , located in the
sf_sandbox / apps / frontend / config / folder and add rules at the very beginning:
list_of_posts:
url: / latest_posts
param: {module: post, action: index}
post:
url: / blog /: title
param: {module: post, action: permalink}
Now we look into the browser and see the new URLs in action. If an error suddenly jumped out, then the cache needs to be cleared - these routing rules have not started working yet. How this is done, we have already said:
$ php symfony cc
We read about addresses in the
smart urls chapter.
Clean the frontend (public part)
OK, our blog is getting better. But that's bad luck: every wise guy can now easily go in and fix any post. Not good, as you might guess. In general, now we need to remove all the functionality for editing posts and comments from the public part (and later - add it to the admin area).
In the
sf_sandbox / apps / frontend / modules / post / templates / showSuccess.php template, remove the link to edit the post. Find yourself what you need to remove there (who is completely in the tank, these lines are in the original article).
We do the same for the
sf_sandbox / apps / frontend / modules / post / templates / indexSuccess.php template , where we remove the link to create a post (tank crews are looking for this place in the original article).
Naturally, we will not get rid of links. Functionality must also be removed. Open
sf_sandbox / apps / frontend / modules / post / actions / actions.class.php and delete
actions executeEdit and
executeDelete .
Note! In Symphony 1.2, you must also delete the executeUpdate action. Who read everything carefully, he himself guessed it.
Everything, now from the public part can be added, but you can not edit posts. With the comments, I think you can handle it yourself.
Do admin panel
And if someone starts writing about racial inequality in our blog? Or leave comments in the spirit of "+1"? We need a tool that would allow us to clean the blog from all kinds of garbage. Well, in the mood, correct typos. We need admin panel.
An admin is a separate application and needs to be created. Run on the command line:
$ php symfony generate: app backend
$ php symfony propel: init-admin backend post BlogPost
$ php symfony propel: init-admin backend comment BlogComment
You may not have noticed, but we just used the
admin generator . In fact, with it you can do things that are much more complex and interesting than just creating add-edit-delete forms. Read about it at your leisure.
In the same way as we did for the frontend application, we will edit the main layout (layout) (
apps / backend / templates / layout.php ) and add global navigation:
<div id = "navigation"> <ul style = "list-style: none;"> <li> <? php echo link_to ('Posts', 'post / index')?> </ li> <li> < ? php echo link_to ('Comments', 'comment / index')?> </ li> </ ul> </ div> <div id = "content"> <? php echo $ sf_data-> getRaw ('sf_content' )?> </ div>
Now we can access the admin panel in the development environment by typing the following:
http: //localhost/sf_sandbox/web/backend_dev.php/post
This shows the great power of the admin generator, which can be used cleverly and skillfully by editing the config.
Replace the contents of the
sf_sandbox / apps / backend / modules / post / config / generator.yml file with the following:
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}
Look: among the fields of the blog_post table, the admin panel will look for the field (or getter method) of nb_comments. Since this is not a stored, but a generated value, we need to add a getter method to our model (
sf_sandbox / lib / model / BlogPost.php ):
public function getNbComments ()
{
return count ($ this-> getBlogComments ());
}
Now update the admin area and admire the changes:
Restrict access to admin panel
Still, every nerd can get into the admin area and erase all posts about Ksenia Sobchak. Let's not let him do it: password password admin. In the
apps / backend / config / folder, edit the
security.yml file, write the following to it:
all:
is_secure: on
Now you can not enter the admin modules without logging in. Wait, where can I get a username with a password and in general any forms of authorization-registration? There is a brilliant solution - plugins! We use the plugin that deals with these issues - sfGuardPlugin. We write the following on the command line:
$ php symfony plugin: install sfGuardPlugin
Here you may have a problem if PEAR is not installed. It's time to install it.
This command downloads the plugin from the Symphony plugin repository. At the end a message should be displayed that everything went well:
$ php symfony plugin: install sfGuardPlugin
>> plugin installing plugin "sfGuardPlugin"
>> sfPearFrontendPlugin Attempting to discover the 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"
Now you need to enable the plugin.
Edit the file
sf_sandbox / apps / backend / config / settings.yml , enable the system login as follows. Uncomment everything that is contained in the key
all: 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
secure_action: secure # The credentials required for an action
.settings:
enabled_modules: [default, sfGuardAuth, sfGuardGroup, sfGuardPermission, sfGuardUser]
Now we will add a new system user, write to the
sf_sandbox / apps / backend / lib / myUser.class.php file instead of all that there is the following:
class myUser extends sfGuardSecurityUser
{
}
Now we need to recreate the model, forms and filters, and update the database:
$ 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
As before, when you run the
propel: insert-sql task, Styphony will delete all the tables and recreate them. Since this will occur quite often during development, it makes sense to record the initial and test data in fixtures (for more details, see the
populating a database chapter).
Now you need to clear the cache again. After that, finally, create a new user:
$ symfony guard: create-user habr oh_no_123456
Note: for some reason, in the original text, the name of the application is also indicated in the parameters for this command. Perhaps it was needed earlier, but in 1.2 it is definitely not needed.
Now we will make the
post management module
post the default module when entering the admin area. To do this, open the
apps / backend / config / routing.yml file and look for the
homepage key there. Change
default to
post .
So, if we now try to get into the admin area, we’ll get the following image:
We read more in the
security chapter.
Conclusion
An hour has passed. I think that for you, as for me, for the first time, everything took much longer. Believe me, with a very small dexterity, everything will be very easy and easy - the Symphony has to do with it.
Now you can use both applications in your work environment:
frontend: http: //localhost/sf_sandbox/web/index.php/
backend: http: //localhost/sf_sandbox/web/backend.php/
Here you may have error messages. This is all due to the fact that we changed the model and did not update the cache. Clean the cache quickly:
$ php symfony cc
And now we look and admire the speed of the applications! No comments.
Unfortunately, I made a mistake with the choice of the type of topic. This is a translation of the article
My first symfony project by Fabien Potencier. I do not know how to fix it.