Reading PersistentCollections in Twig as if they were an Array - php

I have a bidirectional one-to-many relationship between two classes (Protocol and History). While searching for a specific Protocol, I'm expected to see all History entries associated with that protocol.
While rendering my template, I pass the following:
return $this->render('FunarbeProtocoloAdminBundle:Protocolo:show.html.twig', array(
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
'history' => $entity->getHistory(),
)
);
entity->getHistory() returns a PersistentCollection instead of an array, which causes the following to render an error:
{% for hist in history %}
<tr>
<td>{{ hist.dtOcorrencia|date('d/m/Y H:i') }}</td>
<td>{{ hist.dtRetorno|date('d/m/Y H:i') }}</td>
</tr>
{% endfor %}
If instead of $entity->getHistory() I pass $em->getRepository('MyBundle:History')->findByProtocol($entity), it works fine. But I figure the main point of having a bidirectional relationship was to avoid opening repositories and explicitly opening new resultsets.
Am I doing something wrong? How am I supposed to do that?

All I had to do was call the following on my TWIG:
{% for hist in entity.history %}
None of the other answers worked for me. I have to call the property directly in my twig instead of using its getter. Don't know why, but it worked.
Thanks.

Try this:
return $this->render('FunarbeProtocoloAdminBundle:Protocolo:show.html.twig',
array(
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
'history' => $entity->getHistory()->toArray()
)
);

Your code is fine, I always save myself the trouble and get my collections in the twig instead of passing it through my view. You could try this too.
Render code change
return $this->render('FunarbeProtocoloAdminBundle:Protocolo:show.html.twig', array(
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
)
);
You want to access the history directly in your twig.
Twig
{% for hist in entity.getHistory() %}
<tr>
<td>{{ hist.dtOcorrencia|date('d/m/Y H:i') }}</td>
<td>{{ hist.dtRetorno|date('d/m/Y H:i') }}</td>
</tr>
{% endfor %}
If your result after the change is the same, try checking hist for an array, it could be nested! Persistent Collections tend to do that...
{% for history in entity.getHistory() %}
{% for hist in history %}
<tr>
<td>{{ hist.dtOcorrencia|date('d/m/Y H:i') }}</td>
<td>{{ hist.dtRetorno|date('d/m/Y H:i') }}</td>
</tr>
{% endfor %}
{% endfor %}

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.

Laravel & PHP: Cannot reach array items without a trick, why is that?

