Laravel 9 CRUD Using Inertiajs and Vuejs 3

Laravel 9 CRUD Using Inertiajs and Vuejs 3

Laravel 9 CRUD Using Inertiajs and Vuejs 3

In this post we will give you information about Laravel 9 CRUD Using Inertiajs and Vuejs 3. Hear we will give you detail about Laravel 9 CRUD Using Inertiajs and Vuejs 3 And how to use it also give you demo for it if it is necessary.

In this tutorial we will learn how to use Laravel 9 CRUD Using Inertiajs and Vuejs 3 to build CRUD (CREATE-READ-UPDATE-DELETE) pages.

This tutorial requires that you know how to use laravel and Vuejs. We will use Laravel 9 CRUD Using Inertiajs and Vuejs 3 and also we will use inertiajs, if you don’t know inertiajs before so no problem, i will give a brief description about it below or you can learn more about it by clicking this url. I will use bootstrap as the UI library.

Overview About Laravel 9 CRUD Using Inertiajs and Vuejs 3

Interiajs is a library designed to be a middle layer between the frontend application and the backend. For example if you plan to build an application using laravel and vuejs, the first thing you take in consideration is Building an Api to connect the frontend with the backend.

But using interia doesn’t require an api, instead you build the controllers in the normal way which accept a request and return an interia response. The second thing is that interiajs doesn’t require that you create routes in vuejs instead we will use the normal laravel routes and it will work the same way as the vue routes. Also intertiajs provides other features like redirections, form submissions, shared data, and more.

Preparing Project for Laravel 9 CRUD Using Inertiajs and Vuejs 3

Let’s get started, at first make sure you have PHP 8. Next in your server root create a new laravel project using composer:
composer create-project laravel/laravel laravel-inertia-crud

Then after successful creation cd into the project:

cd laravel-inertia-crud

Installing Dependencies

Inertiajs has two packages one server side and the other client side so let’s install the server side package first:
composer require inertiajs/inertia-laravel

Also install this useful package “tightenco/ziggy”, this package enable us to access laravel routes from javascript so we will use it in vue components like so route(‘route name’):
composer require tightenco/ziggy

Then prepare the root template in the main layout. So create this file app.blade.php in resources/views. This file will act as the main template for the inertia app.

resources/views/app.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Laravel 9 CRUD Using Inertiajs and Vuejs 3</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" />
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" />
    <link href="{{ asset('/css/app.css') }}" rel="stylesheet" />
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fcode.jquery.com%2Fjquery-3.3.1.slim.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22https%3A%2F%2Fstackpath.bootstrapcdn.com%2Fbootstrap%2F4.3.1%2Fjs%2Fbootstrap.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
    @routes
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20src%3D%22%7B%7B%20asset('%2Fjs%2Fapp.js')%20%7D%7D%22%20defer%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
</head>
<body>
    

<div class="container">
        @inertia
    </div>


</body>
</html>

In this code i added the @inertia directive. This directive will be replaced at runtime by the root div that will be mounted by the inertia app. Note also the @routes directive which provided by the tightenc/ziggy package.

Next setup the inertia middleware using this command:

php artisan inertia:middleware

Register the middleware in app/Http/Kernel.php as the last item in the web middleware group:

'web' => [
    // ...
    \App\Http\Middleware\HandleInertiaRequests::class,
],

After installing the server side package, it’s time to install the client side package with npm.

Run this command to install the npm packages first:
npm install

Then install the inertia npm package using:

npm install @inertiajs/inertia @inertiajs/inertia-vue3

Here we will use inertia with vue 3 so we also need to install vue 3 and vue-loader:
npm install vue@next vue-loader@^16.2.0 @vue/compiler-sfc@^3.0.11

You can also install the inertia progress package which a display progress bar when navigating between pages:

npm install @inertiajs/progress

Database & Migration

Let’s create a new mysql database using phpmyadmin name it as you like then update laravel db settings in the .env file as follows:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_inertia_crud
DB_USERNAME=<DB username>
DB_PASSWORD=<DB password>

Next create a new migration, we will create a posts table with this command:
php artisan make:migration create_posts_table

Then open the migration you just create and update the up() method like so:

public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string("title");
            $table->text("content");
            $table->string("image")->nullable();
            $table->timestamps();
        });
    }

Here we are creating a posts table with title, content, and image fields. Finally migrate the tables using:
php artisan migrate

This will create the posts table along side other tables like the users table which we will use to authenticate users.

Project Structure

Now let’s talk about the project structure, we will need these pages:

Login Form
Register Form
Page to view all posts
Create Post Page
Edit Post Page.

Inertiajs suggests that every controller action has a Vue component, so based on this convention let’s create the controllers first in app/Http/Controllers, we will create the controllers structure first then we will update them later.

For the authentication, create a new folder “Auth” in app/Http/Controllers, then create two files inside it, LoginController.php and RegisterController.php.

app/Http/Controllers/Auth/LoginController.php

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
class LoginController extends Controller
{
    public function showLoginForm()
    {
        return Inertia::render('Auth/Login');   // Vue component
    }
    public function authenticate(Request $request)
    {
        
    }
    public function logout(Request $request)
    {
        
    }
}

app/Http/Controllers/Auth/RegisterController.php

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;
class RegisterController extends Controller
{
    public function showRegisterForm()
    {
        return Inertia::render('Auth/Register');
    }
    public function register(Request $request)
    {
        
    }
}

Create a resource controller for the posts:

php artisan make:controller PostsController --resource

Open the PostsController and update like so:

PostsController.php

<?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Inertia\Inertia; class PostsController extends Controller { public function __construct() { $this->middleware("auth")->except(["index"]);
    }
    public function index()
    {
        return Inertia::render('Posts/Index', [
            "posts" => Post::orderBy('id', 'DESC')->paginate(10)
        ]);
    }
    public function create()
    {
        return Inertia::render('Posts/Create');
    }
    public function store(Request $request)
    {
        
    }
    public function edit($id)
    {
        return Inertia::render('Posts/Edit', [
            'post' => Post::findOrFail($id)
        ]);
    }
    public function update(Request $request, $id)
    {
        
    }
    public function destroy(Request $request, $id)
    {
        
    }
}

As you see in the above controllers i invoked Inertia::render() method in the Inertia facade, this method act like the view() method which works with blade views, instead this method accept a Vue component name and an optional parameter that specify the props we want to send to the component, for example in the PostsController::edit($id) the method render “Posts/Edit” component and we send the post object as a prop.

Creating The Routes for Laravel 9 CRUD Using Inertiajs and Vuejs 3

As we mentioned previously that we don’t need vue routes instead we will create the routes as usual in laravel, so open routes/web.php and add those routes:

<?php use App\Http\Controllers\Auth\LoginController; use App\Http\Controllers\Auth\RegisterController; use App\Http\Controllers\PostsController; use Illuminate\Support\Facades\Route; Route::get('login', [LoginController::class, 'showLoginForm'])->name('showLoginForm')->middleware('guest');
Route::get('register', [RegisterController::class, 'showRegisterForm'])->name('showRegisterForm')->middleware('guest');
Route::post('login', [LoginController::class, 'authenticate'])->name('login');
Route::post('register', [RegisterController::class, 'register'])->name('register');
Route::post('logout', [LoginController::class, 'logout'])->name('logout');
Route::resource('post', PostsController::class);
Route::redirect('/', 'post');

Now after we created the controllers and routes let’s create the Vue components and pages, this will be in resourceshttps://www.onlinecode.org/js/ directory. Open resourceshttps://www.onlinecode.org/js directory create these directories:

Pages/Auth/
Pages/Posts/

Then create these components inside Pages/Auth/ directory:

Login.vue
Register.vue

In the same way create these components inside Pages/Posts/ directory:

Create.vue
Edit.vue
Index.vue

Update app.js

Open resourceshttps://www.onlinecode.org/js/app.js and update it like so:
require('./bootstrap');
import {createApp, h} from 'vue';
import { App, plugin } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress'
const el = document.getElementById('app');
InertiaProgress.init();
    const app = createApp({
        render: () => h(App, {
            initialPage: JSON.parse(el.dataset.page),
            resolveComponent: name => require(`./Pages/${name}`).default
        })
    });
    app.config.globalProperties.$route = window.route;
    app.provide('$route', window.route);
    app.use(plugin).mount(el);

This file simply start the inertia app mounting process, here we are telling inertia to mount the app in an element whose id is “app”, then we use the Vue 3 way of creating app using the createApp() function, this function available in Vue 3 only.

The createApp() function takes an object, which contains the render() function, the render function as follows:

render: () => h(App, {
            initialPage: JSON.parse(el.dataset.page),
            resolveComponent: name => require(`./Pages/${name}`).default
        })

