SlideShare ist ein Scribd-Unternehmen logo
1 von 130
Downloaden Sie, um offline zu lesen
Samuele Lilli - DonCallisto
Rome, 28 October 2016
A journey into Symfony form component
Samuele Lilli - DonCallisto
WHO AM I?
Samuele Lilli - DonCallisto
Samuele Lilli
DonCallisto
Samuele Lilli - DonCallisto
Samuele Lilli - DonCallisto
Samuele Lilli - DonCallisto
Backend developer @
website: www.madisoft.it
tech blog: labs.madisoft.it
Samuele Lilli - DonCallisto
WE ARE HIRING!
(wanna join? ask us at the end of the talk or visit our website)
Samuele Lilli - DonCallisto
My first talk
Samuele Lilli - DonCallisto
https://joind.in/talk/5a6bd
Samuele Lilli - DonCallisto
FORM COMPONENT
(http://www.freepik.com/free-photos-vectors/smile - Smile vector designed by Freepik)
Samuele Lilli - DonCallisto
FORM COMPONENT
(http://www.freepik.com/free-photos-vectors/smile - Smile vector designed by Freepik)
Samuele Lilli - DonCallisto
FORM COMPONENT
(http://www.freepik.com/free-photos-vectors/smile - Smile vector designed by Freepik)
Samuele Lilli - DonCallisto
FORM COMPONENT
Standalone component (install it via composer/packagist or github)
Samuele Lilli - DonCallisto
FORM COMPONENT
Standalone component (install it via composer/packagist or github)
Provides twig facilities for render labels/fields/errors
Samuele Lilli - DonCallisto
FORM COMPONENT
Standalone component (install it via composer/packagist or github)
Provides twig facilities for render labels/fields/errors
Handling for you data submission (bind to entity if any, validation, data transformations, …)
Samuele Lilli - DonCallisto
FORM COMPONENT
Standalone component (install it via composer/packagist or github)
Provides twig facilities for render labels/fields/errors
Handling for you data submission (bind to entity if any, validation, data transformations, …)
Provides a bunch of built-in types
Samuele Lilli - DonCallisto
SUMMARY
EntityType
CollectionType
Form Data Filtering on Entity / Collection
Form Events
Form Data Types
Data Transformers
Value Objects
Property Path
Samuele Lilli - DonCallisto
ENTITY
TYPE
Samuele Lilli - DonCallisto
Use EntityType when you want list
or “associate” one or more
entities to another
Samuele Lilli - DonCallisto
class Product
{
// ….
/**
* @ORMManyToMany(targetEntity="Category",inversedBy="products")
*/
protected $categories;
class Category
{
// ….
/**
*@ORMManyToMany(targetEntity="Product",mappedBy="categories")
*/
protected $products;
Samuele Lilli - DonCallisto
class Product
{
// ….
/**
* @ORMManyToMany(targetEntity="Category",inversedBy="products")
*/
protected $categories;
class Category
{
// ….
/**
*@ORMManyToMany(targetEntity="Product",mappedBy="categories")
*/
protected $products;
OWNING SIDE
INVERSED SIDE
Samuele Lilli - DonCallisto
class Category
{
// ….
public function addProduct(Product $product)
{
$this->products[] = $product;
return $this;
}
public function removeProduct(Product $product)
{
$this->products->removeElement($product);
}
Samuele Lilli - DonCallisto
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('products', EntityType::class, [
'class' => Product::class,
'multiple' => true,
'required' => false,
]);
}
//….
Samuele Lilli - DonCallisto
Products are not associated to categories
Samuele Lilli - DonCallisto
http://stackoverflow.com/questions/9102063/symfony2-doctrine2-many-to-many-form-not-saving-entities
Samuele Lilli - DonCallisto
http://stackoverflow.com/questions/9102063/symfony2-doctrine2-many-to-many-form-not-saving-entities
Samuele Lilli - DonCallisto
That should not be the right answer
Never bend your needs to software limits (unless strictly necessary)
Doctrine looks only for changes ONLY on the owning side of association, so those
adds will not take place.
Take care yourself for data consistency (during objects lifecycle, ensure that all
references are setted well in both sides). This concept is not ORM-related.
We didn’t add by_reference => false into into FormType.
Samuele Lilli - DonCallisto
That should not be the right answer
Never bend your needs to software limits (unless strictly necessary)
Doctrine looks only for changes ONLY on the owning side of association, so those
adds will not take place.
Take care yourself for data consistency (during objects lifecycle, ensure that all
references are setted well in both sides). This concept is not ORM-related.
We didn’t add by_reference => false into into FormType.
Samuele Lilli - DonCallisto
Data consistency
Class Product
{
// ….
public function addCategory(Category $category)
{
if (!$this->categories->contains($category)) {
$this->categories[] = $category;
$category->addProduct($this);
}
return $this;
}
public function removeCategory(Category $category)
{
if ($this->categories->contains($category)) {
$this->categories->removeElement($category);
$category->removeProduct($this);
}
}
Class Category
{
// ….
public function addProduct(Product $product)
{
if (!$this->products->contains($product)) {
$this->products[] = $product;
$product->addCategory($this);
}
return $this;
}
public function removeProduct(Product $product)
{
if ($this->products->contains($product)) {
$this->products->removeElement($product);
$product->removeCategory($this);
}
}
Samuele Lilli - DonCallisto
This should not be the right answer
Never bend your needs to software limits (unless strictly necessary)
Doctrine looks only for changes ONLY on the owning side of association, so those
adds will not take place. √
Take care yourself for data consistency (during objects lifecycle, ensure that all
references are setted well in both sides). This concept is not ORM-related. √
We didn’t add by_reference => false into into FormType.
Samuele Lilli - DonCallisto
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('products', EntityType::class, [
'class' => Product::class,
'multiple' => true,
'required' => false,
'by_reference' => false,
]);
}
by_refence => false
Samuele Lilli - DonCallisto
WHY BY_REFERENCE => FALSE
It forces setter (adder) to be called on the parent element
As a rule of thumb, set ALWAYS by_reference to false when dealing
with objects (ArrayCollection included)
Samuele Lilli - DonCallisto
BY_REFERENCE => TRUE
Name
Cat.
Name
Name
Name
$cat->getProducts()->get{0}->setName(‘bar’);
$cat->getProducts()->get{1}->setName(‘foobar’);
$product3 = new Product();
$product3->setName();
$cat->getProducts()->add($product3);
$cat->setName(‘foo’);
Samuele Lilli - DonCallisto
BY_REFERENCE => FALSE
Name
Cat.
Name
Name
Name
$cat->getProducts()->get{0}->setName(‘bar’);
$cat->getProducts()->get{1}->setName(‘foobar’);
$product3 = new Product();
$product3>setName();
$cat->addProduct($product3);
$cat->setName(‘foo’);
Samuele Lilli - DonCallisto
COLLECTION
TYPE
Samuele Lilli - DonCallisto
Use CollectionType when you want
to embed a collection of forms or
add/remove directly elements
from collection
Samuele Lilli - DonCallisto
Class User
{
/**
* @ORMOneToMany(targetEntity="Ticket", mappedBy="user", cascade={"persist"})
*/
protected $tickets;
public function addTickets(Ticket $ticket)
{
if (!$this->tickets->contains($ticket)) {
$this->tickets[] = $ticket;
$ticket->setUser($this);
}
return $this;
}
public function removeTicket(Ticket $ticket)
{
$this->tickets->removeElement($ticket);
}
Samuele Lilli - DonCallisto
$builder
->add('username', TextType::class)
->add(tickets, CollectionType::class, [
'entry_type' => TicketType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' => false,
]);
UserType
Samuele Lilli - DonCallisto
Samuele Lilli - DonCallisto
Samuele Lilli - DonCallisto
Element not deleted!
Inthecourtofthecrimsonking-kingcrimson(1969)
Samuele Lilli - DonCallisto
Two possible solutions
“Manually” (programmatically) remove elements
Set orphanRemoval to true on the attribute
If the relationship was ManyToMany and User was
the owning side, no troubles
Samuele Lilli - DonCallisto
“Manually” (programmatically) remove elements
$originalTickets = new ArrayCollection();
foreach ($user->getTickets() as $ticket) {
$originalTickets->add($ticket);
}
if ($this->isFormSubmittedAndValid($form, $request)) {
foreach ($originalTickets as $originalTicket) {
if (!$user->getTickets()->contains($originalTicket)) {
$em->remove($originalTicket);
}
}
}
Samuele Lilli - DonCallisto
“Manually” (programmatically) remove elements
$originalTickets = new ArrayCollection();
foreach ($user->getTickets() as $ticket) {
$originalTickets->add($ticket);
}
if ($this->isFormSubmittedAndValid($form, $request)) {
foreach ($originalTickets as $originalTicket) {
if (!$user->getTickets()->contains($originalTicket)) {
$em->remove($originalTicket);
}
}
}
Samuele Lilli - DonCallisto
Set orphanRemoval to true on the attribute
/**
* @ORMOneToMany(targetEntity="Ticket", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
*/
protected $tickets;
NOT RECOMMENDED
Samuele Lilli - DonCallisto
Set orphanRemoval to true on the attribute
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
Samuele Lilli - DonCallisto
Set orphanRemoval to true on the attribute
public function passTicketsAction(User $yelding, User $beneficiary)
{
foreach ($yelding->getTickets() as $ticket) {
$yelding->removeTicket($ticket);
$beneficiary->addTicket($ticket);
}
}
Samuele Lilli - DonCallisto
Set orphanRemoval to true on the attribute
public function passTicketsAction(User $yelding, User $beneficiary)
{
foreach ($yelding->getTickets() as $ticket) {
$beneficiary->addTicket($ticket);
// OR → $ticket->setUser($beneficiary);
}
}
Samuele Lilli - DonCallisto
FORM DATA
FILTERING
(for entity and collection type)
Samuele Lilli - DonCallisto
EntityType
USE built-in queryBuilder option
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(‘foo’, EntityType::class, [
// ….
‘queryBuilder’ => function (FooRepo $fooRepo) {
return $fooRepo->filterFunction();
}
]);
}
Samuele Lilli - DonCallisto
CollectionType
CollectionType does not have any queryBuilder option
Declare form as a service an inject repository (entity manager)
This is the preferred way if you need the repo
Pass repository as an option
Usually, options are used for what you cannot inject into service
Samuele Lilli - DonCallisto
EntityManager injection
public function __construct(EntityManager $em)
{
$this->fooRepo = $em->getRepository(Foo::class);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(‘foo’, CollectionType::class, [
// ….
'data' => $this->fooRepo->filterFunction()
]);
}
Samuele Lilli - DonCallisto
EntityManager as an option
public function buildForm(FormBuilderInterface $builder, array $options)
{
$fooRepo = $options[‘fooRepo’];
$builder->add(‘foo’, CollectionType::class, [
// ….
'data' => $fooRepo>filterFunction()
]);
}
Samuele Lilli - DonCallisto
Is repository the only way to filter data?
Do I need to create a repository on purpose?
Samuele Lilli - DonCallisto
NO
Samuele Lilli - DonCallisto
What if I need to access entity getters
for filter operations?
Samuele Lilli - DonCallisto
FORM
EVENTS
(overview)
Samuele Lilli - DonCallisto
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) {
/** @var $data User */
$data = $e->getData();
$form = $e->getForm();
$today = new DateTime();
$criteria = Criteria::create()
->where(Criteria::expr()->gte('date', $today));
$form->add('tickets', CollectionType::class, [
// ….
'data' => $data->getTickets()->matching($criteria)
]);
});
});
FORM EVENTS
Samuele Lilli - DonCallisto
/**
* @ORMOneToMany(targetEntity="Ticket", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
*/
protected $tickets;
● All tickets (entities) filtered out in the event will be
removed!
● Remove orphanRemoval option from the attribute and
handle collection yourself
FORM EVENTS
Samuele Lilli - DonCallisto
FORM EVENTS
$form = $this->createForm(UserType::class, $user);
$originalTickets = new ArrayCollection();
foreach ($form->get('tickets')->getData() as $ticket) {
$originalTickets->add($ticket);
}
if ($this->isFormSubmittedAndValid($form, $request)) {
foreach ($originalTickets as $originalTicket) {
if ($user->getTickets()->contains($originalTicket)) {
continue;
}
$em->remove($originalTicket);
}
Samuele Lilli - DonCallisto
FORM EVENTS
$form = $this->createForm(UserType::class, $user);
$originalTickets = new ArrayCollection();
foreach ($form->get('tickets')->getData() as $ticket) {
$originalTickets->add($ticket);
}
if ($this->isFormSubmittedAndValid($form, $request)) {
foreach ($originalTickets as $originalTicket) {
if ($user->getTickets()->contains($originalTicket)) {
continue;
}
$em->remove($originalTicket);
}
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
RENDER
FORM CREATION
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
RENDER
FORM CREATION
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
RENDER
FORM CREATION
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
RENDER
FORM CREATION
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
RENDER
FORM CREATION
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
RENDER
FORM CREATION
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
PRE_SET_DATA POST_SET_DATA
CONTROLLER
VIEW
NEW FORM
BIND
FORM POST
POST
PRE_SUBMIT SUBMIT POST_SUBMIT
IS VALID
PERSIST
Samuele Lilli - DonCallisto
TIP
Every group of events it’s called from START to
END on every FORM. This means that if you
have a chain of embedded form, all events are
called starting from innermost forms, going up
to parent form, ending on top form
Samuele Lilli - DonCallisto
BECAUSE OF THIS
Samuele Lilli - DonCallisto
NEVER SET
PARENT DATA
FROM CHILD FORM
Samuele Lilli - DonCallisto
EVER!
Samuele Lilli - DonCallisto
DATA TYPES
Samuele Lilli - DonCallisto
DATA TYPES
MODEL DATA
NORM DATA
VIEW DATA
Samuele Lilli - DonCallisto
MODEL DATA
Main data type of PRE_SET_DATA and POST_SET_DATA
Reppresent data of underlying object. In previous example with
product form, product field model data is a Product object.
If field type is the same of underlying data, NORM DATA will be the
same of MODEL DATA
If field type is not the same of underlying data, you must use
ModelTransformer to transform MODEL DATA into NORM DATA
and vice versa (Don’t worry, we will talk about transformers next!)
Samuele Lilli - DonCallisto
NORM DATA
Main data type of SUBMIT event
Reppresent data after normalization. Commonly not used directly.
If on MODEL DATA is not present any ModelTransform, this is the
same of MODEL DATA and so the same of underlying object.
If NORM DATA isn’t the same of view reppresentation, you must use
ViewTransformer to transform NORM DATA into VIEW DATA and
vice versa
Samuele Lilli - DonCallisto
VIEW DATA
Main data type of POST_SUBMIT event
Reppresent data presented to the View. It’s the data type that you
get when you post the form.
If on VIEW DATA is not present any ViewTransformer, this is the
same of NORM DATA.
Samuele Lilli - DonCallisto
DATA
TRANSFORMERS
Samuele Lilli - DonCallisto
MODEL
TRANSFORMER
Samuele Lilli - DonCallisto
ENTITY
MODEL DATA NORM DATA
MODEL TRANSFORMER
NEW FORM BIND
TRANSFORM
REVERSE
TRANSFORM
Samuele Lilli - DonCallisto
ENTITY
MODEL DATA NORM DATA
NEW FORM BIND
TRANSFORM
REVERSE
TRANSFORM
MODEL TRANSFORMER
Samuele Lilli - DonCallisto
ENTITY - MODEL DATA
/**
* @ORMColumn(type="array", nullable=true)
*/
protected $tags;
Samuele Lilli - DonCallisto
ENTITY
MODEL DATA NORM DATA
NEW FORM BIND
TRANSFORM
REVERSE
TRANSFORM
MODEL TRANSFORMER
Samuele Lilli - DonCallisto
FORM FIELD
$builder->add('tags', TextType::class)
Samuele Lilli - DonCallisto
Since model data (array) is
different from norm data (text)
we need a model transformer
Samuele Lilli - DonCallisto
$builder->add('tags', TextType::class);
$builder->get(‘tags’)->addModelTransformer(...);
FORM FIELD
Samuele Lilli - DonCallisto
ENTITY
MODEL DATA NORM DATA
NEW FORM BIND
TRANSFORM
REVERSE
TRANSFORM
MODEL TRANSFORMER
Samuele Lilli - DonCallisto
TRANSFORM
public function transform($tagsArray)
{
// transform the array to string
if (null === $tagsArray) {
return '';
}
return implode(',', $tagsArray);
}
Samuele Lilli - DonCallisto
ENTITY
MODEL DATA NORM DATA
NEW FORM BIND
TRANSFORM
REVERSE
TRANSFORM
MODEL TRANSFORMER
Samuele Lilli - DonCallisto
public function reverseTransform($tagsString)
{
// transform the string back to an array
if (!$tagsString) {
return [];
}
return explode(',', $tagsString);
}
REVERSE TRANSFORM
Samuele Lilli - DonCallisto
VIEW
TRANSFORMER
Samuele Lilli - DonCallisto
VIEW
NORM DATA VIEW DATA
CREATE VIEWPOST
TRANSFORM
REVERSE
TRANSFORM
VIEW TRANSFORMER
Samuele Lilli - DonCallisto
DATE TYPE (TRANSFORM)
// Transforms a normalized date into a localized date string/array)
public function transform($dateTime)
{
if (null === $dateTime) {
return '';
}
if (!$dateTime instanceof DateTimeInterface) {
throw new TransformationFailedException('Expected a DateTimeInterface.');
}
$value = $this->getIntlDateFormatter()->format($dateTime->getTimestamp());
if (intl_get_error_code() != 0) {
throw new TransformationFailedException(intl_get_error_message());
}
return $value;
}
Samuele Lilli - DonCallisto
// Transforms a localized date string/array into a normalized date.
public function reverseTransform($value)
{
if (!is_string($value)) {
throw new TransformationFailedException('Expected a string.');
}
if ('' === $value) {
return;
}
$timestamp = $this->getIntlDateFormatter()->parse($value);
// ….
try {
$dateTime = new DateTime(sprintf('@%s', $timestamp));
} catch (Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
// ….
return $dateTime;
}
DATE TYPE ( REVERSE TRANSFORM)
Samuele Lilli - DonCallisto
FORM
EVENTS
(reprise)
Samuele Lilli - DonCallisto
PRE SET DATA EVENT
Modify data given during pre-population.
Don’t modify form data directly but modify event data instead.
Add/Remove form fields
USED FOR
EVENT DATA
MODEL DATA
Samuele Lilli - DonCallisto
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) {
/** @var $data User */
$data = $e->getData(); // ← MODEL DATA
$form = $e->getForm();
$today = new DateTime();
$criteria = Criteria::create()
->where(Criteria::expr()->gte('date', $today));
$form->add(‘tickets’, CollectionType::class, [
// ….
'data' => $data->getTickets()->matching($criteria)
]);
});
});
PRE_SET_DATA
Samuele Lilli - DonCallisto
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) {
if ($e->getData()->canHandleMail()) { // ← MODEL DATA
$e->getForm()->add(‘email’, EmailType::class);
}
});
});
PRE_SET_DATA
Samuele Lilli - DonCallisto
Read pre-populated form data
Don’t remove fields that you’ve setted “statically” on form
building process. Use PRE_SET_DATA and implement the logic
about fields. One exception: you are extending from a parent
form where you cannot control yourself the logic.
USED FOR
EVENT DATA
MODEL DATA
POST SET DATA EVENT
Samuele Lilli - DonCallisto
class FooType extends BarType
{
// ….
// email field added in BarType
->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $e) {
if (!$e->getData()->canHandleMail()) { // ← MODEL DATA
$e->getForm()->remove(‘email’);
}
});
});
POST_SET_DATA
Samuele Lilli - DonCallisto
Change data from the request
Add/Remove form fields
USED FOR
EVENT DATA
REQUEST DATA (ARRAY)
PRE SUBMIT EVENT
Samuele Lilli - DonCallisto
private $canModifyEmail = true;
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) {
$this->canModifyEmail = $e->getData()->canModifyEmail();
$e->getForm()->add(‘email, EmailType::class, [
// ….
‘attr’ => [ ‘disabled’ => !$this->canModifyEmail ],
]);
})
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $e) {
if (!$this->canModifyEmail) {
$e->getForm()->remove(‘email’);
}
});
});
PRE_SUBMIT
Samuele Lilli - DonCallisto
Modify NormData
No real example will be showed
EVENT DATA
NORM DATA
SUBMIT EVENT
USED FOR
Samuele Lilli - DonCallisto
Fetch data after denormalization
Even if faster to read “final” data here, don’t implement any
business logic → hard to test and break SRP.
If you need to modify model data, do it elsewhere just after isValid
call.
USED FOR
EVENT DATA
VIEW DATA
POST SUBMIT EVENT
Samuele Lilli - DonCallisto
POST SUBMIT
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($handler) {
$delete = $event->getForm()->has('delete') ? $event->getForm()->get('delete')->getData() : false;
$entity = $event->getForm()->getParent()->getData();
if (!$delete) {
return;
}
$handler->remove($entity, $event->getForm()->getName());
});
Vichuploader-bundle
Samuele Lilli - DonCallisto
VALUE
OBJECTS
Samuele Lilli - DonCallisto
Two object are equal if their
all fields are equal, not
necessary if they are the
same object
Samuele Lilli - DonCallisto
NO SETTERS
Samuele Lilli - DonCallisto
CONSTRUCTOR
Samuele Lilli - DonCallisto
Class FooBar
{
private $foo;
private $bar;
public function __construct($foo, $bar)
{
$this->foo = $foo;
$this->bar = $bar;
}
public function getFoo(){ … }
public function getBar(){ … }
}
Samuele Lilli - DonCallisto
Class FooBarType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(‘foo’)
->add(‘bar’);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
‘empty_data’ => function (FormInterface $interface) {
return new FooBar($interface->get(‘foo’)->getData(), $interface->get(‘bar’)->getData());
},
]);
}
}
Samuele Lilli - DonCallisto
PROPERTY
PATH
Samuele Lilli - DonCallisto
Class User
{
//….
/*
* @ORMOneToOne(targetEntity=”Address”, inversedBy=”user”)
*/
protected $address;
//….
}
Samuele Lilli - DonCallisto
Class Address
{
//….
/**
* @ORMOneToOne(targetEntity=”User”, mappedBy=”address”)
*/
protected $user;
/**
* @ORMColumn(type=”string”)
*/
protected $street;
/**
* @ORMColumn(type=”string”)
*/
protected $number;
}
Samuele Lilli - DonCallisto
DON’T CREATE AN
EMBEDDED FORM ON
PURPOSE
Samuele Lilli - DonCallisto
DON’T USE
UNMAPPED FIELDS
AND FORM EVENTS
Samuele Lilli - DonCallisto
USE PROPERTY PATH!
Samuele Lilli - DonCallisto
Class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ….
$builder
->add(‘street’, TextType::class, [‘property_path’ => ‘address.street’])
->add(‘number’, TextType::class, [‘property_path’ => ‘address.number’]);
}
}
QUESTIONS?
Samuele Lilli - DonCallisto
THANK YOU

Weitere ähnliche Inhalte

Was ist angesagt?

WordPress 3.1 at DC PHP
WordPress 3.1 at DC PHPWordPress 3.1 at DC PHP
WordPress 3.1 at DC PHPandrewnacin
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuerysergioafp
 
Exemple de création de base
Exemple de création de baseExemple de création de base
Exemple de création de baseSaber LAJILI
 
Biglietti, prego! A ticket for the (command) bus
Biglietti, prego! A ticket for the (command) busBiglietti, prego! A ticket for the (command) bus
Biglietti, prego! A ticket for the (command) busFrancesco Face
 
Drupal is Stupid (But I Love It Anyway)
Drupal is Stupid (But I Love It Anyway)Drupal is Stupid (But I Love It Anyway)
Drupal is Stupid (But I Love It Anyway)brockboland
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05cKaz Yoshikawa
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Templatemussawir20
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Templateguest48224
 
Laravel collections an overview - Laravel SP
Laravel collections an overview - Laravel SPLaravel collections an overview - Laravel SP
Laravel collections an overview - Laravel SPMatheus Marabesi
 

Was ist angesagt? (10)

WordPress 3.1 at DC PHP
WordPress 3.1 at DC PHPWordPress 3.1 at DC PHP
WordPress 3.1 at DC PHP
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuery
 