Introduction to the problem
I have written a class to import a specific CSV file. This class handles everything except moving and reading the imported file. This is done in the controller by using de File facade in Laravel.
The description below only describes one of the arrays, but I use more arrays, and also in the class itself I get the same problem. The problem is that the app crashes with some obscure error everytime I try to access an item in the array by using a string as key value. I tested this in a seperate, non-laravel, php-file and there I don't get an error. It's only in my Laravel app. Strange thing is that I tested the arrays the following way:
a) using array_key_exists - No keys are found (I used both ' and ")
b) using array_keys - This time I get all the keys from the array
c) for example: $report['student_number'] - Doesn't work, I get an error here
d) for example: $report[array_keys($report)[0]] - This works
e) In vanilla PHP I do the same and there I do not get any errors.
The real code
In the controller I handle the upload the following way:
$uploaded_file = $request->file('uploadfile');
$uploaded_file->move(storage_path().'/files', $uploaded_file->getClientOriginalName());
$import_file = storage_path().'/files/'.$uploaded_file->getClientOriginalName();
if(File::exists($import_file) && File::isReadable($import_file)) {
$raw_file_data = File::get($import_file);
$import = ImportStudents::getInstance();
$import_result = $import->import($raw_file_data);
}
In the Import class I have a private property:
private $report = [];
I fill the array with data the following way:
$this->report[$student_number][] = [
'field' => $field,
'db_field' => 'student_number',
'new_value' => $value,
'old_value' => $student_number,
'action' => 'updated'
];
In the class I finaly return this array to the controller:
return $this->report;
In the controller I pass this array to the view:
return view('home.import_report')->with('report', $import_result);
In the blade file I use the following code:
#foreach($report as $report_row)
<td>{{ $report_row['field'] }}</td>
<td>{{ $report_row['db_field'] }}</td>
<td>{{ $report_row['new_value'] }}</td>
<td>{{ $report_row['old_value'] }}</td>
<td>{{ $report_row['action'] }}</td>
#endforeach
The error
The "C:\Windows\Temp\php57CD.tmp" file does not exist or is not
readable.
Remarks
Above is only one part in my code where I get the error. As I mentioned before, I also get the same error in the code of the class.
Specifications
Laravel: 6.x
PHP: 7.3.10
Question
What am I overlooking here? What is the problem?
The solution for the above mentioned problem with showing the report, how could I forget, that I needed an extra foreach loop.
Old code
#foreach($report as $report_row)
<td>{{ $report_row['field'] }}</td>
<td>{{ $report_row['db_field'] }}</td>
<td>{{ $report_row['new_value'] }}</td>
<td>{{ $report_row['old_value'] }}</td>
<td>{{ $report_row['action'] }}</td>
#endforeach
The answer was so obvious already in my own code :-(, this code:
$this->report[$student_number][] = [
'field' => $field,
'db_field' => 'student_number',
'new_value' => $value,
'old_value' => $student_number,
'action' => 'updated'
];
New code (solution)
#forelse($report as $student_number => $report_row)
<tr><td colspan="6" style="color: white; background-color: darkblue; padding: 0;"><h5>{{ $student_number }} - {{ $report_row[0]['name'] }}</h5></td></tr>
#foreach($report_row as $actions)
<tr>
<td></td>
<td>{{ $actions['field'] }}</td>
<td>{{ $actions['db_field'] }}</td>
<td>{{ $actions['new_value'] }}</td>
<td>{{ $actions['old_value'] }}</td>
<td>{{ $actions['action'] }}</td>
</tr>
#endforeach
#empty
<tr><td colspan="6">No changes</td></tr>
#endforelse
But this problem still exists with arrays
a) using array_key_exists - No keys are found (I used both ' and ")
b) using array_keys - This time I get all the keys from the array
c) for example: $report['student_number'] - Doesn't work, I get an error here
d) for example: $report[array_keys($report)[0]] - This works
e) In vanilla PHP I do the same and there I do not get any errors.
What's not working
$student->first_name = $imported_row['Roepnaam'];
But the key 'Roepnaam' does exist in the array.
With array_keys($imported_row) I can see that the key exists.
What is working
$student->first_name = $imported_row[array_keys($imported_row)[1]];
This what puzzles me!

Twig Array to string conversion

This is probably relatively easy to do, but I'm new to twig and I'm frustrated.
I'm adapting code from this answer: https://stackoverflow.com/a/24058447
the array is made in PHP through this format:
$link[] = array(
'link' => 'http://example.org',
'title' => 'Link Title',
'display' => 'Text to display',
);
Then through twig, I add html to it, before imploding:
<ul class="conr">
<li><span>{{ lang_common['Topic searches'] }}
{% set info = [] %}
{% for status in status_info %}
{% set info = info|merge(['{{ status[\'display\'] }}']) %}
{% endfor %}
{{ [info]|join(' | ') }}
</ul>
But I'm getting:
Errno [8] Array to string conversion in
F:\localhost\www\twig\include\lib\Twig\Extension\Core.php on line 832
It's fixed when I remove this line, but does not display:
{{ [info]|join(' | ') }}
Any ideas how I can implode this properly?
** update **
Using Twig's dump function it returns nothing. It seems it's not even loading it into the array in the first place. How can I load info into a new array.
info is an array, so you should simple write
{{ info|join(', ') }}
to display your info array.
[info] is a array with one value : the array info.
You shouldn't really be building complex data structures inside of Twig templates. You can achieve the desired result in a more idiomatic and readable way like this:
{% for status in status_info %}
{{ status.display }}
{% if not loop.last %}|{% endif %}
{% endfor %}
You can user json_encode for serialize array as strig, then show pretty - build in twig
{{ array|json_encode(constant('JSON_PRETTY_PRINT')) }}
if need associative array:
{{info|json_encode(constant('JSON_PRETTY_PRINT'))|raw}}

What is the right way to iterate over nested array in Twig

