how to do a facet and aggreagtion query with date in elasticsearch on symfony2 ?
thanks for your help!
Hugo.
I need to have this result : total of server by year.
Here is my code but, I have not of result or in my Twig I don't know how I can view a result.
Thanks.
Hugo
$search = $this->get('fos_elastica.index.serveurs.serveur');
$queryServeurs = new \Elastica\Query\MatchAll();
$elasticaQueryServeurs = new \Elastica\Query();
$elasticaQueryServeurs->setQuery($queryServeurs);
$elasticaQueryServeurs->setSize(0);
$elasticaAggregDateServeur = new \Elastica\Aggregation\DateHistogram('dateHistogram','dateGarantie','year');
$elasticaAggregDateServeur->setFormat("dd-MM-YYYY");
$typesServeursaggregation = new \Elastica\Aggregation\Terms('types');
$typesServeursaggregation->setField("type");
$elasticaAggregDateServeur->addAggregation($typesServeursaggregation);
$elasticaQueryServeurs->addAggregation($elasticaAggregDateServeur);
$resultsServeursDate[] =$elasticaQueryServeurs;
return $this->container->get('templating')->renderResponse('VefsiffReferentielsBundle:stats:stats.html.twig', array(
'serveursdates' => $resultsServeursDate ));
I posted a new code for aggregation with a date of serveur.
One condition for use DateHistogram : the field "dateGarantie" must be of type Date in your file config.yml.
Extract to the file config.yml :
dateGarantie: {type: date}
here extract to the file controller :
$search = $this->get('fos_elastica.index.serveurs.serveur');
$queryServeurs = new \Elastica\Query\MatchAll();
$elasticaQueryServeurs = new \Elastica\Query();
$elasticaQueryServeurs->setQuery($queryServeurs);
$elasticaQueryServeurs->setSize(0);
$elasticaAggregDateServeur = new \Elastica\Aggregation\DateHistogram('dateHistogram','dateGarantie','year');
$elasticaAggregDateServeur->setFormat("YYYY");
$elasticaQueryServeurs->addAggregation($elasticaAggregDateServeur);
$resultsDateServeurs = $search->search($elasticaQueryServeurs);
return $this->container->get('templating')->renderResponse('refBundle:stats:stats.html.twig', array(
'serveursdates' => $resultsDateServeurs->getAggregations()));
finally this is a code for twig. I use the code of Morris for have beautiful graphs :
<div class="col-sm-8">
<h3>Serveurs à changer</h3>
<style>
#serveur2 {
width: 800px;
height: 300px;
margin: auto 0 auto;
</style>
<div id="serveur2"></div>
<script>
// Use Morris.Bar
Morris.Bar({
element: 'serveur2',
data: [ {% for serveursdate in serveursdates.dateHistogram.buckets %}
{x: '{{serveursdate.key_as_string}}' ,Total: {{ serveursdate.doc_count }} },
{% endfor %} ],
xkey: 'x',
ykeys: [ 'Total'],
labels: ['Total Serveur'],
barColors: function (row, series, type)
{
if (type === 'bar')
{
var red = Math.ceil(255 * row.y /this.ymax);
return 'rgb(' + red + ',0,0)';
}
else {
return '#000';
}
}
});
</script>
<hr>
</div
Related
I'm trying to save a pdf with Snappy after executing a service. The pdf is created but dont display the css and the chart as it should. Of course the page got no problem to display everything.
Here my knp_snappy.yaml :
knp_snappy:
pdf:
enabled: true
binary: '%env(WKHTMLTOPDF_PATH)% --enable-local-file-access --enable-javascript '
options:
debug-javascript: true
enable-javascript: true
javascript-delay: 200
no-stop-slow-scripts: true
user-style-sheet: 'public/feuilleCalcul.css'
image:
enabled: true
binary: '%env(WKHTMLTOIMAGE_PATH)% --enable-local-file-access --enable-javascript '
options: []
Here my service pdfGenerator.php :
class pdfGenerator
{
public function __construct(Environment $twig, Pdf $pdf)
{
$this->twig = $twig;
$this->pdf = $pdf;
}
public function generatePdf($file)
{
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
$snappy->setOption('enable-local-file-access', true);
$snappy->setOption('toc', true);
$snappy->setOption('enable-javascript', true);
$snappy->setOption('debug-javascript', true);
$snappy->setOption('javascript-delay', 200);
$snappy->setOption('enable-local-file-access', true);
$snappy->setOption('no-stop-slow-scripts', true);
$snappy = $this->pdf->generateFromHtml($file, 'myproject/somepath/public/asset/Pdf/test.pdf');
return ;
}
}
and here the controller call :
$chart = $graphGenerator->generateCalculGraph($graphData,$arrayToDisplay,$dataSpecificForm);
$file = $this->render('custom/chartTest.html.twig', [
'chart' => $chart,
'graphData' => $graphData,
'arrayToDisplay' => $arrayToDisplay,
'specificData' => $dataSpecificForm,
]);
$pdfGenerator->generatePdf($file);
And the last one : twig template :
{% extends 'base.html.twig' %}
{% block body %}
<link href="{{ asset('feuilleCalcul.css') }}" rel="stylesheet"/>
<center>
<img class="fit-picture"
src="/asset/image.png"
alt="trench logo" width="25%" height="auto" >
</center>
<div class=page1>
<center>
<div class=topBox>
<p>
<strong>Um :</strong> {{specificData.dataSpecificForm.Um}} kV
</br>
<strong>Uac : </strong>{{specificData.dataSpecificForm.Uac}} kV
</br>
<strong>Ud : </strong>{{graphData.ud}} kV
</p>
</div>
etc..
</div>
{{ render_chart(chart) }}
I dont know what i'm doing wrong and as you can see i tried some options every way i found. So can someone explain what is going on ? :)
My first Symfony 5 attempt and I am trying to use Google Charts for the first time. I am using CMENGoogleChartsBundle which provides a Twig extension and PHP objects to display the Charts.
I want to display different Charts which should be accessible through jQuery UI Tabs I also have a table with all the data being displayed. When now clicking on a Tab with a country for example I want the Graph and table updated. I tried to display all the data directly in my twig template (fruitsoverview.html.twig), but when clicking on a tab I would get the page rendered again just below the tabs, plus I would lose any possible set up of my search filter, if the whole page would be re-loaded. I then read that you can create a view just with the content which needs updating so I have done that and in my controller I now have:
if ($request->isXmlHttpRequest())
{
return $this->render('fruits/chart.html.twig', [
'searchFilter' => $searchFilter->createView(),
'fruitCounts' => $fruitCounts,
'barchart' => $barchart
]);
}else{
return $this->render('fruits/fruitsoverview.html.twig', [
'searchFilter' => $searchFilter->createView(),
'fruitCounts' => $fruitCounts,
'barchart' => $barchart
]);
}}
This solved the problem with the rendering, but the chart is not shown. The chart data is available in the twig view but the Graph only gets shown on the default Tab. I do get the table displayed with the correct data. What could be wrong or better question, how is it supposed to be set up correctly. I am sure I have not understood the concept of Symfony yet and probably not of the jQuery ui Tabs. Any help would be highly appreciated. Thank you very much in advance.
UPDATE
I have now updated my code and got partly working besides the barchart. So the twig variables for the table are updating with the new content after the AJAX request, but the barchart does not change. I have done
{{ dump(barchart) }} in the chart.html.twig and I do get the updated data inside of that as well, but the chart is not re-drawn. How can I achieve that?
controller
if ($request->isXmlHttpRequest())
{
$response = new JsonResponse();
$response->setStatusCode(200);
return new JsonResponse([
'html' => $this->renderView('fruits/chart.html.twig', [
'fruitCounts' => $fruitCounts,
'barchart' => $barchart
])
]);
In my main template I have the below code and Javascript. $("div#client-loop-container").html(data.html); seems to update the twig variables in my chart.twig template.
<div id="client-loop-container">
{% include 'chart.html.twig' %}
</div>
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
<script type="text/javascript">
$( function() {
$('#country-tabs a').on('click', function (e) {
e.preventDefault()
$(this).tab('show');
var $this = $(this),
loadurl = $this.attr('href');
var form = $('form');
var jsonData = $.ajax({
url: loadurl,
type: 'POST',
data: form.serializeArray(),
dataType: 'json',
success: function(data, status) {
$("div#client-loop-container").html(data.html);
}
}).responseText;
});
chart.html.twig
<div id="client-loop-container">
<div class="w-100" id="div_chart"></div>
<table class="table">
<tr>
<th>Date</th>
{% for fruitcount in fruitcounts %}
<th>{{ fruitcount }} </th>
{% endfor %}
</tr>
</table>
</div>
{% block javascripts %}
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
{{ gc_draw(barchart, 'div_chart') }}
</script>
{% endblock %}
Here's a possible solution. The code below uses a single <div> for displaying charts but you might be able to adapt to a tab & id="..." solution. The approach uses jQuery.getScript() to run chart-specific javascript. A ChartService uses the cmen/google-charts-bundle to generate the javascript. The script is supplied to the displaying template by a Controller response. There are six different charts that can be displayed. The page displays charts in pseudo-carousel style.
You might ask why go to all this trouble when it's possible to draw all six charts once and use javascript to hide and show one chart at a time. The answer is that charts 2 - n will usually not have all of its options displayed. I developed this solution so that the vertical axis would always show values at grid lines.
ChartService (with chart definitions eliminated for brevity):
namespace App\Service;
use App\Entity\RV;
use App\Entity\Summary;
use CMEN\GoogleChartsBundle\GoogleCharts\Options\VAxis;
use CMEN\GoogleChartsBundle\GoogleCharts\Charts\Histogram;
use CMEN\GoogleChartsBundle\GoogleCharts\Charts\LineChart;
use CMEN\GoogleChartsBundle\Twig\GoogleChartsExtension;
use Doctrine\ORM\EntityManagerInterface;
class ChartService
{
private $em;
private $gce;
public function __construct(EntityManagerInterface $em, GoogleChartsExtension $gce)
{
$this->em = $em;
$this->gce = $gce;
}
public function buildChart($chartType, $class, $subtype = null)
{
switch ($chartType) {
case 'line':
return $this->lineChart($class, $subtype);
break;
case 'histogram':
return $this->histoChart($class);
default:
break;
}
}
private function lineChart($class, $type)
{
...
return $chart;
}
private function histoChart($class)
{
...
return $histo;
}
public function getChartJs($chartSpecs, $location)
{
$chartType = $chartSpecs['type'] ?? null;
$class = $chartSpecs['class'] ?? null;
$subtype = $chartSpecs['subtype'] ?? null;
$chart = $this->buildChart($chartType, $class, $subtype);
$js = $this->getStart($chart, $location);
$end = $this->getEnd($chart, $location);
$part3 = substr($end, 0, strlen($end) - 1);
return $js . $end;
}
private function getStart($chart, $location)
{
return $this->gce->gcStart($chart, $location);
}
private function getEnd($chart, $location)
{
return $this->gce->gcEnd($chart, $location);
}
}
chartSwitch.js
$(document).ready(function () {
var i = $('#currentChart').attr('data-chart');
chartSwitch(i);
$('#chartNext').on('click', function () {
i++;
if (6 === i) {
i = 0;
}
chartSwitch(i);
});
$('#chartPrevious').on('click', function () {
i--;
if (-1 === i) {
i = 5;
}
chartSwitch(i);
});
function chartSwitch(i) {
$('#currentChart').attr('data-chart', i);
var url = "/js/" + i;
$.getScript(url);
}
});
Controller method for /js/ + i
/**
* #Route("/js/{which}", name="js")
*/
public function returnChartJs(ChartService $chart, $which)
{
$available = [
['type' => 'line', 'class' => 'C', 'subtype' => 'Price'],
['type' => 'line', 'class' => 'C', 'subtype' => 'Count'],
['type' => 'line', 'class' => 'B+', 'subtype' => 'Price'],
['type' => 'line', 'class' => 'B+', 'subtype' => 'Count'],
['type' => 'histogram', 'class' => 'C'],
['type' => 'histogram', 'class' => 'B+'],
];
$js = $chart->getChartJs($available[$which], 'chartA');
$response = new Response($js);
return $response;
}
Relevant pieces from displaying template:
<div class="col-9 text-center">
<div class="row">
<div class="col-3"></div>
<div class="col-3">
<span id="currentChart" data-chart="0"></span>
<nav aria-label="Page navigation">
<ul class="pagination">
<li class="page-item">
<a id="chartPrevious" class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">« Chart</span>
<span class="sr-only">Previous</span>
</a>
</li>
<li class="page-item">
<a id="chartNext" class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">Chart »</span>
<span class="sr-only">Next</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
<div class="row">
<div class="col-12">
<div id="chartA"></div>
</div>
</div>
</div>
...
{% block javascripts %}
{{ parent() }}
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
{{ encore_entry_script_tags('charts') }}
{% endblock %}
I want to display a user picture (avatar) and some more fields in the menu.html.twig template.
I know that we can display these fields in a user.html.twig template.
{{ content.user_picture }}
{{ user.getDisplayName() }}
{{ content.field_name_user[0] }}
and etc.
But I want to display these fields in the menu.html.twig template.
As I think. we can make a variable in preprocess_block () and print the desired value.
Or if there is no necessary variable in the template - do it in the preprocessor of this template!
Help please make a decision on this issue. And what code you need to write.
It is better to write a pre-process and define a variable and insert the desired elements in it.
hook_preprocess_menu__menu_name(array &$variables) {
$userDetails = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
/**
fetch the desired elements and pass to $variables eg:
**/
$variables['userPictureUrl'] = $userDetails->user_picture->entity->url();
}
You can use hook_preprocess_menu:
function YOURMODULE_preprocess_menu(&$variables)
{
$uid = \Drupal::currentUser()->id();
if($uid > 0)
{
// Load user
$user = User::load($uid);
$userName = $user->getUsername();
// If user have a picture, add it to variable
if(!$user->user_picture->isEmpty()){
$pictureUri = $user->user_picture->entity->getFileUri();
// Add style to picture
$userPicture = [
'#theme' => 'image_style',
'#style_name' => 'profile_picture',
'#uri' => $pictureUri,
];
}
// Set variables
$variables['MYMODULE'] = [
'profile_name' => $userName,
'profile_picture' => $userPicture,
'profile_id' => $userId
];
}
}
End show in your menu.html.twig file:
{{ YOURMODULE.profile_name }}
{{ YOURMODULE.profile_picture }}
{{ YOURMODULE.profile_id }}
I want to convert a string value to day by tapping in a widget, e.g. 24 → 1
{% block convert_day %}
<td>{{ form_widget(form['crush']) }}</td>
<td><!-- displaying my value in day --></td>
{% endblock %}
No It doesn't work !
To be more clear ,I want to do something like that but just display number of days : http://www.convertworld.com/en/time/Days.html
{{ hourValue / 24 }}
Or, if you want to round the value to two decimal places:
{{ hourValue / 24 | number_format(2, ".", ",") }}
Documentation:
http://twig.sensiolabs.org/doc/templates.html#math
http://twig.sensiolabs.org/doc/filters/number_format.html
edit
Alternatively, you can put the precision into the form instance itself. When you create your form, you're probably doing something like this in your controller (or if the form has its own class, you're doing something similar in the buildForm() method of that class):
$form = $this->createFormBuilder($entity)
->add('name', 'text')
->getForm();
When you add your crush field, you can then specify the number of decimal places that should be represented on the form by including the precision option:
$form = $this->createFormBuilder($entity)
->add('name', 'text')
->add('crush', 'number', array('precision' => 3) )
->getForm();
The form will then round the value before inserting it into the database.
Documentation:
http://symfony.com/doc/current/reference/forms/types/number.html#precision
that's what I did ;
<script type="text/javascript">
function execute_time_ext(clicked) {
if (document.forms && document.forms['show_convert']) {
var from = document.forms['show_convert'].unit_from.value;
var elms = document.getElementsByName('unit');
var amount = document.forms['show_convert']['value'].value;
var to;
var amount_int = ['show_convert']['value'] - 0;
for (var i=0; i<elms.length; i++) {
to = elms[i].value;
convert(show_convert['value'], from, to, false, false);
}
var cookie = 'default_decimals';
if (getCookie(cookie) != decimals) {
setCookie(cookie, decimals, null, '/');
}
} else {
if (clicked) {
alert('Converter error. Conversion not supported by browser.');
}
}
}
execute_time_ext(false);
</script>
widget :
<div onkeyup="execute_time_ext(true)" onchange="execute_time_ext(true)"> {{ form_widget(form['value']) }} {% endblock %}
<div> <input id="value_4" type="hidden" value="365.25|0|4" name="unit"></div>
</div>
I'm trying to create a form which will add a new text box every time the 'Add new box' link got clicked.
I read through the following example.
http://symfony.com/doc/current/reference/forms/types/collection.html
Basically I was following the example from the book. But when the page is rendered and I click on the link nothing happens.
Any thoughts?
Thanks.
This is my controller.
public function createAction() {
$formBuilder = $this->createFormBuilder();
$formBuilder->add('emails', 'collection', array(
// each item in the array will be an "email" field
'type' => 'email',
'prototype' => true,
'allow_add' => true,
// these options are passed to each "email" type
'options' => array(
'required' => false,
'attr' => array('class' => 'email-box')
),
));
$form = $formBuilder->getForm();
return $this->render('AcmeRecordBundle:Form:create.html.twig', array(
'form' => $form->createView(),
));
}
This is the view.
<form action="..." method="POST" {{ form_enctype(form) }}>
{# store the prototype on the data-prototype attribute #}
<ul id="email-fields-list" data-prototype="{{ form_widget(form.emails.get('prototype')) | e }}">
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
Add another email
</form>
<script type="text/javascript">
// keep track of how many email fields have been rendered
var emailCount = '{{ form.emails | length }}';
jQuery(document).ready(function() {
jQuery('#add-another-email').click(function() {
var emailList = jQuery('#email-fields-list');
// grab the prototype template
var newWidget = emailList.attr('data-prototype');
// replace the "$$name$$" used in the id and name of the prototype
// with a number that's unique to our emails
// end name attribute looks like name="contact[emails][2]"
newWidget = newWidget.replace(/\$\$name\$\$/g, emailCount);
emailCount++;
// create a new list element and add it to our list
var newLi = jQuery('<li></li>').html(newWidget);
newLi.appendTo(jQuery('#email-fields-list'));
return false;
});
})
</script>
This problem can be solved by referring to the following link.
https://github.com/beberlei/AcmePizzaBundle
Here you will find the same functionality being implemented.
I've been through this too.
Answer and examples given to this question and the other question I found did not answer my problem either.
Here is how I did it, in some generic manner.
In generic, I mean, Any collection that I add to the form just need to follow the Form template loop (in a macro, for example) and that's all!
Using which convention
HTML is from Twitter Bootstrap 2.0.x
Javascript code is already in a $(document).ready();
Following Symfony 2.0.x tutorial
Using MopaBootstrapBundle
Form Type class
class OrderForm extends AbstractType
{
// ...
public function buildForm(FormBuilder $builder, array $options)
{
// ...
$builder
->add('sharingusers', 'collection', array(
'type' => new UserForm(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required'=> false
));
// ...
}
}
JavaScript
/* In the functions section out of document ready */
/**
* Add a new row in a form Collection
*
* Difference from source is that I use Bootstrap convention
* to get the part we are interrested in, the input tag itself and not
* create a new .collection-field block inside the original.
*
* Source: http://symfony.com/doc/current/cookbook/form/form_collections.html
*/
function addTagForm(collectionHolder, newBtn) {
var prototype = collectionHolder.attr('data-prototype');
var p = prototype.replace(/\$\$name\$\$/g, collectionHolder.children().length);
var newFormFromPrototype = $(p);
var buildup = newFormFromPrototype.find(".controls input");
var collectionField = $('<div class="collection-field"></div>').append(buildup);
newBtn.before(collectionField);
}
/* ********** */
$(document).ready(function(){
/* other initializations */
/**
* Form collection behavior
*
* Inspired, but refactored to be re-usable from Source defined below
*
* Source: http://symfony.com/doc/current/cookbook/form/form_collections.html
*/
var formCollectionObj = $('form .behavior-collection');
if(formCollectionObj.length >= 1){
console.log('run.js: document ready "form .behavior-collection" applied on '+formCollectionObj.length+' elements');
var addTagLink = $('<i class="icon-plus-sign"></i> Add');
var newBtn = $('<div class="collection-add"></div>').append(addTagLink);
formCollectionObj.append(newBtn);
addTagLink.on('click', function(e) {
e.preventDefault();
addTagForm(formCollectionObj, newBtn);
});
}
/* other initializations */
});
The form template
Trick here is that I would have had used the original {{ form_widget(form }} but I needed to add some specific to the view form and I could not make it shorter.
And I tried to edit only the targeted field and found out it was a bit complex
Here is how I did it:
{# All form elements prior to the targeted field #}
<div class="control-collection control-group">
<label class="control-label">{{ form_label(form.sharingusers) }}</label>
<div class="controls behavior-collection" data-prototype="{{ form_widget(form.sharingusers.get('prototype'))|escape }}">
{% for user in form.sharingusers %}
{{ form_row(user) }}
{% endfor %}
</div>
</div>
{{ form_rest(form) }}