HTML forms. Backend Developer View

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

The introductory part was somewhat stretched and eventually resulted in a separate small article, which, in my opinion, may be of interest to other backend developers (not only PHP or Symfony).

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

image


Previously, working with forms was in a sense simpler: in the form in which the form came to the user, in the same way it was sent back, but with the fields filled in. 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 the application, in which the form comes with one set of fields, and it is sent with a completely different one, when bulky forms have small dynamic subforms in their composition, which, regardless of the main one, can be sent to the server for processing when the selected files are downloaded in the background while the user fills out the rest of the form. Thus, an increasing amount of logic is being transferred to the client side, and this is excellent, first of all, of course, for the user. For the developer, the process of working with forms has become somewhat more complicated. But no matter how hard it was for him, it is impossible to ignore the development of technologies today, so you need to be able to use them effectively.

Backend vs Frontend


We constantly strive to automate everything that we have to work with, especially routine tasks, while trying to bring some order to these processes. Therefore, many developers are somewhat afraid of the dynamism on the client side, which is often perceived by them as uncontrolled chaos. You might think that front-end developers have their hands untied, and they can create like artists and sculptors, their actions are unlimited, and then we can sort out their fantasies on the server. This often happens, but this is an erroneous, dead end path. This can be compared with schemaless data warehouses: no one limits you to a rigid data storage structure, each of the developers within one project can come up with its own storage format, then change it depending on the mood, which in the final analysis is most likely will lead to sad consequences. If you expect flexibility, easy extensibility and dynamic development from your product, you will need to adhere to certain rules that will help maintain order in your application. You have the right to demand the same from front-end developers -do it, but stick to the rules .

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

  1. Stop being afraid of dynamics on the server side.
    The world and technology do not stand still, and if you do not have time to adapt 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 want 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 pre-determined by the developer, but on the data received from users.

  2. Don't blame front-end developers for everything.
    Set the rules in the company, team, project, and then front-end developers will have no less responsibility when working with forms than you. And even better, if in addition to the rules between all developers there will be a trusting relationship, 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. By filling 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 various ways, most of which will require scripting. Consider the main ones:

  1. Clicking the submit button.
  2. Call the submit () method on the form object.
  3. Serialization of form fields.
  4. Creation and formation of an arbitrary request.

The methods in the list are arranged in ascending order by the efforts put forward by the frontend developer to send the form to the server. You can also conditionally divide them into two groups: the first two methods comprise a group in which the browser is responsible for generating and sending the request, the second group includes the last two ways in which JS performs these actions.

Let the browser work


First, consider the first group, both methods of which are not much different from each other. In this case, the user's browser takes all the work and responsibility, and it acts quite straightforwardly:

  1. Defines the form whose fields you want to submit.
  2. Defines a set of fields to send. 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 (name attribute), and the value is the content of this field.
  4. Creates and generates a request that will be executed by the method specified in the attribute of the form tag 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 you submit 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 pressed, and in its absence, by the form’s parent tag for the button. If this is a call to the submit () method, the form submitted is the one associated with the object for which this method is called.

The formmethod, formenctype, and formaction attributes of the pressed button redefine 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 clearly understand what data and in what form we receive on the server.

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

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

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

nickname=ghost&password=123456

Upon receiving such a request, PHP will begin to parse it, 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 defined, they will be placed in the superglobal array $ _POST, which will take the following form:

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