I have this function on a repository:
public function getSolicitudes($usuario_id)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('su.id, su.fecha_creacion, tt.nombre AS tipo_tramite, tr.nombre AS tipo_registro, es.nombre AS estado_solicitud')
->from("ComunBundle:SolicitudUsuario", "su")
->where('su.usuario = :usuario_id')
->join('su.tipo_tramite', 'tt', \Doctrine\ORM\Query\Expr\Join::INNER_JOIN)
->join('su.tipo_registro', 'tr', \Doctrine\ORM\Query\Expr\Join::INNER_JOIN)
->join('su.estado_solicitud', 'es', \Doctrine\ORM\Query\Expr\Join::INNER_JOIN)
->orderBy('su.fecha_creacion', 'DESC')
->setParameter('usuario_id', $usuario_id);
return $qb->getQuery()->getResult();
}
In Twig template I have this:
{{ entities|ladybug_dump }}
And the output is something like image show:
I'm trying to iterate over the result in Twig template as follow:
{% for solicitud in entities %}
<tr>
<td></td>
<td>{{ solicitud.tramite }}</td>
<td>{{ solicitud.id }}</td>
<td>{{ solicitud.solicitud }}</td>
<td>{{ solicitud.estado }}</td>
<td>{{ solicitud.fecha }}</td>
<td></td>
</tr>
{% endfor %}
But I got this error:
Key "tramite" for array with keys "id, fecha_creacion, tipo_tramite,
tipo_registro, estado_solicitud" does not exist in
/var/www/html/src/RPNIBundle/Resources/views/Listado/index.html.twig
at line 25
What I'm missing here?
It's obvious, you're using the wrong key, as the exception suggests:
Key "tramite" for array with keys "id, fecha_creacion, tipo_tramite, tipo_registro, estado_solicitud" does not exist in /var/www/html/src/RPNIBundle/Resources/views/Listado/index.html.twig at line 25
Your object has a tipo_tramite key, not tramite. You should change the output statements.

Symfony2 TWIG show user from know ID

I have a list of tasks stored in the doctrine database which I return like this:
$task = $this->getDoctrine()
->getRepository('SeotoolMainBundle:Tasks')
->findAll();
return array('form' => $form->createView(), 'message' => '', 'list_tasks' => $task);
In one row the ID of and entry from a second table is stored. I don't want only output the ID but also the Name, Description and so on stored in the other table. With normal PHP & MYSQL this would be done by using JOIN - how can I do it with Symfony & TWIG ?
Twig Output:
{% for task in list_tasks%}
{{ task.Id }}
{{ task.TaskTitle }}
{{ task.TaskDescription }}
{{ task.TaskTypes }} /* Here I want not only get the ID but also other fields stored in the database with TaskType ID = task.TaskTypes */
{{ task.User }} /* Here I want not only get the ID but also other fields stored in the database with User ID = task.User */
{% endfor %}
I am assuming that TaskType and User are entities, which belong within the Tasks entity. In which case, try the following. Note that I am getting just one task with id of 1 in my example:
$task = $this->getDoctrine()->getRepository('SeotoolMainBundle:Tasks')->find(1);
$taskTypes = $task->getTaskTypes();
$user = $task->getUser();
return array(
'form' => $form->createView(),
'message' => '',
'list_tasks' => $task,
'task_types' => $taskTypes,
'user' => $user,
);
And in your Twig:
{% for task_type in task_types %}
{{ task_type.Id }} // or whatever fields a task_type has
{% endfor %}
And the same for the user
Edit:
As you are wanting to have ALL tasks processed at once, I wonder if simply the following willl work:
{% for task_list in task_lists %}
{% for task_type in task_list.taskType %}
{{ task_type.Id }} // or whatever fields a task_type has
{% endfor %}
{% endfor %}
I did it now on this way, but I dont think, that it is 100% correct and conform.
How to do better? Luckily this works for me actually:
$task = $this->getDoctrine()
->getRepository('SeotoolMainBundle:Tasks')
->findAll();
$taskTypes = $this->getDoctrine()
->getRepository('SeotoolMainBundle:TaskTypes')
->findAll();
$user = $this->getDoctrine()
->getRepository('SeotoolMainBundle:User')
->findAll();
return array(
'form' => $form->createView(),
'message' => '',
'list_tasks' => $task,
'list_task_types' => $taskTypes,
'list_user' => $user
);
TWIG:
{% for task in list_tasks %}
Task ID: {{ task.ID }} <br/>
{% for type in list_task_types if type.id == task.tasktypes %}
{{ type.tasktypetitle }} <br/>
{% endfor %}
{% for user in list_user if user.id == task.user %}
{{ user.username }} <br/>
{% endfor %}
<hr/>
{% endfor %}

Categories