I create my own bundle. There is a class in this bundle.
In this class I have a variable $Url. I would like to use this variable in my controller in another bundle. How do I pass a variable to a controller in a different bundle?
I think the best option is to create a Controller as a Service. You can read about that in a blog article by Richard Miller.
After you have done that, you can create a service parameter with the value of $Url:
# app/config/config.yml
parameters:
my_first_bundle.url: Some value
services:
# request and response services
my.response:
class: Symfony\Component\HttpFoundation\Response
my.request:
class: Symfony\Component\HttpFoundation\Request
# controller services
my_second_bundle.mycontroller:
class: Acme\MySecondBundle\Controller\MyController
arguments: [ %my.response%, %my.request%, %my_first_bundle.url% ]
It sounds like you need to turn that class into a service and set the $url value with dependency injection.. Or you may néed to persist the $url if you plan to modify $url in one controller and access it again in another controller later on. Either way, you'll need to turn that class file into a service.
Related
I am working in a project with several services called inside controllers in this way :
$service = $this->get('myservice');
but i noticed that i could call 'myservice' in this other way:
$service = $this->container->get('myservice');
Services of course take advantage of DI
Example of service declaration:
myservice:
public: true
class: path/to/service
arguments:
- '#someEntityRepository'
someEntityRepository:
class: Doctrine\ORM\EntityRepository
factory: ['#doctrine.orm.entity_manager', getRepository]
arguments: [path\to\Entity\someEntity]
Is there any difference between this calls? If yes, which should i use? Why?
No difference. Use $this->get() when you extend symfony's Controller class. Its shorter to type.
$this->get('myservice'); is a shortcut for $this->container->get('myservice');. And is available in the Controller base class (Symfony\Bundle\FrameworkBundle\Controller).
Petter was right about the difference between $this-get and $this->container->get(), it is no difference. But you will create a good code if you define Controller as service and inject your services via dependency injections. It adds more flexibility and performance.
I am integrating my project with another one
Multiple types were found that match the controller named 'XXXXX'.
This can happen if the route that services this request ('api/{controller}/{action}/{id}') found multiple controllers defined with the same name but differing namespaces, which is not supported.
The request for 'XXXXX' has found the following matching controllers:
COM.example.host.XXXXXController
COM.example.parasite.XXXXXController
Is there a way to make this somehow work with the least amount of edit from either side?
I would like to avoid having to make modifications to all of my controllers.
Thank you
Unfortunately, that is not very simple because default controller selector (DefaultHttpControllerSelector) does not look for namespace in the full controller name when it selects controller to process request.
So, there are at least two possible solutions to your problem:
Write your own IHttpControllerSelector which takes controller type namespace into account. Sample can be found here.
Rename one of controller types to make then unique.
TL;DR Default controller selector uses the cache of controller types (HttpControllerTypeCache) which holds something like:
{
"Customers" : [
typeof(Foo.CustomersController),
typeof(Bar.CustomersController)
],
"Orders" : [
typeof(Foo.OrdersController)
]
}
So it uses controller name as dictionary key, but it can contain several types which have same controller name part. When request is received, controller selector gets controller name from route data. E.g. if you have default route specified "api/{controller}/{id}" then request api/customers/5 will map controller value to "customers". Controller selector then gets controller types which are registered for this name:
if there is 1 type, then we can instantiate it and process request
if there is 0 types, it throws NotFound exception
if there is 2 or more types, it throws AmbiguousControllerException exception (your case)
So.. definition of another named route will not help you, because you can only provide controller name there. You cannot specify namespace.
Even if you'll use Attribute Routing and specify another route for one of the controller actions
[RoutePrefix("api/customers2")]
public class CustomersController : ApiController
{
[Route("")]
public IHttpActionResult Get()
//...
}
you will still face the controller selector problem because attribute routes are applied to actions - there will be no controller value in route data for those requests. Attributed routes are treated differently - they are processed as sub-routes of the "MS_attributerouteWebApi" route.
The easiest path for you might be to add action based routing to your web api:
RouteTable.Routes.MapRoute(
"WithActionApi",
"api/{controller}/{action}/{id}"
);
To your WebApiConfig.cs, be sure to put it above the default rule. Then, change how you call your controllers by including the method name, which at this point should be or should be made unique.
I have a service that needs to access the current application base URL (what's returned by app.request.getBaseURL() in Twig views). Currently, my config is like this:
services:
WidgetModel:
class: AppBundle\Model\WidgetModel
scope: prototype
arguments: ['%widgets%']
So as a second argument, I would like to inject the base URL. Is it possible? If not, what would be proper solution?
As far as I know there is no builtin base url service. Which is actually a bit of a red flag that maybe having your component depending on it might not be such a good idea. But I can't think of a good reason why.
So normally, one would just inject the request object. But that has it's own problems as documented here: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
Instead, inject the #request_stack service and pull the url from it:
class WidgetModel
{
public __construct($widgets,$requestStack)
{
$this->baseUrl = $requestStack->getCurrentRequest()->getBaseUrl();
If you do find yourself needing the baseUrl in multiple services then you could define your own factory type service to generate it. But again, that might mean your design needs rethinking.
You can use the expression language in your service definition.
This example should do what you want:
services:
WidgetModel:
class: AppBundle\Model\WidgetModel
scope: prototype
arguments: [%widgets%, "#=service('request').getBaseUrl()"]
It fetches the request service and then executes the getBaseUrl method.
You will need to add a second parameter in your WigetModel for the base URL.
To complete the answer, in Symfony 3 you can use request_stack to get the base url using expressions languages (updated link) such as:
services:
WidgetModel:
class: AppBundle\Model\WidgetModel
scope: prototype
arguments: [%widgets%,"#=service('request_stack').getCurrentRequest().getBaseUrl()"
I am unable to create Controllers through the Command line. Does anyone know how can I manually create controllers for my Symfony2 project?
What files I need to update?
You should create file YourControllerNameController.php at src/YourBundle/Controller folder. And put class with name YourControllerNameController into this file. Also be assured that you wrote right namespace according to PSR-0.
If you create controller as a service you can define it as a service and have no problem.
If you want create standard controller that are not registered at service container you could extend your class from Symfony\Bundle\FrameworkBundle\Controller\Controller
In both cases you need also define route for your actions. The easiest way would be to define one route with annotation type. It will generate routes automatically based on your annotations related to actions:
your_route_name:
resource: "#YourBundle/Controller"
type: annotation
It will scan your src/YourBundle/Controller folder and will generate new route for any method in any class in any file that will have annotation #Route:
/**
* #Route("/your/path", name="you_can_specify_name_here_but_it_is_optional")
*/
public function yourAction()
{
// Your code here
}
I want a route that will take requests from
/api/v1/job
to
Controller that lives in
/api/v1/jobController
where the controller name is jobv1Controller
configuration.Routes.MapHttpRoute(
"v1",
"Api/v1/{controller}/{id}",
new { controller = "{controller}v1", id = RouteParameter.Optional });
short version:
the in-built DefaultControllerFactory may not be able to create the controller you need, since it treats the value as a plain name string, and doesn't do any token replacement.
the solution would be to extend DefaultControllerFactory and override the CreateController method. Change the controllerName value and then use the base functionality to get you that controller.
Register your custome factory in Global.asax
ControllerBuilder.Current.SetControllerFactory(
typeof(YourCustomControllerFactory));
long version:
As you know the flow is as follows:
Request >> Routing System >> MVC's Default Controller Factory >> Create Controller >> Invoke it.
I don't think it is possible to give dynamic values in the default route data dictionary and expect the default controller factory to find that controller.
This is because the built-in controller factory works off the direct value of the controller key, given in the route data dictionary.
The MVC Handler does no magic there of trying to parse the controller value provided as some formatted string, and associating Url Fragment values (controller/action etc.) in this format. it sees it as a straight up value.
By default, the DefaultControllerFactory is invoked to get the controller type, and it uses the string value as-is to activate a controller type.
So one way to solve your problem is to define a custom factory implementing IControllerFactory
Once you do that, your factory's
CreateController method will be called, with the RequestContext and ControllerName string as the 2 parameters.
The RequestContext has the RequestContext.RouteData.Values dictionary, which has all the routing data, tokens, constraints, namespaces etc.
Once you have the incoming controller name, you can massage it to whatever format you need (controllername + "v1") and then instantiate that controller (using DI containers or Service Locator or Activator.CreateInstance etc. whatever you wish) and return it. Try to use some sort of look-up cache for this.
Once you have implemented your custom factory, you can register your custom controller factory with MVC as follows in Global.asax:
ControllerBuilder.Current.SetControllerFactory(
typeof(YourCustomControllerFactory));