PART 1: Setup
composer create-project laravel/laravel fruit-system
cd fruit-system
Create database in phpMyAdmin:
fruit_db
Edit .env:
DB_DATABASE=fruit_db
DB_USERNAME=root
DB_PASSWORD=
Install Breeze:
composer require laravel/breeze --dev
php artisan breeze:install blade
php artisan migrate
npm install
npm run build
Install Bootstrap UI and PDF only:
npm 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:
php artisan make:model Fruit -mcr
php artisan make:controller ReportController
PART 2: Migration
Open database/migrations/xxxx_create_fruits_table.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('fruits', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('category');
$table->decimal('price', 8, 2);
$table->integer('stock');
$table->text('description')->nullable();
$table->boolean('is_available')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('fruits');
}
};
Run:
php artisan migrate
PART 3: Model
Open app/Models/Fruit.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Fruit extends Model
{
protected $fillable = [
'name',
'category',
'price',
'stock',
'description',
'is_available',
];
protected $casts = [
'is_available' => 'boolean',
];
}
PART 4: Routes
Open routes/web.php and use this:
<?php
use App\Http\Controllers\FruitController;
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('fruits.index');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Route::resource('fruits', FruitController::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';
Important: use reports.csv, not reports.excel.
PART 5: Fruit Controller
Open app/Http/Controllers/FruitController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Fruit;
use Illuminate\Http\Request;
class FruitController extends Controller
{
public function index()
{
$fruits = Fruit::latest()->paginate(10);
return view('fruits.index', compact('fruits'));
}
public function create()
{
return view('fruits.create');
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'category' => 'required|string',
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0',
'description' => 'nullable|string',
]);
$data = $request->all();
$data['is_available'] = $request->has('is_available') ? 1 : 0;
Fruit::create($data);
return redirect()->route('fruits.index')
->with('success', 'Fruit added successfully!');
}
public function show(Fruit $fruit)
{
return view('fruits.show', compact('fruit'));
}
public function edit(Fruit $fruit)
{
return view('fruits.edit', compact('fruit'));
}
public function update(Request $request, Fruit $fruit)
{
$request->validate([
'name' => 'required|string|max:255',
'category' => 'required|string',
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0',
'description' => 'nullable|string',
]);
$data = $request->all();
$data['is_available'] = $request->has('is_available') ? 1 : 0;
$fruit->update($data);
return redirect()->route('fruits.index')
->with('success', 'Fruit updated successfully!');
}
public function destroy(Fruit $fruit)
{
$fruit->delete();
return redirect()->route('fruits.index')
->with('success', 'Fruit deleted successfully!');
}
}
PART 6: Report Controller
Open app/Http/Controllers/ReportController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Fruit;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
class ReportController extends Controller
{
public function index(Request $request)
{
$query = Fruit::query();
if ($request->filled('category')) {
$query->where('category', $request->category);
}
if ($request->filled('availability')) {
$query->where('is_available', $request->availability);
}
$fruits = $query->get();
$categories = Fruit::distinct()->orderBy('category')->pluck('category');
return view('reports.index', compact('fruits', 'categories'));
}
public function exportPdf(Request $request)
{
$query = Fruit::query();
if ($request->filled('category')) {
$query->where('category', $request->category);
}
if ($request->filled('availability')) {
$query->where('is_available', $request->availability);
}
$fruits = $query->get();
return Pdf::loadView('reports.pdf', compact('fruits'))
->download('fruit-report.pdf');
}
public function exportCsv(Request $request)
{
$query = Fruit::query();
if ($request->filled('category')) {
$query->where('category', $request->category);
}
if ($request->filled('availability')) {
$query->where('is_available', $request->availability);
}
$fruits = $query->get();
$headers = [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="fruit-report.csv"',
];
$callback = function () use ($fruits) {
$file = fopen('php://output', 'w');
fputcsv($file, ['Name', 'Category', 'Price/kg', 'Stock', 'Available', 'Description']);
foreach ($fruits as $fruit) {
fputcsv($file, [
$fruit->name,
$fruit->category,
$fruit->price,
$fruit->stock,
$fruit->is_available ? 'Yes' : 'No',
$fruit->description ?? 'N/A',
]);
}
fclose($file);
};
return response()->stream($callback, 200, $headers);
}
}
{{ $slot }}
Replace with this:
@hasSection('content')
@yield('content')
@else
{{ $slot ?? '' }}
@endif
Navigation
Open resources/views/layouts/navigation.blade.php.
Inside the left-side nav area, add or fix these links:
- Fruits
- Reports
fruits/index.blade.php
Create/open resources/views/fruits/index.blade.php:
@extends('layouts.app')
@section('content')
<h2>Fruit Products</h2>
<a href="{{%20route('fruits.create')%20}}">+ Add Fruit</a>
@if(session('success'))
{{ session('success') }}
@endif
<table>
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price/kg</th>
<th>Stock</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($fruits as $fruit)
<tr>
<td>{{ $fruit->name }}</td>
<td>{{ $fruit->category }}</td>
<td>₱{{ number_format($fruit->price, 2) }}</td>
<td>{{ $fruit->stock }} kg</td>
<td>
@if($fruit->is_available)
<span>Available</span>
@else
<span>Out of Stock</span>
@endif
</td>
<td>
<a href="{{%20route('fruits.show',%20$fruit)%20}}">View</a>
<a href="{{%20route('fruits.edit',%20$fruit)%20}}">Edit</a>
@csrf
@method('DELETE')
Delete
</td>
</tr>
@empty
<tr>
<td colspan="6">No fruits yet. Add one!</td>
</tr>
@endforelse
</tbody>
</table>
{{ $fruits->links() }}
@endsection
fruits/create.blade.php
Create/open resources/views/fruits/create.blade.php:
@extends('layouts.app')
@section('content')
<h2>Add New Fruit</h2>
@if($errors->any())
@foreach($errors->all() as $error)
{{ $error }}
@endforeach
@endif
@csrf
Fruit Name
Category
-- Select Category --
@foreach(['Citrus', 'Berry', 'Tropical', 'Stone Fruit', 'Melon', 'Other'] as $cat)
{{ $cat }}
@endforeach
Price per kg (₱)
Stock Quantity (kg)
Description (Optional)
{{ old('description') }}
Available for Sale
Add Fruit
<a href="{{%20route('fruits.index')%20}}">Cancel</a>
@endsection
fruits/edit.blade.php
Create/open resources/views/fruits/edit.blade.php:
@extends('layouts.app')
@section('content')
<h2>Edit - {{ $fruit->name }}</h2>
@if($errors->any())
@foreach($errors->all() as $error)
{{ $error }}
@endforeach
@endif
@csrf
@method('PUT')
Fruit Name
Category
@foreach(['Citrus', 'Berry', 'Tropical', 'Stone Fruit', 'Melon', 'Other'] as $cat)
category) == $cat ? 'selected' : '' }}>
{{ $cat }}
@endforeach
Price per kg (₱)
Stock Quantity (kg)
Description
{{ old('description', $fruit->description) }}
is_available) ? 'checked' : '' }}>
Available for Sale
Update Fruit
<a href="{{%20route('fruits.index')%20}}">Cancel</a>
@endsection
fruits/show.blade.php
Create/open resources/views/fruits/show.blade.php:
@extends('layouts.app')
@section('content')
<h2>{{ $fruit->name }}</h2>
<p><strong>Category:</strong> {{ $fruit->category }}</p>
<p><strong>Price per kg:</strong> ₱{{ number_format($fruit->price, 2) }}</p>
<p><strong>Stock:</strong> {{ $fruit->stock }} kg</p>
<p><strong>Description:</strong> {{ $fruit->description ?? 'No description provided.' }}</p>
<p>
<strong>Status:</strong>
@if($fruit->is_available)
<span>Available</span>
@else
<span>Out of Stock</span>
@endif
</p>
<p><strong>Added:</strong> {{ $fruit->created_at->format('F d, Y') }}</p>
<a href="{{%20route('fruits.edit',%20$fruit)%20}}">Edit</a>
<a href="{{%20route('fruits.index')%20}}">Back</a>
@endsection
reports/index.blade.php
Create/open resources/views/reports/index.blade.php:
@extends('layouts.app')
@section('content')
<h2>Fruit Reports</h2>
Filter by Category
-- All Categories --
@foreach($categories as $cat)
{{ $cat }}
@endforeach
Filter by Availability
-- All --
Available
Out of Stock
Filter
<a href="{{%20route('reports.index')%20}}">Reset</a>
<a href="{{%20route('reports.pdf',%20request()->query())%20}}">
Export PDF
</a>
<a href="{{%20route('reports.csv',%20request()->query())%20}}">
Export CSV
</a>
<p>Showing <strong>{{ $fruits->count() }}</strong> result(s)</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price/kg</th>
<th>Stock</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@forelse($fruits as $fruit)
<tr>
<td>{{ $fruit->name }}</td>
<td>{{ $fruit->category }}</td>
<td>₱{{ number_format($fruit->price, 2) }}</td>
<td>{{ $fruit->stock }} kg</td>
<td>
@if($fruit->is_available)
<span>Available</span>
@else
<span>Out of Stock</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="5">No results found.</td>
</tr>
@endforelse
</tbody>
</table>
@endsection
reports/pdf.blade.php
Create/open resources/views/reports/pdf.blade.php:
<!DOCTYPE html>
<br>
body { font-family: Arial, sans-serif; font-size: 13px; }<br>
table { width: 100%; border-collapse: collapse; margin-top: 10px; }<br>
th, td { border: 1px solid #000; padding: 6px; }<br>
th { background-color: #eee; }<br>
Fruit Sales Report
Generated: {{ now()->format('F d, Y - h:i A') }}
| Name | Category | Price/kg | Stock | Status |
|---|---|---|---|---|
| {{ $fruit->name }} | {{ $fruit->category }} | ₱{{ number_format($fruit->price, 2) }} | {{ $fruit->stock }} kg | {{ $fruit->is_available ? 'Available' : 'Out of Stock' }} |
| No fruits found. | ||||
Top comments (0)