Doctrine - Create dynamical list in View - php

I am trying to create a view for display the total sales for all the suppliers and customers of a shop.
The total sales for a customer are easy to obtain, the problem is when I have to create the columns with the brands, I have been looking through this site, doctrine, etc, But I cannot find any solution.
SaleController.php
/**
* #Route("/widget/summarize")
*
* #return Render
*/
public function widgetSummarizeAction()
{
$suppliers = $this->getDoctrine()->getEntityManager()->createQueryBuilder()
->select(['su.name AS name'])
->from('AppBundle:Supplier', 'su')
->getQuery()
->getArrayResult()
;
$data = $this->getDoctrine()->getManager()->createQueryBuilder()->groupBy('c.name')
->select([
's.id AS id',
's.invoiceDate AS invoiceDate',
'c.name AS customer',
'SUM(ROUND(s.amount * s.price, 2)) AS totalSales',
'su.name AS supplier',
])
->from('AppBundle:Sale', 's')
->leftJoin('s.supplierCustomer', 'sc')
->leftJoin('sc.customer', 'c')
->leftJoin('sc.supplier', 'su')
->getQuery()
->getArrayResult()
;
return $this->render('AppBundle::home_widget.html.twig', [
'title' => 'Total Sales',
'icon' => 'money',
'urlList' => $this->generateUrl($this->getUrlList()),
'rowRoute' => $this->getUrlEdit(),
'data' => $data,
'columns' => [
'customer' => 'customer',
'totalSales' => 'Total Sales'
]
]);
home_widget.html.twig
<div class="panel panel-default home-widget">
<div class="panel-heading">
<h2 class="panel-title pull-left">
{% if icon is defined and icon is not empty %}<i class="fa fa-{{ icon }}"></i>{% endif %}
{{ title }}
</h2>
{% if urlList %}
<a class="btn btn-primary pull-right" href="{{ urlList }}">See All</a>
{% endif %}
<div class="clearfix"></div>
</div>
<div class="panel-body">
{% if data is not empty and data is iterable %}
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for column in columns %}
<td>{{ column }}</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data %}
{% set href = url(rowRoute, {'id': row.id}) %}
<tr>
{% for key,column in columns %}
<td>
<a href="{{ href }}">
{{ row[key] | raw }}
</a>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>

Maybe try get colulmns name by:
$em = $this->getDoctrine()->getManager();
$columns = $em->getClassMetadata(your_entity_name::class)->getFieldNames();
and loop through the columns in twig template.
<thead>
<tr>
{% for column in columns %}
<td>{{ column }}</td>
{% endfor %}
</tr>
</thead>

Related

url valid but view not loading

