Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public function up(): void
$table->uuid('id')->primary();
$table->string('type');
$table->morphs('notifiable');
$table->foreignId('site_id')
->nullable()
->constrained('sites')
->cascadeOnUpdate()
->cascadeOnDelete();
$table->text('data');
$table->timestamp('read_at')->nullable();
$table->timestamps();
Expand Down
4 changes: 4 additions & 0 deletions src/EclipseServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Eclipse\Core\Models\User;
use Eclipse\Core\Models\User\Permission;
use Eclipse\Core\Models\User\Role;
use Eclipse\Core\Notifications\Channels\SiteDatabaseChannel;
use Eclipse\Core\Policies\User\RolePolicy;
use Eclipse\Core\Providers\AdminPanelProvider;
use Eclipse\Core\Providers\HorizonServiceProvider;
Expand All @@ -28,6 +29,7 @@
use Illuminate\Auth\Events\Login;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Mail\Events\MessageSent;
use Illuminate\Notifications\Channels\DatabaseChannel;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
Expand Down Expand Up @@ -104,6 +106,8 @@ public function register(): self
return new Registry;
});

$this->app->bind(DatabaseChannel::class, SiteDatabaseChannel::class);

return $this;
}

Expand Down
34 changes: 34 additions & 0 deletions src/Models/DatabaseNotification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Eclipse\Core\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Notifications\DatabaseNotification as BaseDatabaseNotification;
use Illuminate\Support\Facades\Context;

class DatabaseNotification extends BaseDatabaseNotification
{
/**
* Allow storing tenant/site identifier alongside the notification payload.
*/
protected $fillable = [
'id',
'type',
'notifiable_type',
'notifiable_id',
'data',
'read_at',
'site_id',
];

protected static function booted(): void
{
static::addGlobalScope('site', function (Builder $builder): void {
$siteId = Context::get('site');

if ($siteId !== null) {
$builder->where($builder->getModel()->getTable().'.site_id', $siteId);
}
});
}
}
10 changes: 10 additions & 0 deletions src/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
Expand Down Expand Up @@ -184,6 +185,15 @@ public function getSettings(string $settingsClass = UserSettings::class): Settin
return $settingsClass::forUser($this->id);
}

/**
* Override notifications relation to use site-aware notification model.
*/
public function notifications(): MorphMany
{
return $this->morphMany(DatabaseNotification::class, 'notifiable')
->orderBy('created_at', 'desc');
}

/**
* The channels the user receives notification broadcasts on.
*/
Expand Down
37 changes: 37 additions & 0 deletions src/Notifications/Channels/SiteDatabaseChannel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Eclipse\Core\Notifications\Channels;

use Illuminate\Notifications\Channels\DatabaseChannel;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Context;

class SiteDatabaseChannel extends DatabaseChannel
{
/**
* Build the payload stored in the notifications table and
* append the current site id so rows are tenant-aware.
*/
protected function buildPayload($notifiable, Notification $notification): array
{
$payload = parent::buildPayload($notifiable, $notification);
$fromNotification = $this->resolveSiteIdFromNotification($notification);
$resolved = $fromNotification ?? Context::get('site');
$payload['site_id'] = $resolved;

return $payload;
}

/**
* Prefer an explicit site id coming from the notification instance
* (useful when dispatching from queues) before resolving ambient context.
*/
protected function resolveSiteIdFromNotification(Notification $notification): ?int
{
if (method_exists($notification, 'getSiteId')) {
return $notification->getSiteId();
}

return null;
}
}