ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 148
Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 46
Abonament PDF

Gestionarea datelor folosind Symfony Forms

Sergiu Stupariu
PHP Developer @Pentalog



PROGRAMARE

Pornind de la dezvoltarea de simple aplicații de tip CRUD și până la cele mai complexe și mari website-uri, gestionarea și lucrul cu form-uri HTML reprezintă unul dintre cele mai frecvente și provocatoare task-uri pentru un web developer. 

 Pe lîngă numeroase module și functionalităti, framework-ul de PHP Symfony pune la dispoziție o componentă specializată pe form-uri care are ca scop ușurarea întregului proces de manipulare și stocare a datelor de intrare. Această componentă poate fi folosită și în afară proiectelor Symfony, fiind o librărie de sine-stătătoare ușor integrabilă, dar este adresată în special aplicațiilor care prezintă o arhitectură construită conform principiului separation of concerns, de regulă Model-View-Controller. Vom vedea în continuare cum, cu ajutorul acestei abordări, componenta reprezintă form-urile sub forma unor clase strâns legate de entități din Model

 Cel mai simplu exemplu ar fi înregistrarea unui nou user pe website. Pentru aceasta se vor defini două clase: User - Entitatea reprezentând utilizatorul, UserType - clasa aferentă form-ului pentru entitatea User.

Un form poate fi creat și folosit direct în interiorul Controller-ului, dar o recomandare sub formă de "best practice" a framework-ului este ca acesta să fie construit într-o clasă separată, de sine stătătoare, care poate fi refolosită în orice alt loc al aplicației. În linii mari, clasa care conține logica construirii form-ului pentru un User arată în felul următor:

class UserType extends AbstractType
{
 public function buildForm(FormBuilderInterface $builder, array $options)
 {
 $builder
 ->add('email', EmailType::class)
 ->add('username', TextType::class)
 ->add('plainPassword', RepeatedType::class, array(
 'type' => PasswordType::class,
 'first_options' => array('label' => 'Password'),
 'second_options' => array('label' => 'Repeat Password'),
 )
 );
 }

 public function configureOptions(OptionsResolver $resolver)
 {
 $resolver->setDefaults(array(
 'data_class' => 'AppBundle\Entity\User',
 ));
 }

}

Astfel, logica reprezentării și manipulării datelor dintr-un form este efectuată separat de Model și separat de template-ul de reprezentare (de regulă Twig). Form-ul este instanțiat în Controller și trimis mai departe către View, unde este afișat în puține linii de cod, cu ajutorul funcțiilor puse la dispoziție de către Twig (ex: form_row, form_widget, form_label).

{# app/Resources/views/registration/register.html.twig #}

{{ form_start(form) }}
 {{ form_row(form.username) }}
 {{ form_row(form.email) }}
 {{ form_row(form.plainPassword.first) }}
 {{ form_row(form.plainPassword.second) }}

 <button type="submit">Register!</button>
 {{ form_end(form) }}

O întrebare logică care decurge din aspectele menționate mai sus este următoarea: De ce am folosi Form-urile Symfony și nu am rămâne la variantă clasică de a folosi form-urilor pur HTML? Vom vedea în continuare o serie de avantaje puse la dispoziție de către autorii framework-ului, care reprezintă un argument suficient de puternic în această direcție. La fel ca în cazul multor librării, eficacitatea folosirii acestei componente e dovedită în momentul în care dificultatea task-urilor crește și apare nevoia dezvoltării de funcționalități noi care ar implica decizii greu de luat din punct de vedere arhitectural.

 Modificarea dinamică a Form-urilor folosind event-uri 

 De multe ori, un form nu poate fi creat static și apare nevoia ca datele "din spate" să fie modificate dinamic de către developer după anumite nevoi. Folosind form events, putem modifica aceste date la stagii diferite ale întregului workflow: de la popularea inițială a form-ului până la extragerea datelor din request, după submit-ul efectiv. 

 Un prim exemplu: avem un form pentru "User" ca și cel din exemplul anterior. De regulă, form-ul generat din această clasă arată la fel, indiferent dacă un nou User este creat sau dacă unul existent este editat. Să presupunem că proprietatea "username" nu mai poate fi schimbată odată ce user-ul a fost creat și implicit field-ul corespunzător nu mai trebuie afișat. Pentru aceasta, ne putem folosi de sistemul componentei EventDispatcher pentru a analiza componența obiectului, modificând form-ul în funcție de aceasta. Acest lucru se realizează adăugând Event Listeneri clasei de form, delegându-le responsabilitatea creării unui field. Symfony pune la dispoziție o multitudine de event-uri pentru lucrul cu form-uri, cum ar fi: PRE_SET_DATA, POST_SET_DATA, PRE_SUBMIT, SUBMIT și POST_SUBMIT.

Două evenimente sunt declanșate în timpul prepopulării form-ului: PRE_SET_DATA și POST_SET_DATA, iar următoarele trei: PRE_SUBMIT, SUBMIT și POST_SUBMIT sunt declanșate în momentul trimiterii lui. Acest evenimente sunt declanșate de către EventDispatcher și sunt "prinse" cu ajutorul unor Event Listeneri sau Subscriberi (colecție de Listeneri). Un exemplu de Subscriber:

class UserFormSubscriber implements EventSubscriberInterface
{
 public static function getSubscribedEvents()
 {
 return array(FormEvents::PRE_SET_DATA => 'preSetData');
 }

public function preSetData(FormEvent $event)
 {
 $user = $event->getData();
 $form = $event->getForm();

 if (!$user || null === $user->getId()) {
 $form->add('username', TextType::class);
 }
 }
}

De asemenea, un mare avantaj al event-urilor puse la dispoziție de către Symfony este posibilitatea de a modifica datele (entitatea) trimise prin intermediul form-ului chiar după Submit, dând astfel multe posibilități programatorului să intervină asupra componenței datelor într-un mod eficient si izolat.

Data Transformers 

În cadrul unui Form Symfony se diferențiază trei tipuri de date: 

Model Data: formatul folosit în modelul de date al aplicației (ex: un obiect de tip User). Când se apelează Form::getData() sau Form::setData() se operează cu "model" data.

Norm Data: o versiune normalizată a datelor. De regulă, nu e folosită în mod direct.

View Data: formatul folosit pentru a afișa efectiv field-urile formului. De asemenea, este formatul datelor în momentul submit-ului efectiv.

Figura 1. Tipuri de Date în Symfony

Data transformers au următoarele roluri: de a translata datele unui field într-un format care să poată fi afișat în pagină (transform) și efectuarea procesului invers (din form -> entitate: reverse transform). Un simplu exemplu ar fi acțiunea unui user în cadrul unui magazin online care dorește să afle statusul unei comenzi efectuate anterior prin introducerea unui tracking number. Folosind un Data transformer avem posibilitatea de a muta logica respectivă din Controller pe Form efectiv, această acțiune fiind una repetabilă în timp și dependentă de acesta. Așadar, clasa OrderToNumberTransformer va fi responsabilă de convertirea din tracking number în Order și invers.

public function reverseTransform($trackingNumber)
{
if (!$trackingNumber) {
return;
}

$order = $this->manager
->getRepository('AppBundle:Order')
->find($trackingNumber)
;

if (null === $order) {
throw new TransformationFailedException(sprintf(
'An order with number "%s" does not exist!',
$trackingNumber
));
}

return $order;
}

Concluzie

Cu ajutorul unor exemple simple și relevante au fost prezentate părțile de bază ale componentei Symfony Forms, necesare pentru a construi form-uri complexe în cadrul unei aplicații. În cazul proiectelor bazate pe Symfony, trebuie să avem în vedere că scopul principal al unui form este acela de a translata datele dintr-un obiect (ex: User) într-un form HTML pentru a permite user-ului modificarea datelor. Al doilea scop este acela de a colecta datele transmise prin da pentru a fi re-aplicate obiectului. Pe lângă aspectele prezentate, framework-ul permite multe alte funcționalități care își dovedesc utilitatea de-a lungul dezvoltării unui proiect.

În aceeaşi ediţie ... (46)

▼ TOATE ARTICOLELE ▼

Conferință TSM

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects