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!
Related
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){
...
}});
});
Here is the scheme of the required URLs:
/service/getBalance should map to CustomerController::getBalance
/service/addBalance should map to CustomerController::addBalance
/customer/getBalance should map to CustomerController::getBalance
/customer/addBalance should map to CustomerController::addBalance
Here is a simple controller
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class CustomerController extends Controller {
/**
* #Route("/getBalance")
*/
public function getBalanceAction(Request $request) {
}
/**
* #Route("/addBalance")
*/
public function addBalanceAction(Request $request) {
}
} // class CustomerController
Have tried the following ways. None of them worked.
# rounting.yml
v1:
resource: "#AppBundle/Controller/CustomerController.php"
prefix: /service
type: annotation
v2:
resource: "#AppBundle/Controller/CustomerController.php"
prefix: /customer
type: annotation
loading the same resource with different prefix always overrides the previous occurrence (the last one works). The following also doesn't work for the same reason and have the same behavior.
# rounting.yml
v1:
resource: "#AppBundle/Resources/config/routing_customer.yml"
prefix: /service
v2:
resource: "#AppBundle/Resources/config/routing_customer.yml"
prefix: /customer
# routing_customer.yml
getBalance:
path: /getBalance
defaults : { _controller: "AppBundle:Customer:getBalance" }
addBalance:
path: /addBalance
defaults : { _controller: "AppBundle:Customer:addBalance" }
Third not working option:
# rounting.yml
v1:
resource: "#AppBundle/Resources/config/routing_v1.yml"
prefix: / # evenr putting /service here instead of inside
v2:
resource: "#AppBundle/Resources/config/routing_v2.yml"
prefix: / # evenr putting /customer here instead of inside
# routing_v1.yml
getBalance:
path: /service/getBalance
defaults : { _controller: "AppBundle:Customer:getBalance" }
addBalance:
path: /service/addBalance
defaults : { _controller: "AppBundle:Customer:addBalance" }
# routing_v2.yml
getBalance:
path: /customer/getBalance
defaults : { _controller: "AppBundle:Customer:getBalance" }
addBalance:
path: /customer/addBalance
defaults : { _controller: "AppBundle:Customer:addBalance" }
I actually need to route / and /customer to the same controller:
/getBalance and /customer/getBalance.
I want to give two prefixes for a group of methods. How to do that in Symfony?
Conclusion from my trials, #goto's answer and #Cerad's comments:
The last Example above could have worked if I used different route names. Route names are unique though out the whole project (Not just unique in file). v1_getBalance and v2_getBalance.
The other solution is to use a custom loader as #goto described.
You could do routes this way:
#Route(
"/{subject}/getBalance",
requirements={
"subject": "customer|service"
}
)
in yml:
subject_getbalance:
path: /{subject}/getBalance
requirements:
subject: customer|service
The requirement is safer than nothing: it allows you to route another route like foo/getBalance on another controller (as it does not match the requirement)
EDIT: for your special case, as you need /getBalance to map to your route too you could do:
subject_getbalance:
path: /{subject}/getBalance
default: { _controller: YourBundle:Controller }
requirements:
subject: customer|service
default_getbalance:
path: /getBalance
default: { _controller: YourBundle:Controller, subject: customer }
Edit: the last idea is to a custom route loader (but I've never tried):
class ExtraLoader extends Loader
{
public function load($resource, $type = null)
{
/* ... */
$prefixes = [ 'default_' =>'','customer_' =>'/customer','service_' =>'/service']
// prepare a new route
$path = '/getbalance/{parameter}';
$defaults = array(
'_controller' => 'YourBundle:Actiona',
);
$requirements = array(
'parameter' => '\d+',
);
foreach($prefixes as $prefixName => $prefixRoute) {
$route = new Route($prefixRoute . $path, $defaults, $requirements);
$routes->add($prefixName . 'getBalance', $route);
}
This will allows you to have 3 routes generated:
default_getBalance: /getBalance
customer_getBalance: /customer/getBalance
service_getBalance: /service/getBalance
I defined the following service:
my_project.widget_listing_content_resolver:
class: MyProject\Widget\ListingBundle\Resolver\WidgetListingContentResolver
arguments:
- "#router"
tags:
- { name: my_project.widget_content_resolver, alias: Listing }
And I want to declare an alias of this service, with a different tag:
my_project.widget_domain_operations_content_resolver:
alias: my_project.widget_listing_content_resolver
tags:
- { name: my_project.widget_content_resolver, alias: DomainOperations }
But in my ContentResolverChain, the service aliased "DomainOperations" is not present. Is there a way to solve this ?
EDIT:
I tried the following configuration:
my_project.widget_listing_content_resolver:
class: MyProject\Widget\ListingBundle\Resolver\WidgetListingContentResolver
arguments:
- "#router"
tags:
- { name: my_project.widget_content_resolver, alias: Listing }
- { name: my_project.widget_content_resolver, alias: DomainOperations }
It results that the "my_project.widget_listing_content_resolver" service is only tagged as "Listing". My problem now is: "How to tag a service with multiple tag aliases"
I found the solution to that problem. The service alias was tagged as espected, but the additionnal tag was not read by my CompilerPass:
This was the errored CompilerPass:
$taggedServices = $container->findTaggedServiceIds(
'my_project.widget_content_resolver'
);
foreach ($taggedServices as $id => $attributes) {
$definition->addMethodCall(
'addResolver',
array($attributes[0]['alias'], new Reference($id))
);
}
As you see, it took only the first alias found ($attributes[0])
I had to change it to:
$taggedServices = $container->findTaggedServiceIds(
'my_project.widget_content_resolver'
);
foreach ($taggedServices as $id => $attributes) {
foreach ($attributes as $attribute) {
$definition->addMethodCall(
'addResolver',
array($attribute['alias'], new Reference($id))
);
}
}
I've created the forms using Sonata Admin Bundle. Then I've created my own Controller (TestController) and override the CRUD controller,
I've added a new function in the TestController,
namespace IFI2\MainProjectBundle\Controller;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Bridge\Monolog\Logger;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
//use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class TestController extends Controller
{
public function getProductPricesAction() {
file_put_contents("/Applications/XAMPP/htdocs/IFI2 CMS/Logs.txt","HELO",FILE_APPEND);
return new Response(json_encode($response));
}
}
Then I'm trying to access this function via my javascript Code,
<script type="text/javascript">
function test1() {
$.ajax({
type:"POST",
//dataType: "json",
url: '{{ path('main_project.admin.test')}}',
success: function(successMsg) {
alert("successMsg");
},
error: function(errorMsg) {
alert("errorMsg");
}
});
}
</script>
Here's my routing.yml,
main_project.admin.test:
pattern: /getProductPrices/
defaults: { _controller: IFI2MainProjectBundle:Test:getProductPrices}
I've already had services.yml entry for this entity,
main_project.admin.cobrand:
class: MainProjectBundle\Admin\TestAdmin
arguments: [~, MainProjectBundle\Entity\Test, "MainProjectBundle:Test"]
tags:
- {name: sonata.admin, manager_type: orm, group: admin, label: Test}
calls:
- [setTemplate, [edit, MainProjectBundle:Test:edit.html.twig]]
I'm getting the following error in my response,
There is no _sonata_admin defined for the controller MainProjectBundle\Controller\TestController and the current route main_project.admin.test
Kindly, help me how to embed it ?
Thanks,
Faisal Nasir
Add new route in your Admin method configureRoutes
protected function configureRoutes(RouteCollection $collection)
{
parent::configureRoutes($collection);
$collection->add('get_product_prices');
}
Remove your route main_project.admin.test
New route has $baseRouteName from your admin as prefix and has name:
base_route_name_get_product_prices
using
{{ path('base_route_name_get_product_prices') }}
//or with admin
{{ admin.generateUrl('get_product_prices') }}
In routing.yml add the following:
main_project.admin.test:
pattern: /getProductPrices/
defaults: { _controller: IFI2MainProjectBundle:Test:getProductPrices,"_sonata_admin": "main_project.admin.cobrand" }
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 }) }}