To create a single-page application (SPA) in Laravel where all CRUD operations happen without page refresh, you can use React for the frontend and Laravel as the backend API. Below is a comprehensive guide to achieve this:
Step 1: Set Up Laravel Backend
- Install Laravel:
composer create-project laravel/laravel laravel-react-spa
cd laravel-react-spa
- Set Up Database:
- Update your
.env
file with your database credentials:env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=your_database_name DB_USERNAME=your_database_user DB_PASSWORD=your_database_password
- Create a Model and Migration:
- Generate a
Post
model and migration:bash php artisan make:model Post -m
- Update the migration file (
database/migrations/xxxx_xx_xx_create_posts_table.php
):php public function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('content'); $table->timestamps(); }); }
- Run the migration:
bash php artisan migrate
- Create a Controller:
- Generate a controller for API operations:
bash php artisan make:controller Api/PostController --api
- Update
app/Http/Controllers/Api/PostController.php
:namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\Request; class PostController extends Controller { // List all posts public function index() { $posts = Post::all(); return response()->json($posts); } // Store a new post public function store(Request $request) { $request->validate([ 'title' => 'required', 'content' => 'required', ]); $post = Post::create($request->all()); return response()->json($post, 201); } // Show a single post public function show($id) { $post = Post::findOrFail($id); return response()->json($post); } // Update a post public function update(Request $request, $id) { $request->validate([ 'title' => 'required', 'content' => 'required', ]); $post = Post::findOrFail($id); $post->update($request->all()); return response()->json($post); } // Delete a post public function destroy($id) { $post = Post::findOrFail($id); $post->delete(); return response()->json(null, 204); } }
- Define API Routes:
- Update
routes/api.php
:use App\Http\Controllers\Api\PostController; Route::apiResource('posts', PostController::class);
Step 2: Set Up React Frontend
- Install React:
- Use Laravel Mix to set up React:
bash npm install react react-dom axios npm install
- Create React Components:
- Create a folder
resources/js/components
and add the following files:PostList.js
(List all posts)PostForm.js
(Create/Edit a post)PostItem.js
(Show a single post)
PostList.js
:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import PostItem from './PostItem';
import PostForm from './PostForm';
const PostList = () => {
const [posts, setPosts] = useState([]);
const [editingPost, setEditingPost] = useState(null);
// Fetch all posts
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
const response = await axios.get('/api/posts');
setPosts(response.data);
};
// Delete a post
const deletePost = async (id) => {
await axios.delete(`/api/posts/${id}`);
fetchPosts();
};
// Edit a post
const editPost = (post) => {
setEditingPost(post);
};
// Cancel editing
const cancelEdit = () => {
setEditingPost(null);
};
return (
<div>
<h1>Posts</h1>
<PostForm
post={editingPost}
onSave={() => {
fetchPosts();
cancelEdit();
}}
/>
<ul>
{posts.map((post) => (
<PostItem
key={post.id}
post={post}
onDelete={deletePost}
onEdit={editPost}
/>
))}
</ul>
</div>
);
};
export default PostList;
PostForm.js
:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const PostForm = ({ post, onSave }) => {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
// Pre-fill form if editing
useEffect(() => {
if (post) {
setTitle(post.title);
setContent(post.content);
}
}, [post]);
// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
const data = { title, content };
if (post) {
await axios.put(`/api/posts/${post.id}`, data);
} else {
await axios.post('/api/posts', data);
}
onSave();
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
/>
<textarea
placeholder="Content"
value={content}
onChange={(e) => setContent(e.target.value)}
required
/>
<button type="submit">{post ? 'Update' : 'Create'}</button>
</form>
);
};
export default PostForm;
PostItem.js
:
import React from 'react';
const PostItem = ({ post, onDelete, onEdit }) => {
return (
<li>
<h2>{post.title}</h2>
<p>{post.content}</p>
<button onClick={() => onEdit(post)}>Edit</button>
<button onClick={() => onDelete(post.id)}>Delete</button>
</li>
);
};
export default PostItem;
- Update
app.js
:
- In
resources/js/app.js
, render thePostList
component:import React from 'react'; import ReactDOM from 'react-dom'; import PostList from './components/PostList'; ReactDOM.render( <PostList />, document.getElementById('root') );
- Update Blade View:
- In
resources/views/welcome.blade.php
, include the React app:html <!DOCTYPE html> <html> <head> <title>Laravel React SPA</title> </head> <body> <div id="root"></div> <script src="{{ mix('js/app.js') }}"></script> </body> </html>
- Compile Assets:
npm run dev
Step 3: Test Your Application
- Run the Laravel development server:
php artisan serve
- Visit
http://localhost:8000
to test your SPA.
Key Features
- No Page Refresh: All CRUD operations are handled dynamically using React and Axios.
- Reusable Components:
PostForm
is used for both creating and editing posts. - API Integration: Laravel serves as a backend API, and React fetches data using Axios.
Let me know if you need further assistance! 🚀