Symfony Twig datatable - php

I'm building a Product Information Manager (PIM). Trying to build this with the symfony framework. The problem I'm facing at the moment. I'm showing my product information with datatables with the twig file example below.
{% set title = 'Product List' %}
{% extends 'table.twig' %}
{% block h1_page_header %} Product List {% endblock %}
{% block panel_heading %} Product List {% endblock %}
{% block thead %}
<tr>
<th>ID</th>
<th>Name</th>
<th>SKU</th>
<td>Actions</td>
</tr>
{% endblock %}
{% block tbody %}
{% for product in products %}
<tr>
<td>
{{ product.id }}
</td>
<td>
{{ product.name }}
</td>
<td>
SKU
</td>
<td>
<a href="{{ path('app_product_getproduct', {'id': product.id}) }}" class="btn btn-success btn-sm" >
<span class="glyphicon glyphicon-pencil"></span>
</a>
<a href="{{ path('app_product_delete', {'id': product.id}) }}" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?')">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
</tr>
{% endfor %}
The problem I have now is that my database has now almost 60-70K. But my product/list/ page is not rendering due to the 70K product which I want to show in datatables. It works fine for few 2-4k Products but above this it does not render.
I did a research and found out that I can resolve my issue with server side processing. But the question I have is there a better solution / alternative to achieve my goal?

Even if server-side processing will help, some navigators may struggle to render all those elements at once. From a user point of view, that not interesting nether. Nobody will ever scroll through all the 70k products. Users are lazy :)
You should implement pagination, search-bar and filters to render a easy readable short list.
If you like JS you can implement a more elegant solution, like an infinite scroll.

Related

KNP Pagination: How to use javascript to get paginated page contents match a given page

A project has a page that allows edit of a Meal entity. Meal has a ManyToMany relationship with a Food entity. The edit page shows a list of food already selected for the meal, along with a KNP paginated list of foods available. When an available food is clicked on the script rewrites the list of selected foods as well as rewrites the list of available foods. So far so good.
What happens, though, is the script builds the available list from the bottom up. So, for instance, if an item on page 3 is selected, the page selector will show page 3 as active but the contents of the page is that of page 1. I'd like instead to either make page 1 active or the page contents be appropriate to the active page number.
If this makes sense, here's some relevant code. (Note: It may be obvious that I'm not fluent in javascript.)
add-food.js (key variable is pageNumber):
$('td').on('click', function (e) {
foodId = $(e.currentTarget).data('foodid');
mealId = $("#mealid").data("mealid");
var pageNumber = $('li.active').first().text().trim();
var pageLimit = $("#mealid").data("pagelimit");
$packet = JSON.stringify([foodId, mealId]);
$.post('http://diet/meal/' + mealId + '/addFoodToMeal', $packet, function (response) {
editFoods = $.parseJSON(response);
var readyToEat = $.parseJSON(editFoods[0]);
var pantry = $.parseJSON(editFoods[1]);
var table = document.getElementById('ready_foods')
$('#ready_foods tr:not(:first)').remove();
$.each(readyToEat, function(key, food) {
var row = table.insertRow(-1);
var cell = row.insertCell(0);
cell.innerHTML = food;
})
var table = document.getElementById('pantry')
$('#pantry tr:not(:first)').remove();
$.each(pantry.slice(0, pageLimit), function(key, array) {
food = array.split(",");
foodId = food[0];
foodName = food[1];
var row = table.insertRow(-1);
var cell = row.insertCell(0);
cell.innerHTML = foodName;
cell.setAttribute('data-foodid', foodId);
})
});
});
edit.html.twig:
{% extends 'base.html.twig' %}
{% block title %}Edit Meal{% endblock %}
{% block body %}
<h1>Edit Meal</h1>
<div id="mealid" data-mealid="{{meal.id}}" data-pagelimit="{{ pageLimit }}"></div>
{{ form_start(form, {'attr': {'class': 'js-meal-form'}}) }}
<div class="row">
<div class="col-6">
{{ include('meal/_form.html.twig') }}
<table class="table" id="ready_foods">
<thead>
<tr>
<th>Ready to eat</th>
</tr>
</thead>
<tbody>
{% for food in meal.foods %}
<tr>
<td>{{ food.foodName }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="col-6">
{{ include('food/_foods.html.twig') }}
</div>
</div>
<button class="btn btn-info">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}
back to list
{{ include('meal/_delete_form.html.twig') }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js" defer ></script>
<script>
$(document).ready(function () {
$('.js-datepicker').datepicker({
format: 'yyyy-mm-dd'
});
});
</script>
{{ encore_entry_script_tags('add-food') }}
{% endblock %}
_foods.html.twig (where pagination happens)
{# templates/food/_foods.html.twig #}
<h3>Available foods</h3>
<div class="input-group mb-3">
<input type="text"
name="q"
class="form-control"
value="{{ app.request.query.get('q') }}"
placeholder="Search..."
>
<div class="input-group-append">
<button type="submit"
class="btn btn-outline-secondary">
<span class="fa fa-search"></span>
Search
</button>
</div>
</div>
{% if pagination is not empty %}
<table class="table" id="pantry">
<thead>
<tr>
<th>Click name to add to meal</th>
</tr>
</thead>
<tbody>
{% for food in pagination %}
<tr>
<td data-foodid={{ food.id }}>{{ food.foodName }}</td>
</tr>
{% else %}
<tr>
<td colspan="3">no records found</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ knp_pagination_render(pagination) }}
{% endif %}
Turns out there's a simple way to make page 1 of the pagination active - since the first page will be rendered by the script. Two lines:
$('li.active').removeClass('active');
$('li.page-item:nth-of-type(2)').addClass('active');

