In my api I am using Redis for caching dispatched jobs from my controllers: This is how my controller looks
class FormSubmissionsController extends Controller
{
/**
* #param StoreRequest $request
* #return \Illuminate\Http\JsonResponse
*/
public function store(StoreRequest $request, FormSubmission $formSubmission)
{
JobStore::dispatch($formSubmission, $request->get('tracking_code'), $request->get('form'));
return response()->json([
'id' => $formSubmission->id
]);
}
}
All is working, and the only change I did to use redis it was some config vars in dot env file. My question:
In another controller I want to use some of Amazon SQSservices for queued jobs, any idea how to config queue and how should I dispatch each job to particular queue handler ?
You can pick a connection that should be used to dispatch a job with onConnection() method:
JobStore::dispatch()->onConnection('sqs');
See https://laravel.com/docs/5.5/queues#dispatching-jobs for more details.
Related
I have a running Laravel application which has the default Console/Kernel.php where all my scheduled commands are defined as usual.
But now I have another Laravel application that I am trying to merge with this existing application, and I have created a folder inside the existing Laravel application and I am using a Service Provider to load all the things. This way I can keep the code of the two projects separate but at the same time all features under the same repository.
<?php
namespace SecondApp;
use Illuminate\Support\ServiceProvider;
class SecondAppServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
include __DIR__ . '/routes/web.php';
include __DIR__ . '/routes/api.php';
$this->app->register('SecondApp\App\Providers\AppServiceProvider');
$this->app->register('SecondApp\App\Providers\AuthServiceProvider');
$this->app->register('SecondApp\App\Providers\BroadcastServiceProvider');
$this->app->register('SecondApp\App\Providers\EventServiceProvider');
$this->app->register('SecondApp\App\Providers\JobServiceProvider');
$this->app->register('SecondApp\App\Providers\MailerServiceProvider');
$this->app->register('SecondApp\App\Providers\RouteServiceProvider');
$this->app->register('SecondApp\App\Providers\StorageServiceProvider');
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
$this->loadMigrationsFrom(__DIR__ . '/database/migrations/');
$this->publishes([
__DIR__ . '/config/' => config_path()
], 'second-app-config');
$this->publishes([
__DIR__ . '/resources/' => base_path('resources')
], 'second-app-resources');
}
}
This is what my service somewhat looks like. Everything else seems to work well, roues, migrations, registering service providers and so on, but this second project now also has Console/Kernel.php file. And those commands are not being called yet, obviously because laravel doesn't know about it. So I am wondering how can I tell Laravel to look at that as well? Is this possible, or will I have merge the code into one main Kernel.php?
I have the same question about Http/Kernel.php as well, but I am sure if someone can suggest how to make one work, I can make the other work as well.
To load commands, need to add them like this in the register method of service provider
$this->commands([
App\Console\Commands\CommandOne::class,
App\Console\Commands\CommandTwo::class,
]);
and so on, keep adding commands to the array.
And to schedule these commands refer to this answer -> How to schedule Artisan commands in a package?
So add something like this to the boot method of your service provider:
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
$schedule->command('some:command')->everyMinute();
});
My Laravel 5.5 application has a Product model. The Product model has a dispatchesEvents property that looks like this:
/**
* The event map for the model.
*
* #var array
*/
protected $dispatchesEvents = [
'created' => ProductCreated::class,
'updated' => ProductUpdated::class,
'deleted' => ProductDeleted::class
];
I also have a listener that is called CreateProductInMagento which is mapped to the ProductCreated event in the EventServiceProvider. This listener implements the ShouldQueue interface.
When a product is created, the ProductCreated event is fired and the CreateProductInMagento listener is pushed to the queue and is run.
I am now trying to write a test for all of this. Here is what I have:
/** #test */
public function a_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento()
{
Queue::fake();
factory(Product::class)->create();
Queue::assertPushed(CreateProductInMagento::class);
}
But I get a The expected [App\Listeners\Magento\Product\CreateProductInMagento] job was not pushed. error message.
How do I test queueable listeners using Laravel's Queue::fake() method?
The problem here is that the listener is not the job pushed to the queue. Instead, there's a Illuminate\Events\CallQueuedListener job that is queued and will in turn call the appropriate listener when resolved.
So you could do your assertion like this:
Queue::assertPushed(CallQueuedListener::class, function ($job) {
return $job->class == CreateProductInMagento::class;
});
Running artisan queue:work won't solve the issue because when testing, Laravel is configured to use the sync driver, which just runs jobs synchronously in your tests. I am not sure why the job is not being pushed, though I would guess it has to do with Laravel handling events differently in tests. Regardless, there is a better approach you can take to writing your tests that should both fix the issue and make your code more extendable.
In your ProductTest, rather than testing that a_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento, you should simply test that the event is fired. Your ProductTest doesn't care what the ProductCreated event is; that is the job of a ProductCreatedTest. So, you can use Event Faking to change your test a bit:
/** #test */
public function product_created_event_is_fired_upon_creation()
{
Event::fake();
factory(Product::class)->create();
Event::assertDispatched(ProductCreated::class);
}
Then, create a new ProductCreatedTest to unit test your ProductCreated event. This is where you should place the assertion that a job is pushed to the queue:
/** #test */
public function create_product_in_magento_job_is_pushed()
{
Queue::fake();
// Code to create/handle event.
Queue::assertPushed(CreateProductInMagento::class);
}
This has the added benefit of making your code easier to change in the future, as your tests now more closely follow the practice of only testing the class they are responsible for. Additionally, it should solve the issue you're having where the events fired from a model aren't queuing up your jobs.
where is the appropriate place to catch every time laravel is used even non http based action?
I want to catch everything even artisan commands, Queues or Task that running.
the only place I can think of is bootstrap\app.php
but its too hacky and with my experience with laravel I am sure there is some built in way of doing it
is there some one place to catch them all?
you can add your logger to your app/Providers/AppServiceProvider.php's boot() function.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
// Your logger goes here
error_log('log...');
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
I am trying to dispatch jobs in Laravel into redis. If I do
Queue::push('LogMessage', array('message' => 'Time: '.time()));
then a job is placed into the queues:default key in redis. (note that I do not yet have the listener running; I'm just trying to show that the queue is being used.) However, if I do
$this->dispatch(new AudienceMetaReportJob(1));
then nothing is added to redis and the job executes immediately.
The Job definition:
<?php
use App\Jobs\Job;
use Illuminate\Queue\InteractsWithQueue;
/**
* Simple Job used to build an Audience Report. Its purpose is to
* allow code to dispatch the command to build the report.
*/
class AudienceMetaReportJob extends Job {
use InteractsWithQueue;
protected $audience_id;
/**
* #param $audience_id
*/
public function __construct($audience_id){
$this->audience_id = $audience_id;
}
/**
* Execute the job.
*
* #return void
*/
public function handle(){
$audience_meta = new AudienceMeta();
$audience_meta->reportable = Audience::findOrFail($this->audience_id);
$audience_meta->clear()
->build()
->save();
}
}
Notes:
The Audience job appears to complete properly, but does so as if the QUEUE_DRIVER env variable was set to sync
Redis has been configured properly, as I can manually use the Queue:push() function and can read from redis
I have set the QUEUE_DRIVER env variable to redis, and the problem exists even if I hardcode redis in the config/queue.php file: 'default' => 'redis',
I do not have php artisan queue:listen running
What other configuration am I missing? Why does the Queue class work with redis, while the dispatch function does not?
so I have created my own blog package in a structure of Packages/Sitemanager/Blog I have a service provider that looks like the following:
namespace Sitemanager\Blog;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
class BlogServiceProvider extends LaravelServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot() {
$this->handleConfigs();
$this->handleMigrations();
$this->handleViews();
$this->handleRoutes();
}
/**
* Register the service provider.
*
* #return void
*/
public function register() {
// Bind any implementations.
$this->app->make('Sitemanager\Blog\Controllers\BlogController');
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides() {
return [];
}
private function handleConfigs() {
$configPath = __DIR__ . '/config/blog.php';
$this->publishes([$configPath => config_path('blog.php')]);
$this->mergeConfigFrom($configPath, 'blog');
}
private function handleTranslations() {
$this->loadTranslationsFrom(__DIR__.'/lang', 'blog');
}
private function handleViews() {
$this->loadViewsFrom(__DIR__.'/views', 'blog');
$this->publishes([__DIR__.'/views' => base_path('resources/views/vendor/blog')]);
}
private function handleMigrations() {
$this->publishes([__DIR__ . '/migrations' => base_path('database/migrations')]);
}
private function handleRoutes() {
include __DIR__.'/routes.php';
}
}
Now, what i would like to do is run the migrations dynamically if they have never been run before or within an installation process i suppose. I've seen in older documentation you could so something like this:
Artisan::call('migrate', array('--path' => 'app/migrations'));
However, this is invalid in laravel 5, how can I approach this?
Artisan::call('migrate', array('--path' => 'app/migrations'));
will work in Laravel 5, but you'll likely need to make a couple tweaks.
First, you need a use Artisan; line at the top of your file (where use Illuminate\Support\ServiceProvider... is), because of Laravel 5's namespacing. (You can alternatively do \Artisan::call - the \ is important).
You likely also need to do this:
Artisan::call('migrate', array('--path' => 'app/migrations', '--force' => true));
The --force is necessary because Laravel will, by default, prompt you for a yes/no in production, as it's a potentially destructive command. Without --force, your code will just sit there spinning its wheels (Laravel's waiting for a response from the CLI, but you're not in the CLI).
I'd encourage you to do this stuff somewhere other than the boot method of a service provider. These can be heavy calls (relying on both filesystem and database calls you don't want to make on every pageview). Consider an explicit installation console command or route instead.
After publishing the package:
php artisan vendor:publish --provider="Packages\Namespace\ServiceProvider"
You can execute the migration using:
php artisan migrate
Laravel automatically keeps track of which migrations have been executed and runs new ones accordingly.
If you want to execute the migration from outside of the CLI, for example in a route, you can do so using the Artisan facade:
Artisan::call('migrate')
You can pass optional parameters such as force and path as an array to the second argument in Artisan::call.
Further reading:
https://laravel.com/docs/5.1/artisan
https://laravel.com/docs/5.2/migrations#running-migrations
For the Laravel 7(and probably 6):
use Illuminate\Support\Facades\Artisan;
Artisan::call('migrate');
will greatly work.