Symfony Undefined variable (contexterror exception) when array empty in controller - php

Here is the thing. A trainer can speak zero to many languages. I want to display the languages for one or several trainers on a page. Note that I'm working on a code I didn't start from scratch.
In my controller :
foreach ($trainers as $trainer){
$trainer_entity = $doctrine->getRepository('AppBundle:Trainer')->find($trainer["id"]);
$listLangues = $this->getDoctrine()
->getManager()
->getRepository('AppBundle:TrainerLangues')
->findByTrainer_id($trainer["id"]);
}
return [
'user' => $user,
'listLangues' => $listLangues,
'trainers' => $trainers,
];
In my twig file:
{% if listLangues %}
<b>Langues</b> :
{% for l in listLangues %}
{{ l.langue.name }}
{% endfor %}
{% endif %}
This works when the person is linked to languages, so listLangues not empty.
Otherwise I get
Notice: Undefined variable: listLangues 500 Internal Server Error -
ContextErrorException
I tried {% if listLangues is defined %} as well, but the system seems to block in the controller. It never happened to me before.
note that initializing $listLangues only leads to no display on the twig page.
Any idea?

what do you really want to display in your view?
Here is a simple tips, you could try in your controller:
/***/
public function yourAction()
{
return $this->render('your-template.html.twig', [
'user' => $this->getUser(),
'trainers' => $this->getDoctrine()->getRepository(Trainer::class)->findAll()
]);
}
/***/
Then, in your view (basic implementation)
{% for trainer in trainers %}
<h4>{{ trainer.name }}</h4>
{% if trainer.languages %}
<ul>
{% for language in trainer.languages %}
<li>{{ language.name }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
Hope, it will help you.

If your array $trainers is empty, the foreach nevers loops so the $listLangues var is never defined.
Try declaring it as an empty array BEFORE the foreach :
$listLangues = array();

You need to instantiate $listLangues because if there isn't trainers the variable can't exist and code is broke.
Try to add this before your foreach:
$listLangues = null;
Or instantiate It with a value or what you want but declare It before your foreach to be sure that exists

Related

How to iterate over many properties in Twig loop?

I have a problem with properties in (probably) Twig. I have controller in Symfony where getCategories(), getWords(), getTranslations() methods (from Doctrine) return the objects (relations). Every property in the controller is an array because I call findAll() method (from Doctrine again) which returns the array. Finally I return all the properties from controller to view (Twig file) where I try display the results by Twig for loop.
The problem is the Twig loop only iterates over flashcards property (I know why ;)) and I have no idea how to make many-properties iterating. I'd like the loop to iterate over all properties returned by the controller.
In the controller foreach loop I tried update the flashcards array with new associative keys such as: category, word and translation so that all the results returned by Doctrine (including relations) are stored in one flashcards property but then Symfony throws exceptions.
I wondered if create one array in the controller to which I would push the flashcards, cateogry, word and translation arrays and then return this one array to the view but I don't think this is good practice.
Here's the controller method code:
public function showAllCards()
{
$flashcards = $this->getDoctrine()->getRepository(Flashcards::class)
->findAll();
foreach ($flashcards as $flashcard) {
$category = $flashcard->getCategories()->getName();
$word = $flashcard->getWords()->getWord();
$translation = $flashcard->getTranslations()->getWord();
}
return $this->render('try_me/index.html.twig', [
'flashcards' => $flashcards,
'category' => $category,
'word' => $word,
'translation' => $translation
]);
}
Here's the Twig loop code:
{% for flashcard in flashcards %}
{{ word }}
<br>
{{ flashcard.pronunciation }}
<br>
{{ flashcard.exampleSentence }}
<br>
{{ category }}
<br>
{{ translation }}
<br>
{% endfor %}
I tried to execute the following controller code...
public function showMeAll()
{
$flashcards = $this->getDoctrine()->getRepository(Flashcards::class)
->findAll();
foreach ($flashcards as $flashcard) {
$flashcards['categories'] = $flashcard->getCategories()->getName();
$flashcards['words'] = $flashcard->getWords()->getWord();
$flashcards['translations'] = $flashcard->getTranslations()->getWord();
}
return $this->render('try_me/index.html.twig', [
'flashcards' => $flashcards,
]);
}
...with the following Twig loop...
{% for flashcard in flashcards %}
{{ flashcard.words }}
<br>
{{ flashcard.pronunciation }}
<br>
{{ flashcard.exampleSentence }}
<br>
{{ flashcard.categories }}
<br>
{{ flashcard.translations }}
<br>
{% endfor %}
...but then Symfony says:
An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Object of class Proxies__CG__\App\Entity\Words could not be converted to string").
Could you give me some tips how to solve this problem, please? I'd like the Twig loop to iterates over many properties (flashcard, word, category, translation). Or write if there's a better solution, please.
Thank you in advance for every answer!
According to your snippets, I'm guessing you want something like the following:
{% for flashcard in flashcards %}
{% for word in flashcard.getWords() %}
{{ word }}<br />
{% endfor %}
{{ flashcard.getPronunciation() }}<br>
{{ flashcard.getExampleSentence() }}<br>
{% for category in flashcard.getCategories()() %}
{{ category.getName() }}<br />
{% endfor %}
{% for translation in flashcard.getTranslations() %}
{{ translation.getWord() }}<br />
{% endfor %}
{% endfor %}
Have a look at this section of the documentation. Basically if you had foo.bar, twig will test if bar is a public property of foo and if not test if there is a public getter, getBar, to fetch bar.
Some sidenotes in both of your loops, the values category, word and translation will only hold the last value of your flashcards, because you are overwriting the value each time.

