Development

Documentation/hu_HU/forms_book/1.1/01-Form-Creation

You must first sign up to be able to contribute.

1. fejezet - Űrlap készítés

Egy űrlap bizonyos mezők (rejtett, beviteli, szövegdoboz, lenyíló list, ...) összessége. Ebben a fejezetben az űrlap készítését és űrlapmezők kezelését mutatjuk be az symfony űrlap keretrendszerének segítségével.

A következő fejezetekben található leírás a symfony 1.1 verziójáról szól. Szükség lesz még egy projektre és azon belül egy frontend alkalmazásra. Symfony projekt készítéséről további információ a könyv ide vonatkozó fejezetében található.

Előkészítés

Egy kapcsolat űrlap felvételével kezdjük.

1-1 ábra mutatja az űrlapot, amin keresztül a felhasználó üzenetet küldhet nekünk.

1-1 ábra - Kapcsolat űrlap

Kapcsolat űrlap

Három mezőt használunk az űrlapon: a felhasználó nevét, az email címét és az üzenetet, amit elküld nekünk. Ezután egyszerűen megjelenítjük az elküldött adatokat, ahogy az 1-2 ábrán látható.

1-2 ábra - Üzenet oldal

Üzenet oldal

1-3 ábra - a felhasználó és az alkalmazás közötti kölcsönhatást mutatja

Figure 1-3 - Kölcsönhatás a felhasználóval

Kölcsönhatás a felhasználóval séma

Widgetek

sfForm és sfWidget osztályok

Az űrlapot mezők alkotják, melyeket a felhasználó tölt ki adatokkal. A symfonyban az űrlap egy objektum, mely az sfForm osztályból származik. Példánkban létrehoztunk egy ContactForm osztályt, melynek őse az sfForm. Az sfForm minden űrlap ősosztálya. Az sfForm teszi lehetővé űrlapjaink egyszerű kezelését és konfigurálását.

Megjegyzés Az sfForm osztály minden űrlap ősosztálya, mely lehetővé teszi azok egyszerű kezelését és konfigurálást.

Az űrlaphoz a configure() metóduson keresztül adhatunk widgeteket.

Egy widget egy űrlap mezőt ábrázol. Példánknál maradva három widgetre lesz szükségünk a három mező ábrázolásához: name, email és message. Az 1-1 jegyzet mutatja be a ContactForm első megvalósítását.

1-1 jegyzet - ContactForm osztály három mezővel

[php]
// lib/form/ContactForm.class.php
class ContactForm extends sfForm
{
  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'message' => new sfWidgetFormTextarea(),
    ));
  }
}

A widgetek a configure() metóduson belül vannak definiálva. Ez a metódus automatikusan meghívásra kerül az sfForm osztály konstruktorában.

A setWidgets() metódussal lehet az űrlapon használt widgeteket definiálni. A setWidgets() metódus egy asszociatív tömböt vár, ahol a kulcsok jelölik az egyes mezők nevét, az értékek pedig a widget objektumokat. Minden widget egy objektum, melynek az sfWidget osztály az őse. Példánkban két típusú widgetet használunk:

  • sfWidgetFormInput : ez a widget egy input mezőt ábrázol
  • sfWidgetFormTextarea: ez a widget egy szövegdobozt (textarea) ábrázol

Megjegyzés Megegyezés szerint a form osztályok a lib/form/ könyvtárban találhatók. Igazság szerint az űrlapok bárhol tárolhatók, ahol a symfony autoloading mechanizmusa megtalálja azokat, de később látni fogjuk, hogy a rendszer a lib/form/ könyvtárba hozza létre a model objektumokból generált űrlapokat.

Displaying the Form

Űrlapok megjelenítése

Űrlapunk használatra készen áll. Már csak létre kell hozni egy modult, hogy megjelenítsük azt.

$ cd ~/PATH/TO/THE/PROJECT
$ php symfony generate:module frontend contact

A contact modul index action-ét módosítsuk, hogy a form példányt átadjuk a templatenek, ahogy az 1-2 jegyzeten látható.

1-2 jegyzet - Action osztály a contact modulban

[php]
// apps/frontend/modules/contact/actions/actions.class.php
class contactActions extends sfActions
{
  public function executeIndex()
  {
    $this->form = new ContactForm();
  }
}

Mikor létrehozzuk az űrlapot, a korábban definiált configure() metódus automatikusan lefut.

Már csak egy templatere van szükségünk, hogy megjelenjen űrlapunk.

1-3 jegyzet - Űrlap megjelenítése templateben

[php]
// apps/frontend/modules/contact/templates/indexSuccess.php
<form action="<?php echo url_for('contact/submit') ?>" method="POST">
  <table>
    <?php echo $form ?>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

Az űrlap csak a felhasználó számára fontos információt kezeli. Az indexSuccess templateben a <?php echo $form ?> sor csak három mezőt jelenít meg. A többi elemet, mint a form tag és a submit gomb magunknak kell hozzáadni. Első pillantásra ez nem túl nyilvánvaló, viszont később látni fogjuk milyen hasznos, ha az űrlapjaink között átfedések vannak.

A <?php echo $form ?> használata rendkívül hasznos űrlap prototípusok készítésénél. Lehetővé teszi, hogy a fejlesztő az üzleti logikára koncentráljon anélkül, hogy a megjelenés miatt keljen aggódnia. A 3. fejezet mutatja be az űrlapok megjelenésének testreszabását.

Megjegyzés

Mikor a <?php echo $form ?> formát használjuk megjelenítésre, a PHP a $form objektum szöveges alakját jeleníti meg. Az objektum szöveggé konvertálásakor a PHP a __toString() magic metódust próbálja meg futtatni. Minden widget megvalósítja ezt, hogy az objektum HTML alakját vissza tudja adni. A <?php echo $form ?> meghívása egyenértékű a <?php echo $form->__toString() ?> hívással.

Most már meg tudjuk jeleníteni az űrlapot a böngészőben (1-4 ábra) és ellenőrizhetük az eredményt a contact/index action begépelésével (/frontend_dev.php/contact).

1-4 ábra - Generált kapcsolat űrlap

Generált kapcsolat űrlap

1-4 jegyzet A template által generált kód

[html]
<form action="/frontend_dev.php/contact/submit" method="POST">
  <table>

    <!-- A <?php echo $form ?> által generált űrlap kezdete -->
    <tr>
      <th><label for="name">Name</label></th>
      <td><input type="text" name="name" id="name" /></td>
    </tr>
    <tr>
      <th><label for="email">Email</label></th>
      <td><input type="text" name="email" id="email" /></td>
    </tr>
    <tr>
      <th><label for="message">Message</label></th>
      <td><textarea rows="4" cols="30" name="message" id="message"></textarea></td>
    </tr>
    <!-- A <?php echo $form ?> által generált űrlap vége -->

    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

Azt láthatjuk, hogy az űrlap egy HTML táblázat három <tr> sorában jelenik meg. Ezért tettük az egészet egy <table> tagba. Minden sor tartalmaz egy <label> taget és egy űrlap taget (<input> vagy <textarea>).

Cimkék

Minden mezőhöz automatikusan létrejön egy cimke. Alapbeállításként a cimke nevet a mező nevéből képzi a rendszer a következő szabályok alapján: az első betű nagy betű, az aláhúzások cseréje szóközökkel. Példa:

[php]
$this->setWidgets(array(
  'first_name' => new sfWidgetFormInput(), // generált cimke: "First name"
  'last_name'  => new sfWidgetFormInput(), // generált cimke: "Last name"
));

Habár az automatikus cimke generálás nagyon hasznos, a keretrendszer megengedi a cimkék testreszabását is a setLabels() metóduson keresztül:

[php]
$this->widgetSchema->setLabels(array(
  'name'    => 'Your name',
  'email'   => 'Your email address',
  'message' => 'Your message',
));

A setLabel() metóduson keresztül az egyes mezők cimkéje is módosítható:

[php]
$this->widgetSchema->setLabel('email', 'Your email address');

Végül, a 3. fejezetben látni fogjuk, hogyan terjeszthetjük ki a cimkéket a templatekben, tovább finomítva az űrlapon.

Széljegyzet Widget Séma

Mikor a setWidgets() metódust használjuk, a symfony létrehoz egy sfWidgetFormSchema objektumot. Ez az objetum is egy widget, amely widgetek egy halmazát ábrázolja. A ContactForm űrlapunkban használtunk a setWidgets() metódust. Ez egyenértékű a következő kóddal:

[php]
$this->setWidgetSchema(new sfWidgetFormSchema(array(
  'name'    => new sfWidgetFormInput(),
  'email'   => new sfWidgetFormInput(),
  'message' => new sfWidgetFormTextarea(),
)));

// majdnem egyenértékű a :

$this->widgetSchema = new sfWidgetFormSchema(array(
  'name'    => new sfWidgetFormInput(),
  'email'   => new sfWidgetFormInput(),
  'message' => new sfWidgetFormTextarea(),
));

The setLabels() method is applied to a collection of widgets included in the widgetSchema object . A setLabels() metódus használatakor azt widgetek egy csoportján alkalmazzuk, melyeket a widgetSchema objektum tartalmmazza.

Az 5. fejezetben látjuk majd, hogy a "schema widget" használata mennyire leegyszerűsíti az egymást átfedő űrlapok használatát.

A generált táblázatokon túl

Habár az űrlap alapvetően HTML táblázatként jelenik meg, a layout megváltoztatható. A különböző layout formák osztályokban definiálhatók, melyek az sfWidgetFormSchemaFormatter osztály leszármazottai. Az űrlapok a táblázat formát (table) használják alapértemlezettként, ami az sfWidgetFormSchemaFormatterTable osztályban van meghatározva. Használhatunk akár lista formát (list) is:

[php]
class ContactForm extends sfForm
{
  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'message' => new sfWidgetFormTextarea(),
    ));

    $this->widgetSchema->setFormFormatterName('list');
  }
}

Ezzel a két formával érkezik alapértelmezetten a rendszerrel. Az 5. fejezetben majd láthatjuk, hogyan lehet létrehozni saját forma osztályt. Most már tudjuk, hogyan jelenítsünk meg egy űrlapot, nézzük meg hogyan kezeljük az elküldött űrlap adatokat.

Az űrlap elküldése

Mikor létrehoztuk az űrlap megjelenését szolgáló templatet, akkor a contact/submit belső URL-t használtuk a form tagban az elküldéshez. Most fel kell vennünk a submit action a contact modulba. Az 1-5 jegyzet bemutatja, hogyan szerezzük be a felhasználó által elküldött adatot és hogyan irányítjuk át a köszönet oldalra, ahol csak megjelenítjük neki azt.

1-5 jegyzet - submit action használata a contact modulban

[php]
public function executeSubmit($request)
{
  $this->forward404Unless($request->isMethod('post'));

  $params = array(
    'name'    => $request->getParameter('name'),
    'email'   => $request->getParameter('email'),
    'message' => $request->getParameter('message'),
  );

  $this->redirect('contact/thankyou?'.http_build_query($params));
}

public function executeThankyou()
{
}

// apps/frontend/modules/contact/templates/thankyouSuccess.php
<ul>
  <li>Name:    <?php echo $sf_params->get('name') ?></li>
  <li>Email:   <?php echo $sf_params->get('email') ?></li>
  <li>Message: <?php echo $sf_params->get('message') ?></li>
</ul>

Megjegyzés A http_build_query a PHP beépített függvénye, amely URL-kódolt query stringet generál az átadott tömb paraméter alapján.

Az executeSubmit() metódus három dolgot tesz:

  • Biztonsági okokból ellenőrizzük, hogy az adatokat a HTTP POST metódusával küldték-e el. Ha nem, akkor a felhasználót átirányítjuk a hiba (404) oldalra. Az indexSuccess templateben a küldési metódust POSTként deklaráltuk (<form ... method="POST">):

    [php]
    $this->forward404Unless($request->isMethod('post'));
    
  • Következő lépésben összegyűjtjük a felhasználó által küldött adatokat egy params tömbbe:

    [php]
    $params = array(
      'name'    => $request->getParameter('name'),
      'email'   => $request->getParameter('email'),
      'message' => $request->getParameter('message'),
    );
    
  • Végül átirányítjuk a felhasználót a Köszönet oldalra (contact/thankyou), hogy megjeleníthessük az elküldött információt:

    [php]
    $this->redirect('contact/thankyou?'.http_build_query($params));
    

Ahelyett, hogy újra átirányítjuk a felhasználót, létrehozhatnánk egy submitSuccess.php templatet. Ez mind igaz, de szerencsésebb, ha a felhasználót POST kérés után mindig továbbirányítjuk:

  • Ezzel megakadályozzuk az űrlap újra küldését, ha a felhasználó frissítené a Köszönet oldalt.

  • A felhasználó tudja használni a Vissza gombot anélkül, hogy egy felugró ablakot kapna, hogy küldje el az űrlapot újra.

Tip Az élelmesebbek megfigyelhették, hogy az executeSubmit() különbözik az executeIndex()től. Mikor meghívjuk ezeket a metódusokat, a symfony az aktuális sfRequest objektumot átadja, mint első paramétert az executeXXX() metódusnak. PHP-ban nem szükséges megadnunk egy függvény vagy metódus összes paraméterét, ezért nem definiáltuk a request változót az executeIndex()ben.

