class AdminExtension extends \Twig_Extension
{
// content...
}
I'm extending twig functionality by adding a class that adds new twig method. Inside this method I want to use current route. How can I do that?
I mean, for example, having:
www.someurl.com/prefix/controller1/aaa/bbb/ccc/ddd
I want to get controller1/aaa/bbb/ccc/ddd part inside the function I described.
Thanks!
You should register the extension as a service and then inject request_stack (as of Symfony 2.4) in the service:
my_extension:
class: ...
arguments: ["#request_stack"]
tags: [{ name: twig.extension }]
Then you can get the request by using RequestStack#getCurrentRequest() and you can get the current url by using Request#getUri().
Related
I've finally got my first API in Symfony3 which is actually working.
Very well so far but problems came when I tried using YML configs for routing instead of annotation. It is driving me crazy because it seems to work, in fact when I chance for eg the controller name, it gives me an internal server error but, when everything is "correct", it seems it's unable to find the method in my controller.
So here is my code:
General routing settings
#config/routing.yml
user_routes:
resource: "#AppBundle/Resources/config/user_routes.yml"
type: rest
Bundle routing settings
#AppBundle/Resources/config/user_routes.yml
user:
type: rest
resource: AppBundle\Controller\UserController
And finally my controller:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use FOS\RestBundle\View\View;
use AppBundle\Entity\User;
class UserController extends FOSRestController
{
// /**
// * #Rest\Get("/user")
// */
public function getAction()
{
$restresult = $this->getDoctrine()->getRepository('AppBundle:User')->findAll();
if ($restresult === null) {
return new View("there are no users exist", Response::HTTP_NOT_FOUND);
}
return $restresult;
}
}
Using annotation it works well and everybody's happy but when I use these settings I get a 404. I tried adding prefix: /api and name_prefix: api_ according to Symfony official documentation but it didn't work.
I tried also adding a defaults: { _controller: AppBundle:Controller:UserController:get but error 404 was always round the corner.
As I said, if I change the name of the controller class in the user_routes.yml, I get a 500 error so it seems the routing is being read but it's evident that something is missing here and I'm unable to find it neither on the official documentation nor in other places.
Change the name of the action from this:
public function getAction()
to this:
public function getUsersAction()
DOCUMENTATION
Well, just in case it may become useful for someone, I found the solution.
The problem lay in the yml user_routes.yml.
Here is the correct configuration:
path: /user
defaults: { _controller: AppBundle:User:get }
requirements:
_method: GET
That's it ;)
I am quite new to Symfony 3. I created the controller files in the AppBundle itself. No new bundle created. Now I am trying to route a new page and it gives an error. This is what I did.
In src\AppBundle\Controller\Patient\PatientController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* #Route("/patient", name="patient_homepage")
*/
public function patientHomeAction(Request $request)
{
// replace this example code with whatever you need
return $this->render('Patient/home.html.twig', array()
);
}
In the app\Resources\views\base.html.twig
Home
This gives an error as
"An exception has been thrown during the rendering of a template
("Unable to generate a URL for the named route "patient_homepage" as
such route does not exist.") in base.html.twig at line 118."
Do I need to do any changes in the app\config\routing.yml also? What am I missing here.
Try this
app:
resource: "#AppBundle/Controller/"
type: annotation
You have to enable route in app/config/routing.yml
app:
resource: "#AppBundle/Controller/Patient/"
type: annotation
Q1.I want to count the unread messages before every page rendered ,and add the number into twig template.i don't know how to make it
Q2.i have read the symfony tutorial,it seems that i will make a service ,but i don't know how to do it.is the following code right? and i don't know what to write at the seconde argument
namespace App\RepairBundle\Service;
use Symfony\Component\DependencyInjection\ContainerInterface;
use App\RepairBundle\Entity\RepairMessageRepository;
class CountUnReadMessage
{
protected $container;
protected $messageRepository;
public function __construct(ContainerInterface $container,RepairMessageRepository $messageRepository)
{
$this->container = $container;
$this->messageRepository = $messageRepository;
}
public function countUnRead()
{
$number = $this->messageRepository->countByUnread($this->container->get('security.token_storage')->getToken()->getUser()->getId());
return $number;
}
}
parameters:
app.repair.count_unread_message: App\RepairBundle\Service\CountUnReadMessage
services:
app.repair.count_unread_message:
class: %app.repair.count_unread_message%
arguments:
- #service_container
- #
If piece of the twig template containing message counter similar in all templates, you can move it to separate template and call service inside this template. You steps to achieve this might look like this:
Write service for getting message counter (you almost got it, but try to avoid injecting whole container in servce since it is a very bad practice. In this case, i think you could inject only security.token_storage)
Make this service visible in twig templates by declare it in config file.
config.yml
twig:
globals:
count_read_message: #app.repair.count_unread_message
In your separate twig file call this service
message_block.html.twig
{{ count_read_message.countUnRead() }}
And include this twig file to needed template (better idea would be keep main template for most of templates and include you file in this template, but this dependenced of template structure)
I hope you got the main idea =)
P.S. Answering for Q2 - it is #doctrine.orm.entity_manager
If you want to inject repository make another service with your repository:
app.message_repository:
class: Doctrine\ORM\EntityRepository
factory: ["#doctrine.orm.default_entity_manager", getRepository]
arguments:
- App\RepairBundle\Entity\RepairMessage
Then in your service:
app.repair.count_unread_message:
class: %app.repair.count_unread_message%
arguments:
- #service_container
- #app.message_repository
BTW you don't need container, inject only security.token_storage instead of container.
From the official documentation (http://symfony.com/doc/current/quick_tour/the_view.html#embedding-other-controllers) I learned how to embed a Controller in a Twig template.
The problem appears when the Controller has injected properties. Is there a way to use Twig's render(controller()) function with Controllers that have a constructor?
When I try following:
{{ render(controller(
'SomeBundle:Some:renderList',
{ 'request': app.request }
)) }}
I get this error:
Missing argument 1 for SomeBundle\Controller\SomeController::__construct()
Constructor for this Controller look like that:
public function __construct($container, SomeService $someService) {
parent::__construct($container);
$this->someService = $someService;
}
Injection of the container and someService is configured in service.yml.
So again, my question is: how to embed controller in Twig's template when this controller uses Dependency Injection?
UPDATE
I could do like so:
{{ render(app.request.baseUrl ~ '/some/route') }}
But I would like to avoid making routes.
UPDATE 2
Service definition from service.yml
some.controller:
class: SomeBundle\Controller\SomeController
arguments: ["#service_container", "#some.service"]
If you have defined your controller as a service, you need to "inject" it into twig in order to make it available and correctly instantiated.
I suggest to create twig global variable
#app/config/config.yml
twig:
globals:
cc: "#comparison.controller"
Then you can use one of the methods (actions?)
{{ cc.foo(aBarVariable) }}
Alternative answer
You could create a twig extension where you could inject your service in order to make it available for views
For controllers as service you just need to use the service name (#some.controller) and action (yourAction) rather than the controller shortcut (SomeBundle:Some:renderList) as you can see in the Sylius templates.
So it would be...
{{ render(controller('some.controller:yourAction')) }}
If you are Symfony 2.4+ you can make use of the request_stack to get the request rather than passing it in as an argument like..
$request = $this->container->get('request_stack')->getMasterRequest();
I'm working on Symfony 2.3 and I declared a new route and new controller, but when I call this controller from the browser I get this error:
The controller for URI "/user/1" is not callable. in /dev.mydomain.org/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php at line 82
This is my simple route configuration:
user_homepage:
pattern: /user
defaults: { _controller: UserBundle:Default:index }
user_show:
pattern: /user/{id}
defaults: { _controller: UserBundle:Default:show }
requirements:
id: \d+
And this is my very simple controller:
public function showUserAction($id)
{
return $this->render('UserBundle:Default:show.html.twig', array());
}
What is wrong?
The logical name UserBundle:Default:show refers to UserBunde\Controller\DefaultController::showAction you have a method called showUserAction.
Either change the method name to showAction or change the logical name to UserBundle:Default:showUser.
Although not relevant to the example given, this error can also be caused if the controller Action is not public
You're defining your controller function as showUserAction while in the definition your saying it is show[Action].
Either change your route configuration
user_show:
pattern: /user/{id}
defaults: { _controller: UserBundle:Default:showUser }
requirements:
id: \d+
or change your controller signature
public function showAction($id)
{
See if this helps
After big search, this worked for me:
1.- Create CRUDController
// src/Acme/DemoBundle/Controller/CRUDController.php
namespace Acme\DemoBundle\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Inter\PimeBundle\Entity\Empresa;
class CRUDController extends Controller
{
public function publicarAction($id)
{
//code here...
}
}
2.- Create the service
# app/config/config.yml
services:
ebtity.admin.service:
class: Acme\DemoBundle\Admin\EntityAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: group, label: label }
arguments:
- NULL
- Acme\DemoBundle\Entity\Entity
- AcmeDemoBundle:EntityAdmin
3.- Create the template for the action button
{# src/Acme/DemoBundle/Resources/views/CRUD/list__action_publicar.html.twig #}
<a class="btn btn-sm" href="{{ admin.generateObjectUrl('publicar', object) }}">Publicar</a>
4.- Configure route
// src/Acme/DemoBundle/Admin/EntityAdmin.php
namespace Acme\DemoBundle\Admin;
// ...
use Sonata\AdminBundle\Route\RouteCollection;
class EntityAdmin extends Admin
{
// ...
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('engine')
->add('rescueEngine')
->add('createdAt')
->add('_action', 'actions', array(
'actions' => array(
'publicar' => array(
'template' => 'AcmeDemoBundle:CRUD:list__action_publicar.html.twig'
)
)
));
}
protected function configureRoutes(RouteCollection $collection)
{
$collection
->add('publicar',
$this->getRouterIdParameter().'/publicar',
array('_controller' => 'AcmeDemoBundle:CRUD:publicar')
);
}
}
5.- Clear cache
Hope it helps
Not the case here. But there is another related issue:
If you forget the 'Action' suffix all will work. But when you realized that you forget the suffix and then add it ... surprise! Same error as the OP.
The problem here is about caching
Symfony creates two file for caching urls:
AppDevUrlGenerator.php
AppDevUrlMatcher.php
If you change your action name (i.e. adding 'Action' suffix) then that cache info is obsolete.
Solution
php app/console cache:clear
Similar to the accepted answer, if your controller is defined as a service, e.g. (in YAML):
services:
user.default:
class: \UserBundle\DefaultController
And your route uses this controller service:
user_show:
pattern: /user/{id}
defaults: { _controller: user.default:showUserAction }
requirements:
id: \d+
Then it's necessary to name the action method in full including the Action suffix, otherwise you will get the "controller ... is not callable" error.
In my case, i was using symfony 2.
Prior version of symfony maintain method naming convention. Method suffix should contain Action word.
example:
in route yml file the method definition was
docudex_report_make_authorization:
pattern: /make-authorization
defaults: { _controller: DocudexReportBundle:Default:makeAuthorization }
and in the controller the method was
public function makeAuthorization(){}
therefore i was getting the error.
After changing the method name to public function makeAuthorizationAction it worked perfectly.
I would like to share my experience & how I solved it:
I was importing one bundle in an application whose routes were defined using annotations and they were importing fine in application too by using:
auth_bundle_routes:
# loads routes from the given routing file stored in some bundle
resource: '#XyzAuthBundle/Controller/'
type: annotation
But since my bundle controllers were defined using services, Symfony was showing error:
The controller for URI "/facebook/request-code" is not callable.
Controller Xyz\AuthBundle\Controller\FacebookController" has required
constructor arguments and does not exist in the container. Did you
forget to define such a service?
I updated my bundle for routing to use routing.yaml file over annotations and referring to controllers as service::method syntax and then this error is gone.
xyz_auth.facebook_request_code:
path: /facebook/request-code
controller: xyz_auth.controller.facebook_controller::requestCode
Bundle routes must be imported using
auth_bundle_routes:
resource: '#XyzAuthBundle/Resources/config/routing.yaml'
Hope this will save someone's time.
The same issue could happen if your env uses .env.local.php and new changes are added to env files without running composer dump-env
One of the reasons for this error is that you missed to add the word "Action" after the controller's method name. If you think that your routing is OK, check the method name.