jeffersongoncalves/laravel-faq
A Laravel package for adding a structured FAQ system to your application. It provides models, migrations, and management tools for organizing questions and answers by category, with a simple API for displaying FAQs in your app.
This is the initial 1.0.0 release, so treat it as a baseline integration rather than an upgrade. The package provides translatable FAQ categories and questions/answers using spatie/laravel-translatable; the main work is publishing assets, configuring locales, and wiring the models into your admin and public-facing FAQ pages.
Start by reviewing:
$translatable and what relationships exist.Minimal setup:
composer require <vendor>/<package>
php artisan vendor:publish --provider="<Vendor>\<Package>\<PackageServiceProvider>"
php artisan migrate
If the package ships seed data, publish and run that separately. Confirm config/app.php locale and fallback locale match the package config, and ensure spatie/laravel-translatable is installed and configured as expected.
First practical use case: create one FAQ category with English and Spanish translations, attach a translated question and answer, then render the FAQ page under each locale. This validates the translation storage, fallback behavior, relationships, and public rendering path before building a full admin CRUD.
Locale-aware CRUD. Build admin forms with one tab or panel per configured locale. Save translations using Spatie’s API rather than assigning translated attributes directly:
$category->setTranslation('name', 'en', 'Billing');
$category->setTranslation('name', 'es', 'Facturación');
$category->save();
For bulk form submissions, loop over locales and call setTranslation() for each translatable field. Keep non-translatable fields such as order, is_active, and parent relationships outside that loop.
Public rendering. Use the current application locale and let the package/Spatie fallback handle missing translations:
app()->setLocale($locale);
$categories = FaqCategory::query()
->where('is_active', true)
->orderBy('order')
->with(['questions' => fn ($query) => $query->where('is_active', true)->orderBy('order')])
->get();
Then render $category->translate('name') or $category->name, depending on the package’s accessors. Confirm whether the package exposes convenience accessors or expects direct Spatie methods.
Admin resource integration. If using Filament, Nova, or custom Blade forms, map each translatable field to locale-specific inputs. Validate each locale explicitly:
'name.en' => ['required', 'string', 'max:255'],
'name.es' => ['nullable', 'string', 'max:255'],
Persist only after validation passes. For questions and answers, use nested form components or repeaters keyed by locale.
API resources. Return both the resolved translation and the full translation map when useful:
return [
'id' => $category->id,
'name' => $category->translate('name'),
'translations' => [
'name' => $category->getTranslations('name'),
],
];
This lets frontends switch locale without another request while keeping the default response simple.
Ordering and filtering. Store sort order and active flags as regular columns. Avoid relying on translated columns for database-level ordering unless you have verified JSON query support on your database. If translated slugs are used for route model binding, resolve them in application code or add a dedicated canonical slug column.
Caching. FAQ content changes infrequently, so cache rendered FAQ trees per locale and clear the cache when categories, questions, or answers are saved. Use model observers or model events to invalidate locale-specific cache keys.
where('name', ...), orderBy('name'), and some database indexes behave differently than normal columns. Filter/sort on regular columns and resolve translated values in PHP when possible.hasTranslation() or getTranslations() so editors know what is missing.fill(), confirm the package’s casts and fillable configuration support your form payload; otherwise use explicit setTranslation() calls.How can I help you explore Laravel packages today?