Symfony2 - Entire child entity in form and Twig template - symfony-2.1

In a Sonata admin form, I'd like to display all images related to the current object. Don't need to edit them.
I managed to get this working with an entity field type and its property option to select the image URL as label so I can call all pictures in the template :
->add('image', 'entity', array(
'class' => 'Hop\HopBundle\Entity\Image',
'property' => 'image_url',
'query_builder' => function($er) use ($object) {
return $er->createQueryBuilder('i')
->where('i.object = :object')
->setParameter('object', $object)
;
},
'expanded' => true,
))
.
{% for child in form %}
<img src="{{ child.vars.label }}" alt="{{ child.vars.label }}" />
{% endfor %}
It's because it seems that the entity type only give label and one value to the template.
But I'd like to get also image width, descriptions, date, ... In other word : how can we get each entire related image object in the Twig template ?
Thanks.

So I googled it a lot and here's what worked for me (Symfony 2.2)
Updated as #flu suggested in a comment
{% for key, child in form %}
<div>
{% set entity = form.vars.choices[key].data %}
{{ form_widget(child) }}
{{ form_label(child) }}
</div>
{% endfor %}
and entity is the child object.
As of Symfony 2.3 there will be just form.vars.data, if I understand correctly: https://github.com/symfony/symfony/pull/5023

I´ve spend the whole day to get a solution for this problem.
First I created a new FormType which extends the ChoiceType:
class NomineesType extends AbstractType
{
protected $em;
public function __construct(EntityManager $em) {
$this->em = $em;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$submissionRepository = $this->em->getRepository('BundleName:Submission');
$choices = $submissionRepository->findBy(array(
'nominee' => true
));
$resolver->setDefaults(array(
'choices' => $choices
));
}
public function getName()
{
return 'nominees';
}
public function getParent()
{
return 'choice';
}
}
Than I had to register the service:
services:
form.type.nominees:
class: Bundle\Form\Type\NomineesType
arguments:
entityManager: "#doctrine.orm.entity_manager"
tags:
- { name: form.type, alias: nominees }
After that I added the new type to my form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$game = $this->game;
$builder->add('submission', 'nominees', array(
'expanded' => true,
'multiple' => false
));
$builder->add('email');
}
Now I am able to get all properties in my twig-template:
{% block nominees_widget %}
{% spaceless %}
{% if expanded %}
<ul {{ block('widget_container_attributes') }}>
{% for child in form %}
<li>
{{ child.get('form').get('label').firstname }}
{{ child.get('form').get('label').lastname }}
{{ form_widget(child) }}
{{ form_label(child) }}
</li>
{% endfor %}
</ul>
{% else %}
{# just let the choice widget render the select tag #}
{{ block('choice_widget') }}
{% endif %}
{% endspaceless %}
{% endblock %}
Because the choice list is returning only integer values I´ve created a new data transformer to transform the id of the choice into an entity:
class IntToEntityTransformer implements DataTransformerInterface
{
/**
* #var ObjectManager
*/
private $om;
/**
* #param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
/**
* Transforms an object (issue) to a string (number).
*
* #param Issue|null $issue
* #return string
*/
public function transform($issue)
{
if (null === $issue) {
return "";
}
return $issue->getNumber();
}
/**
* Transforms a string (number) to an object (issue).
*
* #param string $number
* #return Issue|null
* #throws TransformationFailedException if object (issue) is not found.
*/
public function reverseTransform($number)
{
if (!$number) {
return null;
}
$issue = $this->om
->getRepository('BundleName:Submission')
->findOneBy(array('id' => $number))
;
if (null === $issue) {
throw new TransformationFailedException(sprintf(
'An Submission with number "%s" does not exist!',
$number
));
}
return $issue;
}
}
This data transformer is initialized in the build form method:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new IntToEntityTransformer($this->em);
$builder->add($builder->create('submission', 'nominees', array(
'expanded' => true,
'multiple' => false
))->addModelTransformer($transformer));
$builder->add('email');
}