1-5 ábra mutatja be a kérések folyamatát a felhasználóval való interakció közben

1-5 ábra - Kérések folyamata

Kérések folyamata

Megjegyzés

Mikor megjelenítjük a felhasználó által megadott adatot, számolnunk kell az XSS (Cross-Site Scripting) támadás lehetőségével. A támadás ellen védekezhetünk valamilyen (output) escapelési startégia bevezetésével, amelyről további információ a "The Definitive Guide to symfony" könyv Inside the View Layer fejezetében található.

Az űrlap elküldése után az 1-6 ábrának megfelelő képet kell látnunk.

1-6 ábra - Űrlap elküldés után oldal képe

Űrlap elküldés után oldal képe

A params tömb létrehozása helyett egyszerűbb lenne, ha a felhasználótól érkező információt közvetlenül tömbként kapnánk. Az 1-6 jegyzetben látható módon megváltoztatható a widgetek name HTML attribútuma, így a mező értékeket a contact tömbben kapjuk meg.

1-6 jegyzet - A name HTML attribútum módosítása widgetekben

[php]
class ContactForm extends sfForm
{
  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'message' => new sfWidgetFormTextarea(),
    ));

    $this->widgetSchema->setNameFormat('contact[%s]');
  }
}

Calling setNameFormat() allows us to modify the name HTML attribute for all widgets. %s will automatically be replaced by the name of the field when generating the form. For example, the name attribute will then be contact[email] for the email field. PHP automatically creates an array with the values of a request including a contact[email] format. This way the field values will be available in the contact array.

We can now directly get the contact array from the request object as shown in Listing 1-7.

Listing 1-7 - New format of the name attributes in the action widgets

[php]
public function executeSubmit($request)
{
  $this->forward404Unless($request->isMethod('post'));

  $this->redirect('contact/thankyou?'.http_build_query($request->getParameter('contact')));
}

When displaying the HTML source of the form, you can see that symfony has generated a name attribute depending not only on the field name and format, but also an id attribute. The id attribute is automatically created from the name attribute by replacing the forbidden characters by underscores (_):

| Name | Attribute name | Attribute id | | --------- | -------------------- | ------------------- | | name | contact[name] | contact_name | | email | contact[email] | contact_email | | message | contact[message] | contact_message |

Another solution

In this example, we used two actions to manage the form: index for the display, submit for the submit. Since the form is displayed with the GET method and submitted with the POST method, we can also merge the two methods in the index method as shown in Listing 1-8.

Listing 1-8 - Merging of the two actions used in the form

[php]
class contactActions extends sfActions
{
  public function executeIndex($request)
  {
    $this->form = new ContactForm();

    if ($request->isMethod('post'))
    {
      $this->redirect('contact/thankyou?'.http_build_query($request->getParameter('contact')));
    }
  }
}

You can change the form method in the indexSuccess.php template by changing the form method attribute:

[php]
<form action="<?php echo url_for('contact/index') ?>" method="POST">

As we will see later, we prefer to use this syntax since it is shorter and makes the code more coherent and understandable.

Configuring the Widgets

Widgets options

If a website is managed by several webmasters, we would certainly like to add a drop-down list with themes in order to redirect the message according to what is asked (Figure 1-7). Listing 1-9 adds a subject with a drop-down list using the sfWidgetFormSelect widget.

Figure 1-7 - Adding a subject Field to the Form

![Adding a subject Field to the Form](/images/forms_book/en/01_07.png "Adding a subject Field to the Form")

Listing 1-9 - Adding a subject Field to the Form

[php]
class ContactForm extends sfForm
{
  protected static $subjects = array('Subject A', 'Subject B', 'Subject C');

  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'subject' => new sfWidgetFormSelect(array('choices' => self::$subjects)),
      'message' => new sfWidgetFormTextarea(),
    ));

    $this->widgetSchema->setNameFormat('contact[%s]');
  }
}

Széljegyzet The choices option of the sfWidgetFormSelect Widget

PHP does not make any distinction between an array and an associative array, so the array we used for the subject list is identical to the following code:

[php]
$subjects = array(0 => 'Subject A', 1 => 'Subject B', 2 => 'Subject C');

The generated widget takes the array key as the value attribute of the option tag, and the related value as content of the tag:

[php]
<select name="contact[subject]" id="contact_subject">
  <option value="0">Subject A</option>
  <option value="1">Subject B</option>
  <option value="2">Subject C</option>
</select>

In order to change the value attributes, we just have to define the array keys:

[php]
$subjects = array('A' => 'Subject A', 'B' => 'Subject B', 'C' => 'Subject C');

Which generates the HTML template:

[php]
<select name="contact[subject]" id="contact_subject">
  <option value="A">Subject A</option>
  <option value="B">Subject B</option>
  <option value="C">Subject C</option>
</select>

The `sfWidgetFormSelect' widget, like all widgets, takes a list of options as the first argument. An option may be mandatory or optional. The sfWidgetFormSelect widget has a mandatory option, choices. Here are the available options for the widgets we already used:

| Widget | Mandatory Options | Additional Options | | ---------------------- | --------------------- | -------------------------------- | | sfWidgetFormInput | - | type (default to text) | | | | is_hidden (default to false) | | sfWidgetFormSelect | choices | multiple (default to false) | | sfWidgetFormTextarea | - | - |

Tip If you want to know all of the options for a widget, you can refer to the complete API documentation available online at (http://www.symfony-project.org/api/1_1/). All of the options are explained, as well as the additional options default values. For instance, all of the options for the sfWidgetFormSelect are available here: (http://www.symfony-project.org/api/1_1/sfWidgetFormSelect).

The Widgets HTML Attributes

Each widget also takes a list of HTML attributes as second optional argument. This is very helpful to define default HTML attributes for the generated form tag. Listing 1-10 shows how to add a class attribute to the email field.

Listing 1-10 - Defining Attributes for a Widget

[php]
$emailWidget = new sfWidgetFormInput(array(), array('class' => 'email'));

// Generated HTML
<input type="text" name="contact[email]" class="email" id="contact_email" />

HTML attributes also allow us to override the automatically generated identifier, as shown in Listing 1-11.

Listing 1-11 - Overriding the id Attribute

[php]
$emailWidget = new sfWidgetFormInput(array(), array('class' => 'email', 'id' => 'email'));

// Generated HTML
<input type="text" name="contact[email]" class="email" id="email" />

It is even possible to set default values to the fields using the value attribute as Listing 1-12 shows.

Listing 1-12 - Widgets Default Values via HTML Attributes

[php]
$emailWidget = new sfWidgetFormInput(array(), array('value' => 'Your Email Here'));

// Generated HTML
<input type="text" name="contact[email]" value="Your Email Here" id="contact_email" />

This option works for input widgets, but is hard to carry through with checkbox or radio widgets, and even impossible with a textarea widget. The sfForm class offers specific methods to define default values for each field in a uniform way for any type of widget.

Megjegyzés We recommend to define HTML attributes inside the template and not in the form itself (even if it is possible) to preserve the layers of separation as we will see in Chapter three.

Defining Default Values For Fields

It is often useful to define a default value for each field. For instance, when we display a help message in the field that disappears when the user focuses on the field. Listing 1-13 shows how to define default values via the setDefault() and setDefaults() methods.

Listing 1-13 - Default Values of the Widgets via the setDefault() and setDefaults() Methods

[php]
class ContactForm extends sfForm
{
  public function configure()
  {
    // ...

    $this->setDefault('email', 'Your Email Here');

    $this->setDefaults(array('email' => 'Your Email Here', 'name' => 'Your Name Here'));
  }
}

The setDefault() and setDefaults() methods are very helpful to define identical default values for every instance of the same form class. If we want to modify an existing object using a form, the default values will depend on the instance, therefore they must be dynamic. Listing 1-14 shows the sfForm constructor has a first argument that set default values dynamically.

Listing 1-14 - Default Values of the Widgets via the Constructor of sfForm

[php]
public function executeIndex($request)
{
  $this->form = new ContactForm(array('email' => 'Your Email Here', 'name' => 'Your Name Here'));

  // ...
}

Széljegyzet Protection XSS (Cross-Site Scripting)

When setting HTML attributes for widgets, or defining default values, the sfForm class automatically protects these values against XSS attacks during the generation of the HTML code. This protection does not depend on the escaping_strategy configuration of the settings.yml file. If a content has already been protected by another method, the protection will not be applied again.

It also protects the ' and " characters that might invalidate the generated HTML.

Here is an example of this protection:

[php]
$emailWidget = new sfWidgetFormInput(array(), array(
  'value' => 'Hello "World!"',
  'class' => '<script>alert("foo")</script>',
));
<br />
// Generated HTML
<input
  value="Hello &quot;World!&quot;"
  class="&lt;script&gt;alert(&quot;foo&quot;)&lt;/script&gt;"
  type="text" name="contact[email]" id="contact_email"
/>

Fordította: Sulik Szabolcs