article

Showing posts with label Laravel. Show all posts
Showing posts with label Laravel. Show all posts

Friday, December 6, 2024

Laravel 11 CRUD with Upload Image Search and Pagination

Laravel 11 CRUD with Upload Image Search and Pagination
Download Laravel App
https://laravel.com/docs/11.x/installation

Connecting our Database

open .env file root directory.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel11dev
DB_USERNAME=root
DB_PASSWORD=root
Database Migration

php artisan make:model Product -m
C:\xampp\htdocs\laravel\my-app>php artisan make:model Product -m
database/migrations/create_products_table.php
//database/migrations/create_products_table.php
<?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('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('sku');
            $table->double('price', 10, 2);
            $table->text('description')->nullable();
            $table->string('image')->nullable();;
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};
Create Controller

php artisan make:controller ProductController
C:\xampp\htdocs\laravel\my-app>php artisan make:controller ProductController
app/Http/Controllers/ProductController.php
//app/Http/Controllers/ProductController.php
<?php

namespace App\Http\Controllers;

use App\Models\Product; //php artisan make:model Product -m 
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;

class ProductController extends Controller
{
    public function index() {
        //$products = Product::paginate(2); https://laravel.com/docs/11.x/pagination
        $products = Product::orderBy('created_at', 'DESC')->paginate(3);
        //$products = Product::orderBy('created_at','DESC')->get(); 

        return view('products.list',[
            'products' => $products
        ]);
        
    }

    public function search(Request $request)
    {
        if (!empty($request)) {
            $search = $request->input('search');

            $products = Product::where(
                'name',
                'like',
                "$search%"
            )
                ->orWhere('sku', 'like', "$search%")
                ->paginate(2);

            return view('products.list', compact('products'));
        }

        $products = DB::table('products')
        ->orderBy('id', 'DESC')
        ->paginate(5);
        return view('products.list', compact('products'));
    }

    public function create() {
        return view('products.create');
    }

    public function store(Request $request) {
        $rules = [
            'name' => 'required|min:5',
            'sku' => 'required|min:3',
            'price' => 'required|numeric'            
        ];

        if ($request->image != "") {
            $rules['image'] = 'image';
        }

        $validator = Validator::make($request->all(),$rules);

        if ($validator->fails()) {
            return redirect()->route('products.create')->withInput()->withErrors($validator);
        }

        // insert product
        $product = new Product();
        $product->name = $request->name;
        $product->sku = $request->sku;
        $product->price = $request->price;
        $product->description = $request->description;
        $product->save();

        if ($request->image != "") {
            // store image
            $image = $request->image;
            $ext = $image->getClientOriginalExtension();
            $imageName = time().'.'.$ext; // Unique image name

            // Save image to products directory
            $image->move(public_path('uploads/products'),$imageName);

            // Save image name
            $product->image = $imageName;
            $product->save();
        }        

        return redirect()->route('products.index')->with('success','Product added successfully.');
    }

    public function edit($id) {
        $product = Product::findOrFail($id);
        return view('products.edit',[
            'product' => $product
        ]);
    }

    public function update($id, Request $request) {

        $product = Product::findOrFail($id);

        $rules = [
            'name' => 'required|min:5',
            'sku' => 'required|min:3',
            'price' => 'required|numeric'            
        ];

        if ($request->image != "") {
            $rules['image'] = 'image';
        }

        $validator = Validator::make($request->all(),$rules);

        if ($validator->fails()) {
            return redirect()->route('products.edit',$product->id)->withInput()->withErrors($validator);
        }

        // update product
        $product->name = $request->name;
        $product->sku = $request->sku;
        $product->price = $request->price;
        $product->description = $request->description;
        $product->save();

        if ($request->image != "") {

            // delete old image
            File::delete(public_path('uploads/products/'.$product->image));

            // store image
            $image = $request->image;
            $ext = $image->getClientOriginalExtension();
            $imageName = time().'.'.$ext; // Unique image name

            // Save image to products directory
            $image->move(public_path('uploads/products'),$imageName);

            // Save image name
            $product->image = $imageName;
            $product->save();
        }        

        return redirect()->route('products.index')->with('success','Product updated successfully.');
    }

    public function destroy($id) {
        $product = Product::findOrFail($id);

       // delete image
       File::delete(public_path('uploads/products/'.$product->image));

       // delete product
       $product->delete();

       return redirect()->route('products.index')->with('success','Product deleted successfully.');
    }
}
View Blade File resources/views/products/list.blade.php
//resources/views/products/list.blade.php
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Laravel 11 CRUD with Upload Image Search and Pagination | Cairocoders</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="bg-primary py-3">
        <h3 class="text-white text-center">Laravel 11 CRUD with Upload Image Search and Pagination</h3>
    </div>
    <div class="container">
        <div class="row justify-content-center mt-4">
            <div class="col-md-10 d-flex justify-content-end">
                <form method="GET" action="/products/search">
                    <div class="input-group" style="margin-right:5px;">
                        <div class="form-outline" data-mdb-input-init>
                            <input class="form-control" name="search" placeholder="Searh..." value="{{ request()->input('search') ? request()->input('search') : '' }}">
                        </div>
                        <button type="submit" class="btn btn-success">Search</button>
                    </div>
                </form>
                <a href="{{ route('products.create') }}" class="btn btn-primary">Create</a>
            </div>
        </div>
        <div class="row d-flex justify-content-center">
            @if (Session::has('success'))
            <div class="col-md-10 mt-4">
                <div class="alert alert-success">
                    {{ Session::get('success') }}
                </div>
            </div>
            @endif
            <div class="col-md-10">
                <div class="card borde-0 shadow-lg my-4">
                    <div class="card-header bg-primary">
                        <h3 class="text-white">Products</h3>
                    </div>

                    <div class="card-body">
                        <table class="table">
                            <tr>
                                <th>ID</th>
                                <th></th>
                                <th>Name</th>
                                <th>Sku</th>
                                <th>Price</th>
                                <th>Created at</th>
                                <th>Action</th>
                            </tr>
                            @if ($products->isNotEmpty())
                            @foreach ($products as $product)
                            <tr>
                                <td>{{ $product->id }}</td>
                                <td>
                                    @if ($product->image != "")
                                    <img width="50" src="{{ asset('uploads/products/'.$product->image) }}" alt="">
                                    @endif
                                </td>
                                <td>{{ $product->name }}</td>
                                <td>{{ $product->sku }}</td>
                                <td>${{ $product->price }}</td>
                                <td>{{ \Carbon\Carbon::parse($product->created_at)->format('d M, Y') }}</td>
                                <td>
                                    <a href="{{ route('products.edit',$product->id) }}" class="btn btn-info">Edit</a>
                                    <a href="#" onclick="deleteProduct({{ $product->id  }});" class="btn btn-danger">Delete</a>
                                    <form id="delete-product-from-{{ $product->id  }}" action="{{ route('products.destroy',$product->id) }}" method="post">
                                        @csrf
                                        @method('delete')
                                    </form>
                                </td>
                            </tr>
                            @endforeach

                            @endif

                        </table>

                        {!! $products->withQueryString()->links('pagination::bootstrap-5') !!}
                    </div>

                </div>
            </div>
        </div>
    </div>
    <script>
        function deleteProduct(id) {
            if (confirm("Are you sure you want to delete product?")) {
                document.getElementById("delete-product-from-" + id).submit();
            }
        }
    </script>
</body>