So what are the intialPage and resolveComponent? the initialPage parameter represent the data sent from laravel to the Vue app as a property in the root element like so:

<div id="app" data-page="{}" data-v-app="">
   <!-- Component Body for Laravel 9 CRUD Using Inertiajs and Vuejs 3 -->
</div>

As you see in each page you navigate the above attribute data-page will be populated with the required data to render the component.

The resolveComponent parameter is a callback that specifies where to load the components, in this case we are rendering the components located in resourceshttps://www.onlinecode.org/js/Pages/ directory.

These two lines app.config.globalProperties.$route and app.provide(‘$route’, window.route) tell Vue to inject the route() function, this function available from the ziggy package we installed above so can access it in any component like this.$route().

Open resources/css/app.css and add this css code:

.alert li {
    font-size: 14px;
}
.logout-link {
    border: none;
    background: none;
    display: inline;
    color: #fff !important;
}

Update webpack.mix.js

const mix = require('laravel-mix');
mix.js('resourceshttps://www.onlinecode.org/js/app.js', 'publichttps://www.onlinecode.org/js').vue()
    .postCss('resources/css/app.css', 'public/css', [
    ]);

This is important so that vue 3 can build successfully.

Update HandleInertiaRequests Middleware

An important thing that must be done is to update the HandleInertiaRequests::share() method. The share() method allow us to share global data that is accessible in every Vue page instead of sending this data for controller action. So we will share the current authenticated user and the flash message and the csrf_token.

app/Http/Middleware/HandleInertiaRequests.php

<?php namespace App\Http\Middleware; use Illuminate\Http\Request; use Inertia\Middleware; class HandleInertiaRequests extends Middleware { protected $rootView = 'app'; /** * Determines the current asset version. * */ public function version(Request $request) { return parent::version($request); } public function share(Request $request) { return array_merge(parent::share($request), [ 'csrf_token' => csrf_token(),
            'flash' => [
                'success' => fn () => $request->session()->get('success')
            ],
            'auth.user' => fn () => $request->user() ? $request->user()->only('id', 'name', 'email') : null
        ]);
    }
}

At this point let’s go a head and focus on updating each component separately.
Registration

Open resourceshttps://www.onlinecode.org/js/Pages/Auth/Register.vue and update like so:

<template>
        <app-header></app-header>
        

<div class="row">
            

<div class="col-md-6 offset-md-3">
                

<form method="post" @submit.prevent="submit">
                    

<h2 class="text-center">Create New Account for Laravel 9 CRUD Using Inertiajs and Vuejs 3</h2>


                    <errors-and-messages :errors="errors"></errors-and-messages>
                    

<div class="form-group">
                        <label for="name">Name</label>
                        <input type="text" class="form-control" name="name" id="name" v-model="form.name" />
                    </div>


                    

<div class="form-group">
                        <label for="email">Email</label>
                        <input type="text" class="form-control" name="email" id="email" v-model="form.email" />
                    </div>


                    

<div class="form-group">
                        <label for="password">Password</label>
                        <input type="password" class="form-control" name="password" id="password" v-model="form.password" />
                    </div>


                    <input type="submit" class="btn btn-primary btn-block" value="Register" />
                </form>


            </div>


        </div>


</template>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0Aimport%20AppHeader%20from%20%22..%2F..%2FPartials%2FAppHeader%22%3B%0Aimport%20ErrorsAndMessages%20from%20%22..%2F..%2FPartials%2FErrorsAndMessages%22%3B%0Aimport%20%7Binject%2C%20reactive%7D%20from%20%22vue%22%3B%0Aimport%20%7BusePage%7D%20from%20%22%40inertiajs%2Finertia-vue3%22%3B%0Aimport%20%7BInertia%7D%20from%20%22%40inertiajs%2Finertia%22%3B%0Aexport%20default%20%7B%0A%20%20%20%20name%3A%20%22Register%22%2C%0A%20%20%20%20components%3A%20%7B%0A%20%20%20%20%20%20%20%20ErrorsAndMessages%2C%0A%20%20%20%20%20%20%20%20AppHeader%0A%20%20%20%20%7D%2C%0A%20%20%20%20props%3A%20%7B%0A%20%20%20%20%20%20%20%20errors%3A%20Object%0A%20%20%20%20%7D%2C%0A%20%20%20%20setup()%20%7B%0A%20%20%20%20%20%20%20%20const%20form%20%3D%20reactive(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20email%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20password%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_token%3A%20usePage().props.value.csrf_token%0A%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20const%20route%20%3D%20inject('%24route')%3B%0A%20%20%20%20%20%20%20%20function%20submit()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Inertia.post(route('register')%2C%20form)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20form%2C%20submit%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20scoped%3E%0A%20%20%20%20form%20%7B%0A%20%20%20%20%20%20%20%20margin-top%3A%2020px%3B%0A%20%20%20%20%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

This is the registration form, as you see we are including other partial components which are the and we will create them below. Next i used Vue 3 composition api through the use of the setup() function, to learn more about Vue 3 composition Api click this url.

Inside the setup() function we do a lot of stuff. First we declared the form object which contains the name, email and password and the _token as a reactive object. When declaring an object inside reactive then it will perform well on two way model binding.

To retrieve any prop from the backend you can use usePage().props.value.propname as we done for the csrf_token prop above.

Then i declared the submit() function that will submit the form to the server. The function makes a post request using Inertia.post(url, params) function. There are also other functions like Inertia.get(), Inertia.put() etc. Note also that this is different from making ajax requests because this request treated as a normal server side request. The url here is the register route obtained from route(‘register’) function provided to us by the ziggy package. The setup() function then return the form object and the submit() function.

Finally in the form i binded the form fields using v-model passing the form object and accessing each field property separately, then i submit the form by listening of @submit event on the form element.

Now after completing the vue component let’s update the server side code, this will be in app/Http/Controllers/Auth/RegisterController.php

<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Redirect; use Inertia\Inertia; class RegisterController extends Controller { public function showRegisterForm() { return Inertia::render('Auth/Register'); } public function register(Request $request) { $this->validate($request, [
            'name' => ['required', 'max:100'],
            'email' => ['required', 'email', 'unique:users'],
            'password' => ['required', 'min:4']
        ]);
        $user = new User();
        $user->name = $request->input("name");
        $user->email = $request->input("email");
        $user->password = Hash::make($request->input("password"));
        $user->save();
        $request->session()->flash('success', 'User registered successfully! you can sign in now');
        return Redirect::route('showLoginForm');
    }
}

In the register() method i validate the incoming request, then saving the user, after that i trigger a success flash message using laravel $request->session()->flash(), finally i redirect the user to the login page. As notable here we make the redirection and the flash message using the normal laravel way and inertiajs will translate this under the hood.

Partial Components

resourceshttps://www.onlinecode.org/js/Partials/AppHeader.vue

<template>
    

<header>
        

<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
            <inertia-link :href="$route('post.index')" class="navbar-brand">Inertia CRUD</inertia-link>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            

<div class="collapse navbar-collapse" id="navbarSupportedContent">
                

<ul class="navbar-nav mr-auto">
                    

<li class="nav-item active">
                        <inertia-link :href="$route('post.index')" class="nav-link">Home</inertia-link>
                    </li>


                    

<li class="nav-item">
                        <inertia-link :href="$route('post.create')" class="nav-link">Create Post</inertia-link>
                    </li>


                </ul>


            </div>


            

<div class="navbar-collapse collapse order-3 dual-collapse2">
                

<ul class="navbar-nav ml-auto">
                    

<li class="nav-item" v-if="!user">
                        <inertia-link :href="$route('showLoginForm')" class="nav-link">Login</inertia-link>
                    </li>


                    

<li class="nav-item" v-if="!user">
                        <inertia-link :href="$route('showRegisterForm')" class="nav-link">Register</inertia-link>
                    </li>


                    

<li class="nav-item" v-if="user">
                        <span class="navbar-text" v-if="user">
                            Logged in as {{user.name}}
                        </span>
                        <inertia-link :href="$route('logout')" as="button" method="post" class="nav-link logout-link" style="display: inline" type="button">Logout</inertia-link>
                    </li>


                </ul>


            </div>


        </nav>


    </header>


</template>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0Aimport%20%7Bcomputed%7D%20from%20%22vue%22%3B%0Aimport%20%7BusePage%7D%20from%20%22%40inertiajs%2Finertia-vue3%22%3B%0Aexport%20default%20%7B%0A%20%20%20%20name%3A%20%22AppHeader%22%2C%0A%20%20%20%20setup()%20%7B%0A%20%20%20%20%20%20%20%20const%20user%20%3D%20computed(()%20%3D%3E%20usePage().props.value.auth.user)%3B%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20user%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

resourceshttps://www.onlinecode.org/js/Partials/ErrorsAndMessages.vue
<template>
    

<ul class="alert alert-danger" v-if="errors && Object.keys(errors).length > 0">
        

<li v-for="error in errors">{{error}}</li>


    </ul>


    

<div class="alert alert-success" v-if="$page.props.flash.success">
        {{$page.props.flash.success}}
    </div>


</template>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0Aexport%20default%20%7B%0A%20%20%20%20name%3A%20%22ErrorsAndMessages%22%2C%0A%20%20%20%20props%3A%20%5B%22errors%22%5D%0A%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

In the same way let’s continue completing the other pages.

Login for Laravel 9 CRUD Using Inertiajs and Vuejs 3

Open and update resourceshttps://www.onlinecode.org/js/Pages/Auth/Login.vue

<template>
        <app-header></app-header>
        

<div class="row">
            

<div class="col-md-6 offset-md-3">
                

<form method="post" @submit.prevent="submit">
                    

<h2 class="text-center">Sign In</h2>


                    <errors-and-messages :errors="errors"></errors-and-messages>
                    

<div class="form-group">
                        <label for="email">Email</label>
                        <input type="text" class="form-control" name="email" id="email" v-model="form.email" />
                    </div>


                    

<div class="form-group">
                        <label for="password">Password</label>
                        <input type="password" class="form-control" name="password" id="password" v-model="form.password" />
                    </div>


                    <input type="submit" class="btn btn-primary btn-block" value="Login" />
                </form>


            </div>


        </div>


</template>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0A%20%20%20%20import%20AppHeader%20from%20%22..%2F..%2FPartials%2FAppHeader%22%3B%0A%20%20%20%20import%20ErrorsAndMessages%20from%20%22..%2F..%2FPartials%2FErrorsAndMessages%22%3B%0A%20%20%20%20import%20%7BInertia%7D%20from%20%22%40inertiajs%2Finertia%22%3B%0A%20%20%20%20import%20%7B%20usePage%20%7D%20from%20'%40inertiajs%2Finertia-vue3'%0A%20%20%20%20import%20%7Breactive%2Cinject%7D%20from%20'vue'%3B%0A%20%20%20%20export%20default%20%7B%0A%20%20%20%20%20%20%20%20components%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20ErrorsAndMessages%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20AppHeader%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20name%3A%20%22Login%22%2C%0A%20%20%20%20%20%20%20%20props%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20errors%3A%20Object%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20setup()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20form%20%3D%20reactive(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20email%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20password%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_token%3A%20usePage().props.value.csrf_token%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20route%20%3D%20inject('%24route')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20function%20submit()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Inertia.post(route('login')%2C%20form)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20form%2C%20submit%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20scoped%3E%0A%20%20%20%20form%20%7B%0A%20%20%20%20%20%20%20%20margin-top%3A%2020px%3B%0A%20%20%20%20%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

As you see above i am doing the same thing as the register, now we need to update the login controller.

app/Http/Controllers/Auth/LoginController.php

<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Inertia\Inertia; class LoginController extends Controller { public function showLoginForm() { return Inertia::render('Auth/Login'); } public function authenticate(Request $request) { $credentials = $this->validate($request, [
            'email' => ['required', 'email'],
            'password' => ['required']
        ]);
        if(Auth::attempt($credentials)) {
            $request->session()->regenerate();
            return redirect()->intended('/');
        }
        return back()->withErrors([
            'email' => 'The provided credentials do not match our records.',
        ]);
    }
    public function logout(Request $request)
    {
        Auth::logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();
        return redirect('/');
    }
}

Posts Manipulation

Create Post Component

Update resourceshttps://www.onlinecode.org/js/Pages/Posts/Create.vue

<template>
    <app-header></app-header>
    

<div class="row">
        

<div class="col-md-6 offset-md-3">
            

<form method="post" @submit.prevent="submit">
                

<h2 class="text-left">Create Post</h2>


                <errors-and-messages :errors="errors"></errors-and-messages>
                

<div class="form-group">
                    <label for="title">Title</label>
                    <input type="text" class="form-control" name="title" id="title" v-model="form.title" />
                </div>


                

<div class="form-group">
                    <label for="content">Content</label>
                    <textarea id="content" name="content" class="form-control" v-model="form.content"></textarea>
                </div>


                

<div class="form-group">
                    <label for="image">Image</label>
                    <input type="file" id="image" name="image" class="form-control" @change="selectFile">
                </div>


                <input type="submit" class="btn btn-primary btn-block" value="Save" />
            </form>


        </div>


    </div>


</template>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0Aimport%20AppHeader%20from%20%22..%2F..%2FPartials%2FAppHeader%22%3B%0Aimport%20ErrorsAndMessages%20from%20%22..%2F..%2FPartials%2FErrorsAndMessages%22%3B%0Aimport%20%7Binject%2C%20reactive%7D%20from%20%22vue%22%3B%0Aimport%20%7BInertia%7D%20from%20%22%40inertiajs%2Finertia%22%3B%0Aimport%20%7BusePage%7D%20from%20%22%40inertiajs%2Finertia-vue3%22%3B%0Aexport%20default%20%7B%0A%20%20%20%20name%3A%20%22Create%22%2C%0A%20%20%20%20components%3A%20%7B%0A%20%20%20%20%20%20%20%20ErrorsAndMessages%2C%0A%20%20%20%20%20%20%20%20AppHeader%0A%20%20%20%20%7D%2C%0A%20%20%20%20props%3A%20%7B%0A%20%20%20%20%20%20%20%20errors%3A%20Object%0A%20%20%20%20%7D%2C%0A%20%20%20%20setup()%20%7B%0A%20%20%20%20%20%20%20%20const%20form%20%3D%20reactive(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20content%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20image%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_token%3A%20usePage().props.value.csrf_token%0A%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20const%20route%20%3D%20inject('%24route')%3B%0A%20%20%20%20%20%20%20%20function%20selectFile(%24event)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20form.image%20%3D%20%24event.target.files%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20function%20submit()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Inertia.post(route('post.store')%2C%20form%2C%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20forceFormData%3A%20true%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20form%2C%20submit%2C%20selectFile%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20scoped%3E%0A%20%20%20%20form%20%7B%0A%20%20%20%20%20%20%20%20margin-top%3A%2020px%3B%0A%20%20%20%20%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />


    List Posts Component

Update resourceshttps://www.onlinecode.org/js/Pages/Posts/Index.vue
<template>
        <app-header></app-header>
        

<div class="row">
            

<div class="col-md-12" style="margin-top: 20px">
                

<h2 class="text-left">All Posts</h2>


                <errors-and-messages :errors="errors"></errors-and-messages>
                

<div v-if="posts.data.length >
 0">
                    

<div class="col-md-10 article" v-for="post in posts.data" :key="post.id">
                        

<h4>{{post.title}}</h4>


                        <img v-if="post.image_url" width="300" height="250" :src="post.image_url" class="pull-left img-responsive thumb margin10 img-thumbnail">
                        

<article>
                            

                                {{ post.content }}
                            

                        </article>


                        <inertia-link :href="$route('post.edit', {id: post.id})" class="btn btn-primary pull-right action-btn">Edit Post</inertia-link>
                        <a href="javascript:void(0);" class="btn btn-warning pull-right action-btn" @click.prevent="deletePost(post.id)"><i class="fas fa-trash-alt"></i> Delete Post</a>
                    </div>


                    <!-- Pagination links-->
                    

<nav aria-label="Page navigation" v-if="posts.total > posts.per_page" style="margin-top: 20px">
                        

<ul class="pagination">
                            <!-- Previous link -->
                            

<li :class="'page-item' + (posts.links[0].url == null ? ' disabled' : '')">
                                <inertia-link :href="posts.links[0].url == null ? '#' : posts.links[0].url" class="page-link" v-html="posts.links[0].label"></inertia-link>
                            </li>


                            <!-- Numbers -->
                            

<li v-for="item in numberLinks" :class="'page-item' + (item.active ? ' disabled' : '')">
                                <inertia-link :href="item.active ? '#' : item.url" class="page-link" v-html="item.label"></inertia-link>
                            </li>


                            <!-- Next link -->
                            

<li :class="'page-item' + (posts.links[posts.links.length - 1].url == null ? ' disabled' : '')">
                                <inertia-link :href="posts.links[posts.links.length - 1].url == null ? '#' : posts.links[posts.links.length - 1].url" class="page-link" v-html="posts.links[posts.links.length - 1].label"></inertia-link>
                            </li>


                        </ul>


                    </nav>


                </div>


                

<div class="text-center" v-else>
                    No posts found! <inertia-link :href="$route('post.create')">Create Post</inertia-link>
                </div>


            </div>


        </div>


</template>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0Aimport%20AppHeader%20from%20%22..%2F..%2FPartials%2FAppHeader%22%3B%0Aimport%20ErrorsAndMessages%20from%20%22..%2F..%2FPartials%2FErrorsAndMessages%22%3B%0Aimport%20%7BusePage%7D%20from%20%22%40inertiajs%2Finertia-vue3%22%3B%0Aimport%20%7BInertia%7D%20from%20%22%40inertiajs%2Finertia%22%3B%0Aimport%20%7Bcomputed%2C%20inject%7D%20from%20%22vue%22%3B%0Aexport%20default%20%7B%0A%20%20%20%20name%3A%20%22Posts%22%2C%0A%20%20%20%20components%3A%20%7B%0A%20%20%20%20%20%20%20%20ErrorsAndMessages%2C%0A%20%20%20%20%20%20%20%20AppHeader%0A%20%20%20%20%7D%2C%0A%20%20%20%20props%3A%20%7B%0A%20%20%20%20%20%20%20%20errors%3A%20Object%0A%20%20%20%20%7D%2C%0A%20%20%20%20setup()%20%7B%0A%20%20%20%20%20%20%20%20const%20route%20%3D%20inject('%24route')%3B%0A%20%20%20%20%20%20%20%20const%20deletePost%20%3D%20(id)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!confirm('Are%20you%20sure%3F'))%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20Inertia.delete(route('post.destroy'%2C%20%7Bid%7D))%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20const%20posts%20%3D%20computed(()%20%3D%3E%20usePage().props.value.posts)%3B%0A%20%20%20%20%20%20%20%20const%20numberLinks%20%3D%20posts.value.links.filter((v%2C%20i)%20%3D%3E%20i%20%3E%200%20%26%26%20i%20%3C%20posts.value.links.length%20-%201)%3B%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20posts%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20deletePost%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20numberLinks%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20scoped%3E%0A%20%20%20%20.action-btn%20%7B%0A%20%20%20%20%20%20%20%20margin-left%3A%2012px%3B%0A%20%20%20%20%20%20%20%20font-size%3A%2013px%3B%0A%20%20%20%20%7D%0A%20%20%20%20.article%20%7B%0A%20%20%20%20%20%20%20%20margin-top%3A%2020px%3B%0A%20%20%20%20%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />


    Edit Post Component

Update resourceshttps://www.onlinecode.org/js/Pages/Posts/Edit.vue
<template>
    <app-header></app-header>
    

<div class="row">
        

<div class="col-md-6 offset-md-3">
            

<form method="post" @submit.prevent="submit">
                

<h2 class="text-left">Update Post</h2>


                <errors-and-messages :errors="errors"></errors-and-messages>
                

<div class="form-group">
                    <label for="title">Title</label>
                    <input type="text" class="form-control" name="title" id="title" v-model="form.title" />
                </div>


                

<div class="form-group">
                    <label for="content">Content</label>
                    <textarea id="content" name="content" class="form-control" v-model="form.content"></textarea>
                </div>


                

<div class="form-group">
                    <label for="image">Image</label>
                    <img :src="image_url" width="100" height="90" v-if="image_url" />
                    <input type="file" id="image" name="image" class="form-control" @change="selectFile">
                </div>


                <input type="submit" class="btn btn-primary btn-block" value="Update" />
            </form>


        </div>


    </div>


</template>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0Aimport%20AppHeader%20from%20%22..%2F..%2FPartials%2FAppHeader%22%3B%0Aimport%20%7Binject%2C%20reactive%7D%20from%20%22vue%22%3B%0Aimport%20%7BInertia%7D%20from%20%22%40inertiajs%2Finertia%22%3B%0Aimport%20ErrorsAndMessages%20from%20%22..%2F..%2FPartials%2FErrorsAndMessages%22%3B%0Aimport%20%7BusePage%7D%20from%20%22%40inertiajs%2Finertia-vue3%22%3B%0Aexport%20default%20%7B%0A%20%20%20%20name%3A%20%22Edit%22%2C%0A%20%20%20%20components%3A%20%7B%0A%20%20%20%20%20%20%20%20ErrorsAndMessages%2C%0A%20%20%20%20%20%20%20%20AppHeader%0A%20%20%20%20%7D%2C%0A%20%20%20%20props%3A%20%7B%0A%20%20%20%20%20%20%20%20errors%3A%20Object%0A%20%20%20%20%7D%2C%0A%20%20%20%20setup()%20%7B%0A%20%20%20%20%20%20%20%20const%20form%20%3D%20reactive(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20content%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20image%3A%20null%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_token%3A%20usePage().props.value.csrf_token%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_method%3A%20%22PUT%22%0A%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20retrieve%20post%20prop%0A%20%20%20%20%20%20%20%20const%20%7Btitle%2C%20content%2C%20image_url%2C%20id%20%7D%20%3D%20usePage().props.value.post%3B%0A%20%20%20%20%20%20%20%20form.title%20%3D%20title%3B%0A%20%20%20%20%20%20%20%20form.content%20%3D%20content%3B%0A%20%20%20%20%20%20%20%20const%20route%20%3D%20inject('%24route')%3B%0A%20%20%20%20%20%20%20%20function%20selectFile(%24event)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20form.image%20%3D%20%24event.target.files%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20function%20submit()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Inertia.post(route('post.update'%2C%20%7B'id'%3A%20id%7D)%2C%20form%2C%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20forceFormData%3A%20true%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20form%2C%20submit%2C%20selectFile%2C%20image_url%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20scoped%3E%0A%20%20%20%20form%20%7B%0A%20%20%20%20%20%20%20%20margin-top%3A%2020px%3B%0A%20%20%20%20%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

Update The Posts Controller

app/Http/Controllers/PostsController.php

<?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Inertia\Inertia; class PostsController extends Controller { public function __construct() { $this->middleware("auth")->except(["index"]);
    }
    public function index()
    {
        return Inertia::render('Posts/Index', [
            "posts" => Post::orderBy('id', 'DESC')->paginate(10)
        ]);
    }
    public function create()
    {
        return Inertia::render('Posts/Create');
    }
    public function store(Request $request)
    {
        $this->getValidate($request);
        $post = new Post();
        $post->title = $request->input('title');
        $post->content = $request->input('content');
        if($request->file('image')) {
            $post->image = $this->upload($request);
        }
        $post->save();
        $request->session()->flash('success', 'Post created successfully!');
        return redirect()->route('post.index');
    }
    public function edit($id)
    {
        return Inertia::render('Posts/Edit', [
            'post' => Post::findOrFail($id)
        ]);
    }
    public function update(Request $request, $id)
    {
        $this->getValidate($request, $id);
        $post = Post::find($id);
        $post->title = $request->input('title');
        $post->content = $request->input('content');
        if($request->file('image')) {
            $post->image = $this->upload($request);
        }
        $post->save();
        $request->session()->flash('success', 'Post updated successfully!');
        return redirect()->route('post.index');
    }
    public function destroy(Request $request, $id)
    {
        Post::find($id)->delete();
        $request->session()->flash('success', 'Post deleted successfully!');
        return redirect()->route('post.index');
    }
    /**
     * @param Request $request
     * @throws \Illuminate\Validation\ValidationException
     */
    private function getValidate(Request $request, $id = null): void
    {
        $data = [
            'title' => 'required',
            'content' => 'required'
        ];
        $this->validate($request, $data);
    }
    private function upload($request)
    {
        $image = $request->file('image');
        $imageName = md5(uniqid()) . "." . $image->getClientOriginalExtension();
        $image->move(public_path('uploads'), $imageName);
        return $imageName;
    }
}

Don’t forget to create a writable uploads/ directory inside public/ directory for storing uploaded images.

Update The Post Model

app/Models/Post.php

<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; protected $appends = ['image_url']; function getImageUrlAttribute() { return $this->image ? url('/uploads/' . $this->image) : "";
    }
}

Now launch the application but first run this command to build the vue components:

npm run dev

Then navigate to http://localhost/laravel-inertia-crud/public/

Conclusion for Laravel 9 CRUD Using Inertiajs and Vuejs 3

Hope this code and post will helped you for implement Laravel 9 CRUD Using Inertiajs and Vuejs 3. if you need any help or any feedback give it in comment section or you have good idea about this post you can give it comment section. Your comment will help us for help you more and improve us.

For More Info See :: laravel And github

Leave a Comment

Your email address will not be published. Required fields are marked *

  +  37  =  43

We're accepting well-written guest posts and this is a great opportunity to collaborate : Contact US