I've been working on an online learning website for a school project. I've had trouble passing variales when clicking on a lesson, belonging to a section, inside a course. The idea is that on a course page (formation.html.twig), there's a summary of the course, and you can click on the lessons which are in a sidebar menu. It should then display a page with the same menu, and the content of the lesson showing in place of the course summary (lesson.html.twig).
I've updated the controller so the url goes:
formations/consulter-formationid-sectionid-lessonid
In the view, I wrote {{ path('app_formations_lesson', {'formation':formation.id, 'section': section.id, 'id':lesson.id}) }}
It's working alright for the url and the right values show when I click, but the page refreshes and won't load the right twig view (lesson.html.twig), it loads the same view I'm currently on (formation.html.twig).
FormationsController:
#[Route('/formations/consulter-{id}', name: 'app_formations_see')]
public function see($id): Response
{
$formation = $this->doctrine->getRepository(Formation::class)->findOneById($id);
$section = $this->doctrine->getRepository(Section::class)->findAll();
$lesson = $this->doctrine->getRepository(Lesson::class)->findAll();
return $this->render('formations/formation.html.twig', [
'formation' => $formation,
'sections' => $section,
'lessons' => $lesson
]);
}
#[Route('/formations/consulter-{formation}-{section}-{id}', name: 'app_formations_lesson')]
public function seeLesson($id): Response
{
$lesson = $this->doctrine->getRepository(Lesson::class)->findOneById($id);
return $this->render('formations/lesson.html.twig', [
'lesson' => $lesson
]);
}
formation.html.twig
{% extends 'base.html.twig' %}
{% block title %}{{ formation.title }}{% endblock %}
{% block content %}
<div class="formationcontainer text-center">
{% block sidebar %}
{% include "./formations/sidebar.html.twig" %}
{% endblock %}
<h1>{{ formation.title }} par {{ formation.user.firstname }} {{ formation.user.lastname }}</h1>
{{ formation.description }}
<hr>
<h2>Sommaire</h2>
<div class="tableau">
<table class="table">
{% for section in formation.sections %}
<thead class="table-success">
<tr>
<th scope="col">{{ section.name }}</th>
</tr>
</thead>
<tbody>
{% for lesson in section.lessons %}
<tr>
<td>{{ lesson.title }}</td>
</tr>
{% endfor %}
</tbody>
{% endfor %}
</table>
</div>
</div>
{% endblock %}
lesson.html.twig
{% extends 'base.html.twig' %}
{% block title %}titre de la leçon{% endblock %}
{% block content %}
<div class="formationcontainer text-center">
{% block sidebar %}
{% include "./formations/sidebar.html.twig" %}
{% endblock %}
<h1>Nom de la leçon</h1>
<hr>
<h2>Vidéo</h2>
<h2>Contenu</h2>
</div>
{% endblock %}
sidebar block
<!-- Sidear for lesson pages -->
<nav class="flex-shrink-0flex-shrink-0 p-3 bg-white sidenav">
<button class="btn btn-success" id="sidenav-btn" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-expanded="false" aria-controls="collapseOne">
Sommaire
</button>
<div class="list-unstyled ps-0 ul-custom navbar-collapse collapse show" id="sidebarCollapse" aria-expanded="true">
<li class="mb-1">
{% for section in formation.sections %}
<ul class="list-unstyled align-items-center rounded fw-normal">
<li>{{ section.name }}</li>
</ul>
<div>
{% for lesson in lessons %}
<ul class="list-unstyled fw-normal pb-1 small">
<li>{{ lesson.title }}</li>
</ul>
{% endfor %}
</div>
{% endfor %}
</li>
<li class="border-top my-3"></li>
<li class="mb-1">
<ul class="list-unstyled fw-normal pb-1 small">
<li>retour à la liste des formations</li>
</ul>
</li>
</div>
</nav>
Where does this issue come from? How can I display the right view?
Thanks a lot!
edit: I swapped the code in the controller to match the seeLesson function first.
#[Route('/formations/consulter-{formation}-{section}-{id}', name: 'app_formations_lesson')]
public function seeLesson($id): Response
{
$formation = $this->doctrine->getRepository(Formation::class)->findOneById($id);
$section = $this->doctrine->getRepository(Section::class)->findAll();
$lesson = $this->doctrine->getRepository(Lesson::class)->findOneById($id);
return $this->render('formations/lesson.html.twig', [
'lesson' => $lesson,
'formation' => $formation,
'sections' => $section
]);
}
#[Route('/formations/consulter-{id}', name: 'app_formations_see')]
public function see($id): Response
{
$formation = $this->doctrine->getRepository(Formation::class)->findOneById($id);
$section = $this->doctrine->getRepository(Section::class)->findAll();
$lesson = $this->doctrine->getRepository(Lesson::class)->findAll();
return $this->render('formations/formation.html.twig', [
'formation' => $formation,
'sections' => $section,
'lessons' => $lesson
]);
}
It's rendering the wrong page because your URLs are too similar.
When trying to determine what method to execute, Symfony matches the requested URL against the route definitions above the controller methods from top to bottom.
If you're requesting for example /formations/consulter-1-2-3,
Symfony first tries to match this against /formations/consulter-{id}.
This matches if you substitute 1-2-3 for {id}, so it executes the see method.
To fix you have two options:
Either switch the order of the 2 methods in your controller so that the method seeLesson is defined above the see method. Symfony will then first try to match against the route for the seeLesson method.
Or keep the method order as is, but add a constraint to the {id} parameter in the route for the see method, to specify that the matched {id} can only consist of digits:
#[Route('/formations/consulter-{id}', name: 'app_formations_see', requirements: ['id' => '\d+'])]

Tables side by side