</html>
resources/views/products/create.blade.php
//resources/views/products/create.blade.php
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Laravel 11 CRUD with Upload Image Search and Pagination | Cairocoders</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="bg-primary py-3">
        <h3 class="text-white text-center">Laravel 11 CRUD with Upload Image Search and Pagination</h3>
    </div>
    <div class="container">
        <div class="row justify-content-center mt-4">
            <div class="col-md-10 d-flex justify-content-end">
                <a href="{{ route('products.index') }}" class="btn btn-primary">Back</a>
            </div>
        </div>
        <div class="row d-flex justify-content-center">
            <div class="col-md-10">
                <div class="card borde-0 shadow-lg my-4">
                    <div class="card-header bg-primary">
                        <h3 class="text-white">Create Product</h3>
                    </div>
                    <form enctype="multipart/form-data" action="{{ route('products.store') }}" method="post">
                        @csrf
                        <div class="card-body">
                            <div class="mb-3">
                                <label for="" class="form-label h5">Name</label>
                                <input value="{{ old('name') }}" type="text" class="@error('name') is-invalid @enderror form-control-lg form-control" placeholder="Name" name="name">
                                @error('name')
                                <p class="invalid-feedback">{{ $message }}</p>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Sku</label>
                                <input value="{{ old('sku') }}" type="text" class="@error('sku') is-invalid @enderror form-control form-control-lg" placeholder="Sku" name="sku">
                                @error('sku')
                                <p class="invalid-feedback">{{ $message }}</p>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Price</label>
                                <input value="{{ old('price') }}" type="text" class="@error('price') is-invalid @enderror form-control form-control-lg" placeholder="Price" name="price">
                                @error('price')
                                <p class="invalid-feedback">{{ $message }}</p>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Description</label>
                                <textarea placeholder="Description" class="form-control" name="description" cols="30" rows="5">{{ old('description') }}</textarea>
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Image</label>
                                <input type="file" class="form-control form-control-lg" placeholder="Price" name="image">
                            </div>
                            <div class="d-grid">
                                <button class="btn btn-lg btn-primary">Submit</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>

</body>

</html>
resources/views/products/edit.blade.php
//resources/views/products/edit.blade.php
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Laravel 11 CRUD with Upload Image Search and Pagination | Cairocoders</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="bg-primary py-3">
        <h3 class="text-white text-center">Laravel 11 CRUD with Upload Image Search and Pagination</h3>
    </div>
    <div class="container">
        <div class="row justify-content-center mt-4">
            <div class="col-md-10 d-flex justify-content-end">
                <a href="{{ route('products.index') }}" class="btn btn-primary">Back</a>
            </div>
        </div>
        <div class="row d-flex justify-content-center">
            <div class="col-md-10">
                <div class="card borde-0 shadow-lg my-4">
                    <div class="card-header bg-primary">
                        <h3 class="text-white">Edit Product</h3>
                    </div>
                    <form enctype="multipart/form-data" action="{{ route('products.update',$product->id) }}" method="post">
                        @method('put')
                        @csrf
                        <div class="card-body">
                            <div class="mb-3">
                                <label for="" class="form-label h5">Name</label>
                                <input value="{{ old('name',$product->name) }}" type="text" class="@error('name') is-invalid @enderror form-control-lg form-control" placeholder="Name" name="name">
                                @error('name')
                                <p class="invalid-feedback">{{ $message }}</p>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Sku</label>
                                <input value="{{ old('sku',$product->sku) }}" type="text" class="@error('sku') is-invalid @enderror form-control form-control-lg" placeholder="Sku" name="sku">
                                @error('sku')
                                <p class="invalid-feedback">{{ $message }}</p>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Price</label>
                                <input value="{{ old('price',$product->price) }}" type="text" class="@error('price') is-invalid @enderror form-control form-control-lg" placeholder="Price" name="price">
                                @error('price')
                                <p class="invalid-feedback">{{ $message }}</p>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Description</label>
                                <textarea placeholder="Description" class="form-control" name="description" cols="30" rows="5">{{ old('description',$product->description) }}</textarea>
                            </div>
                            <div class="mb-3">
                                <label for="" class="form-label h5">Image</label>
                                <input type="file" class="form-control form-control-lg" placeholder="Price" name="image">

                                @if ($product->image != "")
                                <img class="w-50 my-3" src="{{ asset('uploads/products/'.$product->image) }}" alt="">
                                @endif
                            </div>
                            <div class="d-grid">
                                <button class="btn btn-lg btn-primary">Update</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>

</body>

</html>
Routes
routes/web.php
//routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController; //php artisan make:controller ProductController 

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

Route::controller(ProductController::class)->group(function () {
    Route::get('/products', 'index')->name('products.index');
    Route::get('/products/create', 'create')->name('products.create');
    Route::post('/products', 'store')->name('products.store');
    Route::get('/products/{product}/edit', 'edit')->name('products.edit');
    Route::put('/products/{product}', 'update')->name('products.update');
    Route::delete('/products/{product}', 'destroy')->name('products.destroy');
    Route::get('/products/search', 'search')->name('search');
});
Run C:\xampp\htdocs\laravel\my-app>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

Tuesday, December 3, 2024

Laravel Dynamic Dependent Dropdown | Country State City | Jquery AJAX

Laravel Dynamic Dependent Dropdown | Country State City | Jquery AJAX
Download Laravel App
https://laravel.com/docs/11.x/installation

Connecting our Database

open .env file root directory.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel11dev
DB_USERNAME=root
DB_PASSWORD=root
Database Migration

