Architecture · Modules · Reference

Technical Documentation

Everything about how Law Farm is built — layers, database schema, every frontend and admin module, the AI service layer, settings, and security. For install steps, see the Setup Guide instead.

14
DB Tables
16
Models
13
Admin Modules
5
AI Features
AArchitecture BDirectory Structure CDatabase Schema DFrontend Modules EAdmin Modules FAI Features GSettings HSecurity IRoles & Data JTesting KLimitations
◈ A. Architecture

Layered MVC + Services

Controllers stay thin — they delegate to Form Requests for validation, Repositories or Services for logic, and Eloquent for persistence.

Controller
Form Request
Repository / Service
Eloquent Model
Blade View
LayerLocationResponsibility
Controllersapp/Http/Controllers/{Frontend,Admin,Api}HTTP entry points; thin, delegate out
Form Requestsapp/Http/Requests/{Frontend,Admin}Validation & input normalization
Repositoriesapp/Repositories/{Contracts,Eloquent}Query logic — Blog & Appointment fully implemented as the reference pattern
Servicesapp/Services, app/Services/AiAI provider calls, email sending, settings-driven runtime config
Modelsapp/ModelsEloquent models, relationships, scopes, casts
Viewsresources/views/{frontend,admin}Server-rendered Blade, Tailwind via CDN
◈ B. Directory Structure

Where everything lives

project tree
app/ ├── Console/Commands/ # lawfarm:prune-ai-logs, lawfarm:prune-audit-logs ├── Events/ # NewsletterSubscribed ├── Http/ │ ├── Controllers/ │ │ ├── Admin/ # CMS, users, roles, settings, media, SEO... │ │ │ └── Ai/ # AiSettingsController, AiContentController... │ │ ├── Api/ # public chatbot endpoint │ │ ├── Auth/ # LoginController │ │ └── Frontend/ # public site controllers │ ├── Middleware/ # CheckRole, CheckMaintenanceMode, LogAdminActivity │ └── Requests/{Admin,Frontend} ├── Models/ # 16 Eloquent models ├── Policies/ # BlogPolicy (reference pattern) ├── Providers/AppServiceProvider.php ├── Repositories/{Contracts,Eloquent} └── Services/{Ai,*} bootstrap/app.php # real Laravel 12 file, extended with admin/api route groups config/*.php # real Laravel 12 defaults (services.php extended with AI block) database/ ├── migrations/ # 14 app migrations + skeleton's cache/jobs migrations ├── seeders/ # dummy data for every module └── factories/ resources/views/ ├── admin/ # 13 modules, each with index + form partials └── frontend/ # 9 public-facing modules routes/ ├── web.php # public site + login ├── admin.php # admin panel, prefixed /admin, named admin.* ├── api.php # public chatbot API └── console.php
Authentic skeleton artisan, bootstrap/, config/*.php, public/index.php, resources/css, resources/js, and vite.config.js are unmodified files from the real, official laravel/laravel 12.0.0 skeleton — not hand-reconstructed.
◈ C. Database Schema

14 migrations, ordered by dependency

TablePurposeKey Relationships
usersAdmin/staff accountsbelongsToMany roles
roles / permissionsRBACmany-to-many via pivots
practice_areasLegal service categoriesbelongsToMany lawyers, hasMany faqs
lawyersAttorney profilesbelongsToMany practice_areas, hasMany appointments
categories / tagsBlog taxonomycategories hasMany blogs; tags belongsToMany blogs
blogsCMS articlesbelongsTo category/user, belongsToMany tags
appointmentsConsultation bookingsbelongsTo lawyer, practice_area
contactsContact form submissions
faqsPractice-area FAQsbelongsTo practice_area
testimonialsClient reviews
settingsKey/value config (7 groups + AI)
ai_logsAI usage & token trackingbelongsTo user
email_logsOutgoing email history
audit_logsAdmin activity trailbelongsTo user, polymorphic auditable
Note The skeleton's own cache, cache_locks, jobs, and failed_jobs migrations are kept as-is and not duplicated. The old migration-conflict workaround (deleting the skeleton's default users migration) is no longer needed — it's already resolved in this project.
◈ D. Frontend Modules

9 public-facing modules

ModuleRoute(s)Notes
Home/Featured practice areas, lawyers, testimonials, latest posts
About/aboutFirm bio + team grid
Practice Areas/practice-areas, /practice-areas/{slug}Detail page shows assigned lawyers + FAQs
Lawyers/lawyers, /lawyers/{slug}Full bio, experience, practice areas
Blog/blog, /blog/{slug}Only published posts with published_at ≤ now() are visible
FAQ/faqGrouped by practice area
Contact/contactRate-limited 5/min, writes to contacts
Appointment Booking/appointmentChecks for lawyer double-booking on the selected date
Search/search?q=Searches published blog title/excerpt/content
NewsletterPOST /newsletter/subscribeFires NewsletterSubscribed event — wire up a listener for your ESP
AI Chatbot WidgetPOST /api/chatbot/messageFloating widget in the site footer; hides automatically when AI is off
◈ E. Admin Panel Modules

13 modules, one RBAC system

All routes below are prefixed /admin and protected by the auth middleware; user/role/category/tag management additionally requires the admin role.

ModuleRoute Name PrefixRole Required
Dashboardadmin.dashboardAny authenticated user
Users & Rolesadmin.users.*, admin.roles.*admin
Lawyersadmin.lawyers.*Any authenticated user
Practice Areasadmin.practice-areas.*Any authenticated user
Blog / CMSadmin.blogs.*, admin.categories.*, admin.tags.*Any authenticated user; categories/tags: admin
Appointmentsadmin.appointments.*Any authenticated user
Contact Messagesadmin.contacts.*Any authenticated user
Testimonialsadmin.testimonials.*Any authenticated user
Media Manageradmin.media.*Any authenticated user
SEOadmin.seo.*Any authenticated user
Audit Logsadmin.audit-logs.indexAny authenticated user
Settingsadmin.settings.*Any authenticated user
AI Settings & Logsadmin.ai.*Any authenticated user
Tighten before production "Any authenticated user" reflects the scaffold as shipped — apply the role: middleware or the included BlogPolicy pattern as you assign real staff roles.
◈ F. AI Features

5 features, 1 service, 2 providers

FeatureServiceTrigger
Blog content draftingBlogContentService::generateDraft()"✨ Generate with AI" on the blog form
SEO metadata suggestionsBlogContentService::generateMetadata()"✨ AI Suggest" on the blog form's SEO fields
FAQ generationFaqGenerationService::generate()POST admin/ai/generate/faq
Email draftingEmailDraftService::draft()POST admin/ai/generate/email
Chatbot (site visitors)ChatbotService::reply()Floating widget → POST /api/chatbot/message

How provider calls work

AiService::complete() checks the ai.enabled setting first, then routes to Anthropic's /v1/messages or OpenAI's /v1/chat/completions via plain Http:: calls — no vendor SDK required. Every call, success or failure, is written to ai_logs with token counts where available.

app/Services/Ai/AiService.php
// app/Services/Ai/AiService.php public function complete(string $feature, string $prompt, array $options = []): string { if (! $this->isEnabled()) { throw new \RuntimeException('AI features are currently disabled...'); } // ...routes to callAnthropic() or callOpenAi(), logs to AiLog }
◈ G. Settings Reference

Live-editable, encrypted where it matters

All settings live in a single settings key-value table (Setting::get/set/group()), grouped by the group column. Secrets are stored encrypted.

GroupKeysEncrypted?
generalsite_name, tagline, contact_email, contact_phone, address
emailsmtp_host, smtp_port, smtp_encryption, smtp_username, smtp_password, from_name, from_emailsmtp_password
seodefault_meta_title, default_meta_description, google_analytics_id, robots_txt
socialfacebook_url, twitter_url, linkedin_url, instagram_url
securitytwo_factor_enabled, login_rate_limit, recaptcha_site_key, recaptcha_secret_keyrecaptcha_secret_key
cache(action only — "Clear Cache Now" runs artisan optimize:clear)
maintenanceenabled, message
aienabled, provider, model, api_keyapi_key
◈ H. Security

What's already handled

◈ I. Roles & Seeded Data

3 roles, fully seeded demo content

Default roles

RoleAccess
adminAll permissions
editorBlogs, Practice Areas, Testimonials
supportAppointments, Contacts

Dummy content

6 Practice Areas 6 Lawyer Profiles 7 Categories / 10 Tags 6 Blog Posts 10 FAQs 6 Testimonials 3 Contacts 3 Appointments

All placeholder photography comes from picsum.photos (practice area & blog cover images) and i.pravatar.cc (lawyer, admin, and testimonial headshots) — both free, non-copyrighted placeholder services safe for local development. Replace with real assets via the Media Manager before launch.

◈ J. Testing & Code Quality

Feature tests included

terminal
# tests/Feature/HomePageTest.php, AdminAuthTest.php php artisan test # Laravel Pint — code style ./vendor/bin/pint

Queues default to the database driver; wire Mail::send() calls to ->queue() where async delivery matters.

◈ K. Known Limitations

What's not built yet

Suggested next steps Wire up real 2FA, extend the repository pattern to the remaining modules, and connect the newsletter event to an ESP of choice.