Ajax CRUD with API tutorial in Laravel 11
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
public function signup(Request $request)
{
// make() method is used to validate form data which is got from "Request" class
// Validator::make() method provides extra functionality to check fails() or success() as shown below :
$validateUser = Validator::make(
$request->all(),
[
"name" => "required",
"email" => "required|email|unique:users,email",
"password" => "required"
]
);
if ($validateUser->fails()) {
return response()->json([
"status" => false,
"message" => "Validation Error",
"errors" => $validateUser->errors()->all()
], 401);
}
$user = User::create([
"name" => $request->name,
"email" => $request->email,
"password" => $request->password
]);
return response()->json([
"status" => true,
"message" => "User Created Successfully",
"user" => $user
], 200);
}
public function login(Request $request)
{
$validateUser = Validator::make(
$request->all(),
[
"email" => "required|email",
"password" => "required"
]
);
if ($validateUser->fails()) {
return response()->json([
"status" => false,
"message" => "Authentication Fails",
"errors" => $validateUser->errors()->all()
], 404);
}
if (Auth::attempt(["email" => $request->email, "password" => $request->password])) {
$authUser = Auth::user();
return response()->json([
"status" => true,
"message" => "User Logged in Successfully",
"token" => $authUser->createToken("API Token")->plainTextToken,
"token_type" => "bearer"
], 200);
} else {
return response()->json([
"status" => false,
"message" => "Email & Password does not matched."
], 401);
}
}
public function logout(Request $request)
{
$user = $request->user();
$user->tokens()->delete(); //Here, delete all tokens from database of particular user
return response()->json([
"status" => true,
"user" => $user,
"message" => "You logged out successfully.",
], 200);
}
}
Above File is app\Http\Controllers\API\AuthController.php FileBelow File is app\Http\Controllers\API\BaseController.php File
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class BaseController extends Controller
{
public function sendResponse($result, $message)
{
$response = [
"success" => true,
"data" => $result,
"message" => $message
];
return response()->json($response, 200);
}
public function sendError($error, $errorMessage = [], $code = 404)
{
$response = [
"success" => false,
"message" => $error
];
if (!empty($errorMessage)) {
$response["data"] = $errorMessage;
}
return response()->json($response, $code);
}
}
Below File is app\Http\Controllers\API\PostController.php File
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Http\Controllers\API\BaseController as BaseController;
class PostController extends BaseController
{
/**
* Display a listing of the resource.
*/
public function index()
{
$data["posts"] = Post::all();
// 1st way we can written as shown below :
// return response()->json([
// "status" => true,
// "message" => "All Post Data.",
// "data" => $data
// ], 200);
// 2nd way we can written as shown below :
return $this->sendResponse($data, "All Post Data.");
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
// make() method is used to validate form data which is got from "Request" class
// Validator::make() method provides extra functionality to check fails() or success() as shown below :
$validateUser = Validator::make(
$request->all(),
[
"title" => "required",
"description" => "required",
"image" => "required|mimes:png,jpg,jpeg,gif"
]
);
if ($validateUser->fails()) {
// 1st way we can written as shown below :
// return response()->json([
// "status" => false,
// "message" => "Validation Error",
// "errors" => $validateUser->errors()->all()
// ], 401);
// 2nd way we can written as shown below :
return $this->sendError("Validation Error", $validateUser->errors()->all());
}
$img = $request->image;
$ext = $img->getClientOriginalExtension();
$imageName = time() . "." . $ext;
$img->move(public_path() . "/uploads/", $imageName);
$post = Post::create([
"title" => $request->title,
"description" => $request->description,
"image" => $imageName
]);
// 1st way we can written as shown below :
// return response()->json([
// "status" => true,
// "message" => "Post Created Successfully",
// "post" => $post
// ], 200);
// 2nd way we can written as shown below :
return $this->sendResponse($post, "Post Created Successfully");
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$data["post"] = Post::select(
"id",
"title",
"description",
"image"
)->where(["id" => $id])->get();
// 1st way we can written as shown below :
// return response()->json([
// "status" => true,
// "message" => "Your Single Post",
// "data" => $data
// ], 200);
// 2nd way we can written as shown below :
return $this->sendResponse($data, "Your Single Post");
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$validateUser = Validator::make(
$request->all(),
[
"title" => "required",
"description" => "required",
"image" => "nullable|image|mimes:png,jpg,jpeg,gif",
]
);
if ($validateUser->fails()) {
// 1st way we can written as shown below :
// return response()->json([
// "status" => false,
// "message" => "Validation Error",
// "errors" => $validateUser->errors()->all()
// ], 401);
// 2nd way we can written as shown below :
return $this->sendError("Validation Error", $validateUser->errors()->all());
}
$postImage = Post::select("id", "image")->where(["id" => $id])->get();
if ($request->image != "") {
$path = public_path() . "/uploads/";
if ($postImage[0]->image != "" && $postImage[0]->image != null) {
$old_file = $path . $postImage[0]->image;
if (file_exists($old_file)) {
unlink($old_file);
}
}
$img = $request->image;
$ext = $img->getClientOriginalExtension();
$imageName = time() . "." . $ext;
$img->move(public_path() . "/uploads/", $imageName);
} else {
$imageName = $postImage[0]->image;
}
$post = Post::where(["id" => $id])->update([
"title" => $request->title,
"description" => $request->description,
"image" => $imageName
]);
// 1st way we can written as shown below :
// return response()->json([
// "status" => true,
// "message" => "Post Updated Successfully",
// "post" => $post
// ], 200);
// 2nd way we can written as shown below :
return $this->sendResponse($post, "Post Updated Successfully");
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$imagePath = Post::select("image")->where("id", $id)->get();
$filePath = public_path() . "/uploads/" . $imagePath[0]["image"];
unlink($filePath);
$post = Post::where("id", $id)->delete();
// 1st way we can written as shown below :
// return response()->json([
// "status" => true,
// "message" => "Your Post has been removed.",
// "post" => $post,
// ], 200);
// 2nd way we can written as shown below :
return $this->sendResponse($post, "Your Post has been removed.");
}
}
Below File is app\Models\Post.php File
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public $timestamps = "true";
protected $table = "posts";
protected $fillable = ["title", "description", "image"];
}
Below File is app\Models\User.php File
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable, HasApiTokens;
public $timestamps = true;
protected $table = "users";
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
Below File is database\migrations\2024_12_31_070900_create_users_table File
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string("name");
$table->string("email", 100)->unique();
$table->string("password");
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
Below File is database\migrations\2024_12_31_070913_create_posts_table File
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string("title");
$table->text("description");
$table->string("image");
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
Below File is resources\views\addpost.blade.php File
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Add Post</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-8 bg-primary text-white mb-4">
<h1>Create Post</h1>
</div>
</div>
<div class="row justify-content-center">
<div class="col-8">
<form id="addForm" method="POST" enctype="multipart/form-data">
<input type="text" id="title" class="form-control mb-3" placeholder="Enter title">
<textarea id="description" class="form-control mb-3" placeholder="Enter text"></textarea>
<input type="file" id="image" class="form-control mb-3">
<input type="submit" class="btn btn-primary">
<a href="/allposts" class="btn btn-secondary">Back</a>
</form>
</div>
</div>
</div>
<script>
var addform = document.querySelector("#addForm");
addform.onsubmit = async (e) => {
e.preventDefault();
const token = localStorage.getItem("api_token");
const title = document.querySelector("#title").value;
const description = document.querySelector("#description").value;
const image = document.querySelector("#image").files[0];
var formData = new FormData();
formData.append("title",title);
formData.append("description",description);
formData.append("image",image);
let response = await fetch("/api/posts",{
method: "POST",
body: formData,
headers: {
"Authorization": `Bearer ${token}`,
}
})
.then(response => response.json())
.then(data => {
console.log(data);
window.location.href = "http://127.0.0.1:8000/allposts";
});
}
</script>
</body>
</html>
Below File is resources\views\allposts.blade.php File
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>All Posts</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-8 bg-primary text-white mb-4">
<h1>All Posts</h1>
</div>
</div>
<div class="row mb-4 justify-content-center">
<div class="col-8">
<a href="/addpost" class="btn btn-sm btn-primary">Add New</a>
<button class="btn btn-sm btn-danger" id="logoutBtn">Logout</button>
</div>
</div>
<div class="row justify-content-center">
<div class="col-8">
<div id="postsContainer"></div>
</div>
</div>
</div>
<!-- Single Post Modal -->
<div class="modal fade" id="singlePostModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="singlePostLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title fs-5" id="singlePostLabel">Single Post</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Update Post Modal -->
<div class="modal fade" id="updatePostModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="updatePostLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title fs-5" id="updatePostLabel">Update Post</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="updateForm">
<div class="modal-body">
<input type="hidden" id="postId" class="form-control" value=""/>
<b>Title</b><input type="text" id="postTitle" class="form-control" value=""/>
<b>Description</b><input type="text" id="postBody" class="form-control" value=""/>
<img id="showImage" width="150px">
<p>Upload Image</p><input type="file" id="postImage" class="form-control"/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<input type="submit" value="Save changes" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script>
document.querySelector("#logoutBtn").addEventListener("click",function(){
const token = localStorage.getItem("api_token");
fetch("/api/logout",{
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
console.log(data);
window.location.href="http://127.0.0.1:8000/";
});
});
function loadData()
{
const token = localStorage.getItem("api_token");
fetch("/api/posts",{
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => {
// console.log(data.data.posts);
var allpost = data.data.posts;
const postContainer = document.querySelector("#postsContainer");
var tabledata = `<table class="table table-bordered">
<tr class="table-dark">
<th>Image</th>
<th>Title</th>
<th>Description</th>
<th>View</th>
<th>Update</th>
<th>Delete</th>
</tr>`;
allpost.forEach(post => {
tabledata += `<tr>
<td><img src="/uploads/${post.image}" width="150px" /></td>
<td>
<h6>${post.title}</h6>
</td>
<td>
<p>${post.description}</p>
</td>
<td><button type="button" class="btn btn-sm btn-primary" data-bs-postid="${post.id}" data-bs-toggle="modal" data-bs-target="#singlePostModal">View</button></td>
<td><button type="button" class="btn btn-sm btn-success" data-bs-postid="${post.id}" data-bs-toggle="modal" data-bs-target="#updatePostModal">Update</button></td>
<td><button onclick="deletePost(${post.id})" class="btn btn-danger">Delete</button></td>
</tr>`
});
tabledata += `</table>`;
postContainer.innerHTML = tabledata;
});
}
loadData();
// Open Single Post Modal
var singleModal = document.querySelector("#singlePostModal");
if (singleModal) {
singleModal.addEventListener('show.bs.modal', event => {
const button = event.relatedTarget;
const modalBody = document.querySelector("#singlePostModal .modal-body");
modalBody.innerHTML = "";
const id = button.getAttribute('data-bs-postid');
console.log(id);
const token = localStorage.getItem("api_token");
fetch(`/api/posts/${id}`,{
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
})
.then(response => response.json())
.then(data => {
const post = data.data.post[0];
modalBody.innerHTML = `
Title : ${post.title}
<br>
Description : ${post.description}
<br>
<img src="http://127.0.0.1:8000/uploads/${post.image}" width="150px"/>
`;
});
})
}
// Update Post Modal
var updateModal = document.querySelector("#updatePostModal");
if (updateModal) {
updateModal.addEventListener('show.bs.modal', event => {
const button = event.relatedTarget;
const id = button.getAttribute('data-bs-postid');
console.log(id);
const token = localStorage.getItem("api_token");
fetch(`/api/posts/${id}`,{
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
})
.then(response => response.json())
.then(data => {
const post = data.data.post[0];
document.querySelector("#postId").value = post.id;
document.querySelector("#postTitle").value = post.title;
document.querySelector("#postBody").value = post.description;
document.querySelector("#showImage").src = `/uploads/${post.image}`;
});
})
}
// Update Post
var updateform = document.querySelector("#updateForm");
updateform.onsubmit = async (e) => {
e.preventDefault();
const token = localStorage.getItem("api_token");
const postId = document.querySelector("#postId").value;
const title = document.querySelector("#postTitle").value;
const description = document.querySelector("#postBody").value;
var formData = new FormData();
formData.append("id",postId);
formData.append("title",title);
formData.append("description",description);
if(!document.querySelector("#postImage").files[0] == "") {
const image = document.querySelector("#postImage").files[0];
formData.append("image",image);
}
let response = await fetch(`/api/posts/${postId}`,{
method: "POST",
body: formData,
headers: {
"Authorization": `Bearer ${token}`,
"x-HTTP-Method-Override": "PUT" //Because we have send "body: formData"
}
})
.then(response => response.json())
.then(data => {
console.log(data);
window.location.href = "http://127.0.0.1:8000/allposts";
});
}
// Delete Post
async function deletePost(postId)
{
const token = localStorage.getItem("api_token");
let response = await fetch(`/api/posts/${postId}`,{
method: "DELETE",
headers: {
"Authorization": `Bearer ${token}`,
}
})
.then(response => response.json())
.then(data => {
console.log(data);
window.location.href = "http://127.0.0.1:8000/allposts";
});
}
</script>
</body>
</html>
Below File is resources\views\login.blade.php File
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-4">
<div class="card">
<div class="card-header">
<h2>Login</h2>
</div>
<div class="card-body">
<div class="mb-3">
<input type="email" id="email" class="form-control" placeholder="Enter email"/>
</div>
<div class="mb-3">
<input type="password" id="password" class="form-control" placeholder="Enter password"/>
</div>
<button id="loginButton" class="btn btn-primary">Login</button>
</div>
<div class="card-footer"></div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
$(document).ready(function(){
$("#loginButton").on("click",function(){
const email = $("#email").val();
const password = $("#password").val();
$.ajax({
url: "/api/login",
type: "POST",
contentType: "application/json",
// The JSON.stringify() method converts JavaScript objects into strings
data: JSON.stringify({
email: email,
password: password,
}),
success: function(response){
console.log(response);
localStorage.setItem("api_token",response.token);
window.location.href = "http://127.0.0.1:8000/allposts";
},
error: function(xhr,status,error){
alert("Error : " + xhr.responseText);
}
});
});
});
</script>
</body>
</html>
Below File is routes\api.php File
<?php
use App\Http\Controllers\API\AuthController;
use App\Http\Controllers\API\PostController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Route::controller(AuthController::class)->group(function () {
Route::post("signup", "signup");
Route::post("login", "login");
});
Route::middleware("auth:sanctum")->group(function () {
Route::post("logout", [AuthController::class, "logout"]);
Route::apiResource("posts", PostController::class);
});
Below File is routes\web.php File
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
.png)
Comments
Post a Comment