php artisan make:model Country -m
C:\xampp\htdocs\laravel\my-app>php artisan make:model Country -m
database/migrations/create_countries_table.php
Country
//database/migrations/create_countries_table.php
<?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() 
    {
        Schema::create('countries', function (Blueprint $table) {
            $table->id();
            $table->string('name');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('countries');
    }
};
State
php artisan make:model State -m
C:\xampp\htdocs\laravel\my-app>php artisan make:model State -m
database/migrations/create_states_table.php
//<?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()
    {
        Schema::create('states', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->integer('country_id');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('states');
    }
};
City
php artisan make:model City -m
C:\xampp\htdocs\laravel\my-app>php artisan make:model City -m
database/migrations/create_cities_table.php
//database/migrations/create_cities_table.php
<?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()
    {
        Schema::create('cities', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->integer('state_id');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('cities');
    }
};
SQL Database Table Counrty State and City : https://github.com/cairocodes/cairocoders/blob/main/country-state-city.sql

Create Controller

php artisan make:controller AccountController
C:\xampp\htdocs\laravel\my-app>php artisan make:controller AccountController
app/Http/Controllers/AccountController.php
//app/Http/Controllers/AccountController.php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;

class AccountController extends Controller
{
    public function index(){
        $countries = \DB::table('countries')->orderBy('name','ASC')->get();
        $data['countries'] = $countries;
        return view('users.create',$data);
    }

    public function fetchStates($country_id = null) {
        $states = \DB::table('states')->where('country_id',$country_id)->get();

        return response()->json([
            'status' => 1,
            'states' => $states
        ]);
    }

    public function fetchCities($state_id = null) {
        $cities = \DB::table('cities')->where('state_id',$state_id)->get();

        return response()->json([
            'status' => 1,
            'cities' => $cities
        ]);
    }

    public function save(Request $request){

        $validator = Validator::make($request->all(),[
            'name' => 'required',
            'email' => 'required|email'
        ]);

        if ($validator->passes()) {

            User::create([
                'name' => $request->name,
                'email' => $request->email,
                'password' => "123456",
                'country' => $request->country,
                'state' => $request->state,
                'city' => $request->city,
            ]);

            $request->session()->flash('success','User added successfully.');

            return response()->json([
                'status' =>  1                
            ]);

        } else {
            // return error 

            return response()->json([
                'status' =>  0,
                'errors' => $validator->errors()
            ]);
        }

    }

    public function list() {
        $users = DB::table('users')->get();
        $data['users'] = $users;
        return view('users.list',$data);
    }

    public function edit($id) {
        $user = User::where('id',$id)->first();

        $countries = \DB::table('countries')->orderBy('name','ASC')->get();
        $data['countries'] = $countries;


        $states = \DB::table('states')->where('country_id',$user->country)->orderBy('name','ASC')->get();        
        $data['states'] = $states;

        $cities = \DB::table('cities')->where('state_id',$user->state)->orderBy('name','ASC')->get();        
        $data['cities'] = $cities;

        if ($user == null ) {
            return redirect(url('/list'));
        }

        $data['user'] = $user;

        return view('users.edit',$data);
    }


    public function update($id, Request $request){

        $user = User::find($id);

        if($user == null) {

            $request->session()->flash('error','Enither user deleted or not found.');

            return response()->json([
                'status' =>  '400'                
            ]);
        }

        $validator = Validator::make($request->all(),[
            'name' => 'required',
            'email' => 'required|email'
        ]);

        if ($validator->passes()) {
            $user->name = $request->name;
            $user->email = $request->email;
            $user->country = $request->country;
            $user->state = $request->state;
            $user->city = $request->city;
            $user->save(); 

            $request->session()->flash('success','User updated successfully.');

            return response()->json([
                'status' =>  1                
            ]);

        } else {
            // return error 

            return response()->json([
                'status' =>  0,
                'errors' => $validator->errors()
            ]);
        }

    }
}
Routes
routes/web.php
//routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AccountController;

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

Route::get('/create', [AccountController::class, 'index']);
Route::post('/fetch-states/{id}', [AccountController::class, 'fetchStates']);
Route::post('/fetch-cities/{id}', [AccountController::class, 'fetchCities']);

Route::post('/save', [AccountController::class, 'save']);
Route::get('/list', [AccountController::class, 'list']);

Route::get('/edit/{id}', [AccountController::class, 'edit']);
Route::post('/edit/{id}', [AccountController::class, 'update']);
View Blade File resources/views/users/create.blade.php
//resources/views/users/create.blade.php
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Dynamic Dependent Dropdown</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">
    <meta name="_token" content="{{ csrf_token() }}">
</head>

<body>
    <div class="bg p-3 shadow-lg text-center">
        <h4>Laravel Dynamic Dependent Dropdown | Country State City | Jquery AJAX</h4>
    </div>
    <div class="container mt-3">
        <div class="row d-flex justify-content-center">
            <div class="col-md-6">
                <a href="{{ url('/list') }}" class="btn btn-primary mb-3">Back</a>
                <div class="card card-primary p-4 border-0 shadow-lg">
                    <form action="" id="frm" name="frm" method="post">
                        <div class="card-body">
                            <h3>Create User</h3>
                            <div class="mb-3">
                                <input type="text" class="form-control-lg form-control" name="name" id="name" placeholder="Enter Name">
                                <p class="invalid-feedback" id="name-error"></p>
                            </div>
                            <div class="mb-3">
                                <input type="text" class="form-control-lg form-control" name="email" id="email" placeholder="Enter Email">
                                <p class="invalid-feedback" id="email-error"></p>
                            </div>
                            <div class="mb-3">
                                <select name="country" id="country" class="form-control-lg form-select">
                                    <option value="">Select Country</option>
                                    @if (!empty($countries))
                                    @foreach ($countries as $country)
                                    <option value="{{ $country->id }}">{{ $country->name }}</option>
                                    @endforeach
                                    @endif
                                </select>
                            </div>

                            <div class="mb-3">
                                <select name="state" id="state" class="form-control-lg form-select">
                                    <option value="">Select State</option>
                                </select>
                            </div>

                            <div class="mb-3">
                                <select name="city" id="city" class="form-control-lg form-select">
                                    <option value="">Select City</option>
                                </select>
                            </div>

                            <div class="d-grid">
                                <button class="btn btn-primary btn-lg">Create</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!--https://blog.jquery.com/2023/08/28/jquery-3-7-1-released-reliable-table-row-dimensions/ https://getbootstrap.com/ -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
    <script>
        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')
            }
        });

        $(document).ready(function() {
            $("#country").change(function() {
                var country_id = $(this).val();

                if (country_id == "") {
                    var country_id = 0;
                }
                //alert(country_id);
                $.ajax({
                    url: '{{ url("/fetch-states/") }}/' + country_id,
                    type: 'post',
                    dataType: 'json',
                    success: function(response) {
                        $('#state').find('option:not(:first)').remove();
                        $('#city').find('option:not(:first)').remove();

                        if (response['states'].length > 0) {
                            $.each(response['states'], function(key, value) {
                                $("#state").append("<option value='" + value['id'] + "'>" + value['name'] + "</option>")
                            });
                        }
                    }
                });
            });


            $("#state").change(function() {
                var state_id = $(this).val();

                //console.log(state_id);

                if (state_id == "") {
                    var state_id = 0;
                }

                $.ajax({
                    url: '{{ url("/fetch-cities/") }}/' + state_id,
                    type: 'post',
                    dataType: 'json',
                    success: function(response) {
                        $('#city').find('option:not(:first)').remove();

                        if (response['cities'].length > 0) {
                            $.each(response['cities'], function(key, value) {
                                $("#city").append("<option value='" + value['id'] + "'>" + value['name'] + "</option>")
                            });
                        }
                    }
                });
            });

        });

        $("#frm").submit(function(event) {
            event.preventDefault();
            $.ajax({
                url: '{{ url("/save") }}',
                type: 'post',
                data: $("#frm").serializeArray(),
                dataType: 'json',
                success: function(response) {
                    alert(response['status']);
                    if (response['status'] == 1) {
                        window.location.href = "{{ url('list') }}";
                    } else {
                        if (response['errors']['name']) {
                            $("#name").addClass('is-invalid');
                            $("#name-error").html(response['errors']['name']);
                        } else {
                            $("#name").removeClass('is-invalid');
                            $("#name-error").html("");
                        }

                        if (response['errors']['email']) {
                            $("#email").addClass('is-invalid');
                            $("#email-error").html(response['errors']['email']);
                        } else {
                            $("#email").removeClass('is-invalid');
                            $("#email-error").html("");
                        }
                    }
                }
            });
        })
    </script>
</body>

</html>
resources/views/users/edit.blade.php
//resources/views/users/edit.blade.php
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Dynamic Dependent Dropdown</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">
    <meta name="_token" content="{{ csrf_token() }}">
</head>

<body>
    <div class="bg p-3 text-white shadow-lg text-center">
        <h4>Laravel Dynamic Dependent Dropdown | Country State City | Jquery AJAX</h4>
    </div>
    <div class="container mt-3">
        <div class="row d-flex justify-content-center">
            <div class="col-md-6">
                <a href="{{ url('/list') }}" class="btn btn-primary mb-3">Back</a>
                <div class="card card-primary p-4 border-0 shadow-lg">
                    <form action="" id="frm" name="frm" method="post">
                        <div class="card-body">
                            <h3>Edit User</h3>
                            <div class="mb-3">
                                <input type="text" class="form-control-lg form-control" name="name" id="name" placeholder="Enter Name" value="{{ $user->name }}">
                                <p class="invalid-feedback" id="name-error"></p>
                            </div>
                            <div class="mb-3">
                                <input type="text" class="form-control-lg form-control" name="email" id="email" placeholder="Enter Email" value="{{ $user->email }}">
                                <p class="invalid-feedback" id="email-error"></p>
                            </div>
                            <div class="mb-3">
                                <select name="country" id="country" class="form-control-lg form-select">
                                    <option value="">Select Country</option>
                                    @if (!empty($countries))
                                    @foreach ($countries as $country)
                                    <option {{ ($user->country == $country->id) ? 'selected' : '' }} value="{{ $country->id }}">{{ $country->name }}</option>
                                    @endforeach
                                    @endif
                                </select>
                            </div>

                            <div class="mb-3">
                                <select name="state" id="state" class="form-control-lg form-select">
                                    <option value="">Select State</option>
                                    @if (!empty($states))
                                    @foreach ($states as $state)
                                    <option {{ ($user->state == $state->id) ? 'selected' : '' }} value="{{ $state->id }}">{{ $state->name }}</option>
                                    @endforeach
                                    @endif
                                </select>
                            </div>

                            <div class="mb-3">
                                <select name="city" id="city" class="form-control-lg form-select">
                                    <option value="">Select City</option>
                                    @if (!empty($cities))
                                    @foreach ($cities as $city)
                                    <option {{ ($user->city == $city->id) ? 'selected' : '' }} value="{{ $city->id }}">{{ $city->name }}</option>
                                    @endforeach
                                    @endif
                                </select>
                            </div>

                            <div class="d-grid">
                                <button class="btn btn-primary btn-lg">Update</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!--https://blog.jquery.com/2023/08/28/jquery-3-7-1-released-reliable-table-row-dimensions/ https://getbootstrap.com/ -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
    <script>
        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')
            }
        });

        $(document).ready(function() {
            $("#country").change(function() {
                var country_id = $(this).val();

                if (country_id == "") {
                    var country_id = 0;
                }

                $.ajax({
                    url: '{{ url("/fetch-states/") }}/' + country_id,
                    type: 'post',
                    dataType: 'json',
                    success: function(response) {
                        $('#state').find('option:not(:first)').remove();
                        $('#city').find('option:not(:first)').remove();

                        if (response['states'].length > 0) {
                            $.each(response['states'], function(key, value) {
                                $("#state").append("<option value='" + value['id'] + "'>" + value['name'] + "</option>")
                            });
                        }
                    }
                });
            });


            $("#state").change(function() {
                var state_id = $(this).val();

                console.log(state_id);

                if (state_id == "") {
                    var state_id = 0;
                }

                $.ajax({
                    url: '{{ url("/fetch-cities/") }}/' + state_id,
                    type: 'post',
                    dataType: 'json',
                    success: function(response) {
                        $('#city').find('option:not(:first)').remove();

                        if (response['cities'].length > 0) {
                            $.each(response['cities'], function(key, value) {
                                $("#city").append("<option value='" + value['id'] + "'>" + value['name'] + "</option>")
                            });
                        }
                    }
                });
            });

        });

        $("#frm").submit(function(event) {
            event.preventDefault();
            $.ajax({
                url: '{{ url("/edit/".$user->id) }}',
                type: 'post',
                data: $("#frm").serializeArray(),
                dataType: 'json',
                success: function(response) {
                    if (response['status'] == 1) {
                        window.location.href = "{{ url('list') }}";
                    } else {
                        if (response['errors']['name']) {
                            $("#name").addClass('is-invalid');
                            $("#name-error").html(response['errors']['name']);
                        } else {
                            $("#name").removeClass('is-invalid');
                            $("#name-error").html("");
                        }

                        if (response['errors']['email']) {
                            $("#email").addClass('is-invalid');
                            $("#email-error").html(response['errors']['email']);
                        } else {
                            $("#email").removeClass('is-invalid');
                            $("#email-error").html("");
                        }
                    }
                }
            });
        })
    </script>
</body>

</html>
resources/views/users/list.blade.php
//resources/views/users/list.blade.php
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Dynamic Dependent Dropdown</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">
    <meta name="_token" content="{{ csrf_token() }}">
</head>

<body>
    <div class="bg p-3 text-white shadow-lg text-center">
        <h4>Laravel Dynamic Dependent Dropdown | Country State City | Jquery AJAX</h4>
    </div>
    <div class="container mt-3">
        <div class="row d-flex justify-content-center">
            <div class="col-md-6">
                <a href="{{ url('/create') }}" class="btn btn-primary mb-3">Create</a>
                <div class="card card-primary p-4 border-0 shadow-lg">

                    @if(Session::has('success'))
                    <div class="alert alert-success">
                        {{ Session::get('success') }}
                    </div>
                    @endif

                    @if(Session::has('error'))
                    <div class="alert alert-danger">
                        {{ Session::get('error') }}
                    </div>
                    @endif

                    <div class="card-body">
                        <h3>Users</h3>
                        <table class="table">
                            <thead>
                                <tr>
                                    <th>ID</th>
                                    <th>Name</th>
                                    <th>Email</th>
                                    <th>Edit</th>
                                </tr>

                                @if (!empty($users))
                                @foreach ($users as $user)
                                <tr>
                                    <td>{{ $user->id }}</td>
                                    <td>{{ $user->name }}</td>
                                    <td>{{ $user->email }}</td>
                                    <td>
                                        <a href="{{ url('edit/'.$user->id) }}" class="btn btn-success">Edit</a>
                                    </td>
                                </tr>
                                @endforeach
                                @endif

                            </thead>
                        </table>
                    </div>

                </div>
            </div>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!--https://blog.jquery.com/2023/08/28/jquery-3-7-1-released-reliable-table-row-dimensions/ https://getbootstrap.com/ -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
    </body> 
    </html>
Run C:\xampp\htdocs\laravel\my-app>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

Tuesday, June 25, 2024

Laravel 11 Auth Api Login Register Profile Logout with CRUD Product and upload Image (Create, Read, Update and Delete) - Laravel Sanctum

Laravel 11 Auth Api Login Register Profile Logout with CRUD Product and upload Image (Create, Read, Update and Delete) - Laravel Sanctum

Download Laravel App

https://laravel.com/docs/11.x/installation

Connecting our Database

open .env file root directory.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel11dev
DB_USERNAME=root
DB_PASSWORD=root

install laravel sanctum
https://laravel.com/docs/11.x/sanctum#main-content

php artisan install:api
C:\xampp\htdocs\laravel\myapp>php artisan install:api

Edit User Model and add API Tokens

app/Models/User.php

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}

Create Controller php artisan make:controller API/ApiController
C:\xampp\htdocs\laravel\myapp>php artisan make:controller API/ApiController

app\Http\Controllers\API\ApiController.php
//app\Http\Controllers\API\ApiController.php
<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use GuzzleHttp\Promise\Create;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class ApiController extends Controller
{
    public function register(Request $request)
    {
        try {
            $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()
                ],401);
            }

            $user = User::create([
                'name' => $request->name,
                'email' => $request->email,
                'password' => $request->password,
            ]);

            return response()->json([
                'status' => true,
                'message' => 'User created Succesfully',
                'token' => $user->createToken('API TOKEN')->plainTextToken
            ], 200);
        } catch (\Throwable $th) {
            // Return Json Response
            return response()->json([
                'status' => false,
                'message' => $th->getMessage(),
            ], 500);
        }
    }

    public function login(Request $request)
    {
        try {
            $validateuser = Validator::make($request->all(),
                [
                    'email' => 'required|email',
                    'password' => 'required',
                ]
            );

            if ($validateuser->fails()) {
                return response()->json([
                    'status' => false,
                    'message' => 'validation error',
                    'errors' => $validateuser->errors()
                ], 401);
            }

            if (!Auth::attempt(($request->only(['email','password'])))) {
                return response()->json(['status' => false,
                    'status' => false,
                    'message' => 'Something went really wrong!',
                ],401);
            }

            $user = User::where('email', $request->email)->first();

            return response()->json([
                'status' => true,
                'message' => 'Succesfully login',
                'token' => $user->createToken('API TOKEN')->plainTextToken
            ], 200);

        } catch (\Throwable $th) {
            // Return Json Response
            return response()->json([
                'status' => false,
                'message' => $th->getMessage(),
            ], 500);
        }
    }

    public function profile()
    {
        // Profile Detail 
        $userData = auth()->user();

        // Return Json Response
        return response()->json([
            'status' => true,
            'message' => 'Profile Info',
            'data' => $userData,
            'id' => auth()->user()->id,
        ], 200);
    }

    public function logout()
    {
        auth()->user()->tokens()->delete();

        // Return Json Response
        return response()->json([
            'status' => true,
            'message' => 'Successfully Logout',
            'data' => []
        ], 200);
    }
}
Create Model and Migration

