I need to add a FlashBag code $session->getFlashBag()->add('foo', $bar); to every controller, along with the code required to get $bar. I am wondering if there is a better way then copying+pasting the code into every controller? Would there be some sort of master controller?
I'd recommend you to create a listener that will run before every controller that you indicate. Following this guide will show everything you need to set it up:
http://symfony.com/doc/2.0/cookbook/event_dispatcher/before_after_filters.html
http://symfony2.ylly.fr/symfony2-simulate-preexecute-postexecute-filters-actions-jordscream/
You should try implementing a service and registering it for onCoreController, then do $event->getController()->preAction() (or whatever function name you want...) , then you can implement those methods in the controllers that you need functionality in
something like
src/My/Bundle/RequestListener.php:
public function onCoreController(FilterControllerEvent $event) {
$evntController = $event->getController();
if (method_exists($evntController[0], 'beforeFilter')) {
$evntController[0]->beforeFilter();
}
}
Look here for more info
http://symfony.com/doc/2.0/book/internals.html#the-event-dispatcher
http://symfony.com/doc/2.0/book/internals.html
http://symfony.com/doc/current/cookbook/service_container/event_listener.html
Related
I followed the symfony 4 documentation to make a login form (https://symfony.com/doc/current/security/form_login_setup.html) and I added a registration form in the same controller.
I'm a beginner, and I would like to make an account page where the user will be able to change his informations, but I would like to know if I should create a new Controller who work with the user entity, on just work onthe same controller than the login and registration ?
Or maybe my user controller have to inherit the securityController?
I'm a noob, sorry ^^'
Thank you
You can give a look at https://symfony.com/doc/current/service_container.html#creating-configuring-services-in-the-container
The path is creating your own service(s), for example App\Servie\UserManager that performs every task on a User object
For example, you could have:
App\Service\UserManager
class UserManager
{
// ...
public function handleUpdatePasswordRequest(Request $request) {...}
// or
public function handleUpdatePasswordForm(Form $form) {...}
// or:
public function handleUpdatePassword(User $user, $newPlainPassword) {...}
...
}
as to say, whatever you want to implement, keeping in mind that the thinner the controllers are better it is, while services can grow (and be split) indefinitely
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.
I have a manager and inside a method.
Inside this method, I want to add a listener connect to Doctrine and use this :
$dispatcher = $this->container->get("event_dispatcher");
$dispatcher->addListener(Events::onFlush, function (Event $event) {
var_dump('test');die;
});
But in this case nothing append.
Do you have a solution to accomplish this ? I read a lot of time documentation about listener but I don't understand why didn't work :/
(and I don't want to use EventSubscriber class)
Thank you
Your code seems good, you have to use the doctrine event dispatcher and not the Symfony one.
$doctrineEventManager = $this->em->getEventManager();
$doctrineEventManager->addEventListener(Events::onFlush, New YourEventListenerClass()});;
Context
I have a custom Event Entity which has several child Entities: Problem and Maintenance (and few others but those two should be enough to describe the problem) entity classes inherit from Event entity class.
The addAction(), seeAction() and modifyAction() of ProblemController and MaintenanceController are (obviously) very similar but with some differences.
I want to have a button to display the see view of an Event, no matter if it is a Problem or a Maintenance. Same for modify.
For the add action it is a bit different: the user has to say (by clicking on child-specific button) what kind of child he want to add.
How I handle this so far
In my seeAction() and modifyAction(), I just forward the "call" depending on the type of the child:
public function seeAction(Event $event)
{
if($event instanceof \Acme\EventBundle\Entity\Problem){
return $this->forward('AcmeEventBundle:Problem:see', array('event_id' => $event->getId()));
}
elseif($event instanceof \Acme\EventBundle\Entity\Maintenance){
return $this->forward('AcmeEventBundle:Maintenance:see', array('maintenance_id' => $event->getId()));
}
}
I have no Event::addAction() but I have a Event::addCommon() which gathers the common parts of the addAction of Problem and Maintenance. Then I call this Event::addCommon() with Controller inheritance.
class ProblemController extends EventController
{
public function addAction(MeasurementSite $measurementSite)
{
$problem = new Problem();
$problem->setMeasurementSite($measurementSite);
$form = $this->createForm(new ProblemType($measurementSite), $problem);
$response = parent::addCommon($problem, $form);
return $response;
}
Problem
All this looks pretty ugly to me. If I want to share common things between Problem::seeAction() and Maintenance::seeAction(), I will have to call an Event function, but Event already forwarded something!! Information jumps from Parent to Child and vice versa...
I would like to know what is the proper way to manage this problem?
I looked a bit at setting Controller as a service, using PHP Traits, Routing inheritance but I couldn't extract anything clear and clean from this research...
I can see how you might end up chasing your tail on this sort of problem.
Instead of multiple controllers, consider have one EventController for all the routes along with individual ProblemHelper and MaintainenceHelper objects. The helper objects would have your add/see/modify methods and could extend a CommonHelper class.
Your controller would check the entity type, instantiate the helper and pass control over to it.
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.