Calling controller from twig - symfony

I'm currently discovering Symfony, and I have some trouble calling a function from one of my controllers.
> class CsvController extends Controller {
public function generateCsvAction(){
$conn=$this->get('database_connection')
$results=$conn->query("
SELECT *
FROM external_attribute;
")
$response=new StreamedResponse();
$response->setCallback(function() use($results)){
$handle=fopen("/home/basile/Documents/backend/src/CampaignBundle/Controller/test.csv","w+");
fputcsv($handle,array('test1
test2,
test3,
test4,
test5,
test6,
test7,
test8')
),';');
}
fclose($handle);
}
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition','attachment; filename="test.csv"');
return $response;
}
I already set everything in my routing.yml :
export_csv:
defaults: { _controller: CampaignBundle:Controller:CsvController.php }
and now I want to call it from a button in a file named "index.html.twig" . I know we can render from the controller some variables and array, but here i directly want to call a function
If you have any kind of idea, it would be very welcomed !

To directly call a controller from your template :
{{ render(controller(
'NameOfYourBundle:NameOfYourClass:NameOfYourFunction'
)) }}
If controller needs parameters (eg) :
{{ render(controller(
'NameOfYourBundle:NameOfYourClass:NameOfYourFunction', { 'id': 3 }
)) }}

you can execute the function directly from your view:
{% render "YourBundle:Csv:generateCsv" with { 'url': 'export_csv' } %}
By JS and AJAX :
$("#button").on('click', function() {
$.ajax({
url: {{render(controller("YourBundle:Csv:generateCsv")) }},
success: function(result){
...
}});
});

Related

How to make symfony Controller variable a global?

public indexFunction(){
$var = 'Apple';
//set $var global
}
So that $var can be accessed in base template.
if you want it as static value just put it in the config file:
# app/config/config.yml
twig:
globals:
var: 'Apple'
if you want it as dynamic variable you can give the twig the service id:
twig:
globals:
# the value is the service's id
var: '#AppBundle\Service\yourData'
You have to pass the variable to the template:
// AppBundle/Controller/DefaultController.php
public indexFunction()
{
$var = 'Apple';
return $this->render('index.html.twig', array(
'var' => $var,
));
}
If you really want to have a global variable available to all of your templates you should set it in your twig config:
# app/config/config.yml
twig:
globals:
var: 'value'
after that you can write out the value of the variable in the twig template with {{ var }}
EDIT:
Or finally example using session - saving the variable into the session
// AppBundle/Controller/DefaultController.php
public indexFunction()
{
$var = 'Apple';
$session = $this->get('session');
$session->set('var', $var);
...
}
after that you can retrieve it in the twig template like this
{# /app/Resources/views/base.html.twig #}
{{ app.session.get('var') }}
Dynamic:
In the bundle path (Example: src/AppBundle) add a file called "YourNameTwigExtension.php". This file will be a class (YourNameTwigExtension) that extends "\Twig_Extension" class and implements the interface "\Twig_Extension_GlobalsInterface". Inside the "YourNameTwigExtension" class implement the getGlobals() method. Example:
//....
class YourNameTwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface {
//...
public function getGlobals() {
$data = array();
$data['user'] = $this->session->get('user');
$data['menu'] = $this->session->get('menu');
$data['actions'] = $this->session->get('actions');
$data['view'] = $this->session->get('view');
return $data;
}
}
In TWIG you will use: {{ user }}, {{ menu }}, {{ actions }}, etc...
Warning: Add in app/config/services.yml:
twig.extension.yourname_twig_extension:
class: YourBundle\YourNameTwigExtension
tags:
- { name: twig.extension }
Static:
If you need to use the static variables, in app/config/parameters.yml:
twig:
globals:
portal_name: 'Portal'
portal_img_logo: logo.png
portal_favicon: favicon.ico
logowidth: 350px
Symfony Doc: How to Inject Variables into all Templates (i.e. global Variables)
Sorry my English.. Good Work!

what is the difference of subscribing collection under waitOn , subscriptions and onBeforeAction

I would like to the difference if any in the following ways of subscribing data,
using waitOn
waitOn:function(){
Meteor.subscribe('//some published function)
}
using onBeforeAction
Router.onBeforeAction : function(){
Meteor.subscribe('//some published function)
}
using subscriptions
subscriptions: function() {
this.subscribe('items');
}
If you want to publish data only for authorized users, it is possible that you check (if user is authenticated) for a route, in onBeforeAction. Something like:
Router.map(function(){
this.route('home', {
path : '/',
template : 'home'
});
this.route('register', {
path : '/register',
template : 'register'
});
this.route('login', {
path : '/login',
template : 'login'
});
this.route('requestlisting', {
path : '/requestlisting',
template : 'requestlisting',
waitOn : function(){
return Meteor.subscribe('RequestsPublication');
}
});
...
});
var requireLogin = function () {
if(!Meteor.user()){
if(Meteor.loggingIn()){
this.render(this.loadingTemplate);
}else{
Router.go('login');
}
} else {
this.next();
}
}
Router.onBeforeAction(requireLogin, {only: ['requestlisting',...]});
In this example, in onBeforeAction, routing occurs for 'requestlisting' only if a user logged in, after then it makes sense to subscribe to any data.

Handlebar.js custom IF helper

I need to write a custom handlebar helper that checks query params an either shows a field, or doesn't depending.
Unfortunately it's returning true or false to the view and outputting it, instead of evaluating true and showing the fields inside the if, or false and not showing anything.
Any help is very welcome.
{{#sortedByDeals}}
<span class="deal-percentage">( {{ percent_off }} % OFF ds: {{ deal_score }})</span>
{{/sortedByDeals}}
Handlebars.registerHelper('sortedByDeals', function() {
console.log("helper method called")
var sortBy = getURLParameter('sort_by');
if(sortBy === "deals") {
console.log("true")
return true
} else {
return false
}
});
Answered my own, by reading the docs more http://handlebarsjs.com/block_helpers.html#conditionals.
Handlebars.registerHelper('if', function(conditional, options) {
// console.log("helper method called")
console.log(conditional)
var sortBy = getURLParameter('sort_by');
if(sortBy === "deals") {
// console.log("true")
return options.fn(this);
} else {
// console.log("else")
return options.inverse(this);
}
});
and in the template
{{#if sortedByDeals}}
<span class="deal-percentage">( {{ percent_off }} % OFF ds: {{ deal_score }})</span>
{{/if}}
Basic block helper will work without using conditional helper. See more here
Handlebars.registerHelper('sortedByDeals', function() {
console.log("helper method called")
var sortBy = getURLParameter('sort_by');
if(sortBy === "deals") {
return options.fn(this);
}else {
return options.inverse(this);
}
});
In template,
{#sortedByDeals}}
<span class="deal-percentage">...</span>
{{/sortedByDeals}}

How to write a controller in Symfony2 to edit content with Jeditable

I'm using jeditable plugin for JavaScript and I want to implement it in my Symfony2 project. I want to edit a name with the plugin and that name to be edited in the database, too, not the change to be gone when I refresh the page, but in my case, it's gone. :(
I'm almost sure that the controller shouldn't be in that way and the problem is from it, but how exactly to write it? Here it is:
public function editCategoryAction(Request $request, $id)
{
$category = $this->repository->find($id);
$form = $this->createForm(new CategoryType(), $category);
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$this->em->persist($category);
$this->em->flush();
return $this->redirect($this->generateUrl('categories'));
}
}
return $this->render(
'AcmeBudgetTrackerBundle:Categories:categories.html.twig', array(
'form' => $form->createView()));
}
This is my template:
<a href="{{ path('edit_category', { 'id': cat.id}) }}">
<strong class="edit">
{{ cat.name }}
</strong>
</a>
<script>
var token = "{{form._token.vars.value}}";
var path = "{{ path('edit_category', { 'id': cat.id}) }}";
</script>
And this is in the .js file:
(function(){
$('.edit').editable(function (value, settings) {
var data = {};
data[this.id] = value;
data["_token"] = token;
console.log(path);
console.log(data);
$.post(path, data);
return(value);
}, {
indicator:'Saving...'
});
}) ();
The output in the console looks fine:
/BudgetTracker/web/app_dev.php/edit_category/52
Object {: "Edu", _token: "9d29860b59ccafbc265ea12346c91fa7e378cc97"}
but the problem is that nothing is posted to the database and when I hit refresh the change I made is gone.
Can you please help me to solve this? Thanks in advance! :)
I think you don't need to use the form component here, you only want to handle a string. So I'll explain a way to do.
JavaScript:
$('.edit').editable(path);
Controller:
public function editCategoryAction(Category $category)
{
//check if exists/valid
//get the text sent from jeditable
$name = $this->request->get('value');
$category->setName($name);
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
//return the name value to jeditable so it can display it
return new Response($name);
}
Twig:
<strong class="edit">{{ cat.name }}</strong>
<script>
var path = "{{ path('edit_category', { 'id': cat.id}) }}";
</script>
jEditable sends edited text named as 'value', so you can get it in the controller. In your controller, you implicitly use paramconverter to get the category from the id in the URL. And that should be OK now.
Note that you can use FORJsRoutingBundle, if you want to avoid mixing twig with javascript in order to access path.

Symfony2: use object to set route parameters

I have a route with 2 parameters:
BBBundle_blog_show:
pattern: /{id}/{slug}
defaults: { _controller: BloggerBlogBundle:Blog:show }
requirements:
_method: GET
id: \d+
Both params are properties of an object blog.
I would like to set up a custom mapper (route generator), so that I can write this:
{{ path('BBBundle_blog_show', {'blog': blog}) }}
instead of this:
{{ path('BBBundle_blog_show', {'id':blog.id, 'slug':blog.slug) }}
This is what I came up with eventually:
I implemented by own generator base class that looks for 'object' parameter and tries to get required parameters from that object.
//src/Blogger/BlogBundle/Resources/config/services.yml
parameters:
router.options.generator_base_class: Blogger\BlogBundle\Routing\Generator\UrlGenerator
//src/Blogger/BlogBundle/Routing/Generator/UrlGenerator.php
namespace Blogger\BlogBundle\Routing\Generator;
use Symfony\Component\Routing\Generator\UrlGenerator as BaseUrlGenerator;
use Doctrine\Common\Util\Inflector;
/**
* UrlGenerator generates URL based on a set of routes.
*
* #api
*/
class UrlGenerator extends BaseUrlGenerator
{
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
{
if (isset($parameters['object']) && is_object($parameters['object'])) {
$object = $parameters['object'];
$parameters = array_replace($this->context->getParameters(), $parameters);
$tparams = array_replace($defaults, $parameters);
$requiredParameters = array_diff_key(array_flip($variables), $tparams);
$parameters = $this->getParametersFromObject(array_flip($requiredParameters), $object);
}
return parent::doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute);
}
protected function getParametersFromObject($keys, $object)
{
$parameters = array();
foreach ($keys as $key) {
$method = 'get' . Inflector::classify($key);
if (method_exists($object, $method)) {
$parameters[$key] = $object->$method();
}
}
return $parameters;
}
}
Now I can write: {{ path('BBBundle_blog_show', {'object': blog}) }} and it will get required parameters (id, slug) from object.
A while ago, I decided I was annoyed by being unable to pass objects as route parameters. I had to concern myself with knowledge of routes and the exact parameter values within templates and other things generating those routes.
I've build this bundle for symfony, which allows you to use and extend this ability (Symfony 2.7 and higher). Please take a look: https://github.com/iltar/http-bundle. It's also available on Packagist as iltar/http-bundle.
The best thing about this bundle is that you don't need to use another router object or generator. It's just turning on the bundle, adjusting the config to your needs if the defaults don't work out for your preferences and you're good to go. The readme should explain everything you need to know but here's a snippet:
Old style:
/**
* #Route("/profile/{user}/", name="app.view-profile")
*/
public function viewProfileAction(AppUser $user);
// php
$router->generate('app.view-profile', ['user' => $user->getId()]);
// twig
{{ path('app.view-profile', { 'user': user.id }) }}
{{ path('app.view-profile', { 'user': user.getid }) }}
{{ path('app.view-profile', { 'user': user.getId() }) }}
{{ path('app.view-profile', { 'user': user[id] }) }}
New style:
// php
$router->generate('app.view-profile', ['user' => $user]);
// twig
{{ path('app.view-profile', { 'user' : user }) }}

Resources