C:\xampp\htdocs\laravel\laravelproject>php artisan make:model Product -m

A new file named Product.php will be created in the app directory and database/migrations directory to generate the table in our database
app/Models/Product.php
//app/Models/Product.php
<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Product extends Model
{
    use HasFactory;
 
    protected $fillable = [
        'name', 
        'image', 
        'price'
    ];
}
database\migrations\create_products_table.php
//database\migrations\create_products_table.ph
<?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('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('image');
            $table->integer('price');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};
Database Migration
php artisan migrate

C:\xampp\htdocs\laravel\laravel10project>php artisan migrate
Migration table created successfully.
check database table
Create Controller and Request
C:\xampp\htdocs\laravel\laravel10project>php artisan make:controller ProductController

app\Http\Controllers\ProductController.php
//app\Http\Controllers\ProductController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Models\Product;
use App\Http\Requests\ProductStoreRequest;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage; //php artisan storage:link = php artisan storage:link = http://127.0.0.1:8000/storage/1.jpg

class ProductController extends Controller
{
    public function index()
    {
        //$products = Product::all(); // All Product

        $products = Product::paginate(5);

        // Return Json Response
        return response()->json([
            'products' => $products
        ], 200);
    }

    public function store(ProductStoreRequest $request)
    {
        try {

            $name = $request->name;
            $price = $request->price;
            $imageName = Str::random(32) . "." . $request->image->getClientOriginalExtension();
            
            Storage::disk('public')->put($imageName, file_get_contents($request->image));

            Product::create([
                'name' => $name,
                'image' => $imageName,
                'price' => $price
            ]);

            // Return Json Response
            return response()->json([
                'results' => "Product successfully created. '$name' -- '$imageName' -- '$price' "
            ], 200);
        } catch (\Exception $e) {
            // Return Json Response
            return response()->json([
                'message' => "Something went really wrong!"
            ], 500);
        }
    }

    public function show($id)
    {
        // Product Detail 
        $product = Product::find($id);
        if (!$product) {
            return response()->json([
                'message' => 'Product Not Found.'
            ], 404);
        }

        // Return Json Response
        return response()->json([
            'product' => $product
        ], 200);
    }

    public function update(ProductStoreRequest $request, $id)
    {
        try {
            // Find product
            $product = Product::find($id);
            if (!$product) {
                return response()->json([
                    'message' => 'Product Not Found.'
                ], 404);
            }

            echo "request : $request->image";
            $product->name = $request->name;
            $product->price = $request->price;

            if ($request->image) {

                // Public storage
                $storage = Storage::disk('public');

                // Old iamge delete
                if ($storage->exists($product->image))
                    $storage->delete($product->image);

                // Image name
                $imageName = Str::random(32) . "." . $request->image->getClientOriginalExtension();
                $product->image = $imageName;

                // Image save in public folder
                $storage->put($imageName, file_get_contents($request->image));
            }

            // Update Product
            $product->save();

            // Return Json Response
            return response()->json([
                'message' => "Product successfully updated."
            ], 200);
        } catch (\Exception $e) {
            // Return Json Response
            return response()->json([
                'message' => "Something went really wrong!"
            ], 500);
        }
    }

    public function destroy($id)
    {
        // Detail 
        $product = Product::find($id);
        if (!$product) {
            return response()->json([
                'message' => 'Product Not Found.'
            ], 404);
        }

        // Public storage
        $storage = Storage::disk('public');

        // Iamge delete
        if ($storage->exists($product->image))
            $storage->delete($product->image);

        // Delete Product
        $product->delete();

        // Return Json Response
        return response()->json([
            'message' => "Product successfully deleted."
        ], 200);
    }
}
php artisan make:request ProductStoreRequest
C:\xampp\htdocs\laravel\myapp>php artisan make:request ProductStoreRequest

app\Http\Requests\ProductStoreRequest.php
//app\Http\Requests\ProductStoreRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ProductStoreRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        //return false;
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        if (request()->isMethod('post')) {
            return [
                'name' => 'required|string|max:258',
                'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
                'price' => 'required|string'
            ];
        } else {
            return [
                'name' => 'required|string|max:258',
                'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
                'price' => 'required|string'
            ];
        }
    }

    public function messages()
    {
        if (request()->isMethod('post')) {
            return [
                'name.required' => 'Name is required!',
                'image.required' => 'Image is required!',
                'price.required' => 'Price'
            ];
        } else {
            return [
                'name.required' => 'Name is required!',
                'price.required' => 'Price is required!'
            ];
        }
    }
}
routes/api.php
//routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;
use App\Http\Controllers\API\ApiController;

Route::post('register', [ApiController::class, 'register']);
Route::post('login', [ApiController::class, 'login']);

Route::group([
"middleware" => ["auth:sanctum"]
], function() {
//profile page
Route::get('profile', [ApiController::class, 'profile']);
//logout
Route::get('logout', [ApiController::class, 'logout']);
//product page
Route::get('products', [ProductController::class, 'index']);
Route::post('products', [ProductController::class, 'store']);
Route::get('products/{id}', [ProductController::class, 'show']);
Route::put('productsupdate/{id}', [ProductController::class, 'update']);
Route::delete('productdelete/{id}', [ProductController::class, 'destroy']);
});

//Route::get('/user', function (Request $request) {
// return $request->user();
//})->middleware('auth:sanctum');
Run C:\xampp\htdocs\laravel\myapp>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000/api/products
generate symbolic links C:\xampp\htdocs\laravel\myapp>php artisan storage:link

Run C:\xampp\htdocs\laravel\myapp>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

open postman new request

User Register
POST : http://127.0.0.1:8000/api/register?name=cairocoders&email=cairocoders@gmail.com&password=123456789

User Login
http://127.0.0.1:8000/api/login?email=cairocoders@gmail.com&password=123456789

User Profile
GET : http://127.0.0.1:8000/api/profile?email=cairocoders@gmail.com&password=123456789
Headers
Key Value
Accept application/json
Authorization Bearer 4|0iwwY1hyRu83B9GyfnAuGIDzHPl57BlsaPf4mkkeeb020ff9

LogOut
GET : http://127.0.0.1:8000/api/logout?email=cairocoders@gmail.com&password=123456789
Headers
Key Value
Accept application/json
Authorization Bearer 4|0iwwY1hyRu83B9GyfnAuGIDzHPl57BlsaPf4mkkeeb020ff9

Products

GET api/products Index All products return.
GET : http://127.0.0.1:8000/api/products
Headers
Key Value
Accept application/json
Authorization Bearer 4|0iwwY1hyRu83B9GyfnAuGIDzHPl57BlsaPf4mkkeeb020ff9

GET api/products/{id} Show Detail of a particular post by ID.
GET : http://127.0.0.1:8000/api/products/1
Headers
Key Value
Accept application/json
Authorization Bearer 4|0iwwY1hyRu83B9GyfnAuGIDzHPl57BlsaPf4mkkeeb020ff9

POST api/products Store Create a new product.
POST : http://127.0.0.1:8000/api/products
body
key value
name Iphone 13
image iphone.jpg = file
price 45
Headers
Key Value
Accept application/json
Authorization Bearer 4|0iwwY1hyRu83B9GyfnAuGIDzHPl57BlsaPf4mkkeeb020ff9

PUT api/products/{id} Update Update a particular product by ID.
POST : http://127.0.0.1:8000/api/products/1
body
key value
_method PUT
name Iphone 13 updated
image iphone.jpg = file
price 46 updated
Headers
Key Value
Accept application/json
Authorization Bearer 4|0iwwY1hyRu83B9GyfnAuGIDzHPl57BlsaPf4mkkeeb020ff9

DELETE api/products/{id} Destroy Delete a particular product by ID.
DELETE : http://127.0.0.1:8000/api/products/1
Headers
Key Value
Accept application/json
Authorization Bearer 4|0iwwY1hyRu83B9GyfnAuGIDzHPl57BlsaPf4mkkeeb020ff9