What should be noted 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 the form, 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, or text in markdown markup, or text in any other format you already know. This is an important point, the awareness of which will help overcome the fear of bulky complex forms. Of course, this does not apply to downloadable files.

  2. Not all fields that are 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 were not selected or do not have an entered value. These are, for example, the following:
    • radio
    • checkbox
    • select with multiple attribute
    This rule is based on the well-known hack with a field of type hidden, which is placed with a field of type checkbox to correctly determine its state when submitting a form. By the end of this list, it will become clear where to place the hidden field and why this hack works.

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

    Fields that are in the disabled state (having the disabled attribute) will also not be sent.

  3. The browser does not change the form field names 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. So it doesn't matter if your form tag contains the name attribute or not. It is also important for understanding.

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

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

    Many speculate that the browser must somehow decide which one of the two fields to send in the request. Logic suggests that you need to send the last (in this case, the second), which overlaps all previous fields with the same name. This assumption is incorrect!
    The browser does not care at all whether there are any name conflicts in the form or not. A non-empty field name is not a criterion for excluding a given field from a sent request. 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
    

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

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

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

    <formmethod="POST"enctype="application/x-www-form-urlencoded"><inputtype="hidden"name="accept"value="0"><inputtype="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 request in the order in which they are presented in the form, which means that the value “0” of the hidden field will be overwritten with the value “1” of the checkbox field. If the checkbox is not selected, then according to paragraph 2, its value is not sent, which means that the request will only have a hidden field with a value of "0", which will be received on the server and placed in the $ _POST array.

  5. Resolving field name conflicts must be done by the developer.
    This item follows from the previous one. Let's look again at the example of the form with two fullname fields that we have examined. 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 the other, 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 simplest and most obvious. Field names are now unique and will not cause a conflict when writing to the $ _POST array.

    • manager [fullname] and developer [fullname] - in this case, the names are also unique, but parsing such keys in PHP will be different from the first option: if PHP encounters paired 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 convenient to use to create 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 it has an array of values ​​in front of it, and in this case it will put all the values ​​in the $ _POST array.
      The request in this case will look like this:

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

      An example 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 convenient to use for a set of values ​​of indefinite size. For example, a set of fields like checkbox.


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

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

    The form contains a list in which we can select several options. Let's say we chose Movies, Cooking and Golf. It can be assumed that when sending the field, a certain separator of values ​​will be used. In fact, the request 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 this as sending three fields that contain one value. 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 paragraph 5 and change the name of the select tag to "hobbies []".

  7. Fields of the radio type with the same name (name attribute) within the same form will be grouped.
    In this case, there will be no name conflicts, because in the group, you can select / mark only one field, the value of which will be sent in the request paired with the group name (name attribute of the selected field). All other fields of this group will not be marked, which means that according to paragraph 2 they 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 is directly involved in creating, forming and sending the request. This group is only distantly related to the topic we are considering, since it does not have a rigid binding to HTML forms: the developer can include in the request and exclude any data from it. The method of serializing fields differs from a completely arbitrary request by the availability of ready-made algorithms in the widespread JS frameworks that take up most of the work. These methods are useful when submitting 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:

<formid="userform"method="GET"enctype="multipart/form-data"><inputtype="text"name="fullname"value="John Doe"><inputtype="text"name="email"value="johndoe@gmail.com"disabled><inputtype="checkbox"name="accept"value="1"checked><inputtype="file"name="photo"><buttontype="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 form fields in the request, only two are included? The email field is not included, because is in the disabled state (attribute disabled), and the photo and submit fields - because The serialize () method of this JS library does not include files and buttons in a set of fields when forming a query string.

Next, create an Ajax request with certain parameters and submit our form in the form of a string received earlier. It is important to understand that when using this method, an HTML form requires only a set of fields with their names and values, all other request parameters are determined directly by the developer. Thus, the action, method, and enctype attributes of the form tag are ignored: when the ajax () method is called, we explicitly indicate that the data will be transferred by the POST method to the “server.php” handler, and the encoding of the fields in this method by default is “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 generating and sending requests, the front-end developer is practically unlimited. The client part of the application can, for example, transmit only a part of the form in the request or send this form in JSON format, while the server part must be able to correctly process such data, therefore, to solve such problems, the interaction and coordination of the work of 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, establish interaction between the backend and front-end developers, the task is much simpler.

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

Also popular now: