Passing parameters to Laravel job is not working - laravel

I've read solutions to this problem before but it seems that I'm doing something else wrong and that's why I'm asking: Usually the solution is adding the parameters to the body of the class as well as the __construct method but even doing that it doesn't work.
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Mail;
use App\Mail\TasksFinished;
class SendMailFinished implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
public $tries = 3;
protected $msg;
protected $subj;
protected $mailto;
public function __construct($msg, $subj, $mailto)
{
//
$this->msg = $msg;
$this->subj = $subj;
$this->mailto = $mailto;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
//Envia correo cuando termine cola.
Mail::to($this->mailto)->queue(new TasksFinished($this->msg, $this->subj));
}
I try to run this through a queue this way in tinker:
use App\Jobs\SendMailFinished;
$job = new SendMailFinished('Hola', 'Prueba', 'ffuentes#example.org');
$job->dispatch();
TypeError: Too few arguments to function
App/Jobs/SendMailFinished::__construct(), 0 passed in
C:/laragon/www/reportes/vendor/laravel/framework/src/Illuminate/Foundation/Bus/Dispatchable.php
on line 16 and exactly 3 expected
Why even after specifying all the parameters both in the class and during instantiation it still cannot see any parameters when dispatching. (I've tried making params public as well but it's the same thing).

When you call dispatch() you should send the parameter there.
Try calling it statically
SendMailFinished::dispatch('Hola', 'Prueba', 'ffuentes#example.org');

There are 2 ways to pass parameters to a dispatching job in laravel.
First is simply call dispatch or dispatchNow as per requirement on your Job class like calling a static method on class:
YourJob::dispatch(argument1, argument2, argument3);
Second is simply pass arguments while creating an instance/object of Job class then pass object to dispatch method(always available in controller) like:
$this->dispatch(new YourJob(argument1, argument2, argument3));
The arguments will be available in the constructor, can you assign them to class local variable properties and use anywhere in Job class.

Related

Laravel 8 Tests: PHPUnit error: Unknown formatter "unique"

