NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky

Balík má vlastnú tabuľku schránky — jedna položka na príjemcu, druh, subjekt a kontext v rámci okna, s počtom a posledným aktérom.

Zlučovanie

Rovnaký druh + subjekt + kontext + príjemca v rámci window() druhu aktualizuje jednu položku namiesto pridania ďalšej: count stúpne, posledný aktér a dáta nahradia staré a prečítaná položka sa znova stane neprečítanou. Okno sa počíta od prvej udalosti, takže hlučný subjekt začne v každom okne novú položku namiesto nekonečného zlučovania.

  • Bez okna → každá udalosť je samostatná položka.
  • Archivovaná alebo zmazaná položka sa prestane zlučovať: ďalšia udalosť začne novú (položka vrátená z archívu zostane uzavretá).
  • Kontext je súčasťou kľúča: rovnaký subjekt v dvoch kontextoch zostane dvoma položkami.
  • Eskalácie majú vlastnú dráhu: príjemca, ktorý dostal pôvodnú notifikáciu, dostane eskaláciu ako samostatnú položku aj e-mail.
  • Vyhľadávanie zahŕňa aj soft-deleted riadky, takže zmazaná položka nikdy neblokuje ďalšiu udalosť.

Dotazy a počty

$inbox = Notifications::inbox($user);               // or $user->inbox()

$inbox->paginate(perPage: 20);                       // not archived, newest activity first
$inbox->cursorPaginate();
$inbox->get();                                       // capped at inbox.max_per_page
$inbox->first();
$inbox->find($id);                                   // another user's id → InboxItemNotFoundException
$inbox->unread()->paginate();                        // read(), archived(), withArchived()
$inbox->in($company)->paginate();                    // or a key string: in('company:3')
$inbox->withoutContext();                            // allContexts() is the default
$inbox->category('requests')->type('request.awaiting_approval')->since(now()->subDay())->get();
$inbox->unreadCount();                               // 7
$inbox->counts();                                    // InboxCounts { unread, total, contexts: list<ContextCount{key, label, unread}> }

Stránky načítajú aktérov a subjekty vopred — bez N+1 — a perPage sa obmedzí na notifications.inbox.max_per_page.

Zmeny

$inbox->markRead($item);                                  // also by id; acknowledges an acknowledgedOnRead() reminder
$inbox->markUnread($item);
$inbox->in($company)->markAllRead();                      // one UPDATE within the handle's filters; returns the count
$inbox->markAllRead(in: $company, category: 'requests');   // explicit arguments replace the handle's context / category
$inbox->archive($item);
$inbox->unarchive($item);
$inbox->delete($item);                                    // soft delete

Metódy vracajú handle; markAllRead() vracia počet. markAllRead() rešpektuje každý filter handle — in() alebo withoutContext(), category(), type() a since() — takže $inbox->in($company)->markAllRead() vyčistí len daný priestor. Položka iného príjemcu je „nenájdená“ — rovnaká odpoveď ako pri chýbajúcom id, takže schránka neprezradí, čo existuje.

Resources

InboxItemResource vykreslí id, type, category, title, body, url (len http(s)), count, data (publicData() druhu), actor, subject, context, priority ({value, label}), read_at, archived_at, first_event_at a last_event_at (ISO-8601) v jazyku požiadavky. InboxCountsResource vykreslí InboxCounts.

use RoundlyConsulting\Notifications\Http\Resources\InboxItemResource;

// A page — eager-loads actors and subjects
return InboxItemResource::collection(Notifications::inbox($request->user())->unread()->paginate());

// One item: wrap it yourself — its own data field makes Laravel skip the wrapper
return ['data' => (new InboxItemResource($item))->resolve()];
{
  "id": 12, "type": "request.awaiting_approval", "category": "requests", "title": "…", "body": null,
  "url": "https://app.test/requests/9", "count": 10, "data": {"reference": "PR-9"},
  "actor": {"id": 4, "type": "User"}, "subject": {"id": 9, "type": "PurchaseRequest"},
  "context": {"key": "company:3", "label": "Acme s.r.o.", "id": 3, "type": "Company"},
  "priority": {"value": "normal", "label": "Normal"}, "read_at": null, "archived_at": null,
  "first_event_at": "2026-11-09T10:00:00+00:00", "last_event_at": "2026-11-09T10:04:31+00:00"
}

Žiadne názvy tried v odpovediach

Aktér, subjekt a kontext sa vykresľujú cez vymeniteľné resources. Nasmerujte kľúč na vlastný JsonResource, ak chcete pridať meno či avatar; prázdna hodnota alebo null je predvolené, čokoľvek, čo nie je inštanciovateľný potomok JsonResource, vyhodí výnimku s názvom kľúča a chýbajúci aktér či subjekt sa vykreslí ako null. Rola príjemcu neexistuje: žiadny endpoint ani udalosť nevykreslí iného používateľa.

RolaKľúč konfigurácieDostanePredvolený výstup
actornotifications.resources.actormodel aktéra{id, type: class_basename}
subjectnotifications.resources.subjectmodel subjektu{id, type: class_basename}
contextnotifications.resources.contextContextReference (snímka, bez dotazu){key, label, id?, type?}
// config/notifications.php
'resources' => [
    'actor' => App\Http\Resources\NotificationActorResource::class,
],

// app/Http/Resources/NotificationActorResource.php — receives the actor model
final class NotificationActorResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->resource->getKey(),
            'name' => $this->resource->name,
            'avatar_url' => $this->resource->avatar_url,
        ];
    }
}

Vysielanie (Reverb)

Balík nikdy nevysiela; počúvajte jeho udalosti po commite a resource vysielajte sami:

use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Notifications\Events\InboxItemCreated;
use RoundlyConsulting\Notifications\Events\InboxItemUpdated;
use RoundlyConsulting\Notifications\Http\Resources\InboxItemResource;

Event::listen([InboxItemCreated::class, InboxItemUpdated::class], function ($event): void {
    $item = $event->item->loadMissing(['actor', 'subject']);

    broadcast(new App\Events\InboxChanged(
        userId: $item->recipient_id,
        item: (new InboxItemResource($item))->resolve(),
        unread: Notifications::inbox($item->recipient)->unreadCount(),
    ));
});

// App\Events\InboxChanged: ShouldBroadcast on new PrivateChannel('App.Models.User.'.$this->userId)
// broadcastWith(): ['item' => $this->item, 'unread' => $this->unread] — the resource only, never a class name.

Kanál inbox v Laraveli

Aj vlastná notifikácia Laravelu sa môže dostať do schránky cez rovnaký krok zlučovania. Platí kontrola prístupu druhu a preferencia schránky; e-mail sa nepoužije — na ten slúži Notifications::send():

use Illuminate\Notifications\Notification;
use RoundlyConsulting\Notifications\Mail\InboxMessage;

final class CommentPosted extends Notification
{
    public function via(object $notifiable): array { return ['inbox', 'broadcast']; }

    public function toInbox(object $notifiable): InboxMessage
    {
        return (new InboxMessage('comment.posted'))
            ->about($this->post)
            ->by($this->author)
            ->in($this->post->blog)
            ->with(['excerpt' => $this->excerpt]);
    }
}

// Programmatically
Notifications::inboxMessage($user, new InboxMessage('comment.posted'));   // ?InboxItem

Keď sa subjekt zmaže

notifications.inbox.on_subject_deleted je keep (predvolené — položky sa vykreslia zo snímky a dát), archive alebo delete. Platí pre subjekty s HasNotificationReminders; pri ostatných zavolajte Notifications::for($subject)->revoke() sami.

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.