Amp Up Your Admin
Amp Up Your AdminAmp Up Your Admin
Amp Up Your Admin
 
Exemple de création de base
Exemple de création de baseExemple de création de base
Exemple de création de base
 
Biglietti, prego! A ticket for the (command) bus
Biglietti, prego! A ticket for the (command) busBiglietti, prego! A ticket for the (command) bus
Biglietti, prego! A ticket for the (command) bus
 
Drupal is Stupid (But I Love It Anyway)
Drupal is Stupid (But I Love It Anyway)Drupal is Stupid (But I Love It Anyway)
Drupal is Stupid (But I Love It Anyway)
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05c
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Template
 
Smarty Template
Smarty TemplateSmarty Template
Smarty Template
 
Laravel collections an overview - Laravel SP
Laravel collections an overview - Laravel SPLaravel collections an overview - Laravel SP
Laravel collections an overview - Laravel SP
 

Andere mochten auch

Multi kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkerneleMulti kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkerneleRadek Baczynski
 
Dev traning 2016 symfony
Dev traning 2016   symfonyDev traning 2016   symfony
Dev traning 2016 symfonySacheen Dhanjie
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Ignacio Martín
 
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...Alexander Lisachenko
 
Laporan observasi rpp dan laboratorium
Laporan observasi rpp dan laboratoriumLaporan observasi rpp dan laboratorium
Laporan observasi rpp dan laboratoriumSamantars17
 
