Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filagora Forums Laravel Package

filagora/filagora-forums

Filagora Forums is a Laravel/PHP package for adding a complete discussion forum to your app. It includes categories, topics, replies, moderation, role-based permissions, configurable models, migrations, and a clean Blade UI, making forum setup and customization straightforward.

View on GitHub
Deep Wiki
Context7

Getting Started

Install filagora/filagora v0.6.0. It pulls filagora-core ^0.2.2, which is a floor, not a nicety — attachments and the adoption tooling rely on it. Run php artisan migrate: the package now ships eight migrations across twenty-one tables, and filagora:adopt expects exactly that shape. Existing sites should re-run filagora:adopt against their database before trusting the new tables; if you maintain a profile package that adds importer columns or composite indexes, declare them through core 0.2.2's drop_indexes rather than hand-editing.

Post a thread, reply, and browse once so you have realistic data. Then, in this order:

  1. ReadTracking — the highest-value, lowest-friction addition. Wire unreadThreads() into a "what's new" page first; it handles baseline, lookback and per-viewer read times in one call.
  2. Mentions — enable via mentions.enabled, confirm UserMentionedNotification sends (mentions.mail.enabled), then decide whether the default named route mentions.routes.thread is good enough or should point at your own thread route.
  3. Attachments — the biggest lift. Needs intervention/image, a storage disk you are happy with, forums.orphaned_attachments retention configured, filagora:forums:prune-attachments scheduled, and a decision about authorizeUsing() if other packages' files live alongside post attachments.
  4. Search (unchanged from v0.5.0, still opt-in) — composer require filagora/filagora-search, publish config, php artisan filagora:search:backfill, then look at config/filagora-search.php (search.post_text_limit, search.fulltext), ForumSearch, ForumSearchCriteria, and the two engines ThreadSearchIndex / ThreadDatabaseSearch.

First use case: give users a "what's new" list with ReadTracking::unreadThreads(). Every forum eventually grows one, and the package handles the three things hand-rolled versions get wrong: per-user baseline (from the user column named by read_tracking.baseline_attribute), a bounded lookback window (read_tracking.unread_max_lookback_days), and read checks that are subqueries so they compose with whatever thread query you already have.

Implementation Patterns

Read tracking belongs at the edges of your controllers, not in your models. Mark read on the thread show action, mark forums read on the forum list action, and use unreadAmong() when hydrating a page of threads rather than looping isUnread() per row — it exists precisely to avoid N+1. unreadThreads() returns a bounded "what's new" list; treat it as a page source, not an unbounded feed. A hand-made unread flag beats every read time, so if your UI exposes "mark unread", respect that the package will keep it unread even after the user later opens the thread until your code clears the flag.

Mentions are listeners, not a service call you make. Turn on mentions.enabled and the package hooks new, edited, approved and restored posts, plus approved threads. The Mentions service does extraction (skipping code fences and quoted blocks, never paging the author, deduping so nobody is told twice, only paging people who can actually see the thread, capped per post by mentions.max_per_post). Your job is the edge: listen for UsersMentioned if you want to add push/webhook/in-app notification on top of the default email, and decide whether mentions.mail.enabled should be on in your environment.

Treat mentions as a per-post budget, not a per-edit budget. The cap is counted across edits with the post row locked, so re-saving a post does not give the author a fresh allowance. If you expose a "preview mentions" UI, count against the same limit or users will be confused when the real post drops some.