Get the id of an element of a GROUP_CONCAT?

I made a query to group all the Audits belonging to a specific Area, like in the next query.
SELECT id_Area as idArea, area.Name as area_name, GROUP_CONCAT(id_Audit) as Audits
FROM helios.fsa_audits
LEFT JOIN helios.fsa_areas area USING (id_Area)
WHERE id_Auditor='4'
GROUP BY id_Area;
As result I get this:
I made this to get a datatable in the view like this:
But What I need is to get the id of the Audit at the moment of click each button in the Auditorias.
I'm working with symfony, and I render the datatable like this:
<table aria-describedby="dataTable_info" cellspacing="0" class="table table-hover dataTable" id="dataTable" role="grid" style="width:100%;" width="100%">
<thead>
<tr>
<th>Areas</th>
<th>Auditorias</th>
</tr>
</thead>
<tbody>
{% for audit in auditsByArea %}
<tr>
<td>{{ audit.area_name }}</td>
<td>
{% for i in 1..4 %}
<input class ="auditBtn mx-2" value="{{'W' ~ i }}" href="" data-id="{{ audit.Audits }}" data-area="{{ audit.area_name }}"id="auditBtn" name="auditBtn" type='text' readonly data-toggle="modal" data-target="#auditModal"></input>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
But it does not put the one id per button(Audit), it assigns all the id's in all the buttons:
data-id="10,11,12,13"
Any idea or suggestion about how to get it?
I'm not in symfony but you should explode your group_concat result
my_array = explode({{ audit.Audits }});
then in you loop refer to the content on the array
<td>
{% for i in 1..4 %}
<input class ="auditBtn mx-2" value="{{'W' ~ i }}" href="" data-id="{{ my_array[i] }}" data-area="{{ audit.area_name }}"id="auditBtn" name="auditBtn" type='text' readonly data-toggle="modal" data-target="#auditModal"></input>
{% endfor %}
</td>

PHP, symfony list data in table

This is a project for school!!!
Let's say that I want to store and show working hours in an HTML table.
I want something like, 'if' the date from DB which user added is equal to the date from the weekly calendar than show me an EDIT button else show me an ADD button?
I want to show Add for the rest of the users who didn't create any data in DB.
<table class="table" id="week">
<thead>
<tr>
<th scope="col">users</th>
{% for day in days %}
<th class="{{ day|date("D-d") }}">
<span>{{ day|date("D") }}</span>
<span>{{ day|date("d") }}</span>
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="days">
<th scope="row">{{ users.username }}</th>
{% for day in days %}
<td id="my_cell {{ day|date("d-m") }}">
{% for time in findTime %}
{% if d|date("d-m-Y") == time.date|date('d-m-Y') and users.id == time.getUser().id %}
{{ time.timeFrom|date("H:i") }} - {{ time.timeTo|date("H:i") }}
{% endif %}
{% endfor %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
I am currently using DB doctrine ORM on symfony 3.4v
I don't have an idea how to solve this problem, I tried with different array function in PHP but I did not find solutions.
Thank you for understanding.

Can't include a template multiple time in Twig

I need to include a template multiple time in Twig.
Here is an explanation of my case:
I have the original template, lets say index.html.twig. This template include diffirent templates : part1.html.twig part2.html.twig etc..
My goal is to include a final template extra.html.twig to each sub templates (part1, part2). I've made the test, who concluded that I can include the final template just once. Twig detects that this template is already concluded and don't display it twice or three.
How can I achieve this result ?
|index.html.twig
|include part1.html.twig
|include extra.html.twig
|include part2.html.twig
|include extra.html.twig
UPDATE :
{% block file_upload %}
<div class="col-sm-12">
<div class="upload_files" style="float: right">
{{ form_widget(upload_form.button) }}
</div>
{{ form_widget(upload_form.attachment) }}
<table class="table table-files-preview" hidden>
<thead>
<td>{% trans %}NAME{% endtrans %}</td>
<td>{% trans %}TYPE{% endtrans %}</td>
<td>{% trans %}SIZE{% endtrans %}</td>
</thead>
<tbody class="tbody-file-preview">
</tbody>
</table>
<input type="hidden" id="files_source" value="{{ source }}">
</div>
{% endblock %}

How use rendered variables in a foreach Symfony 2 ~ twig

its not the first time I met this issue and I cannot fix it!
Actually, I'm rendering a template with a controller who give to the rendered page many variables. One of them, called $categories, in it, There are many of Category objectes so, one of them its a Collection what references to another Category.
The point is, I'm try to do this code, but obviusly I get an error because im trying to print as a String a Collection
{% for category in categories %}
<tr>
<td>{{ category.name }}</td>
<td>{{ category.description }}</td>
<td>{{ category.isPublic }}</td>
<td>{{ category.parentCategory }}</td>
<td>{{ category.childrens }}</td>
<td>
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
<i class="fa fa-times" aria-hidden="true"></i>
</td>
</tr>
{% endfor %}
So, I decided to try something like:
{% for category in categories %}
<tr>
<td>{{ category.name }}</td>
<td>{{ category.description }}</td>
<td>{{ category.isPublic }}</td>
<td>{{ category.parentCategory }}</td>
<td>
{% for children in {{ category.childrens }} %}
children.name
{% endfor %}
</td>
<td>
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
<i class="fa fa-times" aria-hidden="true"></i>
</td>
</tr>
PROBLEM:
I don't know how use a rendered variable in a foreach, im gettin this error:
A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "punctuation" of value "{" in AppBundle:admin:category/listCategory.html.twig at line 55.
{{ }} or {% %} or {# #} are Twig's open and close tags. Similair to <?php ?> in PHP code. Once you use an open tag, the text is parsed by Twig until the close tag is found (this is also how things are done in PHP, the only difference is that Twig has a different tag to echo stuff).
Once open, you don't have to reopen it again. You don't want to dump category.childrens, you want to use it in the for loop. So instead of doing: {% for children in {{ category.childrens }} %}, use {% for children in category.childrens %}.
(you can compare this to PHP, doing
<?php foreach (<?php echo $category->childrens ?> as $children) { ?>
doesn't make much sense).
The error probably comes from this line:
{% for children in {{ category.childrens }} %}
This is not a valid syntax, the {{ }} can't be used inside another Twig tag.
The following code should work:
{% for children in category.childrens %}
To be honest I have 0 experience with the twig templating langauge, so I may be wrong here, but my experience with other languages tells me the following code:
{% for children in {{ category.childrens }} %}
children.name
{% endfor %}
Should probably look like this:
{% for children in category.childrens %}
{{ children.name }}
{% endfor %}

Categories