Avoid repeating the same query in controllers for parent template

I am trying to make a messaging system and I'm facing a small problem. I have a bigger template that displays my menu and my content. The menu includes the number of new messages, and the content can be any page(compose a new message, inbox, sent).
The problem is that I have to render each of the small templates by passing the number of new received messages to each of them, calling the doctrine each time and repeating code. Is there any way to send the number only to the parent template?
Here are my templates:
This is the parent containing the newmsg variable that gives me problems.
{% extends "::base.html.twig" %}
{% block body %}
inbox : {{ newmsg }}
sent
compose
{% endblock body %}
Here is an example of child template:
{% block body %}
{{ parent() }}
{% if messageList %}
{% for message in messageList %}
<li>title = {{ message.title|e }}</li>
<li>cont= {{ message.content|e }}</li>
<li>data= {{ message.date|date('d-m-Y H:m:s') }}</li>
<li>sender= {{ message.sender|e }}</li>
<hr>
{% endfor %}
{% else %}
<div>no messages</div>
{% endif %}
{% endblock body %}
The problem is that each child template is asking me for the newmsg variable
$messages = $this->getDoctrine()->getRepository('MedAppCrudBundle:Message');
$newMessagesNo = count($messages->findBy(array('seen' => '0', 'receiver' => $this->getUser())));
return $this->render(
'MedAppCrudBundle:UserBackend\Message:new.html.twig',
array(
'form' => $form->createView(),
'newmsg' => $newMessagesNo,
)
);
And I have to write this in every single controller. Any way I can shorten this problem?
You could implement a service that gives back the newmsg value and call it on your parent template. Then it would not be necessary to pass the variable.
You can add a service in your bundle's services.yml with something like:
services:
newmessages:
class: Full\Class\Name\NewMessagesService
arguments: ["#doctrine.orm.entity_manager"]
Then, implement the Full\Class\Name\NewMessagesService class. Keep in mind that this class will need a constructor that receives an EntityManager argument. Something like:
<?php
namespace Full\Class\Name;
class NewMessagesService{
private $entityManager;
public function __construct($entityManager){
$this->entityManager = $entityManager;
}
public function methodToCalculate(){
//Perform calculation and return result
}
}
Then, in your parent template, replace {{newmsg} with:
{{ newmessages.methodToCalculate() }}

Twig - Looping through tweet text from twitter API

I am using Abraham's TwitterOAuth library along with Twig to build a mini app where users authorize then search for tweets based on their input.
I can successfully dump the return from the API to my page, but when I try to isolate just the tweet text for each tweet returned I'm having no luck.
Here's the relevant code for my dashboard.php file:
$user = $connection->get("account/verify_credentials");
if(isset($_POST['query'])) {
$query = $_POST['query'];
$statuses = $connection->get("search/tweets", array("q" => "$query"));
echo $twig->render("dashboard.html", array("access_token" => $access_token, "user" => $user, "statuses" => $statuses));
} else {
echo $twig->render("dashboard.html", array("access_token" => $access_token, "user" => $user));
}
And then the problem code in my twig template:
{% if statuses %}
<ul>
{% for status in statuses %}
<li>{{ statuses.text|e }}</li>
{% endfor %}
</ul>
{{ dump(statuses) }}
{% endif %}
The {{ dump(statuses) }} works, but nothing is spit out inside the UL. I'm very new to all of this, so I've just been looking at Abraham's example code here, where he does this:
{% if user.status %}
{{ user.status.text }}
{% else %}
{{ user.description }}
{% endif %}
So I figured I could do the same thing, but it's not working for me. What am I missing?
Well I figured it out by comparing the example responses from the GET account/verify_credentials (the request being sent out in Abraham's user code) and the GET search/tweets.
If you compare the beginnings of the responses, you will notice that there is an extra level to traverse with the search/tweets response.
So instead of doing:
{% for status in statuses %}
<li>{{ statuses.text|e }}</li>
{% endfor %}
I did:
{% for status in statuses.statuses %}
<li>{{ statuses.text|e }}</li>
{% endfor %}
I realize the naming there isn't great, but to break it down for any other newbs like me: the first "statuses" is the variable I sent to twig, the second "statuses" is from the response I'm getting from the API.
In my original code there was nothing to loop through because I hadn't gone far enough down to reach the information about the individual tweets, I was basically trying to loop through the parent.

Symfony2 Variable "name" does not exist

I'm new to Symfony, so this is most certain a simple mistake from my side.
I get the following error: Variable "worker" does not exist.
The template looks like this:
{% extends "NTSBSServiceBundle::layout.html.twig" %}
{% block body %}
<h1>Rapportera</h1>
{% for worker in workers if workers %}
{{ worker.name }}
{% else %}
<em>Det finns inga öppna protokoll för närvarande...</em>
{% endfor %}
{% endblock %}
And the controller method look like this:
/**
* List all open protocols, grouped by worker.
*
* #Route("/", name="report")
* #Method("GET")
* #Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$workers = $em->getRepository('NTSBSServiceBundle:Worker')->findAll();
return array(
'workers' => $workers,
);
}
I have checked, and $workers does contain entities from the database. The twig gets rendered. If I remove the for-loop, naturally the error message disappears.
Hoping that someone can explain to me what I'm doing wrong.
UPDATE:
Have confirmed that the correct controller is used by exiting in indexAction(). If i do a print_r of $workers, I get the following output:
Array
(
[0] => NT\SBSServiceBundle\Entity\Worker Object
(
[id:NT\SBSServiceBundle\Entity\Worker:private] => 2
[name:protected] => Worker 1
[mail:protected] => worker1#example.com
[phone:protected] => 123456789
)
[1] => NT\SBSServiceBundle\Entity\Worker Object
(
[id:NT\SBSServiceBundle\Entity\Worker:private] => 3
[name:protected] => Worker 2
[mail:protected] => worker2#example.com
[phone:protected] => 123456789
)
)
Also I have tried to change the rendering-method by changing from annotation to using the render-method, as such:
return $this->render('NTSBSServiceBundle:Report:index.html.twig',array( 'workers' => $workers ));
You can not do {% for i in x if x %}
You have to do
{% if x | length > 0 %}
{% for i in x %}
instructions
{% endfor %}
{% endif %}
Use twig doc : http://twig.sensiolabs.org/doc
I do always loop through arrays in Twig like that:
{% for b in books %}
{{ b.name }}
{% endfor %}
{% if not books %}
<i>{% trans %}utils.nothing{% endtrans %}</i>
{% endif %}
But your error looks like a variable is missing. What is your error message?
Symfony2 Variable “name” does not exist
or
Variable "worker" does not exist.

If statement when getting data from php in twig

I'm trying to do an if statement with data passed to the twig file from the controller. Below is a line from the controller:
return $this->redirect($this->generateUrl('homepage', array('user' => $user, 'contact' => $contact)));
My goal is to do an if statement with the variable 'contact'. I checked the twig reference and it shows how to do an if statement but that would not work with 'contact'. Below is the code I tried, can somebody tell me what I'm doing wrong?
{% if {{ contact.id }} > 0 %}
{{ contact.addrLineOne }}
{% else %}
--
{% endif %}
You're almost there, just a small syntax modification will make your code working!
{% if contact.id > 0 %}
{{ contact.addrLineOne }}
{% else %}
--
{% endif %}
In Twig, curly braces means that you want to print the value of a variable or an expression. So you're statement in PHP would look like this :
if ((echo contact[id]) > 0)
echo contact[addrLineOne]; // or contact->addrLineOne() according to the context
else
--

Categories