Attachments are two-step: upload inline, claim on submit. The Attachments service supports uploading during composition, then claiming the upload to a post when it saves — unattached uploads are the ones the prune command cleans up. Serve files through the package controller (safe headers, download counter, access that follows the post's visibility) rather than linking to your storage disk directly; if you do bypass it, you re-implement visibility and you will get it wrong. SVGs are deliberately never rendered inline in the page — they are download-only.

Register authorizeUsing() if you have other file-bearing packages. It is the documented hook for "this person may access this file given this post" when ownership is not simply "post attached to". Use it instead of subclassing the service.

Schedule the prune command. filagora:forums:prune-attachments implements the forums.orphaned_attachments retention rule. Without a schedule, failed uploads and abandoned drafts accumulate forever. Run it daily in a low-traffic window.

Keep the search patterns from v0.5.0. Build a ForumSearchCriteria from the request, pass the real viewer (never null), reach for unapproved() for the moderation queue and threads() for the public path, and use posts() when you need to show which reply matched. Bind ForumSearch to ThreadDatabaseSearch in tests so CI needs no search service; you can also fall back to it in production knowingly. Re-run backfill when you add filter fields.

Privacy export and erasure now cover all three new modules. Uploads, mention records and read markers are all included in export and removed on erasure. If you have a custom export/erasure pipeline, verify it still runs end-to-end with the new tables — the package's hooks are the canonical path.

Gotchas and Tips

  • filagora:adopt expectations changed. Twenty-one tables, eight migrations. On an existing install, re-run adopt after migrating; if it complains about tables it does not recognise, that is almost certainly a profile package that has not caught up. Declare importer columns and composite indexes in the profile package via core 0.2.2's drop_indexes.
  • Read tracking: never mark read on GET. Prefetchers, link previewers and email scanners will open every URL you send and quietly mark the whole forum read. Mark on an explicit POST, or gate on a session/CSRF signal.
  • Read tracking: lookback is a hard cutoff. unreadThreads() won't surface something read (or created) outside read_tracking.unread_max_lookback_days. That is a feature — "what's new" should not return year-old threads — but users will file it as a bug. If you need full history, query threads directly and use unreadAmong() on the page.
  • Read tracking: baseline is a user column, not a config timestamp. read_tracking.baseline_attribute names the column (typically something like a registration or "tracking started" date) used as the starting point when a user has no read rows. Point it at the wrong column and new users appear to have read everything (or nothing).
  • Mentions: skipping code and quotes is deliberate. Users will ask why a @name inside a fenced block didn't notify. Do not "fix" it by loosening extraction; you will page people on pasted log snippets.
  • Mentions: nobody is told twice. Edits, re-approvals and restores that re-trigger the listener will not re-notify the same person for the same post. Do not build a "resend mentions" action on top of the listener — emit UsersMentioned yourself if that is genuinely what you want.
  • Mentions: the author is never told. Obvious once you read it, surprising when a developer tests by mentioning themselves. Use a second account.
  • Attachments: AttachmentRefused is the one exception to catch. It is thrown by the hourly per-person limit (and similar refusal cases). Surface it as a validation-style message, not a 500.
  • Attachments: SVG is download-only in the page. Do not render inline or serve with image/svg+xml from a domain that shares cookies with your app.
  • Attachments: orphaned uploads are a retention problem, not a bug. Inline uploads that are never claimed sit on disk until the prune command runs. Track the disk usage; forums.orphaned_attachments is your lever, and intervention/image thumbnails are the other storage cost.
  • Attachments: thumbnails need a working intervention/image install with the right GD/Imagick driver. A broken driver usually surfaces as silent missing thumbnails rather than an exception.
  • Search gotchas still apply. search.post_text_limit truncates post text in thread documents (raise it and re-backfill); unapproved content is invisible to threads() by design; deactivated forums drop out of search without deleting anything; search.fulltext must match across environments or MariaDB dev results will diverge from staging.
  • Debugging order, broadened. First confirm the binding or service you think is wired (log the class name once in local — ForumSearch, ReadTracking, Attachments, Mentions), then dump the criteria/marker/upload object your controller built, then check the underlying rows (ThreadSearchIndex, thread_reads, post_attachments, post_mentions). Recent "it's broken" reports split roughly: read tracking is usually a GET-vs-POST or baseline issue; mentions is almost always a duplicate-suppression or visibility expectation; attachments is usually a missing claim, a disk misconfiguration or a schedule that isn't running; search is a criteria/viewer problem, not an indexing one.
  • Extension points: ThreadSearchIndex (document contents), SearchRelations (what triggers a resync), ForumSearchCriteria (new filterable dimensions), Attachments::authorizeUsing() (cross-package access), and the UsersMentioned event (extra notification channels). Prefer these over subclassing the façade services (ForumSearch, Attachments, Mentions, ReadTracking), which are the thin, least-stable layer.
  • Config caching tripped up the last release and will trip this one. The new keys (mentions.*, read_tracking.*, forums.orphaned_attachments) are read at runtime but cached with the rest of your config. php artisan config:cache after editing, or the values will look ignored.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core