AJAX With Laravel API and More – Learn Laravel Beyond the Limit
Hello, Everyone again with Laravel Advanced Topics Tutorial. Today, I’ll show you how we can implement AJAX in our laravel web application.
What is AJAX:
AJAX is Asynchronous JavaScript And XML and AJAX is not a programming language. It’s uses some combination of Browser and HTML DOM.
Browser – Send XMLHttpRequest Object to server
HTML DOM – Display data instantly on browser
How to use AJAX in Laravel Application:
AJAX is very simple with Laravel integration. Steps:
- Install Jquery Library
- Make some API’s through Laravel
- Call that API inside ajax call. like –
$.ajax({ //.... }); - Send data to server
- Process data in the Server
- Display in the browser after getting the data
Let’s start….
For Base Project we’ll use this main branch – https://github.com/ManiruzzamanAkash/Laravel-Advanced-Topics
Where already – We have a Post model and seeder setup
What we’ll do today with AJAX example:
- Display all the comments through AJAX
- Send a Comment to the server through AJAX
- Display Validation on the Browser DOM
- Refresh data with Reload after creating Comment
- & Many more concepts…
Create New Model, Migration, Seeder for Post Comment
Create Migration and modelphp artisan make:model Comment -m
In migration file – Just add a comment and post_id field as new.<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateCommentsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('comments', function (Blueprint $table) { $table->id(); $table->text('comment'); $table->foreignId('post_id') ->constrained() ->onUpdate('cascade') ->onDelete('cascade'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('comments'); } }
In model – Models/Comment.php file just implement basic fillable, as we’ll create comment later.<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Comment extends Model { use HasFactory; protected $fillable = ['comment', 'post_id']; }
Also make Post model relationed with Comment model – Post has Many comments. 1 to Many relationship using Eloquent.<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; public function comments() { return $this->hasMany(Comment::class); } }
In factories file – database/factories <?php namespace Database\Factories; use App\Models\Comment; use App\Models\Post; use Illuminate\Database\Eloquent\Factories\Factory; class CommentFactory extends Factory { /** * The name of the factory's corresponding model. * * @var string */ protected $model = Comment::class; /** * Define the model's default state. * * @return array */ public function definition() { $post_ids = Post::pluck('id')->toArray(); return [ 'comment' => $this->faker->text(), 'post_id' => $post_ids[array_rand($post_ids)] ]; } }
Then setup is done. Just run the command to migrate the seeder with fresh migration – php artisan migrate:fresh --seed
So, our posts and comments table will be all set and ready to start view post on browser using just Laravel.
Add Routes: in routes/web.php<?php use App\Http\Controllers\PostsController; use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', [PostsController::class, 'index'])->name('index'); Route::get('/{slug}', [PostsController::class, 'show'])->name('index.show');
In PostsController: Just add two routes functionality<?php namespace App\Http\Controllers; use App\Models\Post; class PostsController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $posts = Post::with('comments')->paginate(20); return view('index', compact('posts')); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { $post = Post::find($id); return view('show', compact('post')); } }
We’ll create a main.js file now inside public/js/main.js
Now – Files would be –
Posts List: views/index.blade.php – <!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous"> <title>AJAX Example</title> </head> <body> <div class="row justify-content-center"> <div class="col-md-8 mt-5"> <h2>Posts</h2> @foreach ($posts as $post) <div class="card card-body mt-2"> <h2> {{ $post->title }} </h2> <div> {!! $post->description !!} </div> <a href="{{ route('index.show', $post->id) }}"> View </a> </div> @endforeach </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"> </script> </body> </html>
Post Detail would be – <!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous"> <title>AJAX Example</title> </head> <body> <div class="container row justify-content-center mb-5" id="post-detail" data-post-id="{{ $post->id }}"> <div class="col-md-8 mt-5 card card-body"> <h2>{{ $post->title }}</h2> <div> {!! $post->description !!} </div> </div> <div class="mt-2"> <h3>Comments</h3> <div id="post-comments"> Loading Comments... <i class="fa fa-spin fa-spinner" aria-hidden="true"></i> </div> <h3 class="mt-5">New Comment</h3> <div class="alert alert-success visually-hidden" id="successMessage">Comments saved successfully !!</div> <form action="post" id="comment-submit-form"> @csrf <input type="hidden" name="post_id" id="post-id-comment" type="hidden" value="{{ $post->id }}"> <textarea name="comment" id="comment-input" cols="30" rows="4" class="form-control" placeholder="Enter your comment message in 500 characters"></textarea> <div id="comment-errors-data" class="text-danger"></div> <br> <button class="btn btn-success" type="submit" id="comment-submit-button"> Save </button> </form> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"> </script> <script src="{{ asset('js/main.js') }}"></script> <script> // getCommentsOfPosts({{ $post->id }}); </script> </body> </html>
API Routes:
routes/api.php<?php use App\Http\Controllers\CommentsController; use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::get('/comments/{id}', [CommentsController::class, 'index'])->name('comments.index'); Route::post('/comments/{id}', [CommentsController::class, 'store'])->name('comments.store');
CommentsController to handle API’s – app\Http\Controllers\CommentsController.php<?php namespace App\Http\Controllers; use App\Models\Comment; use Illuminate\Http\Request; class CommentsController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index($id) { $comments = Comment::where('post_id', $id)->orderBy('id', 'desc')->get(); return view('templates.comments', compact('comments')); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $request->validate( [ 'comment' => 'required' ], [ 'comment.required' => 'Please give comment' ] ); $comment = Comment::create([ 'comment' => $request->comment, 'post_id' => $request->post_id ]); return $comment; } }
Add templates view for getting comments- in templates/comments.blade.php@foreach ($comments as $comment) <div class="card card-body m-2"> {{ $comment->comment }} <p> <span class="badge badge-info">at {{ $comment->created_at->diffForHumans() }}</span> </p> </div> @endforeach @if (!count($comments)) <div class="bg-warning p-4"> No Comments found... </div> @endif
Main.js File for ajax actions – $(document).ready(function () { const post_id = $("#post-detail").attr("data-post-id"); getCommentsOfPosts(post_id) $("#comment-submit-form").submit(function(e){ e.preventDefault(); const formData = $(this); const submitButton = $("#comment-submit-button"); const post_id = $("#post-id-comment").val(); submitButton.html('Saving....<i class="fa fa-spin fa-spinner" aria-hidden="true"></i>'); $.ajax({ method: "POST", url: "/api/comments/" + post_id, data: formData.serialize(), success: (result) => { submitButton.html('Save'); $("#comment-errors-data").html(''); $("#comment-input").val(''); $("#successMessage").removeClass('visually-hidden'); getCommentsOfPosts(post_id); }, error: (error) => { if(error.status === 422) { // "Unprocessable Entity" - Form data invalid $("#successMessage").addClass('visually-hidden'); var message = error.responseJSON.errors ? error.responseJSON.errors.comment ? error.responseJSON.errors.comment[0] : '' : ''; $("#comment-errors-data").html(message); submitButton.html('Save'); } } }); }); }); function getCommentsOfPosts(post_id) { if( typeof post_id !== 'undefined' && post_id !== '' && !isNaN(post_id)) { $.ajax({ method: "GET", url: "/api/comments/" + post_id, success: (result) => { $("#post-comments").html(result); }, error: (error) => { alert('Something went wrong to fetch datas...'); } }); } } function getPosts() { $.ajax({ method: "GET", url: "/api/posts", success: (result) => { $("#posts").html(result); }, error: (error) => { alert('Something went wrong to fetch datas...'); } }); }
It’s done.
- Lets check in browser with some posts lists
- In detail posts, comments will be loaded in frontend part
- Someone can create comment instantly with comment reloading…
Thanks for viewing the Laravel ajax implementation way.
Fonte: https://devsenv.com/tutorials/ajax-with-laravel-api-and-more-learn-laravel-beyond-the-limit