How can I put two arrays side by side using twig ?
This is my input
{% for key, item in CompareArray %}
{% for x in item %}
<tr>
{% if key =='last' %}
<td> last</td>
{% endif %}
{% if key == 'primary' %}
<td> primary</td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
The desired output
table,td{
border: 1px solid black;
padding: 1px;
}
<table>
<tr>
<td> last.val1 </td>
<td> last.val2 </td>
<td> last.val3 </td>
<td> #### </td>
<td> primary.val1 </td>
<td> primary.val2 </td>
<td> primary.val3 </td>
</tr>
<tr>
<td> ... </td>
<td> ... </td>
<td> ... </td>
<td> ... </td>
<td> ... </td>
<td> ... </td>
<td> ... </td>
</tr>
<table>
Based on the shared information I see a couple of possible solutions.
But first confirm if I understand correctly what you're trying to achieve.
You want to have table where the item information of the last group is displayed first with a separator column and then the item information of the primary group on the left? And as seen in the screen shot of the array the number of items in the group isn't even necessarily the same?
First think to consider would be should we create two different tables one for primary group and one for last and style them through css to look as if they're in they are side by side.
{% for groupKey, items in CompareArray %}
<table>
{% for item in items %}
<tr>
<td>{{ groupKey }}.{{ item.prop1 }}</td>
<td>{{ groupKey }}.{{ item.prop2 }}</td>
<td>{{ groupKey }}.{{ item.prop3 }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
Second solution would be a complicated use of attribute this means that both arrays have to have same keys. Here you'd iterate over the largest group and then add the corresponding value from the other group. I'm using some macros to better separate the logic. The block named myTemplate is the code you'd insert where you wanted the data to be displayed.
{% block myTemplate %}
{% if CompareArray.last|length > CompareArray.primary|length %}
{{ _self.lastIsLonger(CompareArray.last, CompareArray.primary) }}
{% else %}
{{ _self.primaryIsLonger(CompareArray.last, CompareArray.primary) }}
{% endif %}
{% endblock %}
{% macro lastIsLonger(lastGroup, primaryGroup) %}
{% set primaryLenght = primaryGroup|length %}
<table>
{% for k,item in lastGroup %}
<tr>
{{ _self.buildElementData(item) }}
<td> #### </td>
{{ _self.buildElementData(loop.index <= primaryLenght ? attribute(primaryGroup, k) : {}) }}
</tr>
{% endfor %}
</table>
{% endmacro %}
{% macro primaryIsLonger(lastGroup, primaryGroup) %}
{% set lastGroupLength = lastGroup|length %}
<table>
{% for k,item in primaryGroup %}
<tr>
{{ _self.buildElementData(loop.index <= lastGroupLength ? attribute(lastGroup, k) : {}) }}
<td> #### </td>
{{ _self.buildElementData(item) }}
</tr>
{% endfor %}
</table>
{% endmacro %}
{% macro buildElementData(item) %}
<td>{{ item.prop1 ?? '' }}</td>
<td>{{ item.prop2 ?? '' }}</td>
<td>{{ item.prop3 ?? '' }}</td>
{% endmacro %}
If I understand what you're doing I think a third solution I think would be best. That is that from the PHP side you matched which items you wanted to compare so the data you'd insert would look something like this:
[
[
'last' => [
'prop1' => '...',
'prop2' => '...',
'prop3' => '...',
],
'primary' => [
'prop1' => '...',
'prop2' => '...',
'prop3' => '...',
],
], [
'last' => [
'prop1' => '...',
'prop2' => '...',
'prop3' => '...',
],
'primary' => [
'prop1' => '...',
'prop2' => '...',
'prop3' => '...',
],
]
];
This way you're not relying on dirty tricks and do most of the logic where it belongs. The corresponding generation template generation is thus much more pleasing.
{% block myTemplate %}
<table>
{% for row in CompareArray %}
<tr>
{{ _self.buildElementData(row.last ?? {}) }}
<td> #### </td>
{{ _self.buildElementData(row.primary ?? {}) }}
</tr>
{% endfor %}
</table>
{% endblock %}
{% macro buildElementData(item) %}
<td>{{ item.prop1 ?? '' }}</td>
<td>{{ item.prop2 ?? '' }}</td>
<td>{{ item.prop3 ?? '' }}</td>
{% endmacro %}

Get key name of array value in twig for loop

I'm trying to have twig to output the key name(column name from mysql data).
What I want to do is basicly: <a class="listquestions" href="#" id="{{ key }}"...
Current codebase:
<table id="listquestions" class="table table-striped table-bordered table-hover" cellspacing="0" width="100%">
<thead>
<tr>
{% for key, answer in answers[0] %}
<th>{{ key }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for key,answer in answers %}
<tr>
<td>{{ answer.a_id }}</td>
<td>
<a class="listquestions" href="#" data-name="a_text" data-type="text" data-pk="{{ answer.a_id }}" data-url="{{ path_for('qa.edit') }}" data-title="enter attribute name">
{{ answer.a_text }}
</a>
</td>
<td>
<a class="listquestions" href="#" data-name="a_attribute_name" data-type="text" data-pk="{{ answer.a_id }}" data-url="{{ path_for('qa.edit') }}" data-title="enter attribute name">
{{ answer.a_attribute_name}}
</a>
</td>
</tr>
{% endfor %}
</tbody>
PHP function var_export($data,true) outputs:
array (
0 =>
array (
'a_id' => '1',
'a_text' => 'text',
'a_attribute_name' => 'attr',
),
1 =>
array (
'a_id' => '2',
'a_text' => 'text',
'a_attribute_name' => 'attr',
),
2 =>
array (
'a_id' => '3',
'a_text' => 'text',
'a_attribute_name' => 'attr',
),
)
I tried adding a TwigExtension that does key($answer.a_text) but key() does not work with twig for-loops.
So what am I missing? I can output the key name inside <thead> as you see but I'd like to do this with the second for-loop.
<table id="listquestions" class="table table-striped table-bordered table-hover" cellspacing="0" width="100%">
<thead>
<tr>
{% for key, answer in answers[0] %}
<th>{{ key }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for key,answer in answers %}
{% for field, value in answer %}
<tr>
{% if field == 'a_id' %}
<td>{{ answer.a_id }}</td>
{% else %}
<td>
<a class="listquestions" href="#" data-name="{{ field }}" data-type="text" data-pk="{{ answer.a_id }}" data-url="path_for('qa.edit')" data-title="enter attribute name">
{{ value }}
</a>
</td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>

How can I order my table's columns using a keyword?

My column is currently {article, formation , tous article , tous formation, notif, offre}. I want to make it in this order {article, tous articles , espace&nbsp, formation , tous formation , &nbsp, notif , offre}
This is my code, the td is named: ban.page
<table id="liste-offresemploi" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th width="5">#</th>
<th>Titre 1</th>
<th>Type</th>
<th>Page</th>
<th>pagetest</th>
<th width="20">Statut</th>
<th>URL</th>
<th width="5"></th>
<th width="5" class="header"></th>
</tr>
</thead>
<tbody>
{% if nb_bannieres > 0 %}
{% for bann in bannieres %}
<tr>
<td>{{ bann.idbanniereinterne }}</td>
<td>{{ bann.titre }}</td>
<td>{% if bann.type == 2 %} Publicité {% else %} {% if bann.type == 1 %} Structure {% endif %} {% endif %}</td>
<td id="tri">{{bann.page }}</td>
<td>{% if bann.page == "articles" %} 0 {% else %} {% if bann.page == "toutarticles" %} 1 {% endif %} {% endif %} </td>
<td><a href="#?" class="toogleactif" name="banniereinterne-{{ bann.idbanniereinterne }}" rel="{% if bann.actif %}0{% else %}1{% endif %}">
{% if bann.actif %}
<button class="btn btn-success actifjs">Actif</button>
{% else %}
<button class="btn btn-warning actifjs">Non Actif</button>
{% endif %}
</a>
</td>
<td>{{ bann.url }}</td>
<td><span class="glyphicon glyphicon-edit btnadmin btnedit" title="Editer" rel="table_banniereinterne|id_{{ bann.idbanniereinterne }}"></span></td>
<td><span class="glyphicon glyphicon-trash btnadmin deladmin" title="Supprimer" rel="{{ bann.idbanniereinterne }}|banniereinterne|0"></span></td>
</tr>
An easy solution is to add in your entity a field order (type int) and give the order value
Category | Order
------------ | ------
article | 2
formation | 3
tous article | 1
And then order in your query by this Order column ASC and you will get
tous article
article
formation

Twig template extend ignored

I have 2 Entities: Clinic, Vet, for which I created CRUD templates with app/console generate:doctrine:crud
I first created the Clinic entity and created my "admin template" in AgriHealth/AhpBundle/Resources/views/admin.html.twig and then extend this in
AgriHealth/AhpBundle/Resources/views/Clinic/index.html.twig:
{% extends 'AgriHealthAhpBundle::admin.html.twig' %}
This worked.
Then I created Entity Vet and ran the crud generator. Again I'm extending:
AgriHealth/AhpBundle/Resources/views/Vet/index.html.twig:
{% extends 'AgriHealthAhpBundle::admin.html.twig' %}
But this seems to be ignored, as the layout from my admin template doesn't come through. I have tried:
app/console cache:clear
renaming admin.html.twig: causes an error in both views as expected
I must be missing something? Any ideas?
Twig code below:
src/AgriHealth/AhpBundle/Resources/views/admin.html.twig
{% extends '::base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link href="{{ asset('bundles/agrihealthahp/css/admin.css') }}" rel="stylesheet" />
<link href="{{ asset('bundles/agrihealthsecurity/css/admin.css') }}" rel="stylesheet" />
{% endblock %}
{% block body -%}
<div class="row" id="header">
<div class="small-12 columns">
<h1><a href=""><img src="/bundles/agrihealthahp/images/agrihealth-logo.png" />
<span>Animal Health Planner</span></a></h1>
</div>
</div>
<div class="row" id="menu">
<div class="small-12 columns">
</div>
</div>
<div class="row" id="content">
<div class="small-12 columns">
{% block admin %}{% endblock %}
</div>
</div>
<div class="row" id="black_footer">
<div class="small-12 medium-5 columns footer-black-1">
<div class="moduletable">
<div class="custom">
<p>www.agrihealth.co.nz</p></div>
</div>
</div>
<div class="small-12 medium-7 columns ">
<div class="left footer-black-2">
<div class="moduletable">
<div class="custom">
<p>0800 821 421</p></div>
</div>
</div>
<div class="right footer-black-3">
<div class="moduletable">
<div class="custom">
<p><sup></sup><sup><img style="line-height: 1.1;" src="/bundles/agrihealthahp/images/agrihealth_white.png" alt="agrihealth white"></sup></p></div>
</div>
</div>
</div>
</div>
{% endblock %}
src/AgriHealth/AhpBundle/Resources/views/Clinic/index.html.twig:
{% extends 'AgriHealthAhpBundle::admin.html.twig' %}
{% block admin -%}
<h1>Clinics</h1>
<ul class="actions">
<li>
<a href="{{ path('clinic_new') }}">
Add Clinic
</a>
</li>
</ul>
<table class="records_list">
<thead>
<tr>
<th>Name</th>
<th>Phone</th>
<th>Fax</th>
<th>After Hours</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr>
<td>{{ entity.name }}</td>
<td>{{ entity.phone }}</td>
<td>{{ entity.fax }}</td>
<td>{{ entity.afterhours }}</td>
<td>{{ entity.email }}</td>
<td>
<ul class="actions">
<li>
edit
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
src/AgriHealth/AhpBundle/Resources/views/Vet/index.html.twig:
{% extends 'AgriHealthAhpBundle::admin.html.twig' %}
{% block body -%}
<h1>Vets</h1>
<ul class="actions">
<li>
<a href="{{ path('vet_new') }}">
Add Vet
</a>
</li>
</ul>
<table class="records_list">
<thead>
<tr>
<th>Name</th>
<th>Mobile</th>
<th>Clinic</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr>
<td><a href="{{ path('vet_edit', { 'id': entity.id }) }}">{{ entity.firstname }} {{ entity.lastname }}</td>
<td>{{ entity.mobile }}</td>
<td>{{ entity.clinic }}</td>
<td>
<ul class="actions">
<li>
edit
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Check the source of the page for the stylesheets included in admin.html.twig.
If they are there, the problem is that you are overriding your body block.
Change
src/AgriHealth/AhpBundle/Resources/views/Vet/index.html.twig:
{% extends 'AgriHealthAhpBundle::admin.html.twig' %}
{% block body -%}
to
{% extends 'AgriHealthAhpBundle::admin.html.twig' %}
{% block admin -%}
If your stylesheets are not in the page there must be another problem .

Categories