I have problem to render view for creating new entity and can't find existing problem here being answered so I am going to ask...
My app has entity X that belongs to one entity Y and can have many entities Z.
When console runs, it executes well with all those relations.
php app\console doctrine:schema:update --force,
After crud generating for entity X, listing page shows fine, but page for creating new record throws following exception:
An exception has been thrown during the rendering of a template
("Warning: call_user_func_array() expects parameter 1 to be a valid
callback, class 'Symfony\Bridge\Twig\Extension\FormExtension' does not
have a method 'renderer->humanize' in
%path_to_app%\app\cache\dev\twig\16\16033db1d32d7d10db7a0d24db2f49938a4b2e9a63d231d90bf70d1969563fd0.php
line 880") in form_div_layout.html.twig at line 232.
What could be the problem?
Update 1:
Exception's trigger lays in twig file where data is passed from controller.
At {{ form_widget(form) }}
Update 2:
//controller's method
/**
*
*
* #Route("/new", name="class_new")
* #Template()
*/
public function newAction()
{
$entity = new Class();
$form = $this->createForm(new ClassType(), $entity);
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
//Form class
class ClassType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Admin\MainBundle\Entity\Class'
));
}
public function getName()
{
return 'admin_mainbundle_classtype';
}
}
// view
<form action="{{ path('class_create') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<p>
<button type="submit">Create</button>
</p>
</form>
Try to empty the cache : php bin/console cache:clear and rerun your command.
If problem persit: look at in your controller if you are used a undefined function for exemple renderer->humanize and look layout.html.twig at line 232.
I don't see any problem in your controller and view.
So i tried to look for in form_div_layout.html.twig file and i found that function humanize is used there and the exception says that function isn't defined.
So can you update your project by : composer install
form_div_layout.html.twig :
{%- block form_label -%}
{% if label is not same as(false) -%}
{% if not compound -%}
{% set label_attr = label_attr|merge({'for': id}) %}
{%- endif -%}
{% if required -%}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif -%}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endblock form_label -%}
{%- block button_label -%}{%- endblock -%}
I solved this few weeks ago but now I spotted time for answering.
This was giving me headache before moving the app version from 2.1 to 2.8. Nowadays it works well.
Related
I have an entity Product with a ManyToMany relation to an entity Category
/**
* #ORM\ManyToMany(targetEntity="App\Domain\Category", inversedBy="stalls")
*/
private $categories;
//...
/**
* #return Collection|Category[]
*/
public function getCategories(): Collection
{
return $this->categories;
}
In the ProductCrudController class I have the following configureFields method:
public function configureFields(string $pageName): iterable
{
return [
Field::new('name'),
Field::new('description'),
AssociationField::new('categories'),
];
}
When creating/editing a Product everything works as expected in the relation, but in the list of products instead of showing the related categories I see the number of categories the product has. How can I change this behaviour?
In the following image the first product has 1 category and the second one in the list has 2 different categories. I would like the name of the categories to be shown here.
As a side note: Category class has a __toString method returning the name of the category.
EDIT:
The behaviour I am looking for is the same as the Tags column in the following image:
You can make a template for that like so:
// somewhere here templates/admin/field/category.html.twig
{% for category in field.value %}
{%- set url = ea_url()
.setController('Path\\To\\Your\\CategoryCrudController')
.setAction('detail')
.setEntityId(category.id)
-%}
<a href="{{ url }}">
{{ category.name }}{% if not loop.last %}, {% endif %}
</a>
{% else %}
<span class="badge badge-secondary">None</span>
{% endfor %}
And just add it to the field
// in ProductCrudController
AssociationField::new('categories')->setTemplatePath('admin/field/category.html.twig'),
You can format the value using the method formatValue like this :
->formatValue(function ($value, $entity) {
$str = $entity->getCategories()[0];
for ($i = 1; $i < $entity->getCategories()->count(); $i++) {
$str = $str . ", " . $entity->getCategories()[$i];
}
return $str;
})
I had the same issue on my detail page. So instead of a template, I change the field type depending on the pagename
if (Crud::PAGE_DETAIL === $pageName) {
$field = ArrayField::new('field')->setLabel('label');
} else {
$field = AssociationField::new('field')->setLabel('label');
}
I will do that way :
->formatValue(function ($value, $entity) {
return implode(",",$entity->getCategories()->toArray());
})
Building on top of the most upvoted answer, you could make the Twig snippet universal like this:
{% for member in field.value %}
{%- if field.customOption('crudControllerFqcn') is not empty -%}
{%- set url = ea_url()
.setController(field.customOption('crudControllerFqcn'))
.setAction('detail')
.setEntityId(member.id)
-%}
<a href="{{ url }}">
{{ member }}
</a>
{%- else -%}
{{ member }}
{%- endif -%}
{%- if not loop.last %}, {% endif -%}
{% else %}
<span class="badge badge-secondary">None</span>
{% endfor %}
This way you could set the link destination in your CRUD controller and use the template everywhere. When no CRUD controller is not set, you will still have the individual items enumerated, but not as links.
In my TeamCrudController, I'm doing this:
public function configureFields(string $pageName): \Generator
{
yield TextField::new('name')
->setDisabled()
;
$members = AssociationField::new('members')
->hideOnForm()
;
if (Crud::PAGE_DETAIL === $pageName) { // I want to see the number on INDEX, but the list on DETAIL
$members
->setCrudController(EmployeeCrudController::class)
->setTemplatePath('admin/field/expanded_association_field.html.twig')
;
}
yield $members;
}
Alternatively, if you would like to become the default behaviour, override the EasyAdminBundle template by placing the following content in templates/bundles/EasyAdminBundle/crud/field/association.html.twig:
{# #var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# #var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# #var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{% if 'toMany' == field.customOptions.get('associationType') %}
{% if ea.crud.currentPage == 'detail' %}
{% for member in field.value %}
{%- if field.customOption('crudControllerFqcn') is not empty -%}
{%- set url = ea_url()
.setController(field.customOption('crudControllerFqcn'))
.setAction('detail')
.setEntityId(member.id)
-%}
<a href="{{ url }}">
{{- member -}}
</a>
{%- else -%}
{{ member }}
{%- endif -%}
{%- if not loop.last %}, {% endif -%}
{% else %}
<span class="badge badge-secondary">None</span>
{% endfor %}
{% else %}
<span class="badge badge-secondary">{{ field.formattedValue }}</span>
{% endif %}
{% else %}
{% if field.customOptions.get('relatedUrl') is not null %}
{{ field.formattedValue }}
{% else %}
{{ field.formattedValue }}
{% endif %}
{% endif %}
I'm following the login/security conventions, everything ok with Symfony 2.3 to 2.55 , until I updated to 2.5.6. Now the Login url is displaying:
Neither the property "_username" nor one of the methods "getUsername()", "username()", "isUsername()", "hasUsername()", "__get()" exist and have public access in class "Acme\DemoBundle\Entity\Login".
Any idea to solve this?
namespace Acme\DemoBundle\Entity;
class Login {
protected $_username;
protected $_password;
protected $_remember_me;
public function get_Username() {
return $this->_username;
}
public function set_Username($username) {
$this->_username = $username;
}
public function get_Password() {
return $this->_password;
}
public function set_Password($password) {
$this->_password = $password;
}
public function get_RememberMe() {
return $this->_remember_me;
}
public function set_RememberMe($remember) {
$this->_remember_me = $remember;
}
}
The name of the fields is configurable. You can change _username and _password fields to anything you want and set them in security.yml. For example as I use custom LoginType (alias login_form), I have this in security.yml:
security:
firewalls:
secured_area:
username_parameter: login_form[username]
password_parameter: login_form[password]
This allows me to have $username and $password variables in the Login Model.
Symfony standard Login needs that you submit a _username and _password named inputs.
Initially my entity user properties were username and password.
Unfortunately I found that twig rendered the inputs ignoring the attr{name='_username'} sent from the formtype builder. So I took the shortcut of renaming the entities properties to the underscore version.
Then everything worked fine until I updated Symfony to 2.5.6 version.
Then as Cerad suggested, I got rid of the entity underscores and form id... but still needed to pass the input names with the underscores. So I overrode this Wig block, just changing the name assignation from the first line to the last one:
{% block widget_attributes -%}
id="{{ id }}"
{%- if read_only %} readonly="readonly"{% endif -%}
{%- if disabled %} disabled="disabled"{% endif -%}
{%- if required %} required="required"{% endif -%}
{%- for attrname, attrvalue in attr -%}
{{- " " -}}
{%- if attrname in ['placeholder', 'title'] -%}
{{- attrname }}="{{ attrvalue|trans({}, translation_domain) }}"
{%- elseif attrvalue is sameas(true) -%}
{{- attrname }}="{{ attrname }}"
{%- elseif attrvalue is not sameas(false) -%}
{{- attrname }}="{{ attrvalue }}"
{%- endif -%}
{%- endfor -%}
name={{full_name}}
{%- endblock widget_attributes %}
I would like auto select value from session on radio button from twig. Unfortunately following code doesn't work for me.
In my twig template
{{ form_row(reg_form.sex, {'data' : 2}) }}
My form field type (User::SEX_MALE = 1, User::SEX_FEMALE = 2)
<?php
namespace FWM\CoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use FWM\CoreBundle\Entity\User;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* A form field for selecting user's sex
*/
class SexType extends AbstractType
{
/**
* {#inheritDoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'choices' => array(
User::SEX_MALE => 'label.form.male',
User::SEX_FEMALE => 'label.form.female'
),
'label' => 'label.form.sex',
'expanded' => true,
]);
}
/**
* {#inheritDoc}
*/
public function getParent()
{
return 'choice';
}
/**
* {#inheritDoc}
*/
public function getName()
{
return 'user_sex';
}
}
And my display customization:
{% block form_row %}
{% set error = false %}
{% if errors %}
{% set error = true %}
{% set attr = attr|merge({'class' : attr.class|default('') ~ ' error'}) %}
{% endif %}
{{ form_widget(form, {'attr' : attr, 'error': error}) }}
{% endblock form_row %}
{% block choice_widget %}
{% spaceless %}
{% if expanded %}
{% set attr = attr|merge({'class' : attr.class|default('') ~ ' radio-row'}) %}
<div {{ block('widget_container_attributes') }}>
{{dump(form)}}
{% for child in form %}
<span class="unbreakable">{{ form_widget(child) }} {{ form_label(child) }}</span>
{% endfor %}
</div>
{% else %}
...
If I add "data => 2" to setDefaults(...), field female becomes selected, but I can't find way to make it work by passing value from twig. Could someone help me please?
#qooplmao wrote in a comment:
By the time the form is available in the twig then everything that is or was going to happen to it should have happened. You could set your default in the object class $this->createFormBuilder(Object, {your options}) and then set it or you could hard code it in your entity.
When I've created a form:
$builder = $this->createFormBuilder();
$form = $builder->add( 'add', 'button')->getForm();
and render it:
<div><button type="button" id="form_add" name="form[add]">Add</button></div>
the attributes type, id and name are created.
I want to erase this attributes but I don't know how to do it. I've tried to do:
$builder = $this->createFormBuilder();
$form = $builder->add( 'add', 'button', array( 'attr' => array() ) )->getForm();
without any success.
How could I do it?
Greetings and thanks
I messed around with this for a while and the closest I could get was to have them render with empty attributes. However, that's because the project I tested with is Symfony 2.0, and in that version it's impossible to completely remove the attributes since Symfony\Component\Form\FormView::$vars is private.
However, in Symfony 2.1 and later, that same property is public so you should be able to modify (or delete) the attributes/vars directly without being constrained by the FormView api.
First, create your own type to represent this "naked button"
src/Your/Bundle/Form/NakedButtonType.php
<?php
namespace Your\Bundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
class NakedButtonType extends AbstractType
{
/**
* (non-PHPdoc)
* #see Symfony\Component\Form.FormTypeInterface::getName()
*/
public function getName()
{
return "naked_button";
}
/**
* (non-PHPdoc)
* #see Symfony\Component\Form.AbstractType::getParent()
*/
public function getParent(array $options)
{
return 'button';
}
/**
* (non-PHPdoc)
* #see Symfony\Component\Form.AbstractType::buildViewBottomUp()
*/
public function buildViewBottomUp(FormView $view, FormInterface $form)
{
// Symfony 2.0
// This will still render the attributes, but they will have no value
$view->set('id', null);
$view->setAttribute('type', null);
// Symfomy >= 2.1
// This *should* remove them completely
unset( $view->vars['id'] );
unset( $view->vars['attr']['type'] );
}
}
Now, tell the service container how to build your type
app/config/config.yml
services:
form.type.naked_button:
class: Your\Bundle\Form\NakedButtonType
tags:
- {name: form.type, alias: naked_button}
Then update your parent form to use your new type instead of the ootb "button" type.
$builder = $this->createFormBuilder();
$form = $builder->add( 'add', 'naked_button')->getForm();
All of that being said...
If you want these buttons w/o any attributes, why not just put them like that directly into your view?
<form>
{{ form_errors(form) }}
{{ form_rest(form) }}
<div>
<button>Add</button>
</div>
</form>
All of this custom type nonsense seems like alot of overhead to render something you clearly don't need Symfony to manage for you.
While not a good idea, I will help you load the gun and shoot yourself in the foot :-)
Create a new Resources/views/Form/fields.html.twig file, and put the following in it:
{% block widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}{% endfor %}
{% endspaceless %}
{% endblock widget_attributes %}
If you really, really want to remove all the attributes on there, you can remove the {% for attrname ... %} line. That will remove the attributes from all form fields. If you add some logic, you can have it apply to just specific fields.
Next step, you need to register your fields helpers. In your app/config/config.yml file, add the following line:
twig:
form:
resources:
- 'SomeBundle:Form:fields.html.twig'
What I want to do is get variables stored in form view.
{% form_theme edit_form _self %}
{% block field_widget %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{# MY CODE #}
{% if type == "file" %}
<a class="BOpreview" href="{# NEED TO REPLACE VAR HERE #}">Aperçu</a>
{% endif %}
{# MY ATTEMPT #}
{{ form.title.get('value') }}
{{ form.vars.value.url }}
{% endspaceless %}
{% endblock field_widget %}
My form has properties like url, title, etc and I am trying to access them here to use it in the field widget block.
I searched for it and came on https://groups.google.com/forum/?fromgroups=#!topic/symfony2/onor9uFte9E that suggested:
{{ form.title.get('value') }}
{{ form.vars.value.url }}
which didn't work for me.
Note: If I do a var_dump on $form->createView() in my controller, I get:
object(Symfony\Component\Form\FormView)[331]
private 'vars' =>
array (size=15)
'value' =>
object(Panasonic\TestEtAvisBundle\Entity\Product)[168]
protected 'reviewArray' =>
object(Doctrine\ORM\PersistentCollection)[234]
...
protected 'testArray' =>
object(Doctrine\ORM\PersistentCollection)[221]
...
protected 'fbshareArray' =>
object(Doctrine\ORM\PersistentCollection)[317]
...
private 'id' => int 2
private 'name' => string 'Nom du produit' (length=14)
private 'title' => string '<span>Titre </span>' (length=19)
private 'image' => string 'bundles/testetavis/uploads/product/0d9d9550.png' (length=47)
private 'fbImage' => string 'bundles/testetavis/uploads/product/facebook//product_e928cd96.jpg' (length=65)
private 'description' => string '<span>Descriptif </span>' (length=24)
private 'url' => string 'http://www.google.com' (length=21)
private 'creationDate' =>
object(DateTime)[210]
...
private 'modificationDate' =>
object(DateTime)[209]
...
private 'isDeleted' => int 0
'attr' =>
array (size=0)
empty
'form' =>
&object(Symfony\Component\Form\FormView)[331]
'id' => string 'panasonic_testetavisbundle_producttype' (length=38)
'name' => string 'panasonic_testetavisbundle_producttype' (length=38)
'full_name' => string 'panasonic_testetavisbundle_producttype' (length=38)
I want to access that url for instance but can't seem to be able to do it after many variations. Including use of {{ value }}, {{ value.url }}
But inspite of vars, I can do {{ full_name }} and get panasonic_testetavisbundle_producttype.
Any ideas?
Edit2: I found out the real problem...
Edit3: Seeing that this question is quite popular I decided to clarify on what I attempted to do in case it helps someone in the same situation. If you rely strictly on what the question asks, as I stated from my research and that Besnik supported are indeed correct.
Now what I wanted to do is for every input type file, get url from object used to render form and append a preview link, using retrieved url, beside the input type file.
If you try to get the form var of an input type "file" like this "{{ form.vars.value.url }}" in my code, this doesn't work since, if I recall correctly, you receive a token instead of the url stored inside the object.
You can access the current data of your form via form.vars.value:
{{ form.vars.value.title }}
See Symfony2 Forms documentation: http://symfony.com/doc/current/book/forms.html#rendering-a-form-in-a-template
Dump vars by using dump function:
{{ dump(form.vars.value) }}
If you are using subforms or want to have a value of a specific field:
{{ form.FIELD.vars.VALUE }}
You can access values of the parent parent from a widget block using form.parent.vars
For example, we want to render the value from a type text field called primerNombre we will need
{{ form.vars.value.primerNombre }}
If we wanted to render the name of one of the children we will need
{% for hijo in form.hijos %}
<td><div align="left">{{ form_widget(hijo.vars.value.primerNombre) }}</div></td>
{% endfor %}
Good luck!
In Symfony > 3 you may use:
form.vars.value.Entity.someValue
Edit2:
Finally, I was indeed getting the value of the current row in {{ value }} here:
{% block field_widget %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ **value** }}" {% endif %}/>
{# MY CODE #}
{% if type == "file" %}
<a class="BOpreview" href="{{ value }}">Aperçu</a>
{% endif %}
{% endspaceless %}
{% endblock field_widget %}
But in my case I get a token instead of the value since I am using input type file. This is due to a security measure in Symfony2.