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 File





Below 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
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('login');
});

Route::view("allposts", "allposts");
Route::view("addpost", "addpost");






















Comments

Popular posts from this blog

Eloquent Many to Many Relationship Tutorial in Laravel 11

Eloquent with JSON Data Columns Tutorial in Laravel 11

Blade Template Tutorial Three Template Inheritance in Laravel 11