Sorting table by its column value in Twig - symfony

I created a method in Twig that will count and display results in table.But I want to sort the table based on results column
{% for island in islands %}
<tr>
<td>{{ island.id }}</td>
<td>{{ island.name }}</td>
<td>{{ number_votes(island.id) }}</td>
</tr>
{% endfor %}
results
id name result
1 name1 3000
2 name2 100
3 name3 5000
4 name4 90
As you can see it is sorted by default based on its id.How to sort based on its result column?
My twig filter
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('number_votes', array($this, 'a'))
);
}
public function getName()
{
return 'app.extension';
}
public function a($id)
{
$qb=$this->em->createQueryBuilder();
$qb->select('count(v.id)')
->from('Bundle:Voters','v')
->join('v.city','c')
->join('c.province','p')
->join('p.region','r')
->join('r.island','i')
->where('i.id = :x')
->setParameter('x',$id);
$count = $qb->getQuery()->getSingleScalarResult();
return $count;
//I tried return sort($count)//not working
}
I also tried
{{ number_votes(island.id)|sort }}/throws an error
I'm afraid I have to create another Twig filter,is there any way to prevent this?

You could avoid a twig filter completely and do something like this so you have your islands and your vote counts in one result set:
$islandsAndVotes = $qb->select('i, count(v.id) as vote_count')
->from('Bundle:Voters','v')
->join('v.city','c')
->join('c.province','p')
->join('p.region','r')
->join('r.island','i')
->groupBy('i')
->orderBy('vote_count', 'DESC')
->getQuery()
->getResult()
;
Each element of $islandsAndVotes will contain an Island entity at index 0 and a vote count at index 1
E.g.
foreach ($islandsAndVotes as $islandVote) {
$island = array_shift($islandVote);
$votecount = array_shift($islandVote);
}
In twig you could use first & last to access the island or the vote count respectively as you iterate over the results.
{% for islandVote in islandsAndVotes %}
{% set island = islandVotes|first %}
{% set voteCount = islandVotes|last %}
<tr>
<td>{{ island.id }}</td>
<td>{{ island.name }}</td>
<td>{{ voteCount }}</td>
</tr>
{% endfor %}

Related

Passing an array to twig and displaying it

I have an array in my controller. I pass this to my twig view along with another array one
user_id = Array ( [0] => abc [1] => Def [2] => Hij )
data = [0] => HelpCenterBundle\Entity\New Object
(
[id:HelpCenterBundle\Entity\New:private] => 5
[userId:HelpCenterBundle\Entity\New:private] => 314
[comment:HelpCenterBundle\Entity\New:private] => 1
)
I want to print it in a table.
{% for countlist in data %}
<tr>
<td>{{ countlist.id }}</td>
</tr>
<tr>
<td> here i want to print first element of user_count </td>
</tr>
{% endfor %}
I tried with a for loop like
{% for first in user_id %}
<td>{{ first }}</td>
But it results in all the contents in a same line. Please help
seems you're trying to pass an array of objects to twig, which can still be done like so:
{% for key,value in array %}
{% value.objectProperty %}
{% endfor %}
if you want the count of something you can do this:
{% value|length %}
it might also help for building html using arrays in twig to dump your values to see what you work with:
{{ dump(array) }}
this shows a nice pretty format of data that's passed to your template.

How to get the number of result? - Pagerfanta and Twig (in Symfony)

In my Demo application, use Pagerfanta to read only a few rows from Database.
public function findAllProductsByCategory($category, int $page = 1): Pagerfanta
{
// DQL = Always select full classes from classes
$query = $this->getEntityManager()
->createQuery('
SELECT p
FROM App\Entity\Product p
WHERE p.category = :category
')
->setParameter('category', $category);
return $this->createPaginator($query, $page);
}
Later in Twig, I loop the preselected results. Everything is fine ...
{% if products.haveToPaginate %}
<div class="navigation text-center">
{{ pagerfanta(products, 'default', {routeName: 'category_show_paginated', 'routeParams' : { 'id': category.id}}) }}
</div>
{% endif %}
<table border="1" cellpadding="5">
<tr>
<th>#</th>
<th>Name</th>
<th>Products</th>
<th>Description</th>
</tr>
{% for product in products %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ product.name }}</td>
<td>{{ product.description }}</td>
</tr>
{% endfor %}
</table>
but on each page, "loop.index" is the same 1,2,3,4 ...
I would like to show the number of results 41,42,43 ... with pagerfanta ;)
Is there a special function?
Thank you
Haven't worked with Pagerfanta for a while, but what you want should be doable with something like this:
{{ pagerfanta.currentPageOffsetStart() + loop.index }}
Example test case: https://hotexamples.com/examples/pagerfanta/Pagerfanta/getCurrentPageOffsetStart/php-pagerfanta-getcurrentpageoffsetstart-method-examples.html