I have written a test that involves a factory. When I execute the test, I get this error:
The data provider specified for Tests\Unit\ExampleTest::testTakePlace is invalid. InvalidArgumentException: Unknown formatter "unique" /var/www/html/api/vendor/fakerphp/faker/src/Faker/Generator.php:249
Expected result
This error should not be shown, I should be able to use $this->faker->unique().
How I tried to fix this problem
By reading the doc again and again (no difference was found) and by reading questions&answers on the Internet (only one question & only one answer were found: to extend Laravel's TestCase but the official documentation, as I mentionned it, says the contrary). (Laravel's TestCase is provided by use Illuminate\Foundation\Testing\TestCase;)
Question
Why doesn't it work and how to fix this bug?
Sources
Test sources
It extends PHPUnit\Framework\TestCase (not Laravel's TestCase) because the documentation says to extend it. Indeed: https://laravel.com/docs/8.x/testing#creating-and-running-tests . This is not the only part of the doc mentionning to extend it.
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Models\Project;
class ExampleTest extends TestCase
{
/**
* #dataProvider provideTakePlaceData
*/
public function testTakePlace($project)
{
$response = $this->json('GET', '/controllerUserProject_takePlace', [
'project_id' => $project->id
]);
}
public function provideTakePlaceData() {
return [
Project::factory()->make()
];
}
}
Controller
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ControllerUserProject extends Controller
{
public function takePlace(Request $request, $project_id)
{
return;
}
}
The most important: the factory
<?php
namespace Database\Factories;
use App\Models\Project;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class ProjectFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Project::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'id' => $this->faker->unique()->numberBetween(1, 9000),
];
}
}
Change:
use PHPUnit\Framework\TestCase;
to:
use Tests\TestCase;
Why?
When your ExampleTest extends PHPUnit\Framework\TestCase Laravel app is never initialized in tests, and so you don't have access to Laravel features like factories.
The documentation tells you to extend PHPUnit\Framework\TestCase;, but it refers to Unit tests. Feature tests should extend Tests\TestCase. This is something pretty new. Until Laravel 5.8 both unit and feature tests were extending Tests\TestCase by default. I personally just define all tests as feature tests to avoid such issues.
The problem was due to the use of a dataProvider with the use of the factories. More precisely: PHPUnit data providers should not be used when Laravel factories are.
If you are using faker in Factories please make sure these are working correctly regardless (as per example try running Project::factory()->make(); within Laravel Tinker and see the results)
Second common issue (as mentioned above) is the class that you extend your Test with - see above
Third and frequently omitted one that's causing this error, is a missing parent constructor call in the setUp() - (if you use it)
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Models\Project;
class ExampleTest extends TestCase
{
protected Project $project;
public function setUp(): void
{
parent::setUp();
$this->project = Project::factory()->make();
}
I think you need to use the WithFaker trait to use faker :
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Models\Project;
use Illuminate\Foundation\Testing\WithFaker;
class ExampleTest extends TestCase
{
use WithFaker;
// ...
}

Laravel Eloquent Model Observer deleted method throws an exception. What is wrong?

Can you see what I'm doing wrong with the following code?
/**
* Listen to the AdministerRole deleted event.
* #param AdministerRole $role
* #return void
*/
public function deleted(AdministerRole $role)
{
//This works
$arg_list = func_get_args();
event(new BroadcastingModelEvent($arg_list, 'deleted'));
//This throws an exception
//event(new BroadcastingModelEvent($role, 'deleted'));
//No query results for model [App\Models\AdministerRole].
}
When I delete an AdministerRole model and pass the parameter $role to my BroadcastingModelEvent, an exception is thrown suggesting that perhaps the model can't be found because it's already deleted:
No query results for model [App\Models\AdministerRole]].
However, if I use func_get_args to get the parameters which were passed to the function, and pass the resulting array to BroadcastingModelEvent, I can see a JSON representation of the model collected by Pusher on the client side. What on earth is the difference that's causing this exception?
I got the clue to this problem from this SerializesModels with recently deleted models discussion on the Laravel GitHub page.
My BroadcastingModelEvent's constructor is expecting an Eloquent Model. If I use the SerializesModels trait, it throws an No query results for model exception as discussed in the GitHub page if the event is deleted. The argument seems to be that if the model is already deleted it shouldn't be serializable - or something. The logic escapes me because the deleted event receives the model which has just been deleted and that model is accessible within the deleted event handler, and furthermore, you can call toArray() or toJson() to serialise it! That's my solution. The observer deleted method calls toArray() on the model passed to it and sends that array to the BroadcastingModelEvent constructor. I simply commented out SerializesModels and pre-serialised my model in the observer. If you can explain and justify that seeming inconsistency I'll bake you some shortbread.
public function deleted(AdministerRole $role)
{
event(new BroadcastingModelEvent($role->toArray(), 'deleted'));
}
And BroadcastingModelEvent handles it
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model as Model;
/**
* https://github.com/laravel/framework/issues/10733
*/
class BroadcastingModelEvent implements ShouldBroadcast {
use Dispatchable,
InteractsWithSockets;
//Throws an exception after the deleted event.
//SerializesModels;
public $modelArray;
public $eventType;
public function __construct($modelArray, $eventType) {
$this->modelArray = $modelArray;
$this->eventType = $eventType;
}
public function broadcastOn() {
return new Channel('MyObservers');
}
public function broadcastAs() {
return 'MyBroadcastEvent';
}
}

Telegram notifications in Octobercms plugin

