📜 ⬆️ ⬇️

HTML forms. The look of the backend developer

When preparing the material on the Symfony Form, I decided to pay some attention to the theoretical part on working with forms on the client’s side - what they are, how browsers behave when they are sent, in what format they travel, in what form they go to the server.

The introductory part was somewhat stretched out and eventually turned into a separate short article, which, in my opinion, may be of interest to other backend developers (not just PHP or Symfony).

The text will use two similar terms: the form field and the form element. Both are in most cases interchangeable and denote some part of the form. But for convenience and order, the fields will often be called parts that contain the values ​​directly and are likely to be sent to the server, and the elements are the decorative parts of the form.

image

')
Previously, working with forms was in some sense simpler: in the form in which the form came to the user, in the same way it was sent back, but with filled fields. Backend developers knew exactly what data set they should expect, everything was easy and predictable. With the widespread use of JS, everything has changed: today, no one is surprised by the behavior of an application, in which a form comes with one set of fields, but is sent with a completely different one, when bulky forms have small dynamic subforms that can be sent to the server for processing when selected files are loaded in the background while the user fills out the rest of the form. Thus, an increasing amount of logic is transferred to the client, and this is excellent, first of all, of course, for the user. For the developer, the process of working with forms is somewhat complicated. But no matter how hard it was for him, it is impossible to ignore the development of technologies today, so it is necessary to be able to use them effectively.

Backend vs Front End


We are constantly striving to automate everything we have to work with, especially routine tasks, while trying to bring some order into these processes. Therefore, many developers are somewhat frightened by the dynamism on the client side, which is often perceived as uncontrollable chaos. You might think that front-end developers have a free hand, and they can create like artists and sculptors, their actions are not limited by anything, and then we can sort out flights of their fantasies on the server. Often it happens, but this is an erroneous, dead-end path. This can be compared with schemaless data warehouses: no one restricts you with a rigid data storage structure, each of the developers in one project can come up with their own storage format, then change it depending on the mood, which in the end will most likely lead to sad the consequences. If you expect flexibility, easy extensibility and dynamic development from your product, you will need to follow certain rules that will help maintain order in your application. The same thing you have the right to demand from front-end developers - create, but follow the rules .

So, as a backend developer , you need to understand and accept:

  1. Stop being afraid of the dynamics on the server side.
    The world and technology do not stand still, and if you do not have time to adjust to them, you will lose. Today, dynamism is an integral part of our life, and you have to come to terms with it, whether you like it or not. Now, when working with forms, it is necessary to focus not only and not so much on the rigid structure of fields predefined by the developer, but on the data received from users.

  2. Do not blame the front-end developers.
    Set in the company, team, project rules, and then the front-end developers will have no less responsibility when working with forms than you do. And even better, if, apart from the rules, there will be trust between all developers, and all decisions will be made in cooperation.


Submit the form


Great, we somehow generated HTML for some form and sent it to the user. Having filled in the fields and, possibly, changing the structure of the form, the user needs to send it to the server for processing. How can he do this? There are different ways, most of which will require the execution of scripts. Consider the main ones:

  1. Clicking on the submit button.
  2. Call the submit () method on the form object.
  3. Serialization of form fields.
  4. Creating and forming an arbitrary query.

The methods in the list are arranged in ascending order of the effort made by the front-end developer to send the form to the server. You can also conditionally divide them into two groups: the first two ways make up the group in which the browser is creating and sending the request, the second group includes the last two ways in which these actions are performed by JS.

Let the browser work


To begin, consider the first group, both methods of which differ little from each other. In this case, the user's browser takes all the work and responsibility, and it acts quite straightforwardly:

  1. Specifies the form whose fields you want to send.
  2. Defines the set of fields to be sent. The following form elements are included in this set: button, input, keygen, object, select, textarea.
  3. Generates key = value pairs, where the key is the name of the field (the name attribute) and the value is the contents of this field.
  4. Creates and generates a request that will be executed by the method specified in the tag attribute of the form method and whose body will be encoded depending on the value of the enctype attribute.
  5. Sends a request to the server at the address specified in the tag attribute of the action form.

The definition of the form sent depends on the methods we are considering. If this is a button click, the form is determined by the form attribute of the button being clicked, and if it is missing, by the button’s parent tag form. If it is a call to the submit () method, the form sent is the one associated with the object for which the method is being called.

The formmethod, formenctype and formaction attributes of the pressed button override the values ​​of the method, enctype and action attributes of the parent form, respectively.

This is a very simple process, but it is important to understand it in order to unambiguously understand what kind of data and in what form we get on the server.

Of the entire list, the third point is the most interesting - the generation of pairs key = value. Each form element must have a name attribute that will be used by the browser as a key. Consider an example. There is the following form:

<form method="POST" enctype="application/x-www-form-urlencoded"> <input type="text" name="nickname" value="ghost"> <input type="password" name="password" value="123456"> </form> 