Symfony2, doctrine2, can I return an array as a response in the controller?

I've got an embedded controller that takes in a parameter and retrieves from the db.
{% for key, value in boxes %}
{{ render(controller('ABundle:Reports:getBox', { 'boxnum' : value.boxnum })) }}
<tr>
<td>{{ value.boxnum }}</td>
<td>{{ value.boxname }}</td>
<td>( # of total box )</td>
<td>( # of available box)</td>
</tr>
{% endfor %}
Controller:
public function getBox($boxnum) {
$em = $this->getDoctrine()->getRepository('ABundle:MainBoxes');
$getbox = $em->availBoxes($boxnum);
return new Response(array('getbox' => $getbox)); // this doesn't work obviously...
}
I want the results to retrieve db and print it out in a loop. Something like
<tr>
<td>{{ value.boxnum }}</td>
<td>{{ value.boxname }}</td>
<td>{{ getbox.totalbox }}</td>
<td>{{ getbox.availbox }}</td>
</tr>
I'm getting an error for the wrong response return. It's not allowing me to return an array?
An exception has been thrown during the rendering of a template ("The Response
content must be a string or object implementing __toString(), "array" given.")

Index page go empty in Symfony2

I am trying to fetch the visitors name(from visitors table) corresponding to the visitor id(in entry table).How will i do it?
This is my controller code
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('SystemVmsBundle:EntryDetails')->findAll();
return array(
'entities' => $entities,
);
}
Added one to one mapping
<one-to-one field="visitorId" target-entity="VisitorsDetails">
<join-columns>
<join-column name="visitor_id" referenced-column-name="id"/>
</join-columns>
</one-to-one>
Now my index.html.twig is like
{% for entity in entities %}
<tr>
<td>{{ entity.visitorId }}</td>
<td>{{ entity.sessionId }}</td>
<td>{{ entity.entranceId }}</td>
</tr>
{% endfor %}
But the index page is showing as an empty page. What is the error?
If i remove the '{{ entity.visitorId }}',it works fine! How to show the visitor id and name of the visitor(from visitorDetails table) in the index page?

Editing Twig templates in CKeditor

I'm trying to allow admin users to edit email templates. These templates are stored in the DB as Twig ones. So the variables in them are set as {{ purchase.number }} and there are loops like
{% if cart['shipping'] %}
{% for line in cart['shipping'] %}
<tr>
<td colspan="7">Shipping ({{ line['text'] }})</td>
<td>US${{ line['money'] }}</td>
</tr>
{% endfor %}
{% endif %}
Below is one of the templates where I can reproduce this issue:
<html>
<body>
<h3>Order #{{ purchase.number }} was cancelled</h3>
<p>Order content:</p>
<table>
<tr>
<th>Line</th>
<th>Item #</th>
<th>Product Name</th>
<th>Shipping</th>
<th>UOM</th>
<th>Unit Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
{% for line in cart['cart'] %}
<tr>
<td>{{ line['LineNo'] }}</td>
<td>{{ line['ItemNo'] }}</td>
<td>{{ line['ProductName'] }}</td>
<td>{{ line['Shipping'] }}</td>
<td>{{ line['UOM'] }}</td>
<td>US${{ line['UnitPrice'] }}</td>
<td>{{ line['Quantity'] }}</td>
<td>US${{ line['Subtotal'] }}</td>
</tr>
{% endfor %}
{% if cart['shipping'] %}
{% for line in cart['shipping'] %}
<tr>
<td colspan="7">Shipping ({{ line['text'] }})</td>
<td>US${{ line['money'] }}</td>
</tr>
{% endfor %}
{% endif %}
<tr>
<td colspan="7"><b>Order Item Total:</b></td>
<td>US${{ cart['total'] }}</td>
</tr>
</table>
</body>
</html>
When I just open a page with CKEditor textarea with this template in it, I do no changes to the template and just click on "Source" button and here is how the above mentioned template looks after the click:
<h3>Order #{{ purchase.number }} was cancelled</h3>
<p>Order content:</p>
{% for line in cart['cart'] %} {% endfor %} {% if cart['shipping'] %} {% for line in cart['shipping'] %} {% endfor %} {% endif %}
<table>
<tbody>
<tr>
<th>Line</th>
<th>Item #</th>
<th>Product Name</th>
<th>Shipping</th>
<th>UOM</th>
<th>Unit Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
<tr>
<td>{{ line['LineNo'] }}</td>
<td>{{ line['ItemNo'] }}</td>
<td>{{ line['ProductName'] }}</td>
<td>{{ line['Shipping'] }}</td>
<td>{{ line['UOM'] }}</td>
<td>US${{ line['UnitPrice'] }}</td>
<td>{{ line['Quantity'] }}</td>
<td>US${{ line['Subtotal'] }}</td>
</tr>
<tr>
<td colspan="7">Shipping ({{ line['text'] }})</td>
<td>US${{ line['money'] }}</td>
</tr>
<tr>
<td colspan="7"><b>Order Item Total:</b></td>
<td>US${{ cart['total'] }}</td>
</tr>
</tbody>
</table>
Notice that not only single quote changes to html code, but the main thing is that loops are moved, so it used to be:
{% if cart['shipping'] %}
{% for line in cart['shipping'] %}
<tr>
but becomes:
{% for line in cart['cart'] %} {% endfor %} {% if cart['shipping'] %} {% for line in cart['shipping'] %} {% endfor %} {% endif %}
Why does CKEditor change the source if these entities are NOT html and I don't do any changes, I don't even focus on the field.
I tried using these CKEditor config options:
CKEDITOR.config.enterMode = CKEDITOR.ENTER_BR;
CKEDITOR.config.entities = false;
CKEDITOR.config.forcePasteAsPlainText = false; // default so content won't be manipulated on load
CKEDITOR.config.basicEntities = true;
CKEDITOR.config.entities = true;
CKEDITOR.config.entities_latin = false;
CKEDITOR.config.entities_greek = false;
CKEDITOR.config.entities_processNumerical = false;
CKEDITOR.config.fillEmptyBlocks = function (element) {
return true; // DON'T DO ANYTHING!!!!!
};
But I still experience this. Can anyone advise on the config option or any other workaround, except for not using WYSIWYG. I tried to convince users to edit html/twig, but the just want WYSIWYG. Thanks
One possible workaround for me, was to add the Twig blocks to config.protectedSource:
CKEDITOR.config.protectedSource.push(/\{%\s.+\s%\}/g);
They will be ignored in the WYSIWYG editor, but will still be visible in the source code view.
Additionally you can install the plugin Show protected and there's still a visible hint.
working code is :
CKEDITOR.config.protectedSource.push(/{{[\s\S]?}}/g);
CKEDITOR.config.protectedSource.push(/{\%[\s\S]?%}/g);
CKEDITOR.config.protectedSource.push(/{#[\s\S]*?#}/g);
because we need allow {{ and {% tags for twig
CKEDITOR.config.protectedSource = [
/\{\{[\s\S]*?\}\}/g,
/\{\%[\s\S]*?%\}/g,
/\{\#[\s\S]*?#\}/g,
];
This is why I love Stack Overflow - no matter what you want to ask, someone's probably already asked it! In this case, the answers were close, but for me, adding them as protected source was no good - I wanted to create the twig templates in the string as we use them for Email Templates within our CRM. So, what I did was leave CKEditor to do its thing and then handle it before the template was saved (in our case into the DB, but it could as well be to file).
The function I added is pasted below - feel free to use & abuse as you wish.
This is from our custom Symfony controller onBeforePersist a hook that's called before the entity is persisted... Hopefully its all self explanatory from the code.
Note, the Regex may be a bit dodgy, but appears to work, I'm no Regex expert, so please feel free to suggest a more concise expression.
/**
* Before we persist the data, we need to clean up any twig tags in there as the editor encodes html entities...
*
* #param Request $request
* #param $form
* #param EmailTemplates $entity
*/
public function onBeforePersist(Request $request, $form, $entity)
{
$template = $entity->getView();
$re = '/\{(\{|%)([^{}]|(?R))*(\}|%)\}/';
preg_match_all($re, $template, $matches, PREG_SET_ORDER, 0);
// We only want the first element of each match - I don't like closures as a rule on readability grounds, but this is small enough to be ok.
array_walk($matches,function(&$value) {
if (array($value)) {
$value = $value[0];
}
});
// Now do a replace on them
foreach ($matches as $match) {
$decoded = html_entity_decode($match,ENT_QUOTES);
if ($match != $decoded) {
// Only replace if we have actually changed the string
$template = str_replace($match, $decoded, $template);
}
}
// Update the View...
$entity->setView($template);
}
Similar question CKEditor is escaping html elements
Quick question, the above is a list of config options you have tried one at a time? Having both like this
CKEDITOR.config.entities = false;
CKEDITOR.config.entities = true;
sets entities to true, which is not what you want as it forces html entities in output.
By the way, the other answers are OK but if you have more than one Twig block in the same sentence, are not.
So I definetly recommend to use this Regex instead that works with multiple Twig blocks too:
CKEDITOR.config.protectedSource.push(/\{\{[\s\S]*?\}\}/g);
CKEDITOR.config.protectedSource.push(/\{\%[\s\S]*?%\}/g);

Resources