https://github.com/cairocodes/Laravel-11-Auth-Api-Login-Register-Profile-Logout-with-CRUD-Product-and-upload-Image/

Monday, April 15, 2024

Next.js 14 Laravel 11 CRUD with Upload and Pagination (Create, Read, Update and Delete) Mysql | TailwindCSS DaisyUI

Next.js 14 Laravel 11 CRUD with Upload and Pagination (Create, Read, Update and Delete) Mysql | TailwindCSS DaisyUI

Download Laravel App

https://laravel.com/docs/11.x/installation

Connecting our Database

open .env file root directory.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel11dev
DB_USERNAME=root
DB_PASSWORD=root

Create Model and Migration

C:\xampp\htdocs\laravel\laravelproject>php artisan make:model Product -m

A new file named Product.php will be created in the app directory and database/migrations directory to generate the table in our database
app/Models/Product.php
//app/Models/Product.php
<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Product extends Model
{
    use HasFactory;
 
    protected $fillable = [
        'name', 
        'image', 
        'price'
    ];
}
database\migrations\create_products_table.php
//database\migrations\create_products_table.ph
<?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('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('image');
            $table->integer('price');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};
Database Migration
php artisan migrate

C:\xampp\htdocs\laravel\laravel10project>php artisan migrate
Migration table created successfully.
check database table
Create Controller and Request
C:\xampp\htdocs\laravel\laravel10project>php artisan make:controller ProductController

app\Http\Controllers\ProductController.php
//app\Http\Controllers\ProductController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Models\Product;
use App\Http\Requests\ProductStoreRequest;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage; //php artisan storage:link = php artisan storage:link = http://127.0.0.1:8000/storage/1.jpg

class ProductController extends Controller
{
    public function index()
    {
        //$products = Product::all(); // All Product

        $products = Product::paginate(5);

        // Return Json Response
        return response()->json([
            'products' => $products
        ], 200);
    }

    public function store(ProductStoreRequest $request)
    {
        try {

            $name = $request->name;
            $price = $request->price;
            $imageName = Str::random(32) . "." . $request->image->getClientOriginalExtension();
            
            Storage::disk('public')->put($imageName, file_get_contents($request->image));

            Product::create([
                'name' => $name,
                'image' => $imageName,
                'price' => $price
            ]);

            // Return Json Response
            return response()->json([
                'results' => "Product successfully created. '$name' -- '$imageName' -- '$price' "
            ], 200);
        } catch (\Exception $e) {
            // Return Json Response
            return response()->json([
                'message' => "Something went really wrong!"
            ], 500);
        }
    }

    public function show($id)
    {
        // Product Detail 
        $product = Product::find($id);
        if (!$product) {
            return response()->json([
                'message' => 'Product Not Found.'
            ], 404);
        }

        // Return Json Response
        return response()->json([
            'product' => $product
        ], 200);
    }

    public function update(ProductStoreRequest $request, $id)
    {
        try {
            // Find product
            $product = Product::find($id);
            if (!$product) {
                return response()->json([
                    'message' => 'Product Not Found.'
                ], 404);
            }

            echo "request : $request->image";
            $product->name = $request->name;
            $product->price = $request->price;

            if ($request->image) {

                // Public storage
                $storage = Storage::disk('public');

                // Old iamge delete
                if ($storage->exists($product->image))
                    $storage->delete($product->image);

                // Image name
                $imageName = Str::random(32) . "." . $request->image->getClientOriginalExtension();
                $product->image = $imageName;

                // Image save in public folder
                $storage->put($imageName, file_get_contents($request->image));
            }

            // Update Product
            $product->save();

            // Return Json Response
            return response()->json([
                'message' => "Product successfully updated."
            ], 200);
        } catch (\Exception $e) {
            // Return Json Response
            return response()->json([
                'message' => "Something went really wrong!"
            ], 500);
        }
    }

    public function destroy($id)
    {
        // Detail 
        $product = Product::find($id);
        if (!$product) {
            return response()->json([
                'message' => 'Product Not Found.'
            ], 404);
        }

        // Public storage
        $storage = Storage::disk('public');

        // Iamge delete
        if ($storage->exists($product->image))
            $storage->delete($product->image);

        // Delete Product
        $product->delete();

        // Return Json Response
        return response()->json([
            'message' => "Product successfully deleted."
        ], 200);
    }
}
php artisan make:request ProductStoreRequest
C:\xampp\htdocs\laravel\myapp>php artisan make:request ProductStoreRequest

app\Http\Requests\ProductStoreRequest.php
//app\Http\Requests\ProductStoreRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ProductStoreRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        //return false;
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        if (request()->isMethod('post')) {
            return [
                'name' => 'required|string|max:258',
                'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
                'price' => 'required|string'
            ];
        } else {
            return [
                'name' => 'required|string|max:258',
                'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
                'price' => 'required|string'
            ];
        }
    }

    public function messages()
    {
        if (request()->isMethod('post')) {
            return [
                'name.required' => 'Name is required!',
                'image.required' => 'Image is required!',
                'price.required' => 'Price'
            ];
        } else {
            return [
                'name.required' => 'Name is required!',
                'price.required' => 'Price is required!'
            ];
        }
    }
}
routes/api.php
//routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;

Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');

Route::get('products', [ProductController::class, 'index']);
Route::post('products', [ProductController::class, 'store']);
Route::get('products/{id}', [ProductController::class, 'show']);
Route::put('productsupdate/{id}', [ProductController::class, 'update']);
Route::delete('productdelete/{id}', [ProductController::class, 'destroy']);
Run C:\xampp\htdocs\laravel\myapp>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000/api/products
generate symbolic links C:\xampp\htdocs\laravel\myapp>php artisan storage:link

Run C:\xampp\htdocs\laravel\myapp>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

open postman new request

GET api/products Index All products return.
GET : http://127.0.0.1:8000/api/products

GET api/products/{id} Show Detail of a particular post by ID.
GET : http://127.0.0.1:8000/api/products/1

POST api/products Store Create a new product.
POST : http://127.0.0.1:8000/api/products
body
key value
name Iphone 13
image iphone.jpg = file
price 45

PUT api/products/{id} Update Update a particular product by ID.
POST : http://127.0.0.1:8000/api/products/1
body
key value
_method PUT
name Iphone 13 updated
image iphone.jpg = file
price 46 updated

DELETE api/products/{id} Destroy Delete a particular product by ID.
DELETE : http://127.0.0.1:8000/api/products/1

Install nextjs
https://nextjs.org/
npx create-next-app@latest

install axios
npm install axios
https://www.npmjs.com/package/axios

app\page.tsx
app\page.tsx
//app\page.tsx
import Link from "next/link";
import ProductsTable from "@/components/tabledata";
import { Suspense } from "react";
import { Spinner } from "@/components/spinner";

export default function Home() {
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
      <div className="flex items-center justify-between gap-1 mb-5">
        <h1 className="text-4xl font-bold">Next.js 14 Laravel 11 CRUD with Upload and Pagination <br/>(Create, Read, Update and Delete) Mysql | TailwindCSS DaisyUI</h1>
      </div>    
      <div className="overflow-x-auto">
          <div className="mb-2 w-full text-right">
            <Link
              href="/product/create"
              className="btn btn-primary">
              Add New Product
            </Link>
          </div>
          <Suspense fallback={<Spinner />}>
            <ProductsTable/>
          </Suspense>
      </div>  
    </div>
  );
}
components\tabledata.tsx
//components\tabledata.tsx
"use client";
   
import React, { useEffect, useState } from "react";
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import Link from "next/link";
import Image from 'next/image'

