I want to run some code every time before user session is being destroyed for any reason. I haven't found any events binded to session in official documentation. Has anyone found a workaround about this?
There are no events out of the box for Session component.
You can solve this problem with overriding core yii\web\Session component.
1) Override yii\web\Session component:
<?php
namespace app\components;
use yii\web\Session as BaseSession
class Session extends BaseSession
{
/**
* Event name for close event
*/
const EVENT_CLOSE = 'close';
/**
* #inheritdoc
*/
public function close()
{
$this->trigger(self::EVENT_CLOSE); // Triggering our custom event first;
parent::close(); // Calling parent implementation
}
}
2) Apply your custom component to application config:
'session' => [
'class' => 'app\components\Session' // Passing our custom component instead of core one
],
3) Attach handler with one of available methods:
use app\components\Session;
use yii\base\Event;
Event::on(Session::className(), Session::EVENT_OPEN, function ($event) {
// Insert your event processing code here
});
Alternatively you can specify handler as method of some class, check official docs.
As an alternative to this approach, take a look at this extension. I personally didn't test it. The Yii way to do it I think will be overriding with adding and triggering custom events as I described above.
Related
I know that spark has events that can be listened to when user has registered but I'm totally new to laravel and Events, are there examples that I can make use of to access the events? My goal is to listen to the user created event and send a welcome email to the user.
Finally, here i came up with solution.
Basically, Events call listeners that is defined in the EventServiceProvider class that is store in the providers inside the app folder of the application.
In the EventServiceProvider.php find
'Laravel\Spark\Events\Auth\UserRegistered' => [
'Laravel\Spark\Listeners\Subscription\CreateTrialEndingNotification',
],
it will be store in the $listen of the EventServiceProvider class, this means that the UserRegistered event will call the CreateTrialEndingNotification listener, so we need to create a listerner and attach here , creating listener is easy just create a new
file with name HookRegisteredUser(or your choice) some thing like below in app/Listeners sand add its path into the $listen of the "Laravel\Spark\Events\Auth\UserRegistered"
namespace App\Listeners;
use Laravel\Spark\Events\Auth\UserRegistered;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class HookRegisteredUser
{
/**
* Handle the event.
*
* #param UserRegistered $event
* #return void
*/
public function handle(UserRegistered $event)
{
//your code goes here
}
}
After this add HookRegisteredUser listener in EventServiceProvider.php as follows,
'Laravel\Spark\Events\Auth\UserRegistered' => [
'Laravel\Spark\Listeners\Subscription\CreateTrialEndingNotification',
'App\Listeners\HookRegisteredUser',
],
Now the UserRegistered event will call two listeners i.e CreateTrialEndingNotification , HookRegisteredUser and the method handle will get executed on call to listeners and thats it!
I have two different modules. Now I need to add different authentication mechanism for both modules.
So I added event code first module's Module.php's onBootstrap method
$listener = $serviceManager->get('First\Service\AuthListener');
$listener->setAdapter($serviceManager->get('First\Service\BasicAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
and in second module's Module.php's onBootstrap method
$listener = $serviceManager->get('Second\Service\AuthListener');
$listener->setAdapter($serviceManager->get('Second\Service\AdvAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
Now if I disable one of modules, functionality working fine and request properly authenticated. While enabling both module do some kind of overlapping So even required module properly authenticated, But other module event code also got executed and system give not authenticated error.
I am thinking this due to event handler code in both module.php is executed without take care of requested module url.
I can verify with requested route pattern before authentication, But that is look like a hack instead of good solution.
If better solution exists for handling this issue ?
UPDATE :
My AuthListener Code :
namespace First\Service;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Mvc\MvcEvent;
class AuthListener
{
protected $adapter;
public function setAdapter(AdapterInterface $adapter)
{
$this->adapter = $adapter;
}
public function __invoke(MvcEvent $event)
{
$result = $this->adapter->authenticate();
if (!$result->isValid()) {
$response = $event->getResponse();
// Set some response content
$response->setStatusCode(401);
$routeMatch = $event->getRouteMatch();
$routeMatch->setParam('controller', 'First\Controller\Error');
$routeMatch->setParam('action', 'Auth');
}
}
}
There is a good way to make module specific bootstrap - to use SharedManager:
$e->getApplication()->getEventManager()->getSharedManager()
->attach(__NAMESPACE__, 'dispatch', function(MvcEvent $e) {
// This code will be executed for all controllers in current __NAMESPACE__
}, 100);
Here is a good article to understand difference between EventManager and SharedEventManager
There is no additional info about listeners in the question, but I try to guess:
If you use as listener some callable class - it's ok, just replace function() { } by your $listener.
If you use as listener some class, that implements
ListenerAggregateInterface, you should convert listeners to
SharedListenerAggregateInterface and use method attachAggregate
instead of attach
I hope it helps!
As many people know, the FOS User Bundle doesn't provide roles automatically when a user registers. The most common solution is to either a) modify the User entity constructor to automatically assign a role, or b) override the entire registration controller.
Neither of these solutions seems perfect, and I want to make use of the Events that the FOS user bundle provides.
I have managed to capture the event I want (FOSUserEvents::REGISTRATION_INITIALIZE), but I am having trouble figuring out how to pass the modified User entity back to the registration form.
The code I have so far is as follows:
namespace HCLabs\UserBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Model\UserInterface;
class AutoRoleAssignmentListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [ FOSUserEvents::REGISTRATION_INITIALIZE => 'onRegistrationInitialise' ];
}
public function onRegistrationInitialise( UserEvent $event )
{
$user = $event->getUser();
$user->addRole( 'ROLE_USER' );
// what do
}
}
The YML for the event listener:
services:
hc_labs_user.reg_init:
class: HCLabs\UserBundle\EventListener\AutoRoleAssignmentListener
tags:
- { name: kernel.event_subscriber }
If more code is needed I'm happy to provide it. Thanks for your help.
Answer is very simple - you have to do nothing to get updated User object in registration form after updated User in event listener for FOSUserEvents::REGISTRATION_INITIALIZE event.
Let me explain. FOSUserEvents::REGISTRATION_INITIALIZE is dispatched in RegistrationController by:
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, new UserEvent($user, $request));
And, before this dispatch in code (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Controller/RegistrationController.php#L43) new User is created:
$user = $userManager->createUser();
$user->setEnabled(true);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, new UserEvent($user, $request));
During dispatching, by default PHP call_user_func (http://php.net/manual/en/function.call-user-func.php ) is called with pasted event name (function in defined object) and Event object. After that, event listener has possibility to modify pasted Event object - particularly event property.
In your case, your event listener modify User property via:
$user = $event->getUser();
$user->addRole( 'ROLE_USER' );
So in fact, you have to do nothing to pass the modified User entity back to the registration form.
I made the authorization and authentication via facebook like here:
http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html
and it works
Now I want to make my own event, this event will do something when the user authenticates using facebook. For example-will redirect the user to the home page.
I did it like this
http://symfony.com/doc/current/components/event_dispatcher/introduction.html
So I have this class
http://pastebin.com/2FTndtL4
I do not know how to implement it, what am I supposed to pass as an argument to the constructor
It's really simple. Symfony 2 event system is powerful, and service tags will do the job.
Inject the dispatcher into the class where you want to fire the event. The service id is event_dispatcher;
Fire the event with $this->dispatcher->dispatch('facebook.post_auth', new FilterFacebookEvent($args)) when needed;
Make a service that implements EventSubscriberInterface, defining a static getSubscribedEvents() method. Of course you want to listen to facebook.post_auth event.
So your static method will look like:
static public function getSubscribedEvents()
{
return array(
'facebook.post_auth' => 'onPostAuthentication'
);
}
public function onPostAuthentication(FilterFacebookEvent $event)
{
// Do something, get the event args, etc
}
Finally register this service as a subscriber for the dispatcher: give it a tag (eg. facebook.event_subscriber), then make a RegisterFacebookEventsSubscribersPass (see this tutorial). You compiler pass should retrieve all tagged services and inside the loop should call:
$dispatcher = $container->getDefinition('event_dispatcher');
$subscribers = $container->findTaggedServiceIds('facebook.event_subscriber');
foreach($subscribers as $id => $attributes) {
$definition->addMethodCall('addSubscriber', array(new Reference($id)));
}
This way you can quick make a subscriber (for logging, for example) simply tagging your service.
Event object is just some kind of state/data storage. It keeps data that can be useful for dispatching some kind of events via Subscribers and/or Listeners. So, for example, if you wanna pass facebook id to your Listener(s) - Event is the right way of storing it. Also event is the return value of dispatcher. If you want to return some data from your Listener/Subscriber - you can also store it in Event object.
I have followed the instructions from this tutorial: http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html, and have created a simple listener, that listens for events dispatched by Doctrine on insert or update of an entity. The preInsert and the postInsert events work fine and are dispatched on the creation of a new entity. However, preUpdate and postUpdate are never called on the update of the entity no matter what. The same goes for onFlush. As a side note, I have a console generated controller that supports the basic CRUD operations, and have left it untouched.
Below are some code snippets to demonstrate the way I am doing this.
config.yml
annotation.listener:
class: City\AnnotatorBundle\Listener\AnnotationListener
tags:
- { name: doctrine.event_listener, event: postUpdate}
Listener implementation (I have omitted the other functions and left only the postUpdate for simplicity purposes)
class AnnotationListener
{
public function postUpdate(LifecycleEventArgs $args)
{
$entity=$args->getEntity();
echo $entity->getId();
die;
}
}
The entity id is never displayed, and the script continues its execution until it is complete, despite the die at the end of the function.
Did you forget to add #HasLifecycleCallbacks annotaion? You could use #PreUpdate annotation and skip service definition altogether.
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class YouEntity
{
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpdate(){
// .... your pre-update logic here
}
....
}
In my opinion this way of attaching events is much easier as you don't have to define new services and listeners explicitly. Also you have direct access to data being updated as this method is locations within your entity.
Now, drawback is that you mix logic with your model and that's something that should be avoided if possible...
You can read more about Lifecycle callbacks here:
http://symfony.com/doc/master/cookbook/doctrine/file_uploads.html#using-lifecycle-callbacks