Checking passed object existence in Laravel delayed queue job - laravel

I believe if I pass an Eloquent object to a delayed Laravel job a "findOrFail" method is called to "restore" the object and pass it to the controller of my Job class.
The problem is that the DB record representing the object might be gone by the time the job is actually processed.
So "findOrFail" aborts before even calling the "handle" methods.
Everything seems fine. The problem is that the job now "gets transferred" to the failed jobs list. I know I can remove it from there manually, but that doesn't sound right.
Is there a way to "know" in my job class directly that the passed object "failed to load" or "does not exist" or anything similar?
Basically I would like to be able to do something if "ModelNotFoundException" is thrown while "rebuilding" my passed objects.
Thank you
SOLUTION:
Based on Yauheni Prakopchyk's answer I wrote my own trait and used it instead of SerializesModels where I need my altered behaviour.
Here's my new trait:
<?php
namespace App\Jobs;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Database\ModelIdentifier;
use Illuminate\Database\Eloquent\ModelNotFoundException;
trait SerializesNullableModels
{
use SerializesModels {
SerializesModels::getRestoredPropertyValue as parentGetRestoredPropertyValue;
}
protected function getRestoredPropertyValue($value)
{
try
{
return $this->parentGetRestoredPropertyValue($value);
}
catch (ModelNotFoundException $e)
{
return null;
}
}
}
And that's it - now if the model loading fails I still get a null and can decide what to do with it in my handle method.
And if this is only needed in a job class or two I can keep using original trait everywhere else.

If i'm not mistaken, you have to override getRestoredPropertyValue in your job class.
protected function getRestoredPropertyValue($value)
{
try{
return $value instanceof ModelIdentifier
? (new $value->class)->findOrFail($value->id) : $value;
}catch(ModelNotFoundException $e) {
// Your handling code;
}
exit;
}

Related

Mutator for unique user attribute

In my code the line User::where('socialite_id', $socialite_id)->exists(); is used so many times, that i want to just write a mutator or sth around it to make this line shorter.
So i need to make mutator, that checks if there's a user with the same socialite_id that a new user is about to have.
I've come with
public function hasUniqueSocialiteIdAttribute($value){
return !$this->where('socialite_id', $value)->exists();
}
Mabye it is better to catch the duplicate exception and abort on catch, but.. naah.
But this thing is not working. Any suggestions?
You can use a scope for reusability purposes:
public function scopeSocialiteId($query,$id) {
return $query->where('socialite_id', $id);
}
and then
User::socialiteId($socialite_id)->exists();
For additional info see https://laravel.com/docs/8.x/eloquent#local-scopes

Mocking a service class inside controller