Exposicion de financiamientos
Exposicion de financiamientosExposicion de financiamientos
Exposicion de financiamientosAlita Orpe
 
εργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασης
εργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασηςεργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασης
εργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασηςπεντάλ σχολικό
 
Understanding Android Handling of Touch Events
Understanding Android Handling of Touch EventsUnderstanding Android Handling of Touch Events
Understanding Android Handling of Touch Eventsjensmohr
 
Mobile note mobile by MAdvertise et Bemobee
Mobile note mobile by MAdvertise et BemobeeMobile note mobile by MAdvertise et Bemobee
Mobile note mobile by MAdvertise et BemobeeFranck Deville
 
(151105)영화속 와인 이야기 v2
(151105)영화속 와인 이야기   v2(151105)영화속 와인 이야기   v2
(151105)영화속 와인 이야기 v2휘웅 정
 
Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...
Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...
Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...Unmetric
 
Nuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWSNuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWSMatteo Moretti
 

Andere mochten auch (20)

Multi kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkerneleMulti kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkernele
 
From * to Symfony2
From * to Symfony2From * to Symfony2
From * to Symfony2
 
Dev traning 2016 symfony
Dev traning 2016   symfonyDev traning 2016   symfony
Dev traning 2016 symfony
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
 
SaaS con Symfony2
SaaS con Symfony2SaaS con Symfony2
SaaS con Symfony2
 
Scaling symfony apps
Scaling symfony appsScaling symfony apps
Scaling symfony apps
 
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
 
Clases de noviembre 5 ños 1
Clases de noviembre 5 ños 1Clases de noviembre 5 ños 1
Clases de noviembre 5 ños 1
 
Baby talk 2
Baby talk 2Baby talk 2
Baby talk 2
 
Laporan observasi rpp dan laboratorium
Laporan observasi rpp dan laboratoriumLaporan observasi rpp dan laboratorium
Laporan observasi rpp dan laboratorium
 
resume
resumeresume
resume
 
Great quotes of bruce lee
Great quotes of bruce leeGreat quotes of bruce lee
Great quotes of bruce lee
 
Exposicion de financiamientos
Exposicion de financiamientosExposicion de financiamientos
Exposicion de financiamientos
 
εργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασης
εργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασηςεργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασης
εργασία: κριτήρια επιλογής, συνέπειες βιομηχανικής επανάστασης
 
Understanding Android Handling of Touch Events
Understanding Android Handling of Touch EventsUnderstanding Android Handling of Touch Events
Understanding Android Handling of Touch Events
 
Mobile note mobile by MAdvertise et Bemobee
Mobile note mobile by MAdvertise et BemobeeMobile note mobile by MAdvertise et Bemobee
Mobile note mobile by MAdvertise et Bemobee
 
(151105)영화속 와인 이야기 v2
(151105)영화속 와인 이야기   v2(151105)영화속 와인 이야기   v2
(151105)영화속 와인 이야기 v2
 
Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...
Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...
Comparison of Jacob's Creek, Rosemount Estate, McGuigan Wines, Lindeman's and...
 
Nuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWSNuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWS
 
Conductores electricos
Conductores electricosConductores electricos
Conductores electricos
 

Ähnlich wie Symfony day 2016

Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusIgnacio Martín
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof MenżykPROIDEA
 
Single Page Applications in Angular (italiano)
Single Page Applications in Angular (italiano)Single Page Applications in Angular (italiano)
Single Page Applications in Angular (italiano)Fabio Biondi
 
Lecture 17 - PHP-Object-Orientation.pptx
Lecture 17 - PHP-Object-Orientation.pptxLecture 17 - PHP-Object-Orientation.pptx
Lecture 17 - PHP-Object-Orientation.pptxDavidLazar17
 