I've been fighting this problem with Symfony 2.3. Don't know how it works in 2.1.
In my case I had a entity choice (expanded and multiple) for which I needed to add the entitie's 'descripcion' field, next to the label of each choice.
I solved as follows:
1- created a custom type, whose parent is EntityType, and name 'xxxxxxx_form_reparacionnormalizada'
class ReparacionNormalizadaType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'class' => 'xxxxxxxxx\Entity\TipoReparacion',
));
}
public function getName()
{
return 'xxxxxxx_form_reparacionnormalizada';
}
public function getParent()
{
return 'entity';
}
}
2- register the form type as service as usual
3- created a custom template as follows:
{% block xxxxxxx_form_reparacionnormalizada_widget %}
<ul {{ block('widget_container_attributes_choice_widget') }}>
{% for id, child in form.children %}
<li>
{{ form_label(child, child.vars.label|default(null), { 'in_list_checkbox' : true, 'widget' : form_widget(child) } ) }}
<span>{{ choices[id].data.descripcion }}</span>
</li>
{% endfor %}
</ul>
{% endblock %}
4- in your sonata admin you use your custom type instead of 'entity'
hope it helps
regards
javier

Related

Can't render dateTime

I make an entity "comment" with fields id, pseudo, email, comment and published_at.
My entity :
/**
* #ORM\Column(type="datetime")
*/
private $published_at;
public function __construct()
{
$this->published_at= new \DateTime();
}
public function getPublishedAt(): ?\DateTimeInterface
{
return $this->published_at;
}
public function setPublishedAt(\DateTime $published_at): self
{
$this->published_at = $published_at;
return $this;
}
My migration:
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE comment (id INT AUTO_INCREMENT NOT NULL, pseudo VARCHAR(25) NOT NULL, email VARCHAR(255) NOT NULL, comment LONGTEXT NOT NULL, published_at DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
}
I make a controller :
$comments = $commentRepo->findBy([], ['published_at' => 'desc']);
// dd($comments);
return $this->render('comment/comments.html.twig', [
'commentForm' => $form->createView(),
'comments' => $comments,
]);
My render twig :
<div>
{% for comment in comments %}
<div class="text-light">
{{ comment.pseudo }}
{{ comment.published_at|date('d/m/Y H:i:s')}}
{{ comment.comment | raw }}
</div>
{% endfor %}
</div>
For {{comment.pseudo}} and {{comment.comment}} display well
But for {{comment.published_at}} I have this error :
Neither the property "published_at" nor one of the methods "published_at()", "getpublished_at()"/"ispublished_at()"/"haspublished_at()" or "__call()" exist and have public access in class "App\Entity\Comment".

TWIG form template: call variable in attr

In a twig form template in Symfony 4 app I need to have an attribute with a variable.
Exemple:
{{ form_widget(myform.field, {'attr': {'class': 'bidding-slider','data-slider-value': '800'}}) }}
I need to put a variable in 'data-slider-value' instead of a manual value.
I tried :
{{ form_widget(myform.field, {'attr': {'class': 'bidding-slider','data-slider-value': '{{ variable }}'}}) }}
but it do not work...
{{ variable }} alone return well the value I need to put in 'data-slider-value'.
Second tried:
I have Extended form class 'TextType' method buildView.
To do that I added this file : src/Form/Extension/TextTypeExtension.php
// src/Form/Extension/TextTypeExtension.php
namespace App\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class TextTypeExtension extends AbstractTypeExtension
{
/**
* Return the class of the type being extended.
*/
public static function getExtendedTypes(): iterable
{
// return FormType::class to modify (nearly) every field in the system
return [TextType::class];
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$tjm = $form->get('dailyrate')->getData();
$view->vars['attr']['data-slider-value'] = $tjm;
}
}
But now I am not sure of the way to use that in my form template...
Thank you very much for helping me.
Alex
Extend your form's class method buildView:
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['attr']['data-slider-value'] = ...//
}

reuse select menu with different ids' and names' in twig