export default function Products() {
    const [productsData, setProductsData] = useState([]);
    const [pageprevnext, setPage] = useState({});
  
    const url = "http://127.0.0.1:8000/api/products";
 
    useEffect(() => {
        fetchData(url);
    }, []);
 
    const fetchData = (url) => {
        axios
        .get(url)
        .then((data) => {
            setProductsData(data.data.products.data);
            console.log(data.data.products.data);
            setPage(data.data.products);
            //console.log(data.data.products.next_page_url);
        })
        .catch((error) => {
            console.log(error);
        });
    };

    const handleNextPage = () => {
        console.log(pageprevnext.next_page_url);
        fetchData(pageprevnext.next_page_url);
        window.scrollTo(0, 0);
    };
     
    const handlePreviousPage = () => {
        fetchData(pageprevnext.prev_page_url);
        window.scrollTo(0, 0);
    };

    const deleteProduct = (id) => {
        axios.delete('http://127.0.0.1:8000/api/productdelete/'+id).then(function(response){
            console.log(response.data);
            alert("Successfully Deleted");
            window.location.href = '/';
        });
    }

    return (
        <>
        <table className="table table-zebra">
        <thead className="text-sm text-gray-700 uppercase bg-gray-50">
            <tr>
            <th className="py-3 px-6">#</th>
            <th className="py-3 px-6">Photo</th>
            <th className="py-3 px-6">Name</th>
            <th className="py-3 px-6">Price</th>
            <th className="py-3 px-6 text-center">Actions</th>
            </tr>
        </thead>
        <tbody>
            {productsData.map((rs, index) => (
            <tr key={rs.id} className="bg-white border-b">
                <td className="py-3 px-6">{rs.id}</td>
                <td className="py-3 px-6">
                    <Image
                        src={`http://127.0.0.1:8000/storage/${rs.image}`}
                        width={70}
                        height={70}
                        style={{width:'90px', height: "auto" }}
                        alt="Photo"
                    />
                </td>
                <td className="py-3 px-6">{rs.name}</td>
                <td className="py-3 px-6">${rs.price}.99</td>
                <td className="flex justify-center gap-1 py-3">
                    <Link
                    href={`/product/view/${rs.id}`} 
                    className="btn btn-info">
                    View
                    </Link>
                    <Link
                    href={`/product/edit/${rs.id}`} 
                    className="btn btn-primary">
                    Edit
                    </Link>
                    <button onClick={() => deleteProduct(rs.id)} className="btn btn-secondary">Delete</button>
                </td>
            </tr>
            ))}
        </tbody>
        </table>
        
        <div className="w-1/2 items-center px-4 mt-6">   
            <div className="join grid grid-cols-2">
                {pageprevnext.prev_page_url ? (
                    <button className="join-item btn btn-primary btn-outline" onClick={handlePreviousPage}>
                    Previous
                    </button>
                ) : null}
                {pageprevnext.next_page_url ? (
                    <button className="join-item btn btn-primary btn-outline" onClick={handleNextPage}>
                    Next
                    </button>
                ) : null}
            </div>
        </div> 
        </>
  );
}
components\spinner.tsx
//components\spinner.tsx
export const Spinner = () => {
  return (
    <span className="loading loading-spinner loading-lg"></span>
  );
};
app\product\create\page.tsx
//app\product\create\page.tsx
"use client";

import React, { useState } from "react";
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios

const Product = () => {
    const [name, setName] = useState("");
    const [price, setPrice] = useState("");
    const [image, setFile] = useState()
 
    const onSubmitUpload = async (e) => {
        e.preventDefault();
       try {
          const formData = new FormData()
          formData.append("name", name);
          formData.append("price", price);
          formData.append('image', image)
          axios.post('http://127.0.0.1:8000/api/products',formData )
          .then((response) => {
              console.log(response);
              window.location.href = '/';
          })
          .catch(er => console.log(er))
        } catch (err) {
            console.log("Something Wrong");
        }
    }
    
    return (
    <div className="max-w-md mx-auto mt-5">
        <h1 className="text-2xl text-center mb-2">Add New Product</h1>
        <div>
        <form>
        <div className="mb-5">
          <label htmlFor="name" className="block text-sm font-medium text-gray-900">
            Name
          </label>
          <input
            type="text"
            name="name"
            id="name"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="Name..."
            onChange={(e) => setName(e.target.value)}
          />
        </div>
        <div className="mb-5">
          <label htmlFor="price" className="block text-sm font-medium text-gray-900">
            Price
          </label>
          <input
            type="price"
            name="price"
            id="price"
            className="input input-bordered input-primary w-full max-w-xs"
            placeholder="price..."
            onChange={(e) => setPrice(e.target.value)}
          />
        </div>
        <div className="mb-5">
          <label className="block text-sm font-medium text-gray-900">
            Upload File
          </label>
          <input type="file" onChange={(e) => setFile(e.target.files[0])} name="image" id="image" className="file-input file-input-bordered file-input-secondary w-full max-w-xs"/>
        </div>
        <button type="submit" className="btn btn-primary" onClick={e => onSubmitUpload(e)}>Add New Product</button> 
      </form>
    </div>
    </div>
  );
};
  
export default Product;
app\product\edit\[id]\page.tsx
//app\product\edit\[id]\page.tsx
"use client";
 
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import { useParams } from 'next/navigation'
import Image from 'next/image'

export default function ViewProductPage() {
    const {id}=useParams();

    console.log(id);
 
    useEffect(()=>{
        fetchProduct();
    },[id]);
  
    const fetchProduct=async()=>{
        try{
            const result=await axios.get("http://127.0.0.1:8000/api/products/"+id);
            console.log(result.data.product);
            setInputs(result.data.product)
        }catch(err){
            console.log("Something Wrong");
        }
    }
 
    const[message, setMessage]= useState('');
    const [inputs, setInputs] = useState([]);
    const [fileimage, setPhoto]= useState('');
     
    const handleChange = (event) => {
        const name = event.target.name;
        const value = event.target.value;
        setInputs(values => ({...values, [name]: value}));
    }
     
    const uploadProduct= async()=>{
        const formData= new FormData();
        formData.append('_method', 'PUT');
        formData.append('name', inputs.name);
        formData.append('price',inputs.price);
        formData.append('image', fileimage);
        const response= await axios.post("http://127.0.0.1:8000/api/productsupdate/"+id, formData, {
            headers:{'Content-Type':"multipart/form-data"},
        } );
        setMessage(response.data.message); //"message": "Product successfully updated.."
        console.log(response)
        setTimeout(()=>{
            window.location.href = '/';
        }, 2000);
    }
 
    const handleSubmit= async(e)=>{
      e.preventDefault();
      await uploadProduct();
 
   }
 
    return (
    <div className="max-w-md mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">Edit Form</h1>
        <p className="text-success"><b>{ message }</b></p>  
            <form onSubmit={ handleSubmit}> 
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> ID:</label>
                    <input type="text" id="id" name="id" value={id} disabled />
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900"> Full Name:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" placeholder="Enter Your Full Name" name="name"
                    value={inputs.name} onChange={ handleChange}/>
                </div>
                <div className="mb-3 mt-3">
                    <label className="block text-sm font-medium text-gray-900">Price:</label>
                    <input type="text" className="input input-bordered input-primary w-full max-w-xs" id="price" placeholder="Enter Price" name="price"
                    value={inputs.price} onChange={ handleChange}/>
                </div>
                <div className="mb-5">
                    <label className="block text-sm font-medium text-gray-900">
                    Upload File
                    </label>
                    <input type="file" onChange={(e)=>setPhoto(e.target.files[0])} name="image" id="image" className="file-input file-input-bordered file-input-secondary w-full max-w-xs"/>
                </div>
                <p className="text-center mt-6">
                    <Image
                    src={`http://127.0.0.1:8000/storage/${inputs.image}`}
                    width={200}
                    height={200}
                    alt="Photo"
                    style={{width:'400px', height: "auto" }}
                />
                </p>
                <button type="submit" className="btn btn-primary">Update</button>
            </form>
    </div>
  );
}
app\product\view\[id]\page.tsx
//app\product\view\[id]\page.tsx
"use client";
 