I am trying to write a Feature test for my controller. To simplify my current situation, imagine my controller looks like this:
public function store(Business $business)
{
try {
(new CreateApplicationAction())->execute($business);
} catch (Exception $e) {
return response()->json(['message' => 'error'], 500);
}
return response()->json(['message' => 'success']);
}
What I am trying to achieve is, instead of testing CreateApplication class logic inside my integration test, I want to write another unit test for it specifically.
Is there a way I can simply say CreateApplicationAction expects execute() and bypass testing inside it? (without executing the code inside execute())
/** #test */
public function can_create_application()
{
$business = Business:factory()->create();
$mock = $this->mock(CreateApplicationAction::class, function (MockInterface $mock) use ($business) {
$mock->shouldReceive('execute')
->once()
->with($business)
->andReturn(true);
});
$response = $this->post('/businesses/3/application', $data);
$response->assertOk();
}
I saw online that people create "MockCreateApplicationAction" class but if possible I don't want to create another class as I don't want any logic to be inside it at all.
Is it possible?
class CreateApplicationAction
{
public function execute($business) {
dd("A");
// Business Logic...
}
}
So when I do the Mock, dd() should never be called. Or I am going in the wrong direction?
You will need to use Laravels container to resolve your class. The basic approach is to use the resolve() method helper. PHP does not have dependency injection, so you need to use one to make it possible, in Laravel the container solves that.
resolve(CreateApplicationAction::class)->execute($business);
On constructors, controller methods, jobs, events, listeners and commands (rule of thumb if the method is named handle), you can inject classes into the parameters and they will resolve through the container.
public function store(Business $business, CreateApplicationAction $applicationAction)
{
try {
$applicationAction->execute($business);

Laravel failed job doesn't send notification

When a job fails my notification doesn't arrive, and I'm not sure why. In my Job I have:
use Notification;
use App\Notifications\MonitoringMessage;
public function handle()
{
asdf;
}
public function failed(Exception $exception)
{
$message = ':warning: A job failed.';
Notification::route('slack', config('services.slack.webhook'))->notify(new MonitoringMessage($message));
}
The notification is not queued using use Queueable; etc because I've read that that might cause the issue because the job itself is also queued.
The code above will cause the job to fail of course, and I can see it in the failed_jobs table, but the notification is not send. If I put the notification code somewhere else (eg in a controller) and execute it the notification is sent so that code is correct.
Any job is handled through the handle() method. Make sure this method doesn't fail because of a syntax mistake. If your code fails on a syntax error, the rest of you code can't possibly be executed as it will never be processed. Force a conceptual mistake, for example a division by zero forced error:
use Notification;
use App\Notifications\MonitoringMessage;
public function handle()
{
$result = 1/0;
}
public function failed()
{
$message = ':warning: A job failed.';
Notification::route('slack', config('services.slack.webhook'))->notify(new MonitoringMessage($message));
}
As you have stated yourself, the Exception class implementation is not needed because it's not used.

Laravel Nova – Manually Send Error Alert From Observer Class

I have a Season Resource model with a field named active.
The requirement is to disable deletion for a season with an active status.
I have created an Observer for the season model to watch deleting an event. From this function, I can block the delete in case active is true.
But the issue is with the error message; is there any way to add an error message to session flash from the Observer class?
<?php
public function deleting(Season $season)
{
if($season->active_season)
{
Log::info('Sorry, this season can`t be deleted.
There must be at least one active season.');
}
return false;
}
This is not tested but I was able to achieve something like that in a previous project:
use Illuminate\Validation\ValidationException;
class AbcObserver
{
public function creating(Abc $abc)
{
if ($abc->details != 'test') {
throw ValidationException::withMessages(['details' => 'This is not valid.']);
}
}
}
You can use the Exception class. I tested it in a Nova action and it throws the toast error notification.
use Exception;
throw new Exception('Error message here ...');
// Or
throw_if(
$validator->fails(), // or any true boolean
Exception::class,
'Error message here ...'
);
I don't know how to flash the error message.
But since the requirement is to disable deletion for a season with an active status, I'm suggesting to use policy which won't display the delete icon when doesn't match the condition.
class SeasonPolicy {
...
public function delete(User $user, Season $season) {
if($season->active_season) {
return false;
}
return true;
}
}
and register the policy in AuthServiceProvider.
Note:
Undefined Policy Methods
If a policy exists but is missing a method for a particular action,
the user will not be allowed to perform that action. So, if you have
defined a policy, don't forget to define all of its relevant
authorization methods.

Laravel, autoload classes based on an input array of class names

I have various classes, e.g.
<?php
namespace MyApp\Notifications;
class FirstNotification implements NotificationInterface {
public function getMessage() {
return 'first message';
}
}
and
<?php
namespace MyApp\Notifications;
class SecondNotification implements NotificationInterface {
public function getMessage() {
return 'second message';
}
}
I then have an array like: ['First','Second'].
I'm using:
foreach (['First','Second'] as $class_prefix) {
$class = "MyApp\Notifications\\{$class_prefix}Notification";
$object = new $class();
echo $object->getMessage();
}
but it feels a bit hacky - is there a better/more standard way to do this? The array is supplied elsewhere and will be different depending on the user - my aim is to be able to easily create classes that implement my interface and know this loop will be able to show their messages if they exist.
I ideally don't want to have to add a use statement for all the classes upfront, or pass them into a constructor, I just want magic to happen!
I'm assuming you don't actually mean autoload but rather instantiate the class. Autoloading is the process of including all the files in your application so the contents (usually classes) can be used. If you are using Laravel 5 and you follow PSR-4 (namespace matches directory structure) you don't have to do anything to make those classes available.
The code you already have looks fine and probably works. However you could make use of Laravels Service Container to resolve the class. This has quite a few advantages, one being the availability of automatic dependency injection...
foreach (['First','Second'] as $class_prefix) {
$object = app()->make("MyApp\Notifications\\{$class_prefix}Notification");
echo $object->getMessage();
}
Or even this:
foreach (['First','Second'] as $class_prefix) {
echo app()->callClass("MyApp\Notifications\\{$class_prefix}Notification#getMessage");
}
However, both will cause an exception if the class doesn't exist. You can check for that beforehand or just catch the exception:
foreach (['First','Second'] as $class_prefix) {
try{
$object = app()->make("MyApp\Notifications\\{$class_prefix}Notification");
echo $object->getMessage();
}
catch(ReflectionException $e){
// whooops
}
}

Resources