In this part of implementing a pricing system, we will create a simple dashboard to manage plans CRUD. We will be using Laravel breeze as it provides a ready scaffolding with auth.
Step 1: Install Laravel Breeze
composer require laravel/breeze --dev
Next run the breeze:install command and select Vue with Inertia:
php artisan breeze:install Which Breeze stack would you like to install? ───────────────┐ │ ○ Blade with Alpine │ │ ○ Livewire (Volt Class API) with Alpine │ │ ○ Livewire (Volt Functional API) with Alpine │ │ ○ React with Inertia │ │ › ● Vue with Inertia │ │ ○ API only
After selecting vue with Inertia wait some time and then run:
npm install && npm run dev
This gives you:
-
/login,/register,/dashboard -
Vue 3 + Inertia setup
Also the breeze: install already generates some Vue views and components in resources/js/.
You can now log in as a user and access /dashboard.

Update HandleInertiaRequests::share() method. The share() method allows to share global data to be accessible in our Vue app, in this case let’s share the flash messages:
app/Http/Middleware/HandleInertiaRequests.php:
public function share(Request $request): array
{
return [
...parent::share($request),
....
....
'flash' => [
'success' => fn () => $request->session()->get('success')
]
];
}
Step 2: Restrict Admin Access
For simplicity, we’ll assume only users with is_admin = true can access admin pages.
Add is_admin column to users table:
Step 3: Admin Controllers
Create the admin controllers for plans and features CRUD:
php artisan make:controller Admin/PlanController php artisan make:controller Admin/FeatureController
app/Http/Controllers/Admin/PlanController.php
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Feature;
use App\Models\Plan;
use Illuminate\Http\Request;
use Inertia\Inertia;
class PlanController extends Controller
{
public function index()
{
$plans = Plan::with('features')->get();
return Inertia::render('Admin/Plans/Index', [
'plans' => $plans
]);
}
public function create()
{
$features = Feature::all();
return Inertia::render('Admin/Plans/Create', compact('features'));
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required',
'slug' => 'required|unique:plans',
'price' => 'required|numeric',
'billing_cycle' => 'required|in:monthly,yearly',
]);
$plan = Plan::create($validated);
// Attach selected features
if ($request->has('features')) {
foreach ($request->features as $feature) {
$plan->features()->attach($feature['id'], ['value' => $feature['value'] ?? null]);
}
}
return redirect()->route('admin.plans.index')->with('success', 'Plan created!');
}
public function edit(Plan $plan)
{
$plan->load('features');
$features = Feature::all();
return Inertia::render('Admin/Plans/Edit', compact('plan', 'features'));
}
public function update(Request $request, Plan $plan)
{
$validated = $request->validate([
'name' => 'required|string',
'slug' => 'required|string|unique:plans,slug,' . $plan->id,
'price' => 'required|numeric',
'billing_cycle' => 'required|in:monthly,yearly',
'description' => 'nullable|string',
]);
$plan->update($validated);
// Sync features with values
$syncData = [];
foreach ($request->features as $f) {
if (!empty($f['value'])) {
$syncData[$f['id']] = ['value' => $f['value']];
}
}
$plan->features()->sync($syncData);
return redirect()->route('admin.plans.index')->with('success', 'Plan updated!');
}
public function destroy(Plan $plan)
{
$plan->delete();
return back()->with('success', 'Plan deleted.');
}
}
app/Http/Controllers/Admin/FeatureController.php
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Feature;
use Illuminate\Http\Request;
use Inertia\Inertia;
class FeatureController extends Controller
{
public function index()
{
return Inertia::render('Admin/Features/Index', [
'features' => Feature::all()
]);
}
public function store(Request $request)
{
$request->validate(['name' => 'required|string|max:255']);
Feature::create($request->only('name'));
return back()->with('success', 'Feature added!');
}
public function update(Request $request, Feature $feature)
{
$validated = $request->validate(['name' => 'required|string|max:255']);
$feature->update($validated);
return back()->with('success', 'Feature updated!');
}
public function destroy(Feature $feature)
{
$feature->delete();
return back()->with('success', 'Feature deleted.');
}
}
As shown above the full controller logic for plan and features handling.
Step 4: Admin Routes
Create an admin route group in routes/web.php:


