⬆️ ⬇️

The first symfony project, part 2

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:









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.



Source: https://habr.com/ru/post/52389/



All Articles