Symfony workaround for using of protected methods - symfony

Background: symfony3
I have just stuck in the fact that redirectToRoute and addFlash methods in controller are protected in symfony. I have a separate class for action.
namespace AppBundle\Action;
class Base {
public function __construct($controller) {
$this->controller = $controller;
}
}
As you can see base action class requires a controller. Basically it is logical because action class is part of a controller and should have access to all its methods. However I cannot call $this->controller->addFlash as it is protected. If it is protected then there might be some reason for it. I cannot find it. Can you please hint me how I can change my action class so that it could use controller methods.
The variant about extending action from a controller does not fit me as I have additional functionality in the main controller. It is configured in a proper way.
Update: my goal is to devide controller functionality by responsibility. I invented an action class. My end code look like following:
public function editAction() {
$instance = new \AppBundle\Action\MyController\Edit($this);
return $insance->run();
}
In this case I keep controller clean and not verbose.

Here is a link to Symfony Controller Trait, that you can duplicate, if you really want to work that way.
But since you are injecting a whole symfony controller into your own controllers, you will be better off with extending instead. Injection is used here for injecting separate service by their IDs.

Related

Override MVC routes where applicable without inheriting controller

Say I have a controller FooController which has a bunch of action methods. Some of these methods would I like to override, but none of the methods are marked as virtual and I do not want to change the code of FooController.
So I implement a CustomFooController (not inheriting from FooController) and write new versions of the methods that I want.
Now I want to route first to CustomFooController and if the action is not available there I want to default to FooController. I have set up this route config to override the route:
routes.MapRoute(
"customFoo",
"foo/{action}",
new { controller = "CustomFoo", action = UrlParameter.Optional }
);
Here is some example definitions:
public class FooController : Controller
{
public ActionResult Bar { ... }
public ActionResult Baz { ... }
}
public class CustomFooController : Controller
{
public ActionResult Bar { ... }
}
So when accessing /Foo/Bar we should hit CustomFooController.Bar() and when accessing /Foo/Baz we should hit FooController.Baz() since Baz() is not implemented in CustomFooController.
But I get "The resource cannot be found", I understand why, but can I somehow work around it without modifying FooController?
You might want to have a look at HandleUnknownAction() (see msdn reference page).
It is invoked when a request matches a controller, but no method with the specified action name is found in that controller.
So you could override that method in your CustomFooController to redirect to the appropriate action in FooController (or even other controllers based on your custom inspection of request related data).
Redirection can be done with RedirectToAction() (see msdn reference page)
I think I found a neat solution myself. Since I don't want to modify FooController and a bunch of other controllers from another application I'm extending (and relying on updates from) I decided to use extension method MapMvcAttributeRoutes() from System.Web.Mvc.RouteCollectionAttributeRoutingExtensions in my startup RouteConfig in order to first match with route attributes on e.g. my CustomFooController which takes precedence over the conventional routing.
Just to be on the safe side: HandleUnknownAction() is implemented only in CustomFooController. You don't have to modify FooController (or other controllers).

Best place to implement a method that return response in Symfony2/MVC

I got a method able to create a CSV file thanks to StreamedResponse object of Symfony2 framework. I use the method several times so I put a callback parameter to personalise the behavior (I forget the buzz word for this practice in Object-Oriented Programming).
Where is the best place to put this method in a MVC project?
Repository? (Model/DAO/Manager)
Entity? (POPO)
Controller
Service
Through a interface (This object able to create CSV file)
Other
As your logic returns a response, the most adapted context is a controller.
Also, if your logic is called from multiple contexts or by multiple classes of the same context (e.g. controllers), to avoid duplicated code, you have two possibilities (at least) :
1 - Use an AbstractController and make your controllers extends the abstract.
2- Use a service (i.e. CsvManager).
If you want some example implementations, see Symfony2 reusable functions in controllers and the Controller as a service chapter of the Symfony documentation.
An example of service implementation:
// src/AppBundle/Services/CsvManager.php
class CsvManager
{
public function generate(/** params */)
{
// Return your streamed response
}
}
The service declaration :
// app/config/services.yml
services:
# ...
app.csv_manager:
class: AppBundle\Services\CsvManager
Now, you can use the service from all your controllers and other contexts that implements the services container. example:
// src/AppBundle/Controller/TestController.php;
class TestController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
public function printCsvAction()
{
$csvManager = $this->get('app.csv_manager');
return $csvManager->generate(/** params */);
}
}

Symfony2 - What's the difference between DependencyInjection and Services?

Well, the question is in the title, but to sum it up, I'm not sure to understand what's the difference between DependencyInjection and Service, as they are both called the same way (by invoking them using the container, e.g. $this->get('my_service_or_dependency') from a controller).
I looked into the docs but I couldn't find a clear answer to that question.
Thanks for your insights!
TLDR: DI is a design pattern, service is a class
It's hard to compare one to another. Dependency Injection is design pattern in which you don't hardcode dependencies in your class but you inject them into your class (most likely as a parameter to your constructor).
Example (using Dependency Injection):
class someController
{
public function __construct(ObjectManager $doctrine)
{
$this->doctrine = $doctrine;
}
}
The same example without Dependency Injection:
class someController
{
public function __construct()
{
$this->doctrine = new Doctrine();
//there's no such thing as new Doctrine() but this is just an example
}
}
In second example whenever you want to change your ObjectManager implementation (for example switch Doctrine to Propel you need to refactor your code. So your code has dependency hardcoded.
In first example you inject your ObjectManager object, so when you want to switch to another implementation you just change configuration (in Symfony it would be services.yml most likely)
Service in this case is a class (SomeController) which get dependency injected (see doc for better definition)

How can I run a method every page load in MVC3?

With WebForms, if I wanted to run a method on every page load, then I would call this method in the Page_Load() method of the main Master Page.
Is there an alternative, perhaps better solution, when using MVC3?
you could create a class base controller
public class BaseController : Controller
{
public BaseController()
{
// your code here
}
}
and let every new controller of yours impelement the base controller like
public class MyController: BaseController
also i have found the basecontroller very usefull to store other functions i need a lot in other controllers
I think the most appropriate way to do this in MVC is with filters
MSDN provides a good description of them, and there are dozens of articles and explanatins about them on the net, such as this one
EDIT
This sample is even better: It provides a simple action filter, which is then registered in global.asax, and executed on every request, before the actual Action in the relevan controller executes. Such concept allows you to access the request object, and modify whatever you want before the actual controller is executing.
You could put the code in the constructor of the controller.
Like this:
public class FooController : Controller
{
public FooController()
{
doThings();
}

asp.net mvc controller for masterpage? [duplicate]

I'm using a masterpage in my ASP.NET MVC project. This masterpage expects some ViewData to be present, which displays this on every page.
If I don't set this ViewData key in my controllers, I get an error that it can't find it. However, I don't want to set the ViewData in every controller (I don't want to say ViewData["foo"] = GetFoo(); in every controller).
So, I was thinking of setting this in a base controller, and have every controller inherit from this base controller. In the base controller default constructur, I set the ViewData. I found a similar approach here: http://www.asp.net/learn/MVC/tutorial-13-cs.aspx. So far so good, this works... but the problem is that this data comes from a database somewhere.
Now when I want to Unit Test my controllers, the ones that inherit from the base controller call its default constructor. In the default constructor, I initialize my repository class to get this data from the database. Result: my unit tests fail, since it can't access the data (and I certainly don't want them to access this data).
I also don't want to pass the correct Repository (or DataContext, whatever you name it) class to every controller which in turn pass it to the default controller, which I could then mock with my unit tests. The controllers in turn rely on other repository classes, and I would end up passing multiple parameters to the constructor. Too much work for my feeling, or am I wrong? Is there another solution?
I've tried using StructureMap but in the end I didn't feel like that is going to fix my problem, since every controller will still have to call the base constructor which will initialize the repository class, so I can't mock it.
This is a similar question but I find no satisfactory answer was given. Can I solve this in a neat way, maybe using StructureMap as a solution? Or should I jsut suck it and pass a Repository to every controller and pass it again to the base controller? Again, It feels like so much work for something so simple. Thanks!
I see two options:
First:
Set the ViewData for MasterPage in YourBaseController.OnActionExecuting() or YourBaseController.OnActionExecuted():
public class YourBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
Second:
Or create custom filter:
public class DataForMasterPageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
and then apply to your controllers:
[DataForMasterPage]
public class YourController : YourBaseController
{
...
}
I think the second solution is exactly for your case.

Resources