[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...
[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...
[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...Mateusz Zalewski
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonKris Wallsmith
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsŁukasz Chruściel
 
Introduction to Clean Code
Introduction to Clean CodeIntroduction to Clean Code
Introduction to Clean CodeJulio Martinez
 
What is the difference between a good and a bad repository? (Forum PHP 2018)
What is the difference between a good and a bad repository? (Forum PHP 2018)What is the difference between a good and a bad repository? (Forum PHP 2018)
What is the difference between a good and a bad repository? (Forum PHP 2018)Arnaud Langlade
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form componentSamuel ROZE
 

Ähnlich wie Symfony day 2016 (20)

Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - Limenius
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
 
Be pragmatic, be SOLID
Be pragmatic, be SOLIDBe pragmatic, be SOLID
Be pragmatic, be SOLID
 
Single Page Applications in Angular (italiano)
Single Page Applications in Angular (italiano)Single Page Applications in Angular (italiano)
Single Page Applications in Angular (italiano)
 
Value objects
Value objectsValue objects
Value objects
 
Build your own entity with Drupal
Build your own entity with DrupalBuild your own entity with Drupal
Build your own entity with Drupal
 
Lecture 17 - PHP-Object-Orientation.pptx
Lecture 17 - PHP-Object-Orientation.pptxLecture 17 - PHP-Object-Orientation.pptx
Lecture 17 - PHP-Object-Orientation.pptx
 
[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...
[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...
[ForumPHP 2023] Lights and shadows of BDD in Sylius (and probably other compa...
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-london
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
 
Introduction to Clean Code
Introduction to Clean CodeIntroduction to Clean Code
Introduction to Clean Code
 
What is the difference between a good and a bad repository? (Forum PHP 2018)
What is the difference between a good and a bad repository? (Forum PHP 2018)What is the difference between a good and a bad repository? (Forum PHP 2018)
What is the difference between a good and a bad repository? (Forum PHP 2018)
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 

Kürzlich hochgeladen

Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort ServiceEnjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort ServiceDelhi Call girls
 
Russian Call girl in Ajman +971563133746 Ajman Call girl Service
Russian Call girl in Ajman +971563133746 Ajman Call girl ServiceRussian Call girl in Ajman +971563133746 Ajman Call girl Service
Russian Call girl in Ajman +971563133746 Ajman Call girl Servicegwenoracqe6
 
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...roncy bisnoi
 
APNIC Updates presented by Paul Wilson at ARIN 53
APNIC Updates presented by Paul Wilson at ARIN 53APNIC Updates presented by Paul Wilson at ARIN 53
APNIC Updates presented by Paul Wilson at ARIN 53APNIC
 
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...SUHANI PANDEY
 
Moving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providersMoving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providersDamian Radcliffe
 
Enjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort ServiceEnjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort ServiceDelhi Call girls
 
Nanded City ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready ...
Nanded City ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready ...Nanded City ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready ...
Nanded City ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready ...tanu pandey
 
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...nilamkumrai
 
(+971568250507 ))# Young Call Girls in Ajman By Pakistani Call Girls in ...
(+971568250507  ))#  Young Call Girls  in Ajman  By Pakistani Call Girls  in ...(+971568250507  ))#  Young Call Girls  in Ajman  By Pakistani Call Girls  in ...
(+971568250507 ))# Young Call Girls in Ajman By Pakistani Call Girls in ...Escorts Call Girls
 
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
2nd Solid Symposium: Solid Pods vs Personal Knowledge GraphsEleniIlkou
 
Al Barsha Night Partner +0567686026 Call Girls Dubai
Al Barsha Night Partner +0567686026 Call Girls  DubaiAl Barsha Night Partner +0567686026 Call Girls  Dubai
Al Barsha Night Partner +0567686026 Call Girls DubaiEscorts Call Girls
 
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...Neha Pandey
 
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Bookingdharasingh5698
 
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...SUHANI PANDEY
 
Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.
Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.
Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.soniya singh
 

Kürzlich hochgeladen (20)

Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort ServiceEnjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
 
Russian Call girl in Ajman +971563133746 Ajman Call girl Service
Russian Call girl in Ajman +971563133746 Ajman Call girl ServiceRussian Call girl in Ajman +971563133746 Ajman Call girl Service
Russian Call girl in Ajman +971563133746 Ajman Call girl Service
 
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
 
APNIC Updates presented by Paul Wilson at ARIN 53
APNIC Updates presented by Paul Wilson at ARIN 53APNIC Updates presented by Paul Wilson at ARIN 53
APNIC Updates presented by Paul Wilson at ARIN 53
 
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
 
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
 
Russian Call Girls in %(+971524965298 )# Call Girls in Dubai
Russian Call Girls in %(+971524965298  )#  Call Girls in DubaiRussian Call Girls in %(+971524965298  )#  Call Girls in Dubai
Russian Call Girls in %(+971524965298 )# Call Girls in Dubai
 
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
 
Moving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providersMoving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providers
 
Enjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort ServiceEnjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Samalka Delhi >༒8448380779 Escort Service
 
Nanded City ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready ...
Nanded City ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready ...Nanded City ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready ...
Nanded City ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready ...
 
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
 
(+971568250507 ))# Young Call Girls in Ajman By Pakistani Call Girls in ...
(+971568250507  ))#  Young Call Girls  in Ajman  By Pakistani Call Girls  in ...(+971568250507  ))#  Young Call Girls  in Ajman  By Pakistani Call Girls  in ...
(+971568250507 ))# Young Call Girls in Ajman By Pakistani Call Girls in ...
 
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
 
Al Barsha Night Partner +0567686026 Call Girls Dubai
Al Barsha Night Partner +0567686026 Call Girls  DubaiAl Barsha Night Partner +0567686026 Call Girls  Dubai
Al Barsha Night Partner +0567686026 Call Girls Dubai
 
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
 
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
 
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
 
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
 
Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.
Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.
Call Now ☎ 8264348440 !! Call Girls in Green Park Escort Service Delhi N.C.R.
 

Symfony day 2016

  • 1. Samuele Lilli - DonCallisto Rome, 28 October 2016 A journey into Symfony form component
  • 2. Samuele Lilli - DonCallisto WHO AM I?
  • 3. Samuele Lilli - DonCallisto Samuele Lilli DonCallisto
  • 4. Samuele Lilli - DonCallisto
  • 5. Samuele Lilli - DonCallisto
  • 6. Samuele Lilli - DonCallisto Backend developer @ website: www.madisoft.it tech blog: labs.madisoft.it
  • 7. Samuele Lilli - DonCallisto WE ARE HIRING! (wanna join? ask us at the end of the talk or visit our website)
  • 8. Samuele Lilli - DonCallisto My first talk
  • 9. Samuele Lilli - DonCallisto https://joind.in/talk/5a6bd
  • 10. Samuele Lilli - DonCallisto FORM COMPONENT (http://www.freepik.com/free-photos-vectors/smile - Smile vector designed by Freepik)
  • 11. Samuele Lilli - DonCallisto FORM COMPONENT (http://www.freepik.com/free-photos-vectors/smile - Smile vector designed by Freepik)
  • 12. Samuele Lilli - DonCallisto FORM COMPONENT (http://www.freepik.com/free-photos-vectors/smile - Smile vector designed by Freepik)
  • 13. Samuele Lilli - DonCallisto FORM COMPONENT Standalone component (install it via composer/packagist or github)
  • 14. Samuele Lilli - DonCallisto FORM COMPONENT Standalone component (install it via composer/packagist or github) Provides twig facilities for render labels/fields/errors
  • 15. Samuele Lilli - DonCallisto FORM COMPONENT Standalone component (install it via composer/packagist or github) Provides twig facilities for render labels/fields/errors Handling for you data submission (bind to entity if any, validation, data transformations, …)
  • 16. Samuele Lilli - DonCallisto FORM COMPONENT Standalone component (install it via composer/packagist or github) Provides twig facilities for render labels/fields/errors Handling for you data submission (bind to entity if any, validation, data transformations, …) Provides a bunch of built-in types
  • 17. Samuele Lilli - DonCallisto SUMMARY EntityType CollectionType Form Data Filtering on Entity / Collection Form Events Form Data Types Data Transformers Value Objects Property Path
  • 18. Samuele Lilli - DonCallisto ENTITY TYPE
  • 19. Samuele Lilli - DonCallisto Use EntityType when you want list or “associate” one or more entities to another
  • 20. Samuele Lilli - DonCallisto class Product { // …. /** * @ORMManyToMany(targetEntity="Category",inversedBy="products") */ protected $categories; class Category { // …. /** *@ORMManyToMany(targetEntity="Product",mappedBy="categories") */ protected $products;
  • 21. Samuele Lilli - DonCallisto class Product { // …. /** * @ORMManyToMany(targetEntity="Category",inversedBy="products") */ protected $categories; class Category { // …. /** *@ORMManyToMany(targetEntity="Product",mappedBy="categories") */ protected $products; OWNING SIDE INVERSED SIDE
  • 22. Samuele Lilli - DonCallisto class Category { // …. public function addProduct(Product $product) { $this->products[] = $product; return $this; } public function removeProduct(Product $product) { $this->products->removeElement($product); }
  • 23. Samuele Lilli - DonCallisto class CategoryType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name', TextType::class) ->add('products', EntityType::class, [ 'class' => Product::class, 'multiple' => true, 'required' => false, ]); } //….
  • 24. Samuele Lilli - DonCallisto Products are not associated to categories
  • 25. Samuele Lilli - DonCallisto http://stackoverflow.com/questions/9102063/symfony2-doctrine2-many-to-many-form-not-saving-entities
  • 26. Samuele Lilli - DonCallisto http://stackoverflow.com/questions/9102063/symfony2-doctrine2-many-to-many-form-not-saving-entities
  • 27. Samuele Lilli - DonCallisto That should not be the right answer Never bend your needs to software limits (unless strictly necessary) Doctrine looks only for changes ONLY on the owning side of association, so those adds will not take place. Take care yourself for data consistency (during objects lifecycle, ensure that all references are setted well in both sides). This concept is not ORM-related. We didn’t add by_reference => false into into FormType.
  • 28. Samuele Lilli - DonCallisto That should not be the right answer Never bend your needs to software limits (unless strictly necessary) Doctrine looks only for changes ONLY on the owning side of association, so those adds will not take place. Take care yourself for data consistency (during objects lifecycle, ensure that all references are setted well in both sides). This concept is not ORM-related. We didn’t add by_reference => false into into FormType.
  • 29. Samuele Lilli - DonCallisto Data consistency Class Product { // …. public function addCategory(Category $category) { if (!$this->categories->contains($category)) { $this->categories[] = $category; $category->addProduct($this); } return $this; } public function removeCategory(Category $category) { if ($this->categories->contains($category)) { $this->categories->removeElement($category); $category->removeProduct($this); } } Class Category { // …. public function addProduct(Product $product) { if (!$this->products->contains($product)) { $this->products[] = $product; $product->addCategory($this); } return $this; } public function removeProduct(Product $product) { if ($this->products->contains($product)) { $this->products->removeElement($product); $product->removeCategory($this); } }
  • 30. Samuele Lilli - DonCallisto This should not be the right answer Never bend your needs to software limits (unless strictly necessary) Doctrine looks only for changes ONLY on the owning side of association, so those adds will not take place. √ Take care yourself for data consistency (during objects lifecycle, ensure that all references are setted well in both sides). This concept is not ORM-related. √ We didn’t add by_reference => false into into FormType.
  • 31. Samuele Lilli - DonCallisto public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name', TextType::class) ->add('products', EntityType::class, [ 'class' => Product::class, 'multiple' => true, 'required' => false, 'by_reference' => false, ]); } by_refence => false
  • 32. Samuele Lilli - DonCallisto WHY BY_REFERENCE => FALSE It forces setter (adder) to be called on the parent element As a rule of thumb, set ALWAYS by_reference to false when dealing with objects (ArrayCollection included)
  • 33. Samuele Lilli - DonCallisto BY_REFERENCE => TRUE Name Cat. Name Name Name $cat->getProducts()->get{0}->setName(‘bar’); $cat->getProducts()->get{1}->setName(‘foobar’); $product3 = new Product(); $product3->setName(); $cat->getProducts()->add($product3); $cat->setName(‘foo’);
  • 34. Samuele Lilli - DonCallisto BY_REFERENCE => FALSE Name Cat. Name Name Name $cat->getProducts()->get{0}->setName(‘bar’); $cat->getProducts()->get{1}->setName(‘foobar’); $product3 = new Product(); $product3>setName(); $cat->addProduct($product3); $cat->setName(‘foo’);
  • 35. Samuele Lilli - DonCallisto COLLECTION TYPE
  • 36. Samuele Lilli - DonCallisto Use CollectionType when you want to embed a collection of forms or add/remove directly elements from collection
  • 37. Samuele Lilli - DonCallisto Class User { /** * @ORMOneToMany(targetEntity="Ticket", mappedBy="user", cascade={"persist"}) */ protected $tickets; public function addTickets(Ticket $ticket) { if (!$this->tickets->contains($ticket)) { $this->tickets[] = $ticket; $ticket->setUser($this); } return $this; } public function removeTicket(Ticket $ticket) { $this->tickets->removeElement($ticket); }
  • 38. Samuele Lilli - DonCallisto $builder ->add('username', TextType::class) ->add(tickets, CollectionType::class, [ 'entry_type' => TicketType::class, 'allow_add' => true, 'allow_delete' => true, 'prototype' => true, 'by_reference' => false, ]); UserType
  • 39. Samuele Lilli - DonCallisto
  • 40. Samuele Lilli - DonCallisto
  • 41. Samuele Lilli - DonCallisto Element not deleted!
  • 43. Samuele Lilli - DonCallisto Two possible solutions “Manually” (programmatically) remove elements Set orphanRemoval to true on the attribute If the relationship was ManyToMany and User was the owning side, no troubles
  • 44. Samuele Lilli - DonCallisto “Manually” (programmatically) remove elements $originalTickets = new ArrayCollection(); foreach ($user->getTickets() as $ticket) { $originalTickets->add($ticket); } if ($this->isFormSubmittedAndValid($form, $request)) { foreach ($originalTickets as $originalTicket) { if (!$user->getTickets()->contains($originalTicket)) { $em->remove($originalTicket); } } }
  • 45. Samuele Lilli - DonCallisto “Manually” (programmatically) remove elements $originalTickets = new ArrayCollection(); foreach ($user->getTickets() as $ticket) { $originalTickets->add($ticket); } if ($this->isFormSubmittedAndValid($form, $request)) { foreach ($originalTickets as $originalTicket) { if (!$user->getTickets()->contains($originalTicket)) { $em->remove($originalTicket); } } }
  • 46. Samuele Lilli - DonCallisto Set orphanRemoval to true on the attribute /** * @ORMOneToMany(targetEntity="Ticket", mappedBy="user", cascade={"persist"}, orphanRemoval=true) */ protected $tickets; NOT RECOMMENDED
  • 47. Samuele Lilli - DonCallisto Set orphanRemoval to true on the attribute http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
  • 48. Samuele Lilli - DonCallisto Set orphanRemoval to true on the attribute public function passTicketsAction(User $yelding, User $beneficiary) { foreach ($yelding->getTickets() as $ticket) { $yelding->removeTicket($ticket); $beneficiary->addTicket($ticket); } }
  • 49. Samuele Lilli - DonCallisto Set orphanRemoval to true on the attribute public function passTicketsAction(User $yelding, User $beneficiary) { foreach ($yelding->getTickets() as $ticket) { $beneficiary->addTicket($ticket); // OR → $ticket->setUser($beneficiary); } }
  • 50. Samuele Lilli - DonCallisto FORM DATA FILTERING (for entity and collection type)
  • 51. Samuele Lilli - DonCallisto EntityType USE built-in queryBuilder option public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add(‘foo’, EntityType::class, [ // …. ‘queryBuilder’ => function (FooRepo $fooRepo) { return $fooRepo->filterFunction(); } ]); }
  • 52. Samuele Lilli - DonCallisto CollectionType CollectionType does not have any queryBuilder option Declare form as a service an inject repository (entity manager) This is the preferred way if you need the repo Pass repository as an option Usually, options are used for what you cannot inject into service
  • 53. Samuele Lilli - DonCallisto EntityManager injection public function __construct(EntityManager $em) { $this->fooRepo = $em->getRepository(Foo::class); } public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add(‘foo’, CollectionType::class, [ // …. 'data' => $this->fooRepo->filterFunction() ]); }
  • 54. Samuele Lilli - DonCallisto EntityManager as an option public function buildForm(FormBuilderInterface $builder, array $options) { $fooRepo = $options[‘fooRepo’]; $builder->add(‘foo’, CollectionType::class, [ // …. 'data' => $fooRepo>filterFunction() ]); }
  • 55. Samuele Lilli - DonCallisto Is repository the only way to filter data? Do I need to create a repository on purpose?
  • 56. Samuele Lilli - DonCallisto NO
  • 57. Samuele Lilli - DonCallisto What if I need to access entity getters for filter operations?
  • 58. Samuele Lilli - DonCallisto FORM EVENTS (overview)
  • 59. Samuele Lilli - DonCallisto ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) { /** @var $data User */ $data = $e->getData(); $form = $e->getForm(); $today = new DateTime(); $criteria = Criteria::create() ->where(Criteria::expr()->gte('date', $today)); $form->add('tickets', CollectionType::class, [ // …. 'data' => $data->getTickets()->matching($criteria) ]); }); }); FORM EVENTS
  • 60. Samuele Lilli - DonCallisto /** * @ORMOneToMany(targetEntity="Ticket", mappedBy="user", cascade={"persist"}, orphanRemoval=true) */ protected $tickets; ● All tickets (entities) filtered out in the event will be removed! ● Remove orphanRemoval option from the attribute and handle collection yourself FORM EVENTS
  • 61. Samuele Lilli - DonCallisto FORM EVENTS $form = $this->createForm(UserType::class, $user); $originalTickets = new ArrayCollection(); foreach ($form->get('tickets')->getData() as $ticket) { $originalTickets->add($ticket); } if ($this->isFormSubmittedAndValid($form, $request)) { foreach ($originalTickets as $originalTicket) { if ($user->getTickets()->contains($originalTicket)) { continue; } $em->remove($originalTicket); }
  • 62. Samuele Lilli - DonCallisto FORM EVENTS $form = $this->createForm(UserType::class, $user); $originalTickets = new ArrayCollection(); foreach ($form->get('tickets')->getData() as $ticket) { $originalTickets->add($ticket); } if ($this->isFormSubmittedAndValid($form, $request)) { foreach ($originalTickets as $originalTicket) { if ($user->getTickets()->contains($originalTicket)) { continue; } $em->remove($originalTicket); }
  • 63. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM RENDER FORM CREATION
  • 64. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM RENDER FORM CREATION
  • 65. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM RENDER FORM CREATION
  • 66. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM RENDER FORM CREATION
  • 67. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM RENDER FORM CREATION
  • 68. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM RENDER FORM CREATION
  • 69. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 70. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 71. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 72. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 73. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 74. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 75. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 76. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 77. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 78. Samuele Lilli - DonCallisto PRE_SET_DATA POST_SET_DATA CONTROLLER VIEW NEW FORM BIND FORM POST POST PRE_SUBMIT SUBMIT POST_SUBMIT IS VALID PERSIST
  • 79. Samuele Lilli - DonCallisto TIP Every group of events it’s called from START to END on every FORM. This means that if you have a chain of embedded form, all events are called starting from innermost forms, going up to parent form, ending on top form
  • 80. Samuele Lilli - DonCallisto BECAUSE OF THIS
  • 81. Samuele Lilli - DonCallisto NEVER SET PARENT DATA FROM CHILD FORM
  • 82. Samuele Lilli - DonCallisto EVER!
  • 83. Samuele Lilli - DonCallisto DATA TYPES
  • 84. Samuele Lilli - DonCallisto DATA TYPES MODEL DATA NORM DATA VIEW DATA
  • 85. Samuele Lilli - DonCallisto MODEL DATA Main data type of PRE_SET_DATA and POST_SET_DATA Reppresent data of underlying object. In previous example with product form, product field model data is a Product object. If field type is the same of underlying data, NORM DATA will be the same of MODEL DATA If field type is not the same of underlying data, you must use ModelTransformer to transform MODEL DATA into NORM DATA and vice versa (Don’t worry, we will talk about transformers next!)
  • 86. Samuele Lilli - DonCallisto NORM DATA Main data type of SUBMIT event Reppresent data after normalization. Commonly not used directly. If on MODEL DATA is not present any ModelTransform, this is the same of MODEL DATA and so the same of underlying object. If NORM DATA isn’t the same of view reppresentation, you must use ViewTransformer to transform NORM DATA into VIEW DATA and vice versa
  • 87. Samuele Lilli - DonCallisto VIEW DATA Main data type of POST_SUBMIT event Reppresent data presented to the View. It’s the data type that you get when you post the form. If on VIEW DATA is not present any ViewTransformer, this is the same of NORM DATA.
  • 88. Samuele Lilli - DonCallisto DATA TRANSFORMERS
  • 89. Samuele Lilli - DonCallisto MODEL TRANSFORMER
  • 90. Samuele Lilli - DonCallisto ENTITY MODEL DATA NORM DATA MODEL TRANSFORMER NEW FORM BIND TRANSFORM REVERSE TRANSFORM
  • 91. Samuele Lilli - DonCallisto ENTITY MODEL DATA NORM DATA NEW FORM BIND TRANSFORM REVERSE TRANSFORM MODEL TRANSFORMER
  • 92. Samuele Lilli - DonCallisto ENTITY - MODEL DATA /** * @ORMColumn(type="array", nullable=true) */ protected $tags;
  • 93. Samuele Lilli - DonCallisto ENTITY MODEL DATA NORM DATA NEW FORM BIND TRANSFORM REVERSE TRANSFORM MODEL TRANSFORMER
  • 94. Samuele Lilli - DonCallisto FORM FIELD $builder->add('tags', TextType::class)
  • 95. Samuele Lilli - DonCallisto Since model data (array) is different from norm data (text) we need a model transformer
  • 96. Samuele Lilli - DonCallisto $builder->add('tags', TextType::class); $builder->get(‘tags’)->addModelTransformer(...); FORM FIELD
  • 97. Samuele Lilli - DonCallisto ENTITY MODEL DATA NORM DATA NEW FORM BIND TRANSFORM REVERSE TRANSFORM MODEL TRANSFORMER
  • 98. Samuele Lilli - DonCallisto TRANSFORM public function transform($tagsArray) { // transform the array to string if (null === $tagsArray) { return ''; } return implode(',', $tagsArray); }
  • 99. Samuele Lilli - DonCallisto ENTITY MODEL DATA NORM DATA NEW FORM BIND TRANSFORM REVERSE TRANSFORM MODEL TRANSFORMER
  • 100. Samuele Lilli - DonCallisto public function reverseTransform($tagsString) { // transform the string back to an array if (!$tagsString) { return []; } return explode(',', $tagsString); } REVERSE TRANSFORM
  • 101. Samuele Lilli - DonCallisto VIEW TRANSFORMER
  • 102. Samuele Lilli - DonCallisto VIEW NORM DATA VIEW DATA CREATE VIEWPOST TRANSFORM REVERSE TRANSFORM VIEW TRANSFORMER
  • 103. Samuele Lilli - DonCallisto DATE TYPE (TRANSFORM) // Transforms a normalized date into a localized date string/array) public function transform($dateTime) { if (null === $dateTime) { return ''; } if (!$dateTime instanceof DateTimeInterface) { throw new TransformationFailedException('Expected a DateTimeInterface.'); } $value = $this->getIntlDateFormatter()->format($dateTime->getTimestamp()); if (intl_get_error_code() != 0) { throw new TransformationFailedException(intl_get_error_message()); } return $value; }
  • 104. Samuele Lilli - DonCallisto // Transforms a localized date string/array into a normalized date. public function reverseTransform($value) { if (!is_string($value)) { throw new TransformationFailedException('Expected a string.'); } if ('' === $value) { return; } $timestamp = $this->getIntlDateFormatter()->parse($value); // …. try { $dateTime = new DateTime(sprintf('@%s', $timestamp)); } catch (Exception $e) { throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); } // …. return $dateTime; } DATE TYPE ( REVERSE TRANSFORM)
  • 105. Samuele Lilli - DonCallisto FORM EVENTS (reprise)
  • 106. Samuele Lilli - DonCallisto PRE SET DATA EVENT Modify data given during pre-population. Don’t modify form data directly but modify event data instead. Add/Remove form fields USED FOR EVENT DATA MODEL DATA
  • 107. Samuele Lilli - DonCallisto ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) { /** @var $data User */ $data = $e->getData(); // ← MODEL DATA $form = $e->getForm(); $today = new DateTime(); $criteria = Criteria::create() ->where(Criteria::expr()->gte('date', $today)); $form->add(‘tickets’, CollectionType::class, [ // …. 'data' => $data->getTickets()->matching($criteria) ]); }); }); PRE_SET_DATA
  • 108. Samuele Lilli - DonCallisto ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) { if ($e->getData()->canHandleMail()) { // ← MODEL DATA $e->getForm()->add(‘email’, EmailType::class); } }); }); PRE_SET_DATA
  • 109. Samuele Lilli - DonCallisto Read pre-populated form data Don’t remove fields that you’ve setted “statically” on form building process. Use PRE_SET_DATA and implement the logic about fields. One exception: you are extending from a parent form where you cannot control yourself the logic. USED FOR EVENT DATA MODEL DATA POST SET DATA EVENT
  • 110. Samuele Lilli - DonCallisto class FooType extends BarType { // …. // email field added in BarType ->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $e) { if (!$e->getData()->canHandleMail()) { // ← MODEL DATA $e->getForm()->remove(‘email’); } }); }); POST_SET_DATA
  • 111. Samuele Lilli - DonCallisto Change data from the request Add/Remove form fields USED FOR EVENT DATA REQUEST DATA (ARRAY) PRE SUBMIT EVENT
  • 112. Samuele Lilli - DonCallisto private $canModifyEmail = true; ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $e) { $this->canModifyEmail = $e->getData()->canModifyEmail(); $e->getForm()->add(‘email, EmailType::class, [ // …. ‘attr’ => [ ‘disabled’ => !$this->canModifyEmail ], ]); }) ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $e) { if (!$this->canModifyEmail) { $e->getForm()->remove(‘email’); } }); }); PRE_SUBMIT
  • 113. Samuele Lilli - DonCallisto Modify NormData No real example will be showed EVENT DATA NORM DATA SUBMIT EVENT USED FOR
  • 114. Samuele Lilli - DonCallisto Fetch data after denormalization Even if faster to read “final” data here, don’t implement any business logic → hard to test and break SRP. If you need to modify model data, do it elsewhere just after isValid call. USED FOR EVENT DATA VIEW DATA POST SUBMIT EVENT
  • 115. Samuele Lilli - DonCallisto POST SUBMIT $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($handler) { $delete = $event->getForm()->has('delete') ? $event->getForm()->get('delete')->getData() : false; $entity = $event->getForm()->getParent()->getData(); if (!$delete) { return; } $handler->remove($entity, $event->getForm()->getName()); }); Vichuploader-bundle
  • 116. Samuele Lilli - DonCallisto VALUE OBJECTS
  • 117. Samuele Lilli - DonCallisto Two object are equal if their all fields are equal, not necessary if they are the same object
  • 118. Samuele Lilli - DonCallisto NO SETTERS
  • 119. Samuele Lilli - DonCallisto CONSTRUCTOR
  • 120. Samuele Lilli - DonCallisto Class FooBar { private $foo; private $bar; public function __construct($foo, $bar) { $this->foo = $foo; $this->bar = $bar; } public function getFoo(){ … } public function getBar(){ … } }
  • 121. Samuele Lilli - DonCallisto Class FooBarType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add(‘foo’) ->add(‘bar’); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ ‘empty_data’ => function (FormInterface $interface) { return new FooBar($interface->get(‘foo’)->getData(), $interface->get(‘bar’)->getData()); }, ]); } }
  • 122. Samuele Lilli - DonCallisto PROPERTY PATH
  • 123. Samuele Lilli - DonCallisto Class User { //…. /* * @ORMOneToOne(targetEntity=”Address”, inversedBy=”user”) */ protected $address; //…. }
  • 124. Samuele Lilli - DonCallisto Class Address { //…. /** * @ORMOneToOne(targetEntity=”User”, mappedBy=”address”) */ protected $user; /** * @ORMColumn(type=”string”) */ protected $street; /** * @ORMColumn(type=”string”) */ protected $number; }
  • 125. Samuele Lilli - DonCallisto DON’T CREATE AN EMBEDDED FORM ON PURPOSE
  • 126. Samuele Lilli - DonCallisto DON’T USE UNMAPPED FIELDS AND FORM EVENTS
  • 127. Samuele Lilli - DonCallisto USE PROPERTY PATH!
  • 128. Samuele Lilli - DonCallisto Class UserType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { // …. $builder ->add(‘street’, TextType::class, [‘property_path’ => ‘address.street’]) ->add(‘number’, TextType::class, [‘property_path’ => ‘address.number’]); } }
  • 130. Samuele Lilli - DonCallisto THANK YOU