I would like to send a telegram message to a specific user at 17:00 using laravel's Telegram notification channel, I however can't seem to get it going. I currently use a cmd command for testing, but keep getting errors and don't know what to do.
Here are my files for the command and notification:
SendNotification.php
<?php
namespace Rogier\Lab\Console;
use Illuminate\Console\Command;
use Rogier\Lab\Notifications\DailyTelegram;
class SendNotifications extends Command
{
protected $name = 'lab:notifications:send';
protected $description = 'Send notifications';
protected $userid = 919871501;
/**
* Execute the console command.
* #return void
*/
public function handle()
{
$this->output->writeln('Sending notifications');
$notification = new DailyTelegram($this->userid);
$notification->via()->toTelegram();
$this->output->writeln('Done');
}
}
and DailyTelegram.php
<?php
namespace Rogier\Lab\Notifications;
use NotificationChannels\Telegram\TelegramChannel;
use NotificationChannels\Telegram\TelegramMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Notifiable;
class DailyTelegram extends Notification
{
protected $userid = 919871501;
public function via()
{
return [TelegramChannel::class];
}
public function toTelegram()
{
return TelegramMessage::create()
// Optional recipient user id.
->to($this->userid)
// Markdown supported.
->content("Hello there!\nYour invoice has been *PAID*");
}
}
I currently get the error "Call to a member function toTelegram() on array", but I feel like I tried everything, maybe I'm doing it completely wrong. Does anyone know how I should do it?
thanks in advance
Yes, you are doing it wrong. Notifiables have notify() method, you should use it:
$user->notify(new DailyTelegram);
In this example $user is App\User instance (which is notifiable out of the box).
You should check out both Laravel's sending notifications and laravel-notification-channels/telegram docs.

call a model from a command class in Laravel 5, and pass the command class itself

I have a laravel 5.5 artisan command working, so of course I can use methods like $this->info() and $this->arguments() etc.. it looks like this:
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;
use Compasspointmedia\Julietmenu\Model\Menu;
class MenuManagementCommand extends Command
{
/**
* The console command name.
*
* #var string
*/
protected $signature = 'julietmenu:menu
{action=list}';
protected $description = 'Manages menu elements per the Juliet CMS specification';
public function __construct() {
parent::__construct();
//trying to make this command methods available in Menu
$this->menu = new Menu($this);
}
/**
* Execute the console command.
*/
public function handle()
{
// this works just fine
$this->info('Calling ' . $this->argument('action'));
$action = $this->argument('action');
$this->menu->action();
}
}
Of course, I would like do the actual work in the Model, not the command, using the command like a controller. Here's the model class so far:
namespace Compasspointmedia\Julietmenu\Model;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model {
//
public function __construct($command){
$this->command = $command;
// this says the `arguments` method is present:
print_r(get_class_methods($this->command));
// but, it does not work!
// error: "Call to a member function getArguments() on null"
$this->arguments = $this->command->arguments();
}
public function node(){
echo '--- it works! -----';
}
}
To the point, how do I pass the Command object to the Model so that I can use $this->command->arguments() or the other Command features inside the model?
P.S. I'd be very grateful to know if there's a native "Laravel way" to do this better than passing the entire $this to a constructor.

Laravel Job - No handler registered for command

I tried to get a simple job running exactly like the example in the Laravel Documentation - https://laravel.com/docs/5.2/queues#writing-job-classes - but I get this error: "No handler registered for command [App\Jobs\SendReminderEmail]".
I followed the instructions to make the jobs table and even the failed_jobs table and have the exact example code.
I assured that the handle() function is there so I don't know what else can be missing.
Regards.
Update with code:
First I used the class in the Laravel example but then I simplified it to this:
<?php
namespace App\Jobs;
use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class SyncFromJson extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
//
$var = "fooooo";
\Log::info("job is running!!!", $var);
}
}
To call the job I created a simple method in a controller that dispatches the job:
$job = (new SyncFromJson())->delay(3);
$this->dispatch($job);
Also tried this:
$this->dispatch(new SyncFromJson());
For what it's worth, I was having a similar problem and implementing 'SelfHandling' seems to have fixed the issue.
Try changing this:
class SyncFromJson extends Job implements ShouldQueue
To this:
class SyncFromJson extends Job implements ShouldQueue, SelfHandling
I had the exact same problem. In my case it turned out it was the incorrect provider in app config that was the problem. I used a previous project as a framework for my new project and inherited the Laravel collective bus package (That was not required.) The moment I removed the collective bus package ( laravelcollective/bus ) , ran composer update and replaced
Collective\Bus\BusServiceProvider::class,
with
Illuminate\Bus\BusServiceProvider::class,
everything worked fine. This may be a similar issue?

Resources