Laravel event RevokeOldToken error - php

I'm stuck with a problem: I created an event according to the Laravel docs.
I created the files with php artisan event:generate
This command created two files:
Listeners/RevokeOldToken.php
Listeners/PruneOldTokens.php
After that when I hit the API, it returns this error:
Argument 1 passed to App\Listeners\RevokeOldTokens::handle() must be an instance of App\Events\Laravel\Passport\Events\AccessTokenCreated, instance of Laravel\Passport\Events\AccessTokenCreated given
What i am doing wrong? How can I create the instance said by the error?

You need import that two class in listener.
use Laravel\Passport\Events\AccessTokenCreated;
use Laravel\Passport\Events\RefreshTokenCreated;
I think you didn't import them, so laravel think your listener is expecting that two wrong classes, App\Events\Laravel\Passport\Events\AccessTokenCreated and App\Events\Laravel\Passport\Events\AccessTokenCreated

tl;dr
Artisan generates an incorrect use path for the event classes in the listener files so you have to fix them, by removing the App\Events part from the front.
Explanation
The Generating Events & Listeners subsection under the documentation's Registering Events & Listeners section mentions that the event and listener classes are generated based on the app/Providers/EventServiceProvider.php class' listen attribute.
This is a nice feature, but there is a little problem with it: if the array's keys (the event file paths) in the $listen attribute reference an event from the vendor folder (Laravel\Passport\Events in this case) then in the generated listener file, the imported event's path will be prefixed with App\Events\. This is the thing that you have to remove.
Because of this, the use path now references a non existing class that doesn't raise an error like "using non existing class", so when the code gets executed and the event is fired, then thanks to the mappings in the EventServiceProvider.php file, the proper event listener will be found for the event, but when the listener's handle method is called with the event, it will raise a type error (which is a PHP thing), because the argument's type hinting references a different (non existing) class.
Catching the bug in action
If we dig deep into the framework, we can find the Illuminate/Foundation/Console/EventGenerateCommand.php class the gets executed when you call php artisan event:generate. You can see that it parses the EventServiceProvider class' listen attribute then starts to generate event and listener files.
The listener generation will happen in the Illuminate/Foundation/Console/ListenerMakeCommand.php file, that will create the generated file based on the stub found in Illuminate/Foundation/Console/stubs/listener.stub.
The dummy import path will be replaced in the buildClass function. The new path has been calculated just before the replace took place and that is where the error happens:
if (! Str::startsWith($event, [
$this->laravel->getNamespace(),
'Illuminate',
'\\',
])) {
$event = $this->laravel->getNamespace().'Events\\'.$event;
}
In our case $event will start with Laravel\Passport\Events and not with App\, Illuminate or \ so the path will be prefixed with App\Events\. That is the thing you should remove in the generated files.
I hope this is official enough.

Related

Symfony ResponseEvent arguments

I'm following a Symfony tutorial and am currently on The Event Dispatcher section. In the first code block (after the composer part) I'm instructed to set up the dispatcher
// dispatch a response event
$this->dispatcher->dispatch(new ResponseEvent($response, $request), 'response');
I'm using PHPStorm, and when I create the ResponseEvent class it is asking for 4 arguments
new ResponseEvent($kernel, $request, $requestType, $response)
While I could create a $kernel element by importing the HttpKernelInterface, I'm unsure if this is correct.
I also have no idea where to get the $requestType from, since the ResponseEvent class definition says that this should be of type int and I only know of request types in string format (e.g. 'GET', 'POST' etc).
Here is the full file in case I have made a mistake elsewhere.
The tutorial that you are following wants you to use the Simplex ResponseEvent, that method signature is for the kernel ResponseEvent
At the top of your file you should see something like this:
use Symfony\Component\HttpKernel\Event\ResponseEvent;
Replace it with
use Simplex\ResponseEvent;

Service override doesn't work as expected

I create a service inside my module with the name of an existing core service (prestashop.adapter.data_provider.product). It successfully replaces it as seen in php ./bin/console debug:container output.
In yaml:
prestashop.adapter.data_provider.product:
class: PrestaShop\Module\MyModule\Adapter\Product\ProductDataProvider
The problem now is that I have 500 errors in some pages in BO. These errors are type errors like:
Type error: Argument 4 passed to
PrestaShopBundle\Model\Product\AdminModelAdapter::__construct() must
be an instance of
PrestaShop\PrestaShop\Adapter\Product\ProductDataProvider, instance of
PrestaShop\Module\MyModule\Adapter\Product\ProductDataProvider given,
called in....
I understand now that, whenever a constructor has an argument of type ProductDataProvider, the app tries to load my service but find a differentuse statement in the class (i.e in PrestaShopBundle\Model\Product\AdminModelAdapter)
These errors can be fixed by replacing the use statement in each file containing the problem, but as you may know, touching core files must be avoided.
Is there a way of overriding an existing service, but also make the override work all across the app "bypassing the use statements of the old service".

Laravel Event - fails with class does not have a method

Using Laravel 5.5, I have a listener called JobEventSubscriber that is using a database queue. It has a method called uploadFileToPartner that is triggered whenever a JobFilesUploaded event is fired.
Here is the code of my subscribe method:
public function subscribe($events){
$events->listen(JobSaved::class, JobEventSubscriber::class . '#syncToCrm');
$events->listen(JobFilesUploaded::class, JobEventSubscriber::class . '#uploadFileToPartner');
}
Whenever either of these events fire, the database Listener fails with the following error:
ErrorException: call_user_func_array() expects parameter 1 to be a valid callback,
class 'App\Listeners\JobEventSubscriber' does not have a method 'uploadFileToPartner'
in /var/www/html/vendor/laravel/framework/src/Illuminate/Events/CallQueuedListener.php:79
When I change my queue_driver to sync it works. I also went into Tinker and typed:
>>> use App\Listeners\JobEventSubscriber
>>> $eventSubscriber = app(JobEventSubscriber::class);
=> App\Listeners\JobEventSubscriber {#879
+connection: "database",
}
>>> method_exists($eventSubscriber, 'uploadFileToPartner');
=> true
What is wrong here where it cannot find methods that are definitely there.
It may be relevant to mention that I recently updated this app from Larvel 5.4.
Upon reading the docs it says that if you change your code you need to restart your queue process.
Specifically it says:
Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to restart your queue workers.
I changed the name of the methods, and then I didn't restart the queue. So the queue was receiving events with the new names but it was executing old code. As a result the method names weren't recognized.

What's the difference between Commands and Events in the context of Laravel 5?

So Laravel 5 was finally released yesterday with the final implementation of the command bus but I was wandering, what's the real difference in using a command bus over event mechanisms that we have in the previous releases?
Ok, I see the reason that it can be used to create commands from Request objects which is pretty useful but beyond that it seems to behave in a similar way even down to the whole queuing functionality for events now?
Can you please provide examples of use cases and where the pros and cons of either are?
Commands are things about to happen right now. i.e. "CreateUser"
Events are things that have just occured right now - i.e. "UserSuccessfullyCreated"
The differences appear minor - but have some key differences.
Commands must be specifically called/dispatched. I.e. if you want to
do CommandX - you must call CommandX somewhere.
Events respond to an event firing anywhere in your application.
The great thing is multiple event handling classes can respond to the
same event.
Lets do an example to illustrate it best. Lets say we create a user, and we want to send them a welcome email and also update our newsletter list.
In a Command Scenario would would do
AdminController {
function create() {
Bus::dispatch(new CreateUser(Auth::user());
}
}
then in our CommandClass - we would do
public function handle(CreateUser $auth)
{
// 1. Create the user here
// 2. Send welcome email
// 3. Update our newsletter
}
But if we use events - we would do something like this in our CommandClass
public function handle(CreateUser $auth)
{
// 1. Create the user here
Event::fire(new UserWasCreated($user));
}
then we can create as many events as we want to listen to that event and do something:
EventClassA
Event::listen('UserWasCreated', function($event)
{
// 2. Send welcome email
});
EventClassB
Event::listen('UserWasCreated', function($event)
{
// 3. Update our newsletter
});
The great thing is separation of concerns. The command "createuser" now does not need to worry itself about what happens after a user is created. It just needs to CreateUser.
Also - if we want to add another function after a user signs up - say enter them in a lotto draw - you can just add another Event Class and add a new event listener.
EventClassC
Event::listen('UserWasCreated', function($event)
{
// 4. Register them in lotto
});
Notice how we didnt need to touch the command CreateUser class code at all? This provides a true separation concerns of classes in a OOP style approach.
I just want to share my understanding of this concept on top of the correct answer:
The main difference is that Commands can change a Model state, while Events just react to a state change.
COMMANDS:
Commands in Laravel represent the implementation of the Command design pattern.
The main adventages of Commands:
The can be accessed from anywhere
They are very easy to read by any other developer
To create a Command in Laravel 5:
You need to generate a command DTO (which can implement the SelfHandling interface). Using php artisan make:command {command-name}
Example: php artisan make:command Course/PostCourseCommand
The naming convention for commands: speak the business language and add postfix Command to it
To call (dispatch) the command from you controller, you can use:
$this->dispatch(new PostCourseCommand())
or
Bus::dispatch(new PostCourseCommand());
Side Note:
The "dispatch from request” feature is a nice way to skip passing the variables to the command constructor one by one, instead it will resolve this for you:
Example:
$test_request = Request::create('/test', 'GET', [
'name' => 'Mahmoud',
'nickname' => 'Mega'
]);
$result = Bus::dispatchFrom(
CreateCourse::class, $test_request
);
Finally:
You can separate the handler function and it’s logic from the command DTO to the Handlers directory, to do so:
Generate a command handler via artisan
art handler:command --command="Course/PoatCourseCommand"
remove the SelfHandling interface from the Command class so it will search for a handler to handle it.
EVENTS:
Events in Laravel represent the implementation of the Observer design pattern.
To create an Event in Laravel 5:
use artisan: art make:event {event-name}
Example: art make:event LogSomething
generate an event handler for that event
art handler:event LogSomething --event="LogSomething"
register the event and it’s handler in the event service provider (app/Providers/EventServiceProvider.php)
Example:
protected $listen = [
\Zzz\Events\LogSomething::class => [ // event.name
\Zzz\Handlers\Events\LogSomething::class, //EventListener
],
],
To call (fire) an Event:
use:
Event::fire(New LogSomething());
or you can use the event helper
event(New LogSomething());
Side Note:
alternatively you can generate an event by simply registering the event in the service provider then running this command.
php artisan event:generate << this will automatically add the two classes for you
Also you can listen to an event without creating an event handler or registering a lister in the listeners array, by just going to the event service prover and inside the boot function writing your event and its action (NOT RECOMMENDED). Example:
Event::listen('XXX\Events\DoSomethingElse', function($event)
{
dd('handle me :)');
});
Finally: you can queue an event or even subscribe to multiple events from within the class itself..

How to enable ORM annotation prefix outside of Symfony2?

I'm converting an old PHP project to the Symfony2 framework. Some of the pages are now handled by my Symfony2 front controller (index.php), but many pages have not yet been converted.
The problem is that, within Symfony, all of my Doctrine entity annotations must begin with the ORM\ prefix, but outside of Symfony, that prefix does not appear to be enabled, and so I get the following error:
Class MyProject\MyBundle\Entity\MyClass is not a valid entity or mapped super class.
I've tried to duplicate whatever magic Symfony does to set this up, including following these instructions [doctrine-project.org], and actually including app/autoload.php entirely into my legacy bootstrap process. But nothing works.
Does anyone know how I can manually replicate whatever it is that Symfony does to enable the ORM\ prefix for my Doctrine annotations?
I got the answer from the Symfony2 Google group. The problem is that the Doctrine configuration shown in the documentation uses SimpleAnnotationReader behind the scenes, but you need regular AnnotationReader to use the ORM\ namespace prefix. I got it to work by replacing this:
$config = new Doctrine\ORM\Configuration();
$driver = $config->newDefaultAnnotationDriver('/path/to/my/entities');
with this:
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
// ...
$config = new Doctrine\ORM\Configuration();
$reader = new AnnotationReader();
$driver = new AnnotationDriver($reader, '/path/to/my/entities');
I ended up with:
Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration($paths, $devMode, null, null, false);`
The 3rd and 4th null arguments are default. The 5th false argument tells it to make a standard AnnotationReader rather than a basic one.
I'm using Doctrine 2.5.6.
Explanation
I found I couldn't get Ian's solution working without calling Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration before making my own config. I was getting this error:
'[Semantical Error] The annotation "#Doctrine\ORM\Mapping\Entity" in class My\Class does not exist, or could not be auto-loaded.'
I was really confused so I took a look at the source code.
It turns out createAnnotationMetadataConfiguration calls Doctrine\ORM\Configuration::newDefaultAnnotationDriver rather than creating the annotation driver directly. This calls AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php'); which seems to be critical. After that, newDefaultAnnotationDriver just creates a new AnnotationDriver().

Categories