I am creating an edit page where there will be as many forms as there will be results from my query. So after my query, I created a foreach loop to pass each "result" to a form and then I pass it all to my Twig.
$repo = $this->getDoctrine()->getRepository(Classement::class);
$q = $repo->createQueryBuilder('c');
$q->select('c', 'h')
->join('c.clubHistos', 'h')
->where('c.country = '.$country)
->where('c.season = '.$season);
$result = $q->getQuery()->getResult();
foreach ($result as $table) {
$table = $table;
$form = $this->createForm(ClassementType::class, $table);
}
return $this->render('back/editDivisionTables.html.twig', array(
'form' => $form->createView(),
'table' => $table
));
The problem is that I am only getting the last form when I should be having all of them. I did a dump($form) in my loop and everything seemed okay. So the problem is either in my return statement or in my Twig.
{{ form_start(form) }}
<div class="season-table-info">
<div class="country">
{{ form_row(form.country) }}
</div>
<div class="division">
{{ form_row(form.division) }}
</div>
{{ form_row(form.season) }}
</div>
<table id="dynamic-club-table">
<thead></thead>
<tbody id="dynamic-club-table-body" data-row-prototype="{{ formMacro.form(form.clubHistos.vars.prototype)|e('html_attr') }}">
{% for clubHistos in form.clubHistos %}
{{ formMacro.form(clubHistos) }}
{% endfor %}
</tbody>
</table>
{{ form_end(form) }}
I tried this solution but it didn't do the trick.
Related
I am working on an existing Symfony 3.4 based project and trying to add and render a new form. Although the 'label' => false option was used, the fields are rendered including a label. Why?
// Symfony
class SomeController extends Controller {
public function userListAction(Request $request) {
$users = $someService->getUsers();
$formBuilder = $this->createFormBuilder()
->add('users', EntityType::class, [
'label' => false, // also tested '' and 'someLabel'
'class' => 'AppBundle:User',
'choices' => $users,
'multiple' => true,
'expanded' => true,
]);
$variables = array(
'form' => $formBuilder->getForm()->createView(),
);
return $this->render('AppBundle:Pages:user_list.html.twig', $variables);
}
}
// Twig
{% extends 'AppBundle::layout.html.twig' %}
{% block page_content %}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{% endblock %}
This shows a list of checkboxes for all users including the username as label.
Where does Symfony get the information to use the username as label? As far as I know no custom form widget was defined for the User class. Is there any way to check this for sure? Maybe there is something hidden in the vendor bundles like FOSUserBundle?
Why is the 'label' => false option ignored?
Edit:
Different ways of rendering the form does not solve the problem:
{{ form_start(form) }}
{{ form_row(form) }}
{{ form_end(form) }}
Result:
<div id="form_users">
<div class="form-group">
<div class="checkbox">
<label for="form_users_547">
<input type="checkbox" id="form_users_547" name="form[users][]" value="547">
someUserName
</label>
</div>
</div>
</div>
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Result:
<div id="form_users">
<div class="checkbox">
<label for="form_users_547">
<input type="checkbox" id="form_users_547" name="form[users][]" value="547">
someUserName
</label>
</div>
</div>
{{ form_start(form) }}
{% for userFormView in form.users %}
{{ form_row(userFormView) }}
{% endfor %}
{{ form_end(form) }}
Result:
Basically the same as before with form_row
You need to use ‘choice_label’ => ‘YOUR PROPERTY PATH’ in the field options.
Pretty match is written in the docs: https://symfony.com/doc/current/reference/forms/types/entity.html#choice-label
If the entity class cast to string then is used if is not it will throw an exception. It looks like your entity User cast to the user name and that’s why it works.
You should try to use {{ form_row(form) }} which should render the whole field correctly.
I'm new to Symfony, I got a page in where I have a list of articles, and I created a form above it that allow me to filter the results of my query :
The form works fine, but when I click on the 2nd page of my paginator table, the form reset, so my filter is reset too. What I want is that we can filter results and be able to paginate the table, with the same filter, and that the form keep his data.
Here's my code right now :
Controller :
public function listeAction(Request $request, $page) {
if ($page < 1 && strlen($page) != 0) {
throw new NotFoundHttpException('Page "' . $page . '" inexistante.');
} else if (strlen($page) == 0) {
$page = 1;
}
$nbPerPage = 5;
$form = $this->createForm(ArticleRechercheType::class);
$form->setData(array('rds' => true));
//if we use filters
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$data = $form->getData();
$form = $this->createForm(ArticleRechercheType::class, $data);
if ($data['famille'] != null) {
$data['famille'] = $data['famille']->getId();
}
$listArticles = $this->getDoctrine()
->getManager()
->getRepository('GRBackOfficeBundle:Article')
->getFilteredArticles($page, $nbPerPage, $data);
} else {
$data = $form->getData();
$form = $this->createForm(ArticleRechercheType::class, $data);
$listArticles = $this->getDoctrine()
->getManager()
->getRepository('GRBackOfficeBundle:Article')
->getArticles($page, $nbPerPage);
}
$nbPages = ceil(count($listArticles) / $nbPerPage);
if ($nbPages != 0 && $page > $nbPages) {
throw new NotFoundHttpException('Page "' . $page . '" inexistante.');
}
if ($nbPages == 0) {
$nbPages = 1;
}
return $this->render('GRBackOfficeBundle:Article:liste_articles.html.twig', array(
'form' => $form->createView(),
'listArticles' => $listArticles,
'nbPages' => $nbPages,
'page' => $page
));
}
Form :
class ArticleRechercheType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('famille', EntityType::class, array(
'class' => 'GRBackOfficeBundle:Famille',
'choice_label' => 'nom',
'required' => false
))
->add('rds', CheckboxType::class, array('required' => false))
->add('recherche', TextType::class, array('required' => false))
->add('Rechercher', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'translation_domain' => false
));
}
}
View :
{% extends "GRBackOfficeBundle::layout.html.twig" %}
{% block title %}
Liste des articles
{% endblock %}
{% block gr_bo_body %}
<h2>Liste des articles</h2>
<div class="row">
<div class="well row">
{{ form_start(form) }}
{{ form_errors(form) }}
<div class="form-group col-md-2">
{{ form_label(form.famille, "Famille d'article", {'label_attr': {'class': 'control-label'}}) }}
{{ form_errors(form.famille) }}
{{ form_widget(form.famille, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="form-group col-md-4">
{{ form_widget(form.rds, {'attr': {'class': ''}}) }}
{{ form_label(form.rds, "Montrer les articles en rupture de stock", {'label_attr': {'class': 'control-label'}}) }}
{{ form_errors(form.rds) }}
</div>
<div class="form-group col-md-3">
{{ form_label(form.recherche, "Recherche", {'label_attr': {'class': 'control-label'}}) }}
{{ form_errors(form.recherche) }}
{{ form_widget(form.recherche, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="form-group col-md-3" style="text-align: center">
{{ form_widget(form.Rechercher, {'attr': {'class': 'btn btn-primary'}}) }}
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
<div class="well row">
<table class="table table-bordered table-striped" id="listeArticles" style="width: 100%" cellspacing="0">
<thead>
<tr>
<th>Référence client</th>
<th>Référence interne</th>
<th>Famille</th>
<th>Libellé</th>
<th>Alerte</th>
<th>Stock</th>
</tr>
</thead>
<tbody id="bodyListeArticles">
{% for article in listArticles %}
<tr>
<td>{{ article.RefArticle }}</td>
<td>{{ article.RefLogistique }}</td>
<td>{{ article.famille.nom }}</td>
<td>{{ article.libelle }}</td>
<td>{{ article.StockAlerte }}</td>
<td>{{ article.StockActuel }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if nbPages > 1 %}
<ul class="pagination">
{% for p in range(1, nbPages) %}
<li {% if p == page %} class="active"{% endif %}>
{{ p }}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}
If anyone have an idea of how I can do that, that will be appreciated
You can use a bundle (like the knp paginator bundle for exemple), or put your args in session. But the bundle will make it simple as it does everything for you, you just have to pass your datas
I created a form in Symfony like this:
$form = $this->createFormBuilder($template)
->add('product1', 'text')
->add('product2', 'text')
->add('save', 'submit')
->getForm();
Now this is my twig:
{{ form_start(form) }}
{% for i in 1..2 %}
<div class="col-md-3">
<div class="product">
<div class="name">
{{ form_label(form.product{{ i }} ) }}
{{ form_errors(form.product{{ i }} ) }}
{{ form_widget(form.product{{ i }} ) }}
</div>
</div>
</div>
{% endfor %}
{{ form_end(form) }
The main idea is iterate over the for and get a new form.product<X> each loop.
I can't make it works and I don't even know if it can be done in this way. Any idea?
I would recommend you to use Collection type for this purpose. But if you want do it your way you should do it this way:
{{ form_start(form) }}
{% for i in 1..2 %}
<div class="col-md-3">
<div class="product">
<div class="name">
{{ form_label( attribute(form, 'product' ~ i) ) }}
{{ form_errors( attribute(form, 'product' ~ i) ) }}
{{ form_widget( attribute(form, 'product' ~ i) ) }}
</div>
</div>
</div>
{% endfor %}
{{ form_end(form) }
You're right, it probably won't work. For information, concatenation symbol in Twig is "~".
In your case, if your entity is supposed to have 2 or more "products" you should use collections instead of creating manually each product.
In your entity you would have something like
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
protected $products;
And on the product entity, you would have
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
And then in your first entity __constructor or in your controller you iterate to create as many product as you want and you add them to the entity.
In your form, you would just have to add :
$builder->add('products', 'collection');
and you would be able to iterate on it in Twig.
Hopefully this will help you
You should start with a Entity with only a ManyToOne relation to the "product" entity. Lets say that we call this entity "ProductContainer".
Then you create an form for the ProductContainer with only one field with the type 'collection' which will make a list of products for you.
You can follow this tutorial: http://symfony.com/doc/current/cookbook/form/form_collections.html
I try to implement a searchform in my Symfony-Application. POST works fine, but I want to use /search?q=foo. I have a form with method=GET but I can't bind my Request to the Form with the following Request:
search?form%5B_token%5D=HC5M4nG-B-r5BUUSmCchkryqNQyu9RvDDlTvcTDuFFY&form%5Bq%5D=foobar
This is my Form
$data = array();
return $this->createFormBuilder($data)
->add('q', 'text')
->getForm();
And the current Controller
/**
* #var $form Form
*/
$form = $this->getSearchForm();
if ($request->isMethod('GET')) {
// contains q = 'foobar'
dump($_GET);
// null
dump($request->request->get('q'));
// null
dump($request->request->get('form'));
$form->submit($request->request->get($form->getName()));
$form->handleRequest($request);
$data = $form->getData();
// null
dump($data);
...
}
the form-template
{{ form_start(form, {'action': path('search'), 'method': 'GET', 'attr': { 'class' : 'navbar-form navbar-left', 'role' : 'search' }}) }}
{{ form_errors(form) }}
<div class="form-group">
{{ form_label(form.q) }}
{{ form_errors(form.q) }}
{{ form_widget(form.q, { 'attr': {'placeholder': 'Find' }}) }}
</div>
<button type="submit" class="btn btn-default">Find</button>
{{ form_end(form) }}
$request->request->all() is all of the $_POST parameters.
$request->query->all() is all of the $_GET parameters.
You should be using $request->query->get('q') or $request->get('a') (which will go through each of the parameter bags) to get your parameters.
For more info see the docs.
I got stuck while trying to customize the rendering of a specific field in my form.
It looks like this:
$builder->add('players', 'entity', array(
'class' => 'Acme\Bundle\Entity\Player',
'expanded' => true,
'multiple' => true,
'required' => false,
));
The form itself is beeing rendered with a simple:
{% block form_content %}
{% form_theme form 'AcmeBundle:Form:fields_child.html.twig' %}
{{ form_widget(form) }}
{% endblock %}
Now inside fields_child.html.twig i'm extending from another form template but there is nothing special there.
My HTML looks like this:
Players:
- [checkbox-input] 1
Where 1 equals the id of the only player in the database. However instead of rendering the ID im trying to render his picture and full name after the checkbox.
I have tried many combinations of the form theming to override it but failed each time.
Could someone post the twig block to render what i want here?
Thanks
You have to create custom form field type together with custom widget template for it.
http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html
I recently ran into this problem (was a little bit different situation. I needed to show products as table with checkboxes..), Form's child data always returned null value that's why I ended up with this (dirty:)) solution:
Controller action:
...
$productRepository = $entityManager->getRepository('VendorMyBundle:Product');
$products = [];
$formChildren = $productListForm->createView()->children;
foreach ($formChildren['products'] as $formProduct) {
$formProductId = $formProduct->vars['value'];
$productEntity = $productRepository->find($formProductId);
$products[$formProductId] = $productEntity;
}
...
return $this->render('TEMPLATE', [
'productListForm' => $productListForm->createView(),
'products' => $products,
]);
Template:
...
{% for productForm in productListForm.products %}
{% set id = productForm.vars.value %}
<tr>
<td class="check">
{{ form_widget(productForm) }}
</td>
<td class="photo">
{% if products[id].getImages().isEmpty() == false %}
{% set productImage = products[id].getImages().first() %}
<img src="{{ productImage.getWebPath() | imagine_filter('st_product_cabinet_thumbnail') }}" />
{% else %}
<span class="no-image">No image</span>
{% endif %}
</td>
<td class="title">
{{ products[id].getName() }}
</td>
<td class="status">
{{ products[id].getStatusName(products[id].getStatus()) }}
</td>
<td class="price">
<ul>
{% for productPrice in products[id].getPrices() %}
<li>{{ productPrice.getValue() ~ ' ' ~ productPrice.getCurrencyCode() }}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
...