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 */
}
Related
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".
I've created custom fields for a custom post-type using WCK plugin for the fields, and want to get the fields into my Timber Twig template. (I know Timber recommends ACF for this, but am trying WCK first, as this is for a non-profit website and WCK offers things in their free plugin version which ACF doesn't). Here is the relevant line of the php file:
$context['home_page_content'] = Timber::get_posts('post_type=vopthomepage');
and in my twig template I printed:
{{ dump(home_page_content) }}
The relevant parts (I think?) which I get are:
public 'object_type' => string 'post' (length=4)
public 'post_type' => string 'vopthomepage' (length=12)
public 'are_you' => // this is the field group
public 'question_1' => string 'New to the area, looking for a church to join?' (length=46) // this is the field
So to get the field I tried {{ post.question_1 }} , no joy, so then tried {{ post.get_field('question_1') }} I also tried following the advice for ACF with {{ post.meta('are_you').question_1 }} but none of these prints anything on the page.
Here's the full dump:
array (size=1)
0 =>
object(Timber\Post)[5652]
public 'ImageClass' => string 'Timber\Image' (length=12)
public 'PostClass' => string 'Timber\Post' (length=11)
public 'TermClass' => string 'Timber\Term' (length=11)
public 'object_type' => string 'post' (length=4)
public 'custom' =>
array (size=7)
'_edit_last' => string '1' (length=1)
'_edit_lock' => string '1583704531:1' (length=12)
'are_you' =>
array (size=1)
...
'_rsc_content_availability' => string 'everyone' (length=8)
'_rsc_capability' => string '' (length=0)
'question_1' => string 'New to the area, looking for a church to join?' (length=46)
'answer_1' => string 'Take a look under the What we do menu, as well as Churches for info and the locations of buildings.' (length=99)
protected '___content' => null
protected '_permalink' => null
protected '_next' =>
array (size=0)
empty
protected '_prev' =>
array (size=0)
empty
protected '_css_class' => null
public 'id' => int 293
public 'ID' => int 293
public 'post_author' => string '1' (length=1)
public 'post_content' => string '' (length=0)
public 'post_date' => string '2020-03-04 16:42:52' (length=19)
public 'post_excerpt' => string '' (length=0)
public 'post_parent' => int 0
public 'post_status' => string 'publish' (length=7)
public 'post_title' => string 'Home page content' (length=17)
public 'post_type' => string 'vopthomepage' (length=12)
public 'slug' => string 'help' (length=4)
protected '__type' => null
public '_edit_last' => string '1' (length=1)
public '_edit_lock' => string '1583704531:1' (length=12)
public 'are_you' =>
array (size=1)
0 =>
array (size=2)
...
public '_rsc_content_availability' => string 'everyone' (length=8)
public '_rsc_capability' => string '' (length=0)
public 'question_1' => string 'New to the area, looking for a church to join?' (length=46)
public 'answer_1' => string 'Take a look under the What we do menu, as well as Churches for info and the locations of buildings.' (length=99)
public 'post_date_gmt' => string '2020-03-04 16:42:52' (length=19)
public 'comment_status' => string 'closed' (length=6)
public 'ping_status' => string 'closed' (length=6)
public 'post_password' => string '' (length=0)
public 'post_name' => string 'help' (length=4)
public 'to_ping' => string '' (length=0)
public 'pinged' => string '' (length=0)
public 'post_modified' => string '2020-03-08 21:08:08' (length=19)
public 'post_modified_gmt' => string '2020-03-08 21:08:08' (length=19)
public 'post_content_filtered' => string '' (length=0)
public 'guid' => string 'https://mbp-2.local:5757/?post_type=vopthomepage&p=293' (length=65)
public 'menu_order' => int 0
public 'post_mime_type' => string '' (length=0)
public 'comment_count' => string '0' (length=1)
public 'filter' => string 'raw' (length=3)
public 'status' => string 'publish' (length=7)
Your content is in home_page_content which is an array of Timber\Post objects. To get the content you have to loop through them like so:
{% for post in home_page_content %}
{{ post.question_1 }}
{% endfor %}
Alternatively, if you want just the data from a single, specific element in the array you can do something like
{{ home_page_content[0].question_1 }}
where the number, in this case 0, refers to which element you want.
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());
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
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