-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deserializing model in event constructor fails for a queued event listener? #21
Comments
@localpathcomp
namespace App\Models;
use Illuminate\Support\Facades\Log;
use Kitar\Dynamodb\Model\Model as DynamodbModel;
use function Illuminate\Events\queueable;
class Model extends DynamodbModel
{
protected $table = 'my-table';
protected $primaryKey = 'PK';
protected $sortKey = 'SK';
protected $fillable = ['PK', 'SK'];
protected static function booted()
{
static::created(queueable(function ($item) {
Log::info("Model created (PK:{$item->PK}, SK:{$item->SK})");
}));
}
}
use App\Models\Model;
Model::create(['PK' => 'PK1', 'SK' => 'SK1']);
php artisan queue:work And then, job was executed with no error and log was successfully created.
Could you share a bit more details for reproducing the problem? |
Hi Sure thing. Here's the two models I use. I can try again as well with more debugging in place if it would help. I was thinking maybe its calling get default connection and I have a multi tenant setup? Queue connection = database <?php
namespace App\Models;
use Illuminate\Support\Str;
use Kitar\Dynamodb\Model\Model;
class SystemEvent extends Model
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'dynamodb';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'system_events';
/**
* The Partition Key.
*
* @var string
*/
protected $primaryKey = 'PK';
/**
* The Sort Key.
*
* @var string|null
*/
protected $sortKey = 'SK';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'id',
'tenant_uuid',
'event_data',
'event_origin',
'event_type',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'PK',
'SK',
'GSI1PK',
'GSI1SK',
'GSI2PK',
'GSI2SK',
'TYPE',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'event_data' => 'array',
];
protected static function booted()
{
static::creating(function ($systemEvent) {
if (empty($systemEvent->event_type)) {
throw new \InvalidArgumentException();
}
$uuid = (string) Str::uuid();
// index attributes
$systemEvent->PK = "EVENT#{$uuid}";
$systemEvent->SK = "EVENT#{$uuid}";
$systemEvent->GSI1PK = "EVENT_TYPE#{$systemEvent->event_type}";
$systemEvent->GSI1SK = "EVENT#{$uuid}";
if (! empty($systemEvent->tenant_uuid)) {
$systemEvent->GSI2PK = "TENANT#{$systemEvent->tenant_uuid}";
$systemEvent->GSI2SK = "EVENT#{$uuid}";
}
$systemEvent->TYPE = self::class;
// item attributes
$systemEvent->id = $uuid;
});
}
public static function find($id)
{
return parent::find(['PK' => "EVENT#{$id}", 'SK' => "EVENT#{$id}"]);
}
public static function extractLastEvaluatedKey($item)
{
if (empty($item)) {
return null;
}
return $item->meta()['LastEvaluatedKey'] ?? null;
}
} <?php
namespace App\Models;
use Kitar\Dynamodb\Model\Model;
class WebhookLog extends Model
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'dynamodb';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'webhook_logs';
/**
* The Partition Key.
*
* @var string
*/
protected $primaryKey = 'PK';
/**
* The Sort Key.
*
* @var string|null
*/
protected $sortKey = 'SK';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'id',
'event_id',
'application_id',
'attempt',
'headers',
'http_verb',
'webhook_url',
'payload',
'response',
'error_type',
'error_message',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'PK',
'SK',
'GSI1PK',
'GSI1SK',
'GSI2PK',
'GSI2SK',
'TYPE',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'payload' => 'array',
'response' => 'array',
'headers' => 'array',
];
protected static function booted()
{
static::creating(function ($webhookLog) {
if (empty($webhookLog->id)) {
throw new \InvalidArgumentException();
}
if (empty($webhookLog->application_id)) {
throw new \InvalidArgumentException();
}
if (empty($webhookLog->event_id)) {
throw new \InvalidArgumentException();
}
// index attributes
$webhookLog->PK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->SK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->GSI1PK = "APPLICATION_ID#{$webhookLog->application_id}";
$webhookLog->GSI1SK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->GSI2PK = "EVENT#{$webhookLog->event_id}";
$webhookLog->GSI2SK = "WEBHOOK#{$webhookLog->id}";
$webhookLog->TYPE = self::class;
});
}
public static function find($id)
{
return parent::find(['PK' => "WEBHOOK#{$id}", 'SK' => "WEBHOOK#{$id}"]);
}
public static function extractLastEvaluatedKey($item)
{
if (empty($item)) {
return null;
}
return $item->meta()['LastEvaluatedKey'] ?? null;
}
} |
Thanks for sharing! The code looks interesting. However, I can't reproduce the problem yet. Let me clarify a few things.
If you dump the |
Hi @kitar. yes we are using multiple DB connections.
I think thats what it is. On my dynamo DB model do I need to explicitly define the
The model works perfectly outside of the queue.
The event class. I wanted to inject the model instance and not the plain array. <?php
namespace App\Events\SystemEvents;
use App\Models\SystemEvent;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class SystemEventCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @param array $systemEvent
* @return void
*/
public function __construct(
public array $systemEvent,
) {
}
} The listener for example <?php
namespace App\Listeners\Webhooks;
use App\Events\SystemEvents\SystemEventCreated;
use App\Models\WebhookSubscriber;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Cache;
use Spatie\WebhookServer\WebhookCall;
class SendSystemEventNotifications implements ShouldQueue
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \App\Events\SystemEvents\SystemEventCreated $event
* @return void
*/
public function handle(SystemEventCreated $event)
{
// use the model off the event class for example
}
} |
@localpath Sorry for the late reply. Thanks for the information and you were indeed right. However, there are several issues involved that make it difficult to resolve immediately. Please pass keys or array instead of a model instance to the listener at this time. Notes on this issue:
|
@kitar Hi. Thanks for looking at it. Yes that makes perfect sense. Easy enough to get around and just rehydrate models later. Thanks!! |
@kitar Hi, I have same issue with broadcast notifications. what should I do with it? this is my example code: Notification::send(User::query()->limit(1)->scan()->first(), new TestNotification() this is the User Model: class User extends Model implements AuthenticationContract {
use Authenticatable, HasFactory, Notifiable;
public const CREATED_AT = 'CreatedAt';
public const UPDATED_AT = 'UpdatedAt';
protected $table = 'Users';
protected $primaryKey = 'Id';
protected $connection = 'dynamodb';
protected $fillable = [
'FirstName',
'LastName',
'Mobile',
'Email',
'Username',
'Avatar',
'Password',
'RememberToken',
'DeletedAt',
];
/**
* @var array<int, string>
*/
protected $hidden = [
'Password',
'RememberToken',
];
public function getRememberToken(): string
{
return 'RememberToken';
}
protected function casts(): array
{
return [
'DeletedAt' => 'date',
];
}
protected static function boot(): void
{
parent::boot();
static::creating(function ($model) {
if ($model->{$model->getKeyName()} === null) {
$model->setAttribute($model->getKeyName(), Str::orderedUuid()->toString());
}
});
}
public function getIncrementing(): bool
{
return false;
}
public function getKeyType(): string
{
return 'string';
}
} I've made some changes to your code but still not working well: Model: // adding $implode to get keys as string
/**
* Get the key of the current item.
*
* @return string|array
*/
public function getKey(bool $implode = true)
{
/**
* Your code here
*/
if ($implode) {
return implode("|", $key);
}
return $key;
}
public function newQueryWithoutScopes()
{
return $this->newQuery();
}
public function newQueryForRestoration($ids)
{
return $this->newQueryWithoutScopes()->whereKey($ids);
} Builder: public function whereKey($id)
{
if ($id instanceof Model) {
$id = $id->getKey(false);
}
if (is_array($id) || $id instanceof Arrayable) {
$this->filterIn('Id', $id);
return $this;
}
if ($id !== null && is_string($id)) {
$id = (string) $id;
}
return $this->filter("Id", '=', $id);
}
public function get($columns = [])
{
return collect($this->scan()->all());
} I know this changes not helping. but I think it's better to send to you |
Hello. Ty for the great package. Using it I've come across a serialization issue when using queued event listeners. Seems like it's trying to connect on a null connection? Like it's reverting to the default database driver? Using the model created event and listening to that event with a queued event listener fails. I can toArray() or pass the DynamoDB partition keys to do a lookup but seems like laravel should be able to hydrate no matter the driver right?
Laravel 9.x
laravel-dynamodb 1.x
The text was updated successfully, but these errors were encountered: