I'm trying to generate a form type in particular the "ProductLanguageType".
I want to generate the ProductLanguageType as many times as the current numbers of existing languages in the Language table.
For example if I have (English, French, Russian, Chinese) in the Language table, it would generate 4 ProductLanguageType form on the same page.
I would like to know how do I query language table and generate multiple form of the same type on the same page, is the form builder capable of doing it or is there another workaround? Been having some trouble with this for some time now and would be happy to find a good solution for this.
ProductLanguageType:
class ProductLanguageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('id_language', 'entity', array(
'class' => 'AdminBundle:Language',
'data_class' => 'Main\AdminBundle\Entity\Language',
'property' => 'language'
)
)
->add('name', 'text')
->add('description', 'ckeditor', array(
'config_name' => 'admin',
'config' => array(
'filebrowser_image_browse_url' => array(
'route' => 'elfinder',
'route_parameters' => array('instance' => 'default'),
),
)
))
->add('short_description', 'text');
}
public function getName(){
return 'productLanguage';
}
}
ProductType(ProductLanguageType is embeded in here):
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
$builder->add('productLanguage', new ProductLanguageType())
->add('id_seller','text')
->add('price','text')
->add('cost_price','text')
->add('retail_price','text')
->add('hot', 'checkbox')
->add('featured', 'checkbox')
->add('new', 'checkbox')
->add('free_shipping', 'checkbox')
->add('status','text') //active or inactive, to be decided if hidden or visible
->add('Add', 'submit');
}
}
Now in Symfony 3.0 they changed the createNamedBuilder, so it's possible to solve this by only calling:
use AppBundle\Form\ShippingTrackCodeReturnType;
$uniqueForm = $this->get('form.factory')->createNamedBuilder('ship_form_'.$orderRecord->getId(), ShippingTrackCodeReturnType::class, $orderRecord)->getForm();
So, you just need to loop to display and save them:
foreach ($invoice->getOrderRecords() as $key => $orderRecord)
{
// creates the forms with different names
$returnShipTrackCodeForm = $this->get('form.factory')->createNamedBuilder('ship_form_'.$orderRecord->getId(), ShippingTrackCodeReturnType::class, $orderRecord)->getForm();
$returnShipTrackCodeForm->handleRequest($request);
if ($returnShipTrackCodeForm->isSubmitted() && $returnShipTrackCodeForm->isValid())
{
// flush object
}
$returnForms[$orderRecord->getId()] = $returnShipTrackCodeForm;
}
Hope this will help somebody.
The proper way will be to use Symfony's form.factory service to create a named builder and in turn create a form from that. I recommend creating a function for the form creation process as (as I understand, correct me if I', wrong) each FormBuilder instance must be different to create a different form.
Just as an explanation of the purpose of the code:
I needed to show a list of open tickets in the help system with ability to assign a person (authorized without logging in) based on their login token to the ticket. That required to display N forms on single page that would be unique but generated from single "template" or Type if you will.
This code achieved that without any hacks. There is just no shorthand method to createForm with specific ID (name) from the controller AFAIK.
ex.
public function generateFormForTicketAssignment(Ticket $ticket) {
$ticketAssignType = new TicketAssignInvokeType();
$ticketAssignType->setNameSuffix($ticket->getId());
/**
* @var Symfony\Component\Form\FormBuilderInterface
*/
$builder = $this->get('form.factory')->createNamedBuilder($ticketAssignType->getName(), $ticketAssignType );
$builder->setAutoinitialize(true);
$formTicket = $builder->getForm();
$formTicket->get('ticket')->setData($ticket);
return $formTicket;
}
AND (this is important part) the ticketAssignInvokeType looks like this:
class TicketAssignInvokeType extends AbstractType {
private $nameSuffix = null;
private $name = 'ticket_assign_invoke';
public function __constructor(string $suffix = null) {
//parent::__construct();
$this->nameSuffix = $this->generateNameSuffix();
}
private function generateNameSuffix() {
if ($this->nameSuffix == null || $this->nameSuffix == '') {
$generator = new SecureRandom();
//change data to alphanumeric string
return bin2hex($generator->nextBytes(10));
}
return $this->nameSuffix;
}
/**
*
* @param string $suffix When set suffix added to form will use selected
* string (note it should be unique for all forms on a page).
*/
public function setNameSuffix($suffix){
$this->nameSuffix = $suffix;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('loginToken',new AuthenticateTokenType())
->add('ticket',new TicketIDType())
->add('save', 'submit', array('label' => 'form.button.assign'))
;
}
public function getName() {
if ($this->nameSuffix == null || $this->nameSuffix == "" ) {
$this->nameSuffix = $this->generateNameSuffix();
}
return $this->name .'_'. $this->nameSuffix;
}
public function configureOptions(OptionsResolver $resolver) {
// ...
}
}
This is somewhat overly complex logic and testing for name suffix but at some point I could not figure out why all I go was form name without suffix hence the complexity.
PS. I have created forms as services and will later test if a service can be used as well as this was my first intention.
PS2. I am not sure about the setAutoinitialize
method yet, but will test it furtner as well.