In this case, the browser will form two pairs of key = value: nickname = ghost and password = 123456. When sending, they will be glued together using an ampersand (&), and as a result a request will be sent to the server, the body of which contains the string:

 nickname=ghost&password=123456 

After receiving such a request, PHP will begin its analysis, an approximate algorithm of which can be found in the description of the parse_str function. As a result of the analysis, two fields and their values ​​will be determined, they will be placed in the $ _POST superglobal array, which takes the following form:

 $_POST = [ 'nickname' => 'ghost', 'password' => '123456', ] 

What should be addressed in this process:

  1. All information coming to you will be presented in the form of text values.
    No matter how complicated the client part of working with a form is, in any case, you will receive all the information in text (string) form. If this is a work with map services, the result will be the coordinates. If it is a WYSIWYG editor, then depending on the type of editor, the result will be either HTML code, text in the markdown markup, or text in any other format you know in advance. This is an important point, the realization of which will help to overcome the fear of bulky complex forms. Of course, this does not apply to downloadable files.

  2. Not all fields contained in the form will be sent in the request.
    For example, there are field types that will not be included in the query if they have not been selected or do not have the entered value. These are, for example, the following:
    • radio
    • checkbox
    • select with multiple attribute
    This rule is based on a well-known hack with a field of type hidden, which is placed along with a field of type checkbox, to correctly determine its state when sending a form. By the end of this list, it will be clear where to place the hidden field and why this hack works.

    Also, the request will not include the values ​​of the buttons, both ordinary and submit, which are not involved in the form submission, i.e. those buttons that were not pressed. Thus, if the submission is done using the submit () method, no buttons will be included in the request.

    Fields that are in a disabled state (with the disabled attribute) will not be sent either.

  3. The browser does not change the name of the form field when generating key = value pairs.
    The key value in a pair depends solely on the field itself, or rather on its name attribute, and no parent tags affect it in any way. Thus, it does not matter whether your form tag contains the name attribute or not. It is also important to understand.

  4. The browser does not resolve name conflicts in several fields inside the form.
    And this is an important point, which many do not know or think they know, but they are mistaken.
    Imagine that we have this form:

     <form method="POST" enctype="application/x-www-form-urlencoded"> <input type="text" name="fullname" value="John Doe"> <input type="text" name="fullname" value="Mike Ross"> </form> 

    Many assume that the browser must somehow decide which one of the two fields to send in the request. Logic dictates that you need to send the last (in this case, the second), which overlaps all previous fields with the same name. This assumption is wrong!
    The browser doesn't care at all; there are some name conflicts in the form or not. A non-empty field name is not a criterion for excluding this field from the request being sent. In other words, for the browser, both fields are correct and both will be included in the set of sent fields, and in the order in which they are presented in the form.
    Thus, the above form when sending to the server will take the following form:

     fullname=John+Doe&fullname=Mike+Ross 

    Such a request will come to our server. You can check this by reading the php: // input value. What will PHP do with this query? Everything is as usual - parses the string and forms the $ _POST array. But, as we know, the keys in the arrays are unique, so the original value of “John Doe” will be overwritten by the value of “Mike Ross”. As a result, we get the following result:

     $_POST = [ 'fullname' => 'Mike Ross', ] 

    Now it should be clear how the hack works with a hidden checkbox field. Let's look at the following form:

     <form method="POST" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="accept" value="0"> <input type="checkbox" name="accept" value="1"> </form> 

    We see two fields with the same name. If the checkbox is selected, both fields will be added to the query in the order in which they are presented in the form, which means that the value “0” in the hidden field will be overwritten with the value “1” in the checkbox field. If the checkbox is not selected, then according to item 2 its value is not sent, which means that the request will only have a hidden field with the value “0”, which will be received on the server and placed in the $ _POST array.

  5. Resolve field name conflicts should the developer.
    This item is derived from the previous one. Take another look at the example form we considered with two fullname fields. Imagine that the first field is the name of the manager, the second is the developer. On the server, we need both values, but since PHP overwrites one value with another, we need to somehow help it correctly parse the incoming data. Logic tells you to change field names. There are several options:

    • manager_fullname and developer_fullname are the easiest and most obvious. Field names are now unique and will not cause conflict when writing to the $ _POST array.

    • manager [fullname] and developer [fullname] - in this case, the names are also unique, but parsing similar keys in PHP will be different from the first option: if PHP meets the pair of square brackets in the key, it interprets them as an array.
      After parsing, $ _POST will look like this:

       $_POST = [ 'manager' = [ 'fullname' => 'John Doe', ], 'developer' = [ 'fullname' => 'Mike Ross', ], ] 

      This option is useful for creating dynamic forms, or, for example, if you need to select some fields in a logical subform.

    • fullname [] is an interesting option: the field names will be the same, but since they contain square brackets, PHP will understand that the array of values ​​is in front of it, and in this case it will put all the values ​​into the $ _POST array.
      The request in this case will look like this:

       fullname[]=John+Doe&fullname[]=Mike+Ross 

      Approximate algorithm for parsing such a request in PHP:

       $_POST = []; $_POST['fullname'][] = 'John Doe'; $_POST['fullname'][] = 'Mike Ross'; 

      Thus, the $ _POST array will contain the following values:

       $_POST = [ 'fullname' = [ 'John Doe', 'Mike Ross', ], ] 

      This option is useful for a set of values ​​of undefined size. For example, a set of fields such as checkbox.


  6. The field with the multiple attribute is transmitted in the request as N separate fields with the same name, where N is the number of selected values ​​of this field.
    Suppose that we have the following form:

     <form method="POST" enctype="application/x-www-form-urlencoded"> <select name="hobbies" multiple> <option value="movies">Movies</option> <option value="music">Music</option> <option value="cooking">Cooking</option> <option value="photography">Photography</option> <option value="painting">Painting</option> <option value="golf">Golf</option> </select> </form> 

    The form contains a list in which we can choose several options. Let's say we chose Movies, Cooking and Golf. It can be assumed that a certain value separator will be used when sending the field. In fact, the query will look like this:

     hobbies=movies&hobbies=cooking&hobbies=golf 

    Those. in the browser, we send one field with three values, but the server side sees it as sending three fields that contain one value at a time. Obviously, according to paragraph 4, the $ _POST array will contain only one last golf value, which will overwrite the first two. To solve this problem, you need to use the advice from step 5 and change the name of the select tag to “hobbies []”.

  7. Fields of type radio with the same name (name attribute) within the same form will be combined into a group.
    In this case, there will be no conflicts of names, since in the group, you can select / check only one field, the value of which will be sent in the request together with the group name (the name attribute of the selected field). All other fields of this group will not be marked, and therefore according to paragraph 2 are not included in the sent request.


The developer will answer for everything


Briefly also consider the second group of ways to submit forms. This group differs from the first in that the developer deals with the creation, shaping and sending of a request directly by the developer. This group has only a distant relationship to the topic we are considering, since it does not have a strict binding to HTML forms: the developer can include in the request and exclude from it any data. The way the fields are serialized differs from a completely arbitrary query by the presence of ready-made algorithms in widely used JS frameworks, which assume most of the work. These methods are useful when sending form data using Ajax.

Consider a small example of using the jQuery JS library to form and submit a form in this way.

The form:

 <form id="userform" method="GET" enctype="multipart/form-data"> <input type="text" name="fullname" value="John Doe"> <input type="text" name="email" value="johndoe@gmail.com" disabled> <input type="checkbox" name="accept" value="1" checked> <input type="file" name="photo"> <button type="submit" name="submit" value="Send"> </form> 

JS code:

 var postbody = $("#userform").serialize(); $.ajax({ url: "server.php", type: "POST", data: postbody, success: function(data) { // ... }, error: function(data) { // ... } }); 

All the main work is performed by the serialize () method, which, using the names and values ​​of the form fields, generates the following line:

 fullname=John+Doe&accept=1 

Why of the five fields of the form in the request included only two? The email field does not turn on, because is in the disabled state (the attribute is disabled), and the photo and submit fields are The serialize () method of this JS library does not include files and buttons in the set of fields when forming the query string.

Next, create an Ajax request with certain parameters and send our form as a previously received string. Here it is important to understand that when using this method, the HTML form requires only a set of fields with their names and values, all the other query parameters are determined directly by the developer. Thus, the action, method and enctype attributes of the form tag are ignored: when calling the ajax () method, we explicitly specify that the data will be transferred by the POST method to the “server.php” handler, and the encoding of the fields in this method defaults to “application / x -www-form-urlencoded.

On the server side, such a request will be almost identical to the usual one, and only thanks to additional headers can we determine that it was executed using Ajax technology. Based on this information, the format and content of the response in most cases will be different from ordinary requests.

When using this group of methods for forming and sending requests, the front-end developer is practically unlimited. The client part of the application can, for example, transfer only some part of the form in the request or transfer this form in JSON format, while the server part should be able to correctly process such data, therefore, to solve such problems, the interaction and coordination of the backend and front-end developers should be strengthened.

Why all this article?


Working with forms is considered one of the most difficult tasks in web development. But if you clearly understand how the forms work, find convenient, functional and flexible tools for both the client and server parts, and improve the interaction between the backend and the front-end developers, the task is much simpler.

This article does not answer all the existing questions, many will not without reason say that everything described is well-known facts, but I hope that the information provided will seem useful to someone and force you to reconsider your attitude to working with forms, and perhaps even help to overcome a phobia. In front of them.

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


All Articles