PART 1: Setup
bashcomposer create-project laravel/laravel medicine-system
cd medicine-system
Create database in phpMyAdmin:
medicine_db
Edit .env:
DB_DATABASE=medicine_db
DB_USERNAME=root
DB_PASSWORD=
Install Breeze:
bashcomposer require laravel/breeze --dev
php artisan breeze:install blade
php artisan migrate
npm install
npm run build
Install Bootstrap UI and PDF only:
bashnpm uninstall tailwindcss postcss autoprefixer
composer require aldhix/breeze-bootstrap-ui
php artisan breeze-bootstrap-ui:install --force
composer require barryvdh/laravel-dompdf
npm install
npm run build
Create files:
bashphp artisan make:model Medicine -mcr
php artisan make:controller ReportController
PART 2: Migration
Open database/migrations/xxxx_create_medicines_table.php:
php<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('medicines', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('generic_name');
$table->string('category');
$table->integer('quantity');
$table->date('expiration_date');
$table->decimal('price', 8, 2);
$table->string('status')->default('available');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('medicines');
}
};
Run:
bashphp artisan migrate
PART 3: Model
Open app/Models/Medicine.php:
php<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Medicine extends Model
{
protected $fillable = [
'name',
'generic_name',
'category',
'quantity',
'expiration_date',
'price',
'status',
];
protected $casts = [
'expiration_date' => 'date',
];
public function isExpired(): bool
{
return $this->expiration_date->isPast();
}
public function isLowStock(): bool
{
return $this->quantity <= 10;
}
}
PART 4: Routes
Open routes/web.php and use this:
php<?php
use App\Http\Controllers\MedicineController;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ReportController;
use Illuminate\Support\Facades\Route;
Route::middleware('auth')->group(function () {
Route::get('/', function () {
return redirect()->route('medicines.index');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Route::resource('medicines', MedicineController::class);
Route::get('/reports', [ReportController::class, 'index'])->name('reports.index');
Route::get('/reports/pdf', [ReportController::class, 'exportPdf'])->name('reports.pdf');
Route::get('/reports/csv', [ReportController::class, 'exportCsv'])->name('reports.csv');
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
require DIR.'/auth.php';
PART 5: Medicine Controller
Open app/Http/Controllers/MedicineController.php:
php<?php
namespace App\Http\Controllers;
use App\Models\Medicine;
use Illuminate\Http\Request;
class MedicineController extends Controller
{
public function index()
{
$medicines = Medicine::latest()->paginate(10);
return view('medicines.index', compact('medicines'));
}
public function create()
{
return view('medicines.create');
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'generic_name' => 'required|string|max:255',
'category' => 'required|string',
'quantity' => 'required|integer|min:0',
'expiration_date' => 'required|date',
'price' => 'required|numeric|min:0',
'status' => 'required|string',
]);
Medicine::create($request->all());
return redirect()->route('medicines.index')
->with('success', 'Medicine added successfully!');
}
public function show(Medicine $medicine)
{
return view('medicines.show', compact('medicine'));
}
public function edit(Medicine $medicine)
{
return view('medicines.edit', compact('medicine'));
}
public function update(Request $request, Medicine $medicine)
{
$request->validate([
'name' => 'required|string|max:255',
'generic_name' => 'required|string|max:255',
'category' => 'required|string',
'quantity' => 'required|integer|min:0',
'expiration_date' => 'required|date',
'price' => 'required|numeric|min:0',
'status' => 'required|string',
]);
$medicine->update($request->all());
return redirect()->route('medicines.index')
->with('success', 'Medicine updated successfully!');
}
public function destroy(Medicine $medicine)
{
$medicine->delete();
return redirect()->route('medicines.index')
->with('success', 'Medicine deleted successfully!');
}
}
PART 6: Report Controller
Open app/Http/Controllers/ReportController.php:
php<?php
namespace App\Http\Controllers;
use App\Models\Medicine;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
class ReportController extends Controller
{
public function index(Request $request)
{
$query = Medicine::query();
if ($request->filled('category')) {
$query->where('category', $request->category);
}
if ($request->filled('expiration_status')) {
if ($request->expiration_status === 'expired') {
$query->where('expiration_date', '<', now());
} elseif ($request->expiration_status === 'not_expired') {
$query->where('expiration_date', '>=', now());
}
}
$medicines = $query->get();
$categories = Medicine::distinct()->orderBy('category')->pluck('category');
return view('reports.index', compact('medicines', 'categories'));
}
public function exportPdf(Request $request)
{
$query = Medicine::query();
if ($request->filled('category')) {
$query->where('category', $request->category);
}
if ($request->filled('expiration_status')) {
if ($request->expiration_status === 'expired') {
$query->where('expiration_date', '<', now());
} elseif ($request->expiration_status === 'not_expired') {
$query->where('expiration_date', '>=', now());
}
}
$medicines = $query->get();
return Pdf::loadView('reports.pdf', compact('medicines'))
->download('medicine-report.pdf');
}
public function exportCsv(Request $request)
{
$query = Medicine::query();
if ($request->filled('category')) {
$query->where('category', $request->category);
}
if ($request->filled('expiration_status')) {
if ($request->expiration_status === 'expired') {
$query->where('expiration_date', '<', now());
} elseif ($request->expiration_status === 'not_expired') {
$query->where('expiration_date', '>=', now());
}
}
$medicines = $query->get();
$headers = [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="medicine-report.csv"',
];
$callback = function () use ($medicines) {
$file = fopen('php://output', 'w');
fputcsv($file, ['Name', 'Generic Name', 'Category', 'Quantity', 'Expiration Date', 'Price', 'Status']);
foreach ($medicines as $medicine) {
fputcsv($file, [
$medicine->name,
$medicine->generic_name,
$medicine->category,
$medicine->quantity,
$medicine->expiration_date->format('Y-m-d'),
$medicine->price,
$medicine->status,
]);
}
fclose($file);
};
return response()->stream($callback, 200, $headers);
}
}
PART 7: Layout Fix
Open resources/views/layouts/app.blade.php.
Find this:
html
{{ $slot }}
Replace with this:
html
@hasSection('content')
@yield('content')
@else
{{ $slot ?? '' }}
@endif
PART 8: Navigation
Open resources/views/layouts/navigation.blade.php.
Inside the left-side nav area, add or fix these links:
html
-
<li class="nav-item"> <x-nav-link :active="request()->routeIs('reports.*')" :href="route('reports.index')"> Reports </x-nav-link> </li>
The rest is views
Top comments (0)