I would like to reuse a select menu, but change every time the id and the name for it in Twig. How could it be done?
I thought of a wrapper block about the select menu and then extend it each time of reuse. But how to set the values for id and name using {{ parent() }}?
I do this by storing and creating the keys in my custom Twig_Extension class, here is a small setup for it. By keeping it global I can ensure unique id's in every view/macro/include ...
example.twig
{% for i in 1..5 %}
<ul id="{{ get_unique_key('my_menu') }}">
<li>Foo</li>
</ul>
{% endfor %}
ProjectTwigExtension
class ProjectTwigExtension extends Twig_Extension {
private $keys = array();
public function getFunctions() {
return array(
new Twig_SimpleFunction('get_unique_key', array($this, 'getUniqueKey')),
);
}
/**
* Create an unique HTML identifier
*
* #param $name String to make unique
*
* #returns String
*/
public function getUniqueKey($name) {
if (!in_array($name, $this->keys)) {
$this->keys[] = $name;
return $name;
}
$i = 0;
while(in_array($name.++$i,$this->keys)) {}
$this->keys[] = $name.$i;
return $name.$i;
}
public function getName() {
return 'ProjectTwigExtension';
}
}
Register extension
$twig = new Twig_Environment($loader);
$twig->addExtension(new ProjectTwigExtension());

Twig extension: render template with variables

I have created a twig extension te render a view with some variables , but I have this error
An exception has been thrown during the rendering of a template ("Notice: Array to string conversion")
in :Backend\twig:activate.html.twig at line 1.
This is the Class extension:
namespace AppBundle\Twig;
class ActivateExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
new \Twig_SimpleFilter('activate', array($this, 'booleanFilter'), array('is_safe' => array('html'),
'needs_environment' => true)),
);
}
public function booleanFilter(\Twig_Environment $twig, $var1, $var2)
{
return $twig->render(':Backend/twig:activate.html.twig', array(
'var1' => $var1,
'var2' => $var2
));
}
public function getName()
{
return 'activate_extension';
}
}
this is the view to be rendred:
//test
{{ var1 }}<br>
{{ var2 }}
and this is how the filter is called in the other view:
{{ entity.activate|activate('test var1', 'test var2') }}
Referring to the TWIG developer guide about Filters:
When called by Twig, the PHP callable receives the left side of the
filter (before the pipe |) as the first argument and the extra
arguments passed to the filter (within parentheses ()) as extra
arguments.
So the first argument of the php function is the value of entity.activate probably the array of the errors, then the other params are passed as arguments. As example you should change your filters as follow:
public function booleanFilter(\Twig_Environment $twig, $activate, $var1, $var2)
{
return $twig->render(':Backend/twig:activate.html.twig', array(
'var1' => $var1,
'var2' => $var2
));
}
Hope this help. Sorry for my comment about twig functions

Check existence of an object inside a for loop in twig

I'm trying to figure out how to check the existance of an object while doing a for loop in twig.
This is how my code looks like.
{% for topic in topics %}
{% set id = topic.id %}
{% set ratings = authRatings.id %}
{% if ratings is defined %}
{% else %}
{% endif %}
{% endfor %}
topic.id is the needle and authRatings is a haystack of different objects named after its topic_id. If an object match the topic.id ratings should be defined.
object
(stdClass)[76]
public '24' =>
object
(stdClass)[77]
public 'topic_id' => string '24' (length=2)
public 'user_id' => string '2' (length=1)
public 'likes' => string '0' (length=1)
public 'dislikes' => string '1' (length=1)
public 'time' => string '1348927295' (length=10)
public '15' =>
object
(stdClass)[78]
public 'topic_id' => string '15' (length=2)
public 'user_id' => string '2' (length=1)
public 'likes' => string '1' (length=1)
public 'dislikes' => string '0' (length=1)
public 'time' => string '1348927363' (length=10)
You could write a custom function that does the job. something like this:
$twig = new Twig_Environment($loader);
$twig->addFunction('searchRatings', new Twig_Function_Function('twig_search_ratings'));
function twig_search_ratings($topic_id, $ratings) {
/* do the search in php */
}

Resources