In FOSUserBundle, i need to override FOSUserBundle RegistrationController because i need to add this:
if($user->getType()=="Student") {
$user->addRole("ROLE_Student");
}
else {
$user->addRole("ROLE_TEACHER");
}
It works when i add it in vendor--->...---->registrationcontroller. That's why i need to override registration controller, but how?
Do not override the controller. You should use the event system! Create an event handler which subscribe to FOSUserEvents::REGISTRATION_COMPLETE and then perform the role addition.
Documentation:
Symfony: EventDispatcher component
Symfony: Using events
FOSUserBundle: Hooking into the controllers
The listener:
class RegistrationListener implements EventSubscriberInterface
{
public function __construct(/* ... */)
{
// ...
}
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_COMPLETE => 'addRole',
);
}
public function addRole(FilterUserResponseEvent $event)
{
$user = $event->getUser();
// Add the role here
// ...
}
}
The service definition:
<service id="my_app.event.registration" class="MyApp\Event\RegistrationListener">
<tag name="kernel.event_subscriber" />
<!-- ... -->
</service>
Related
I have a subscriber that is listening to HttpCacheHitEvent and I would like to find the navigationId for the requested page.
For storefront events I use $event->getRequest()->getRequestUri(). But for this event i get URLs like /navigation/5943fc.... I currently use the basename() function to get the navigationIds of those URLs but that does not seem to be the clean way to do it.
Is there an alternative way to retrieve the navigationId from a HttpCacheHitEvent?
When you subscribe to this event you can't access the _route and other parameter attributes as usual, as the cached response will be returned before they are usually set.
$request = $event->getRequest();
var_dump($request->attributes->get('_route'));
// null
To solve that issue, you may inject the router service when registering your listener.
<service id="Foo\MyPlugin\CacheHitListener">
<argument type="service" id="router"/>
<tag name="kernel.event_subscriber"/>
</service>
In your listener you then retrieve your route parameters using the service and the request object from the event, so you can determine which route is being requested. Depending on the route, you can then go ahead and use parameters of the specific route.
class CacheHitListener implements EventSubscriberInterface
{
private $matcher;
/**
* #param UrlMatcherInterface|RequestMatcherInterface $matcher
*/
public function __construct($matcher)
{
$this->matcher = $matcher;
}
public static function getSubscribedEvents(): array
{
return [HttpCacheHitEvent::class => 'onCacheHit'];
}
public function onCacheHit(HttpCacheHitEvent $event): void
{
if ($this->matcher instanceof RequestMatcherInterface) {
$parameters = $this->matcher->matchRequest($event->getRequest());
} else {
$parameters = $this->matcher->match($event->getRequest()->getPathInfo());
}
if ($parameters['_route'] === 'frontend.navigation.page') {
$navigationId = $parameters['navigationId'];
//...
}
}
}
I try to save initial value for user field in UserService entity. The reason is, I use this entity in EasyAdminBundle and when I build a form, I want to set a default value for user_id (ManyToOne to User entity).
init entity manager in constructor,
I override save method.
I get user from security session context and set to user service object, persist and flush.
...but I still can't see a change during save.
class UserServiceRepository extends ServiceEntityRepository
{
protected $_em;
public function __construct(RegistryInterface $registry)
{
$this->_em = $this->entityManager;
parent::__construct($registry, UserService::class);
}
// I override save method:
public function save(UserService $userService)
{
// Get current user from security:
$user = $this->get('security.token_storage')->getToken()->getUser();
// set to useService...
$userService->setUser($user);
// and persist & flush:
$this->_em->persist($userService);
$this->_em->flush();
}
// I override save method:
You're overriding non-existent method in parent, there's no save method in ServiceEntityRepository nor EntityRepository. So what's the main point of what you are doing and why you're setting default user_id in service repository?
UPDATE:
services:
my.listener:
class: UserServiceListener
arguments:
- "#security.token_storage"
tags:
- { name: doctrine.event_listener, event: prePersist }
Listener:
class UserServiceListener
{
private $token_storage;
public function __construct(TokenStorageInterface $token_storage)
{
$this->token_storage = $token_storage;
}
public function prePersist(LifeCycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof UserService) {
return;
}
$entity->setUser($this->token_storage->getToken()->getUser());
}
}
Aware that there is a lot of information around the net regarding this, I am still having a lot of trouble getting this to work.
I have created a custom service:
<?php
namespace App\Service;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\AccommodationType;
use App\Entity\Night;
class AvailabilityChecks {
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function nightAvailable(string $RoomCode, string $NightDate) {
$GetRoom = $this->em->getDoctrine()->getRepository(AccommodationType::class)->findOneBy([
'RoomCode' => $RoomCode
]);
$RoomQnt = $GetRoom->getNightlyQnt();
$GetNight = $this->em->getDoctrine()->getRepository(Night::class)->findOneBy([
'RoomCode' => $RoomCode,
'NightDate' => $NightDate
]);
$NumberOfNights = $GetNight->count();
if($NumberOfNights<$RoomQnt) {
return true;
}
else {
return false;
}
}
}
and have put this in services.yaml:
AvailabilityChecks.service:
class: App\Service\AvailabilityChecks
arguments: ['#doctrine.orm.entity_manager']
So when I try and use this in my controller, I get this error:
Too few arguments to function App\Service\AvailabilityChecks::__construct(), 0 passed in /mypath/src/Controller/BookController.php on line 40 and exactly 1 expected
I just can't figure out why it's not injecting the ORM stuff into the constructor! Any help greatly appreciated
The problem is in your BookController. Even though you didn't posted its code I can assume you create new AvailabilityChecks in it (on line 40).
In Symfony every service is intantiated by service container. You should never intantiate service objects by yourself. Instead BookController must ask service container for AvailabilityChecks service. How should it do it ?
In Symfony <3.3 we used generally :
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MyController extends Controller
{
public function myAction()
{
$em = $this->get('doctrine.orm.entity_manager');
// ...
}
}
Nowadays services can be injected in controllers using autowiring which is way easier:
use Doctrine\ORM\EntityManagerInterface;
class MyController extends Controller
{
public function myAction(EntityManagerInterface $em)
{
// ...
}
}
You are using the wrong service for what you want to do. The alias doctrine that is used, e.g. in the AbstractController when you call getDoctrine() is bound to the service Doctrine\Common\Persistence\ManagerRegistry.
So the code you wrote fits better with that and you should either add #doctrine or #Doctrine\Common\Persistence\ManagerRegistry to the service definition.
Both with your current configuration or the changed one, you don't have to call $this->em->getDoctrine(), because $this->em is already equivalent to $this->getDoctrine() from your controller. Instead you could create a (private) method to make it look more like that code, e.g.:
private function getDoctrine()
{
return $this->em;
}
Then you can call $this->getDoctrine()->getRepository(...) or use $this->em->getRepository(...) directly.
In Symfony 4, you dont need to create it as services. This is automatically now. Just inject the dependencies what you need in the constructor. Be sure that you have autowire property with true value in services.yml (it is by default)
Remove this from services.yml:
AvailabilityChecks.service:
class: App\Service\AvailabilityChecks
arguments: ['#doctrine.orm.entity_manager']
You dont need EntityManagerInterface because you are not persisting anything, so inject repositories only.
<?php
namespace App\Service;
use App\Entity\AccommodationType;
use App\Entity\Night;
use App\Repository\AccommodationTypeRepository;
use App\Repository\NightRepository;
class AvailabilityChecks {
private $accommodationTypeRepository;
private $nightRepository
public function __construct(
AcommodationTypeRepository $acommodationTypeRepository,
NightRepository $nightRepository
)
{
$this->acommodationTypeRepository = $acommodationTypeRepository;
$this->nightRepository = $nightRepository;
}
public function nightAvailable(string $RoomCode, string $NightDate) {
$GetRoom = $this->acommodationTypeRepository->findOneBy([
'RoomCode' => $RoomCode
]);
$RoomQnt = $GetRoom->getNightlyQnt();
$GetNight = $this->nightRepository->findOneBy([
'RoomCode' => $RoomCode,
'NightDate' => $NightDate
]);
$NumberOfNights = $GetNight->count();
if($NumberOfNights<$RoomQnt) {
return true;
}
else {
return false;
}
}
}
In SF4, you no longer need to specify dependencies required by your custom service in the service.yaml file. All you have to do is to use dependency injection.
So remove config lines, and call your service directly in the controller method :
<?php
namespace App\Controller;
use App\Service\AvailabilityChecks ;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class AppController extends AbstractController
{
public function index(AvailabilityChecks $service)
{
...
}
}
Having said that, i think you don't need custom service to do simple operations on database. Use repository instead.
Is it possible to configure symfony2/3 to handle more than 1 domain with different views?
For example I have site1.com and site2.com, I would create a site1 and site2 folders inside app/Resources/views and serve a different set of templates depending on the domain.
Models and controllers should be in common so site1.com/mypage and site2.com/mypage should serve the same content with different layout.
Any suggestion or best practice related to it is welcome.
Thanks
Check for the host in your controller :
namespace Acme\FooBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
class DemoController
{
public function showAction(Request $request)
{
switch($request->getHost())
{
case 'site1.com':
return $this->render('site1/show.html.twig');
break;
case 'site2.com':
return $this->render('site2/show.html.twig');
break;
default:
return $this->render('default/show.html.twig');
}
}
}
EDIT : Something more generic
Create a onKernelRequest listener :
namespace AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class DomainRequestListener
{
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$host = explode('.',$request->getHost());
$request->request->attributes->set('_domain',$host[0]);
}
}
Add this listener in services.yml :
app.listener.domain_request:
class: AppBundle\EventListener\DomainRequestListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest}
Then you can use the '_domain' routing parameter in all your controllers :
return $this->render($request->attributes->get('_domain').'/show.html.twig');
Not tested, but I expect the following should work. You'll want to register a kernel request listener that uses the Twig loader service (responsible for locating the templates) and registers a path based on the request's hostname.
Create a request listener:
<?php
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class RegisterTwigPathSubscriber implements EventSubscriberInterface
{
private $loader;
public function __construct(\Twig_Loader_Filesystem $loader)
{
$this->loader = $loader;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => 'registerTwigPath'
];
}
public function registerTwigPath(GetResponseEvent $event)
{
$host = $event->getRequest()->getHost();
$path = '...'; // determine path based on hostname
$this->loader->addPath($path, 'Theme'); // the second argument is a namespace for templates located under this folder and can be chosen
}
}
Register the event listener:
services:
register_twig_path_listener:
class: RegisterTwigPathSubscriber
arguments: ["#twig.loader"]
tags: [{ name: kernel.event_subscriber }]
Now to reference the template:
return $this->render('#Theme/path/to/actual/template.html.twig');
How can make an Object accessible in a controllers which was set in kernel.controller Event?
I have a onKernelController method which is run before controller and I need some data in a controller which was set in onKernelController.
You can use dependency injection to solve this:
1) Turn your object/class into a service and inject it into the listener.
services:
your_object:
class: Your\Namespace\YourObjectClass
your_listener:
class: Your\Namespace\YourListener
arguments: [ #your_object ]
tags:
- { name: kernel.controller, event: kernel.request, method: onKernelController }
2) Set some property (can be an object aswell) on the injected object
listener class
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class YourListener
{
protected $object;
public function __construct($object)
{
$this->object = $object;
}
public function onKernelController(FilterControllerEvent $event)
{
// ...
$object->setProperty('some_property_value');
}
}
3.) Get the property inside a container-aware controller (or turn your controller into a service aswell and inject #your_object )
controller
use Symfony\Component\DependencyInjection\ContainerAware;
// or: use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class SomeController extends ContainerAware // or: extends Controller
{
public function someAction()
{
$property = $this->container->get('your_object')->getProperty;
// $property => 'some_property_value'
}