import React, { useState, useEffect } from 'react';
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import Link from "next/link";
import { useParams } from 'next/navigation'
import Image from 'next/image'

export default function ViewProductPage() {
    const {id}=useParams();
 
    console.log(id);
 
    const[product,setProduct]=useState([]);
  
    useEffect(()=>{
        fetchProduct();
    },[id]);
  
    const fetchProduct=async()=>{
        try{
        const result=await axios.get("http://127.0.0.1:8000/api/products/"+id);
          console.log(result.data.product);
          setProduct(result.data.product)
  
        }catch(err){
            console.log("Something Wrong");
        }
    }
 
    return (
    <div className="max-w-2xl mx-auto mt-5">
      <h1 className="text-2xl text-center mb-2">View Product</h1>
      <table className="table table-zebra">
          <thead className="text-sm text-gray-700 uppercase bg-gray-50">
            <tr>
              <th>S No.</th>
              <th>Product Name</th>
              <th>Price</th>             
            </tr>
          </thead>
          <tbody>
            <tr>
                <td>{product.id}</td>
                <td>{product.name}</td>
                <td>{product.price}</td>
            </tr>
          </tbody>
      </table>
      <p className="text-center mt-6">
        <Image
        src={`http://127.0.0.1:8000/storage/${product.image}`}
        width={200}
        height={200}
        alt="Photo"
        style={{width:'400px', height: "auto" }}
      />
      </p>
    </div>
  );
}
next.config.mjs
/** @type {import('next').NextConfig} */
//const nextConfig = {};
const nextConfig = {
  reactStrictMode: true,
  images : {
    domains : ['localhost', 'cairocoders-ednalan.com', '127.0.0.1'] // == Domain name
  }
}
export default nextConfig;
run C:\nextjs>npm run dev
Github - Next.js 14 Laravel 11 CRUD with Upload and Pagination (Create, Read, Update and Delete) Mysql | TailwindCSS DaisyUI

Saturday, April 13, 2024

Next.js 14 Laravel 11 Pagination Next Prev Mysql | TailwindCSS DaisyUI

Next.js 14 Laravel 11 Pagination Next Prev Mysql | TailwindCSS DaisyUI

Download Laravel App

https://laravel.com/docs/11.x/installation

Connecting our Database

open .env file root directory.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel11dev
DB_USERNAME=root
DB_PASSWORD=root

Create Model and Migration

C:\xampp\htdocs\laravel\laravelproject>php artisan make:model Product -m

A new file named Product.php will be created in the app directory and database/migrations directory to generate the table in our database
app/Models/Product.php
//app/Models/Product.php
<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Product extends Model
{
    use HasFactory;
 
    protected $fillable = [
        'name', 
        'image', 
        'price'
    ];
}
database\migrations\create_products_table.php
//database\migrations\create_products_table.ph
<?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('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('image');
            $table->integer('price');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};
Database Migration
php artisan migrate

C:\xampp\htdocs\laravel\laravel10project>php artisan migrate
Migration table created successfully.
check database table
Create Controller and Request
C:\xampp\htdocs\laravel\laravel10project>php artisan make:controller ProductController

app\Http\Controllers\ProductController.php
//app\Http\Controllers\ProductController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Product;
use Illuminate\Support\Facades\Storage; //php artisan storage:link = php artisan storage:link = http://127.0.0.1:8000/storage/1.jpg

class ProductController extends Controller
{
    public function index()
    {
        //$products = Product::all(); // All Product

        $products = Product::paginate(3);

        // Return Json Response
        return response()->json([
            'results' => $products
        ], 200);
    }
}
Routes
install
php artisan install:api
All API requests will need the header Accept: application/json.
open routes/api.php and update the following code
routes\api.php
//routes\api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

Route::get('products', [ProductController::class, 'index']);
Run C:\xampp\htdocs\laravel\myapp>php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

Install nextjs
https://nextjs.org/
npx create-next-app@latest

install axios
npm install axios
https://www.npmjs.com/package/axios

app\page.tsx
app\page.tsx
//app\page.tsx
import TableData from "@/components/tabledata";

export default function Home() {
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center bg-gray-300">
      <div className="flex items-center justify-between gap-1 mb-5">
        <h1 className="text-4xl font-bold">Next.js 14 Laravel 11 Pagination Next Prev Mysql | TailwindCSS DaisyUI</h1>
      </div>    
      <TableData/>
    </div>
  );
}
components\tabledata.tsx
//components\tabledata.tsx
"use client";

import React, { useEffect, useState } from "react";
import axios from 'axios' //npm install axios https://www.npmjs.com/package/axios
import Image from 'next/image' 

export default function Products() {
    const [products, setProducts] = useState([]);
    const [info, setInfo] = useState({});

    const url = "http://127.0.0.1:8000/api/products";

    useEffect(() => {
        fetchProducts(url);
    }, []);

    const fetchProducts = (url) => {
        axios
        .get(url)
        .then((data) => {
            setProducts(data.data.results.data);
            //console.log(data.data.results.data);
            setInfo(data.data.results);
            //console.log(data.data.results.next_page_url);
        })
        .catch((error) => {
            console.log(error);
        });
    };

    const handleNextPage = () => {
        fetchProducts(info.next_page_url);
        window.scrollTo(0, 0);
    };
    
    const handlePreviousPage = () => {
        fetchProducts(info.prev_page_url);
        window.scrollTo(0, 0);
    };

    return (
            <div className="">
            <div className="container mx-auto px-4">
                <h2 className="text-3xl font-bold text-white mb-8">Introducing Our Latest Product</h2>
                <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
                
                {products.map((item, index) => (
                <div key={index} className="bg-white rounded-lg shadow-lg p-8">
                    <div className="relative overflow-hidden">
                        <Image
                        src={`http://127.0.0.1:8000/storage/${item.image}`}
                        width={400}
                        height={400}
                        alt="Photo"
                        />
                    <div className="absolute inset-0 bg-black opacity-40" />
                    <div className="absolute inset-0 flex items-center justify-center">
                        <button className="bg-white text-gray-900 py-2 px-6 rounded-full font-bold hover:bg-gray-300">View Product</button>
                    </div>
                    </div>
                    <h3 className="text-xl font-bold text-gray-900 mt-4">{item.name}</h3>
                    <p className="text-gray-500 text-sm mt-2">Description: {item.name}</p>
                    <div className="flex items-center justify-between mt-4">
                    <span className="text-gray-900 font-bold text-lg">${item.price}.99</span>
                    <button className="bg-gray-900 text-white py-2 px-4 rounded-full font-bold hover:bg-gray-800">Add to Cart</button>
                    </div>
                </div>
                ))}

                </div>
                <div className="w-1/2 items-center px-4 mt-6">   
                    <div className="join grid grid-cols-2">
                        {info.prev_page_url ? (
                            <button className="join-item btn btn-primary btn-outline" onClick={handlePreviousPage}>
                            Previous
                            </button>
                        ) : null}
                        {info.next_page_url ? (
                            <button className="join-item btn btn-primary btn-outline" onClick={handleNextPage}>
                            Next
                            </button>
                        ) : null}
                    </div>
                </div> 
            </div>
            </div>
  );
}
next.config.mjs
/** @type {import('next').NextConfig} */
//const nextConfig = {};
const nextConfig = {
  reactStrictMode: true,
  images : {
    domains : ['localhost', 'cairocdoers-ednalan.com', '127.0.0.1'] // == Domain name
  }
}
export default nextConfig;
run C:\nextjs>npm run dev
Github - Next.js 14 Laravel 11 Pagination Next Prev Mysql | TailwindCSS DaisyUI

Related Post