Admin Books Create View
Create resources/views/admin/books/create.blade.php:
@extends('layouts.app')
@section('title', 'Admin - Add New Book')
@section('content')
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Add New Book</div>
<div class="card-body">
<form action="/" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control @error('title') is-invalid @enderror" id="title" name="title" value="{{ old('title') }}" required>
@error('title')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="author" class="form-label">Author</label>
<input type="text" class="form-control @error('author') is-invalid @enderror" id="author" name="author" value="{{ old('author') }}" required>
@error('author')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control @error('description') is-invalid @enderror" id="description" name="description" rows="4" required>{{ old('description') }}</textarea>
@error('description')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="price" class="form-label">Price</label>
<input type="number" step="0.01" class="form-control @error('price') is-invalid @enderror" id="price" name="price" value="{{ old('price') }}" required>
@error('price')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="stock" class="form-label">Stock</label>
<input type="number" class="form-control @error('stock') is-invalid @enderror" id="stock" name="stock" value="{{ old('stock') }}" required>
@error('stock')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="cover_image" class="form-label">Cover Image</label>
<input type="file" class="form-control @error('cover_image') is-invalid @enderror" id="cover_image" name="cover_image">
@error('cover_image')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="d-flex justify-content-between">
<a href="/" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Create Book</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
Admin Books Index View
Create resources/views/admin/books/index.blade.php:
@extends('layouts.app')
@section('title', 'Admin - Books')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Manage Books</h1>
<a href="/" class="btn btn-primary">Add New Book</a>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Price</th>
<th>Stock</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>The Great Gatsby</td>
<td>F. Scott Fitzgerald</td>
<td>$19.99</td>
<td>15</td>
<td>
<a href="/edit" class="btn btn-sm btn-warning">Edit</a>
<form action="/" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure?')">Delete</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
@endsection
Admin Books Edit View
Create resources/views/admin/books/edit.blade.php:
@extends('layouts.app')
@section('title', 'Admin - Edit Book')
@section('content')
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Edit Book</div>
<div class="card-body">
<form action="/" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control @error('title') is-invalid @enderror" id="title" name="title" value="{{ old('title', 'The Great Gatsby') }}" required>
@error('title')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="author" class="form-label">Author</label>
<input type="text" class="form-control @error('author') is-invalid @enderror" id="author" name="author" value="{{ old('author', 'F. Scott Fitzgerald') }}" required>
@error('author')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control @error('description') is-invalid @enderror" id="description" name="description" rows="4" required>{{ old('description', 'A classic American novel about the Jazz Age and the American Dream.') }}</textarea>
@error('description')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="price" class="form-label">Price</label>
<input type="number" step="0.01" class="form-control @error('price') is-invalid @enderror" id="price" name="price" value="{{ old('price', '19.99') }}" required>
@error('price')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="stock" class="form-label">Stock</label>
<input type="number" class="form-control @error('stock') is-invalid @enderror" id="stock" name="stock" value="{{ old('stock', '15') }}" required>
@error('stock')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="cover_image" class="form-label">Cover Image</label>
<input type="file" class="form-control @error('cover_image') is-invalid @enderror" id="cover_image" name="cover_image">
<div class="mt-2">
<img src="{{ asset('storage/covers/great-gatsby.jpg') }}" alt="Current cover" style="max-width: 200px;">
</div>
@error('cover_image')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="d-flex justify-content-between">
<a href="/" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Update Book</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
User Books Index View
Create resources/views/books/index.blade.php:
@extends('layouts.app')
@section('title', 'Bookstore - All Books')
@section('content')
<div class="container">
<h1 class="mb-4">Our Book Collection</h1>
<div class="row">
<div class="col-md-4 mb-4">
<div class="card h-100">
<div class="card-img-top bg-light d-flex align-items-center justify-content-center" style="height: 300px;">
<i class="fas fa-book fa-3x text-muted"></i>
<img src="https://dummyimage.com/400" class="h-100 w-100 object-fit-cover"/>
</div>
<div class="card-body">
<h5 class="card-title">The Great Gatsby</h5>
<p class="card-text text-muted">by F. Scott Fitzgerald</p>
<p class="card-text">A classic American novel about the Jazz Age and the American Dream.</p>
<div class="d-flex justify-content-between align-items-center">
<span class="h5 text-primary mb-0">$19.99</span>
<a href="/" class="btn btn-outline-primary">View Details</a>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
User Books Show View
Create resources/views/books/show.blade.php:
@extends('layouts.app')
@section('title', 'The Great Gatsby')
@section('content')
<div class="container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Books</a></li>
<li class="breadcrumb-item active" aria-current="page">The Great Gatsby</li>
</ol>
</nav>
<div class="row">
<div class="col-md-4">
<div class="bg-light rounded d-flex align-items-center justify-content-center" style="height: 400px;">
<i class="fas fa-book fa-5x text-muted"></i>
<img src="https://dummyimage.com/400" class="h-100 w-100 object-fit-cover"/>
</div>
</div>
<div class="col-md-8">
<h1 class="mb-3">The Great Gatsby</h1>
<p class="text-muted h5 mb-3">by F. Scott Fitzgerald</p>
<p class="mb-4">A classic American novel about the Jazz Age and the American Dream.</p>
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-body text-center">
<h3 class="text-primary mb-0">$19.99</h3>
<p class="text-muted mb-0">Price</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body text-center">
<h3 class="text-success mb-0">15</h3>
<p class="text-muted mb-0">In Stock</p>
</div>
</div>
</div>
</div>
<div class="d-flex gap-2">
<a href="/" class="btn btn-outline-secondary">Back to Books</a>
<button class="btn btn-primary">Add to Cart</button>
</div>
</div>
</div>
</div>
@endsection
Order Create View
Create resources/views/orders/create.blade.php for the order placement form:
@extends('layouts.app')
@section('title', 'Place Order')
@section('content')
<div class="container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Books</a></li>
<li class="breadcrumb-item"><a href="/books/book">Book</a></li>
<li class="breadcrumb-item active" aria-current="page">Place Order</li>
</ol>
</nav>
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Order Information</h4>
</div>
<div class="card-body">
<form method="get" action="/orders">
@csrf
<!-- Customer Information -->
<div class="mb-4">
<h5 class="border-bottom pb-2">Customer Information</h5>
<div class="row">
<div class="col-md-6 mb-3">
<label for="first_name" class="form-label">First Name *</label>
<input type="text" class="form-control @error('first_name') is-invalid @enderror"
id="first_name" name="first_name" value="{{ old('first_name') }}" required>
@error('first_name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6 mb-3">
<label for="last_name" class="form-label">Last Name *</label>
<input type="text" class="form-control @error('last_name') is-invalid @enderror"
id="last_name" name="last_name" value="{{ old('last_name') }}" required>
@error('last_name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email Address *</label>
<input type="email" class="form-control @error('email') is-invalid @enderror"
id="email" name="email" value="{{ old('email') }}" required>
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="phone" class="form-label">Phone Number</label>
<input type="tel" class="form-control @error('phone') is-invalid @enderror"
id="phone" name="phone" value="{{ old('phone') }}">
@error('phone')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<!-- Shipping Address -->
<div class="mb-4">
<h5 class="border-bottom pb-2">Shipping Address</h5>
<div class="mb-3">
<label for="address_line1" class="form-label">Address Line 1 *</label>
<input type="text" class="form-control @error('address_line1') is-invalid @enderror"
id="address_line1" name="address_line1" value="{{ old('address_line1') }}" required>
@error('address_line1')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="address_line2" class="form-label">Address Line 2</label>
<input type="text" class="form-control @error('address_line2') is-invalid @enderror"
id="address_line2" name="address_line2" value="{{ old('address_line2') }}">
@error('address_line2')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="city" class="form-label">City *</label>
<input type="text" class="form-control @error('city') is-invalid @enderror"
id="city" name="city" value="{{ old('city') }}" required>
@error('city')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-3 mb-3">
<label for="state" class="form-label">State *</label>
<input type="text" class="form-control @error('state') is-invalid @enderror"
id="state" name="state" value="{{ old('state') }}" required>
@error('state')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-3 mb-3">
<label for="zip_code" class="form-label">ZIP Code *</label>
<input type="text" class="form-control @error('zip_code') is-invalid @enderror"
id="zip_code" name="zip_code" value="{{ old('zip_code') }}" required>
@error('zip_code')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
</div>
<!-- Order Notes -->
<div class="mb-4">
<label for="notes" class="form-label">Order Notes (Optional)</label>
<textarea class="form-control @error('notes') is-invalid @enderror" id="notes" name="notes" rows="3"
placeholder="Any special instructions or notes for your order...">{{ old('notes') }}</textarea>
@error('notes')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary btn-lg">Make Payment</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Order Summary</h5>
</div>
<div class="card-body">
<!-- Sample order items - in real app, this would come from session/cart -->
<div class="d-flex justify-content-between align-items-center mb-2">
<div class="d-flex align-items-center gap-2">
<img src="https://dummyimage.com/40x60" alt="The Great Gatsby" class="rounded"
style="width: 40px; height: 60px; object-fit: cover;">
<div>
<span>The Great Gatsby</span>
<div class="d-flex align-items-center gap-2 mt-1">
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="updateQuantity(-1)">-</button>
<span class="quantity-display">1</span>
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="updateQuantity(1)">+</button>
</div>
</div>
</div>
<span class="price-display">$19.99</span>
</div>
<hr>
<div class="d-flex justify-content-between mb-2">
<span>Subtotal</span>
<span class="subtotal-amount">$19.99</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Shipping</span>
<span>$5.99</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Tax</span>
<span class="tax-amount">$2.00</span>
</div>
<hr>
<div class="d-flex justify-content-between fw-bold">
<span>Total</span>
<span class="total-amount">$27.98</span>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let quantity = 1;
const basePrice = 19.99;
function updateQuantity(change) {
const newQuantity = quantity + change;
if (newQuantity >= 1) {
quantity = newQuantity;
document.querySelector('.quantity-display').textContent = quantity;
const totalPrice = (basePrice * quantity).toFixed(2);
document.querySelector('.price-display').textContent = `$${totalPrice}`;
// Update subtotal
const subtotal = (basePrice * quantity).toFixed(2);
document.querySelector('.subtotal-amount').textContent = `$${subtotal}`;
// Update tax (assuming 10% tax rate)
const tax = (basePrice * quantity * 0.1).toFixed(2);
document.querySelector('.tax-amount').textContent = `$${tax}`;
// Update total
const shipping = 5.99;
const total = (parseFloat(subtotal) + parseFloat(tax) + shipping).toFixed(2);
document.querySelector('.total-amount').textContent = `$${total}`;
}
}
</script>
@endsection
Order Index View
Create resources/views/orders/index.blade.php for displaying order history:
@extends('layouts.app')
@section('title', 'Order History')
@section('content')
<div class="container mt-4">
<div class="row">
<div class="col-12">
<h2>Order History</h2>
<p class="text-muted">Your book orders</p>
</div>
</div>
<!-- Order 1 -->
<div class="row mb-3">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-8">
<div class="d-flex align-items-center mb-2">
<span class="badge bg-success me-2">Delivered</span>
<small class="text-muted">Order #ORD-2024-001</small>
</div>
<h5>The Great Gatsby</h5>
<p class="text-muted mb-1">F. Scott Fitzgerald</p>
<p class="text-muted mb-0">Paperback • 180 pages</p>
</div>
<div class="col-md-4">
<div class="text-end">
<p class="mb-1"><strong>$12.99</strong></p>
<p class="text-muted small mb-2">Ordered: Dec 15, 2024</p>
<p class="text-muted small mb-3">Delivered: Dec 18, 2024</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Pagination -->
<div class="row">
<div class="col-12">
<nav>
<ul class="pagination justify-content-center">
<li class="page-item disabled">
<a class="page-link" href="#">Previous</a>
</li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
@endsection
How Routes Work in Laravel
Routes in Laravel define the URL patterns and map them to controller actions. The
routes/web.php file contains all web routes for your application.
// Basic route structure
Route::method('url', [Controller::class, 'action']);
// Examples:
Route::view('/', 'home');
Route::post('/users', [UserController::class, 'store']);
Route::put('/users/{id}', [UserController::class, 'update']);
Route::delete('/users/{id}', [UserController::class, 'destroy']);
// Define named route
Route::get('/books/{id}', [BookController::class, 'show'])->name('books.show');
Resource Routes
Laravel provides resource routes that automatically create all CRUD routes for a
resource:
// Creates all resource routes
Route::resource('books', BookController::class);
// This creates the following routes:
// GET /books → index() (list all books)
// GET /books/create → create() (show create form)
// POST /books → store() (save new book)
// GET /books/{id} → show() (show specific book)
// GET /books/{id}/edit → edit() (show edit form)
// PUT /books/{id} → update() (update book)
// DELETE /books/{id} → destroy() (delete book)
Middleware in Routes
Middleware can be applied to routes to filter HTTP requests:
// Apply middleware to single route
Route::get('/admin', [AdminController::class, 'index'])->middleware('auth');
// Apply multiple middleware
Route::get('/admin', [AdminController::class, 'index'])
->middleware(['auth', 'admin']);
// Apply to route group
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/profile', [ProfileController::class, 'show']);
Route::put('/profile', [ProfileController::class, 'update']);
});
BookController
Create app/Http/Controllers/BookController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Book;
class BookController extends Controller
{
public function index()
{
$books = Book::all();
return view('books.index', compact('books'));
}
public function show(Book $book)
{
return view('books.show', compact('book'));
}
}
AdminBookController
Create app/Http/Controllers/AdminBookController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Book;
use Illuminate\Http\Request;
class AdminBookController extends Controller
{
public function index()
{
$books = Book::all();
return view('admin.books.index', compact('books'));
}
public function create()
{
return view('admin.books.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'author' => 'required|string|max:255',
'description' => 'required|string',
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0',
'cover_image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048'
]);
Book::create($validated);
return redirect()->route('admin.books.index');
}
public function edit(Book $book)
{
return view('admin.books.edit', compact('book'));
}
public function update(Request $request, Book $book)
{
$validated = $request->validate([
'title' => 'required|string|max|max:255',
'author' => 'required|string|max:255',
'description' => 'required|string',
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0',
'cover_image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048'
]);
if ($request->hasFile('cover_image')) {
// Delete old image if exists
if ($book->cover_image) {
Storage::disk('public')->delete($book->cover_image);
}
$path = $request->file('cover_image')->store('covers', 'public');
$validated['cover_image'] = $path;
}
$book->update($validated);
return redirect()->route('admin.books.index');
}
public function destroy(Book $book)
{
$book->delete();
return redirect()->route('admin.books.index');
}
}
OrderController
Create app/Http/Controllers/OrderController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Order;
use Illuminate\Http\Request;
class OrderController extends Controller
{
public function create()
{
return view('orders.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'book_id' => 'required|exists:books,id',
'quantity' => 'required|integer|min:1'
]);
Order::create($validated);
return redirect()->route('orders.index');
}
}
AdminOrderController
Create app/Http/Controllers/AdminOrderController.php:
<?php
namespace App\Http\Controllers;
use App\Models\Order;
class AdminOrderController extends Controller
{
public function index()
{
$orders = Order::with('book')->get();
return view('admin.orders.index', compact('orders'));
}
public function show(Order $order)
{
return view('admin.orders.show', compact('order'));
}
}
Publish Project to GitHub
First, initialize a Git repository and push your project to GitHub:
# Initialize Git repository
git init
# Add all files to Git
git add .
# Create initial commit
git commit -m "Initial Laravel bookstore project"
# Add GitHub remote (replace with your repository URL)
git remote add origin https://github.com/yourusername/bookstore.git
# Push to GitHub
git push -u origin main
Before pushing to GitHub:
- Make sure
.env is in your .gitignore file
- Create a
.env.example file with sample configuration
- Update
APP_ENV=production in your production environment
Create Account at Laravel Cloud
Laravel Cloud is the fastest way to deploy and scale Laravel applications without managing
servers:
# Visit Laravel Cloud
https://cloud.laravel.com
# Sign up for an account
# Connect your GitHub account
# No server management needed!
Laravel Cloud Features:
- One-click autoscaling (2-4 replicas)
- Built-in databases (MySQL & Serverless Postgres)
- Redis-compatible caching
- S3-compatible object storage
- Automatic SSL certificates
- DDoS protection and edge caching
- Push-to-deploy from Git
Deploy Your Application
Deploy your Laravel application in under 60 seconds with Laravel Cloud:
# In Laravel Cloud dashboard:
# 1. Click "New application"
# 2. Select your GitHub repository
# 3. Choose your application name
# 4. Cloud automatically:
# - Installs dependencies
# - Runs migrations
# - Sets up environment
# - Deploys your app
# 5. Your app is live in seconds!
# For automatic deployments:
# Just push to your main branch
git push origin main
# Cloud automatically redeploys