<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Laravel - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Laravel - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 21 May 2026 16:11:20 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/laravel/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Learn Laravel by Building a Medium Clone ]]>
                </title>
                <description>
                    <![CDATA[ Laravel is a popular PHP web development framework known for its elegant syntax, developer-friendly tools, and built-in features for routing, authentication, and database interactions. It's an excellent choice for both beginners and experienced devel... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-laravel-by-building-a-medium-clone/</link>
                <guid isPermaLink="false">68016b821cf57215a14fca5c</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 17 Apr 2025 20:58:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744923486796/26044ea1-7eaf-4e33-b20f-8519ea197d44.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Laravel is a popular PHP web development framework known for its elegant syntax, developer-friendly tools, and built-in features for routing, authentication, and database interactions. It's an excellent choice for both beginners and experienced developers looking to create full-featured web applications quickly and efficiently.</p>
<p>We just published a course on the freeCodeCamp.org YouTube channel that will teach you how to build a fully-functional Medium clone using the Laravel PHP framework. Taught by The Codeholic, this in-depth, hands-on course takes you from setting up your Laravel environment to developing advanced features like user profiles, following/unfollowing other users, and managing blog posts. By the end, you'll not only understand Laravel's core features but also how to put them together to build a real-world social publishing platform.</p>
<p>The course kicks off with an introduction and a demo of the final app, then walks you through the entire development process. You'll learn how to set up a Laravel project, use Artisan (Laravel’s command-line tool), and configure your app. From there, you’ll implement user registration and login using Laravel Breeze, enable email verification, and work with Tinker to interact with your database.</p>
<p>Throughout the course, you'll explore key Laravel concepts such as models, migrations, factories, and seeders, and you'll make use of Blade templating, components, and layouts to build dynamic and reusable UI elements. You’ll also build critical features like post creation and editing, pagination, category filtering, image uploads and resizing, and user interactions such as liking posts and following other users.</p>
<p>By following along, you'll gain a deep understanding of Laravel’s MVC architecture, database relationships, form validation, and more.</p>
<p>Here is a list of sections in the course:</p>
<ul>
<li><p>Intro &amp; Demo</p>
</li>
<li><p>Setup Environment &amp; Create Project</p>
</li>
<li><p>Explore Directory Structure</p>
</li>
<li><p>Introduce Artisan</p>
</li>
<li><p>Laravel Configuration</p>
</li>
<li><p>Create Basic Route</p>
</li>
<li><p>Registration &amp; Login (Laravel Breeze)</p>
</li>
<li><p>Email Verification</p>
</li>
<li><p>Tinker</p>
</li>
<li><p>Models &amp; Migrations</p>
</li>
<li><p>Factories &amp; Seeders</p>
</li>
<li><p>Output Categories &amp; Posts</p>
</li>
<li><p>Pagination</p>
</li>
<li><p>Blade Directives</p>
</li>
<li><p>Components</p>
</li>
<li><p>Layouts</p>
</li>
<li><p>Create New Post</p>
</li>
<li><p>Profile Update Page</p>
</li>
<li><p>Post Details Page</p>
</li>
<li><p>User's Public Profile Page</p>
</li>
<li><p>Implement Follow/Unfollow</p>
</li>
<li><p>Implement Like/Unlike Post</p>
</li>
<li><p>Filtering posts by Category</p>
</li>
<li><p>Show only posts from following users</p>
</li>
<li><p>Image Resize</p>
</li>
<li><p>Home Page Optimization</p>
</li>
<li><p>Editing and Deleting Posts</p>
</li>
<li><p>Make image optional during Update</p>
</li>
<li><p>Show post owner in post item</p>
</li>
<li><p>Use Published At field to publish posts</p>
</li>
<li><p>Fix profile page avatar</p>
</li>
<li><p>Conclusion</p>
</li>
</ul>
<p>Check out the full course for free on the <a target="_blank" href="https://youtu.be/MG1kt_wiIz0">freeCodeCamp.org YouTube channel</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/MG1kt_wiIz0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Code a full stack Instagram Clone with Laravel and MongoDB ]]>
                </title>
                <description>
                    <![CDATA[ Are you ready to transform your web development skills by building a complex, real-world application? We just posted a course on the freeCodeCamp.org YouTube channel that will teach you how to use Laravel and MongoDB to create a full stack Instagram ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/code-a-full-stack-instagram-clone-with-laravel-and-mongodb/</link>
                <guid isPermaLink="false">67ed4efef1d155fc22688c46</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 02 Apr 2025 14:51:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743605110164/9ae2ed21-4956-4b0e-b7c4-4ccc14e9df9f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you ready to transform your web development skills by building a complex, real-world application? We just posted a course on the freeCodeCamp.org YouTube channel that will teach you how to use Laravel and MongoDB to create a full stack Instagram clone.</p>
<p>I created this course myself. I’ll teach you how to code a feature-rich Instagram clone from the ground up, leveraging the power of the Laravel framework and the flexibility of the MongoDB database.</p>
<h3 id="heading-why-build-an-instagram-clone">Why Build an Instagram Clone?</h3>
<p>Building a clone of a popular application like Instagram forces you to tackle real-world challenges: managing user accounts, handling media uploads, implementing social interactions like likes and comments, and structuring data efficiently. Completing such a project demonstrates a strong grasp of full-stack development principles and provides tangible proof of your abilities to potential employers or clients.</p>
<h3 id="heading-the-tech-stack">The Tech Stack</h3>
<p>This tutorial pairs Laravel, a leading PHP framework, with MongoDB, a popular NoSQL database. This combination forms a very versatile technology stack for modern web development. It offers exceptional flexibility, scalability, and developer experience.</p>
<ul>
<li><p><strong>Laravel:</strong> Laravel provides a robust structure for building web applications. It follows the Model-View-Controller (MVC) architectural pattern, promoting clean, organized, and maintainable code. Laravel significantly simplifies common web development tasks such as routing (directing web requests), authentication (handling user login and registration), and interacting with databases through its powerful Object-Relational Mapper (ORM), Eloquent.</p>
</li>
<li><p><strong>MongoDB:</strong> Unlike traditional SQL databases that use tables and predefined schemas, MongoDB is a document database. It stores data in flexible, JSON-like documents, making it incredibly adaptable. This is ideal for applications where data structures might evolve over time. MongoDB's schema-less nature allows you to easily add new fields (like adding a 'username' or 'bio' to a user profile after initial creation, as demonstrated in the tutorial) without complex database migrations.</p>
</li>
</ul>
<p>Combining Laravel's structured development approach with MongoDB's flexible data storage provides the best of both worlds, especially for applications like an Instagram clone that manage diverse content types (profiles, posts, images, comments, likes) and their intricate relationships.</p>
<h3 id="heading-key-concepts-and-features">Key Concepts and Features</h3>
<p>Throughout the tutorial, you will gain hands-on experience with:</p>
<ol>
<li><p><strong>Full-Stack Integration:</strong> Learn how the frontend (what the user sees and interacts with) and the backend (server-side logic and database) work together seamlessly.</p>
</li>
<li><p><strong>Laravel Fundamentals:</strong> Master core Laravel concepts including the MVC pattern, defining routes (<code>web.php</code>), creating controllers to handle logic, building Eloquent models to interact with the database, and crafting user interfaces with the Blade templating engine.</p>
</li>
<li><p><strong>MongoDB with Laravel:</strong> Discover how to configure Laravel to work with MongoDB, install necessary drivers and packages (like <code>mongodb/laravel-mongodb</code>), and utilize Eloquent models specifically adapted for MongoDB collections and documents. You'll learn how relationships (like a user having many posts, or a post having many comments) are handled in a NoSQL context within Laravel.</p>
</li>
<li><p><strong>User Authentication &amp; Profiles:</strong> Implement secure user registration and login functionality using Laravel's built-in features. Build user profiles, allowing users to update their information (name, username, bio) and upload profile pictures.</p>
</li>
<li><p><strong>Core Social Features:</strong> Develop the essential functionalities of a social platform: creating posts with images and captions, displaying a feed of posts, implementing a like/unlike system, and adding/deleting comments on posts.</p>
</li>
<li><p><strong>Image Handling:</strong> Learn how to manage file uploads, specifically storing user-uploaded images (for posts and profiles) using Laravel's file storage system, initially locally and understanding how to potentially extend this to cloud storage like AWS S3.</p>
</li>
<li><p><strong>Database Management with MongoDB Atlas:</strong> Go beyond local development by learning how to set up a free cloud database cluster on MongoDB Atlas, configure security (users and network access), and connect your Laravel application to this cloud database – a crucial skill for deploying real-world applications.</p>
</li>
<li><p><strong>Frontend Styling:</strong> Utilize Bootstrap 5 (via CDN) to style the application, creating a clean and responsive user interface that mimics the look and feel of Instagram.</p>
</li>
</ol>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>To get the most out of this tutorial, you should have a foundational understanding of:</p>
<ul>
<li><p>PHP basics</p>
</li>
<li><p>Web fundamentals (HTML, CSS, basic JavaScript, HTTP concepts)</p>
</li>
<li><p>Command-line usage</p>
</li>
<li><p>You'll also need PHP, Composer (PHP package manager), and MongoDB installed on your local machine. The tutorial briefly covers installation pointers for different operating systems.</p>
</li>
</ul>
<p>Ready to build your own Instagram clone and significantly boost your web development expertise? Watch the full course on <a target="_blank" href="https://youtu.be/VK-2j5CNsvM">the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VK-2j5CNsvM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Google Authentication in Laravel Applications ]]>
                </title>
                <description>
                    <![CDATA[ In this digital world, it’s important for your applications to have a smooth and secure authentication process. This helps improve user experience and the overall security of your apps. Google Authentication is among the most trusted and convenient w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-google-auth-in-laravel-apps/</link>
                <guid isPermaLink="false">674f227adf280a40b7047b7f</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ google authenticator ]]>
                    </category>
                
                    <category>
                        <![CDATA[ google auth ]]>
                    </category>
                
                    <category>
                        <![CDATA[ socialite  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Abhijeet Dave ]]>
                </dc:creator>
                <pubDate>Tue, 03 Dec 2024 15:23:38 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732711351776/931a1ade-e652-4a0b-a16e-925482128fc0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this digital world, it’s important for your applications to have a smooth and secure authentication process. This helps improve user experience and the overall security of your apps.</p>
<p>Google Authentication is among the most trusted and convenient ways for users to log into a site using their Google account. And it means that they don’t have to remember yet another username and password.</p>
<p>Integrating Google OAuth into your <a target="_blank" href="https://laravel.com/">Laravel</a> application simplifies the login process, encourages user engagement, and boosts the credibility of your platform. In this tutorial, I’ll guide you through the steps of implementing Google Authentication in a Laravel application. We’ll go from setting up the Google API credentials to configuring Laravel’s Socialite package.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-using-google-auth-in-a-laravel-app">Benefits of Using Google Auth in a Laravel App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-laravel-google-login">How to Set Up Laravel Google Login</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before you begin, make sure you have the following prerequisites:</p>
<ol>
<li><p>Laravel 11</p>
</li>
<li><p>A Google Developer account.</p>
</li>
<li><p>Basic knowledge of Laravel and authentication.</p>
</li>
<li><p>Composer for managing packages</p>
</li>
</ol>
<p>Once you have these prerequisites ready, you’re all set to dive into integrating Google Authentication into your Laravel app.</p>
<h3 id="heading-benefits-of-using-google-auth-in-a-laravel-app">Benefits of Using Google Auth in a Laravel App</h3>
<p>There are many benefits to this set up. A few of them are:</p>
<ul>
<li><p>Simplified integration with Socialite</p>
</li>
<li><p>Seamless user authentication</p>
</li>
<li><p>Improved security</p>
</li>
<li><p>Customizable user flow</p>
</li>
<li><p>Improved scalability</p>
</li>
<li><p>Solid ecosystem support</p>
</li>
<li><p>Easier maintenance</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-laravel-google-login">How to Set Up Laravel Google Login</h2>
<p>Whether you’re working on a personal project or a production-ready application, following these steps will help you smoothly integrate Google Authentication. Let’s get started.</p>
<h3 id="heading-step-1-set-up-a-google-cloud-project">Step 1: Set up a Google Cloud project</h3>
<p>To use Google Authentication in your Laravel application, first you need to configure a Google Cloud project. Follow these steps to set up your project:</p>
<ol>
<li><p>Visit the <a target="_blank" href="https://console.cloud.google.com/">Google Cloud console</a> and log in with your Google account.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733208476305/836ff373-152a-4b99-93f3-c7684591e5c7.png" alt="Laravel auth step 1" class="image--center mx-auto" width="1920" height="968" loading="lazy"></p>
</li>
<li><p>Click on the <strong>“Select a Project”</strong> dropdown in the top navigation bar. In the popup, click on <strong>“New Project”</strong> to create a new project and provide the requested details. Then click on <strong>Create project</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733208488866/409092b9-20b5-4888-9c15-f22eff4226c8.png" alt="Laravel auth create project" class="image--center mx-auto" width="610" height="396" loading="lazy"></p>
</li>
<li><p>Once you create the project, open the console’s left side menu and select <strong>APIs &amp; Services &gt; Credentials</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733208517526/9b7df08c-8e8f-4db4-b297-a3f320edd0f0.png" alt="APIs &amp; Credentials" class="image--center mx-auto" width="660" height="966" loading="lazy"></p>
</li>
<li><p>On the Credentials page, click <strong>Create Credentials</strong> &gt; <strong>OAuth Client ID</strong>.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733208549370/6dcd64f1-1fe4-4a8b-b37e-f74b38bd694a.png" alt="OAuth Client ID" class="image--center mx-auto" width="1915" height="626" loading="lazy"></p>
</li>
<li><p>If this is your first time creating a client ID, it will ask you to configure the consent screen. You can configure your consent screen by clicking <strong>Configure</strong> <strong>Consent Screen</strong>. If you have already configured the consent screen, you can skip this step.</p>
<ul>
<li><p>Select <strong>External</strong> if your app is for public use, or <strong>Internal</strong> if it’s limited to users within your Google Workspace organization.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733208660871/53aaf9a2-74d4-4cca-b4e5-9c5cfa9b3066.png" alt="OAuth consent" class="image--center mx-auto" width="666" height="724" loading="lazy"></p>
</li>
<li><p>Fill out the required details, like the <strong>app name</strong>, <strong>user support email</strong>, and any branding information. Click <strong>Save and Continue</strong>.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733208766209/4f458336-9a2a-4238-af89-de954ed2bcf4.png" alt="OAuth Consent Screen" class="image--center mx-auto" width="609" height="753" loading="lazy"></p>
</li>
</ul>
</li>
</ol>
<p>    After configuring the consent screen, return to the <strong>Credentials</strong> page and select <strong>OAuth Client ID</strong> again.</p>
<ol start="6">
<li><p>Choose the <strong>Application Type</strong> as <strong>Web Application</strong> and provide a name for client credentials (for example, Laravel Social Login).</p>
</li>
<li><p>Under <strong>Authorized Redirect URIs</strong>, add the callback URL for your application:</p>
<ul>
<li><p>Example: <code>http://your-app-url.com/callback/google</code></p>
</li>
<li><p>If you're testing locally, use: <a target="_blank" href="http://127.0.0.1:8000/api/auth/google/callback">http://127.0.0.1:8000/api/auth/google/callback</a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733208840271/b7c309bc-4880-481b-acda-c0fecf4a0ee5.png" alt="OAuth client ID" class="image--center mx-auto" width="604" height="887" loading="lazy"></p>
</li>
</ul>
</li>
<li><p>Click on <strong>Create</strong>, and Google will generate a <strong>Client ID</strong> and <strong>Client Secret</strong> for your project. Save these credentials, as they will be required in the next steps.</p>
</li>
</ol>
<h3 id="heading-step-2-create-a-new-laravel-project-and-install-the-laravel-socialite-package">Step 2: Create a new Laravel project and Install the Laravel Socialite package</h3>
<p>If you don’t have one ready, you can create a new Laravel project using the below command:</p>
<pre><code class="lang-bash">composer create-project --prefer-dist laravel/laravel social-auth-example
</code></pre>
<p>To integrate Google Authentication into a Laravel project, we’ll use <a target="_blank" href="https://laravel.com/docs/11.x/socialite">Laravel Socialite</a>. Socialite is a first-party Laravel package that simplifies OAuth authentication with popular services like Google, Facebook, Twitter, and more.</p>
<p>To install Socialite, open your terminal in the root directory of your Laravel project and run the following command:</p>
<pre><code class="lang-bash">composer require laravel/socialite
</code></pre>
<h3 id="heading-step-3-configure-environment-variables">Step 3: Configure environment variables</h3>
<p>In this step, we will configure our Laravel application to use the Google OAuth credentials that we collected in Step 1.</p>
<p>Locate your <code>.env</code> file in the root directory of your project and add the following environment variables:</p>
<pre><code class="lang-tsx">GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URL=http://your-domain.com/auth/google/callback
</code></pre>
<p>Go ahead and replace all placeholders with secrets.</p>
<p>Let's understand each environment variable one by one:</p>
<ul>
<li><p><code>GOOGLE_CLIENT_ID</code>: A unique identifier for your app, provided by Google.</p>
</li>
<li><p><code>GOOGLE_CLIENT_SECRET</code>: A private key used by your app to authenticate itself securely with Google’s API.</p>
</li>
<li><p><code>GOOGLE_REDIRECT_URL</code>: The URL where Google redirects users after they log in. This should match the redirect URI you specified when creating the credentials in Step 1.</p>
</li>
</ul>
<h3 id="heading-step-4-update-the-config-files">Step 4: Update the config files</h3>
<p>To enable Laravel Socialite to use Google OAuth credentials, we need to configure the provider details in the <code>config/services.php</code> file.</p>
<p>In the <code>services.php</code> file, add the following configuration for the Google provider:</p>
<pre><code class="lang-php"><span class="hljs-string">'google'</span> =&gt; [
    <span class="hljs-string">'client_id'</span> =&gt; env(<span class="hljs-string">'GOOGLE_CLIENT_ID'</span>),        <span class="hljs-comment">// Your Google Client ID</span>
    <span class="hljs-string">'client_secret'</span> =&gt; env(<span class="hljs-string">'GOOGLE_CLIENT_SECRET'</span>), <span class="hljs-comment">// Your Google Client Secret</span>
    <span class="hljs-string">'redirect'</span> =&gt; env(<span class="hljs-string">'GOOGLE_REDIRECT_URL'</span>),      <span class="hljs-comment">// Your Google Redirect URL</span>
]
</code></pre>
<h3 id="heading-step-5-create-controllers-and-routes-for-authentication">Step 5: Create controllers and routes for authentication.</h3>
<p>In this step, we will create a controller to handle Google OAuth redirection and callbacks and set up the necessary routes to trigger these methods.</p>
<p>Run the following Artisan command to generate the <code>GoogleAuthController</code> controller:</p>
<pre><code class="lang-php">php artisan make:controller GoogleAuthController
</code></pre>
<p>This will create a controller at <code>app/Http/Controllers/GoogleAuthController.php</code>.</p>
<p>Replace the contents of the newly created <code>GoogleAuthController.php</code> with the following code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\\<span class="hljs-title">Http</span>\\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\\<span class="hljs-title">Http</span>\\<span class="hljs-title">Controllers</span>\\<span class="hljs-title">Controller</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\\<span class="hljs-title">Models</span>\\<span class="hljs-title">User</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Laravel</span>\\<span class="hljs-title">Socialite</span>\\<span class="hljs-title">Facades</span>\\<span class="hljs-title">Socialite</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\\<span class="hljs-title">Support</span>\\<span class="hljs-title">Facades</span>\\<span class="hljs-title">Auth</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\\<span class="hljs-title">Support</span>\\<span class="hljs-title">Str</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Throwable</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GoogleAuthController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-comment">/**
     * Redirect the user to Google’s OAuth page.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">redirect</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> Socialite::driver(<span class="hljs-string">'google'</span>)-&gt;redirect();
    }

    <span class="hljs-comment">/**
     * Handle the callback from Google.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">callback</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Get the user information from Google</span>
            $user = Socialite::driver(<span class="hljs-string">'google'</span>)-&gt;user();
        } <span class="hljs-keyword">catch</span> (<span class="hljs-built_in">Throwable</span> $e) {
            <span class="hljs-keyword">return</span> redirect(<span class="hljs-string">'/'</span>)-&gt;with(<span class="hljs-string">'error'</span>, <span class="hljs-string">'Google authentication failed.'</span>);
        }

        <span class="hljs-comment">// Check if the user already exists in the database</span>
        $existingUser = User::where(<span class="hljs-string">'email'</span>, $user-&gt;email)-&gt;first();

        <span class="hljs-keyword">if</span> ($existingUser) {
            <span class="hljs-comment">// Log the user in if they already exist</span>
            Auth::login($existingUser);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Otherwise, create a new user and log them in</span>
            $newUser = User::updateOrCreate([
                <span class="hljs-string">'email'</span> =&gt; $user-&gt;email
            ], [
                <span class="hljs-string">'name'</span> =&gt; $user-&gt;name,
                <span class="hljs-string">'password'</span> =&gt; bcrypt(Str::random(<span class="hljs-number">16</span>)), <span class="hljs-comment">// Set a random password</span>
                <span class="hljs-string">'email_verified_at'</span> =&gt; now()
            ]);
            Auth::login($newUser);
        }

        <span class="hljs-comment">// Redirect the user to the dashboard or any other secure page</span>
        <span class="hljs-keyword">return</span> redirect(<span class="hljs-string">'/dashboard'</span>);
    }
}
</code></pre>
<p>This controller contains two functions:</p>
<ol>
<li><p>Redirect: Redirects the user to Google’s OAuth Page.</p>
</li>
<li><p>Callback: Handles the callback from Google and redirects the user to the dashboard or any other secure page.</p>
</li>
</ol>
<p>Let’s define the routes of <code>redirect</code> and <code>callback</code> in the <code>routes/web.php</code> file:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\\<span class="hljs-title">Http</span>\\<span class="hljs-title">Controllers</span>\\<span class="hljs-title">GoogleAuthController</span>;

<span class="hljs-comment">// Route to redirect to Google's OAuth page</span>
Route::get(<span class="hljs-string">'/auth/google/redirect'</span>, [GoogleAuthController::class, <span class="hljs-string">'redirect'</span>])-&gt;name(<span class="hljs-string">'auth.google.redirect'</span>);

<span class="hljs-comment">// Route to handle the callback from Google</span>
Route::get(<span class="hljs-string">'/auth/google/callback'</span>, [GoogleAuthController::class, <span class="hljs-string">'callback'</span>])-&gt;name(<span class="hljs-string">'auth.google.callback'</span>);
</code></pre>
<h3 id="heading-step-6-test-laravel-google-authentication-in-your-project">Step 6: Test Laravel Google authentication in your project.</h3>
<p>We’ve set up Google authentication, so now it’s time to test it to make sure it works seamlessly. In this step, we’ll use a login button that redirects the user to Google’s authentication page and returns them to a protected route upon successful login.</p>
<p>First, we will add the following button that gives users the option to Login With Google:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ route('auth.google.redirect') }}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn bg-blue-100 p-3 shadow-sm border rounded-md text-blue-900"</span>&gt;</span>
    Login with Google 
<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>For testing purposes, I have defined a protected route and a <code>dashboard</code>. This route will only be accessible to authenticated users. After logging in, we will redirect users to this page. Let’s define this route in <code>web.php</code>:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/dashboard'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> view(<span class="hljs-string">'dashboard'</span>);
})-&gt;middleware(<span class="hljs-string">'auth'</span>)-&gt;name(<span class="hljs-string">'dashboard'</span>);
</code></pre>
<p>Next, create a blade view file for the dashboard at <code>resources/views/dashboard.blade.php</code>. Here’s the contents of the dashboard:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Welcome to the dashboard, {{ auth()-&gt;user()-&gt;name }}!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Here, we’re using the <code>auth()-&gt;user()</code> helper to display the logged-in user’s name, which is fetched from the Google account they used to sign in.</p>
<p>Now, Let’s try to log in.</p>
<p>This is the login page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732703980184/ecc90d0d-142b-43fe-8457-1b84a54f62d3.png" alt="Login screen - &quot;Login with Google&quot;" class="image--center mx-auto" width="582" height="193" loading="lazy"></p>
<p>Clicking on the button will redirect you to Google’s consent screen:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732703936372/59f4a5bf-907d-4dda-ba8b-6909cf0a4376.png" alt="Laravel Google auth example - consent screen" class="image--center mx-auto" width="1486" height="923" loading="lazy"></p>
<p>Click on the continue, and you should be logged in to the app. You will be redirected to a screen like below. You can see the welcome message with the user’s name.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732703886846/17ab7939-34c1-4d82-9527-99afb78cf3eb.png" alt="Laravel auth example - welcome screen" class="image--center mx-auto" width="552" height="196" loading="lazy"></p>
<p>That’s it! You’ve successfully implemented and tested Google Authentication in your Laravel project. Now your users can sign in using their Google accounts, enhancing both security and convenience.</p>
<p>To refer to the full implementation, you can find the complete source code for this project on GitHub here: <a target="_blank" href="https://github.com/DeepKumbhare85/social-auth-example"><strong>Google Login Integration for Laravel</strong> - GitHub Repository</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You’ve now set up Google authentication in your Laravel application using Socialite! You can extend this method to include other OAuth providers like Facebook, Twitter, or GitHub by adding additional configurations to the <code>config/services.php</code> file.</p>
<p>Google OAuth integration is a common feature for modern web applications, and Laravel Socialite makes it easy to implement.</p>
<p>In case you need more social login options like GitHub, Twitter, and Facebook, then you can consider ready-to-use Laravel SaaS boilerplates.</p>
<p>Most of the pre-built Laravel SaaS boilerplates offer seamless integration with popular platforms such as Google, GitHub, Facebook, and Twitter. For example, there are some premium and open source resources like:</p>
<ul>
<li><p><a target="_blank" href="https://demos.themeselection.com/jetship-laravel-starter-kit/">Laravel Starter Kit</a> (Premium)</p>
<ul>
<li><p>Based on Tailwind CSS</p>
</li>
<li><p>Comes with One Click Magic Link Setup</p>
</li>
<li><p>Supports various authentication methods including the traditional email/password login</p>
</li>
<li><p>2FA Authentication</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://github.com/miracuthbert/saas-boilerplate">SaaS Boilerplate</a> (Open Source)</p>
<ul>
<li><p>Single Database Multi-tenancy</p>
</li>
<li><p>Developer Panel</p>
</li>
<li><p>Manage Personal Access Tokens</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://github.com/fumeapp/laranuxt">Laranuxt</a> (Open Source)</p>
<ul>
<li><p>Nuxt UI a collection of components built by the NuxtJS team, powered by Tailwind CSS</p>
</li>
<li><p>Authentication library to assist with user sessions and logging in/out</p>
</li>
<li><p>Example Authentication Middleware</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://github.com/alefesouza/laravel-vue-boilerplate">Laravel Vue Boilerplate</a> (Open Source)</p>
<ul>
<li><p>WebSockets with Laravel Echo and Pusher.</p>
</li>
<li><p>Workbox for better PWA development.</p>
</li>
<li><p>Laravel GraphQL</p>
</li>
</ul>
</li>
</ul>
<p>Using one of these Laravel SaaS boilerplates can speed up your workflows as you don’t need to set up everything from scratch.</p>
<p>Special thanks to <a target="_blank" href="https://github.com/DeepKumbhare85">Deep Kumbhare</a>, an experienced Laravel Developer and enthusiast, who has helped me with preparing this article.</p>
<p>I hope this article helps you with setting up Google Login with Laravel.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript Frameworks vs Laravel – Which Should You Choose for Web Development? ]]>
                </title>
                <description>
                    <![CDATA[ For a long time, developers in the JavaScript ecosystem have tried to launch Laravel-like frameworks for JavaScript. We've seen Blitz for NextJS, Adonis for NodeJS, RedwoodJS, and more. All these frameworks tried to build a Laravel for JavaScript. Bu... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/javascript-frameworks-vs-laravel/</link>
                <guid isPermaLink="false">66d45d5a33b83c4378a517b0</guid>
                
                    <category>
                        <![CDATA[ framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Afan Khan ]]>
                </dc:creator>
                <pubDate>Mon, 17 Jun 2024 14:28:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/06/Group-3148.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>For a long time, developers in the JavaScript ecosystem have tried to launch Laravel-like frameworks for JavaScript.</p>
<p>We've seen Blitz for NextJS, Adonis for NodeJS, RedwoodJS, and more. All these frameworks tried to build a Laravel for JavaScript.</p>
<p>But none of them were really successful. Why? We'll figure it out in this article. It's worth going over because beginners learning JavaScript struggle to find an all-in-one solution.</p>
<p>This means it can be tough to choose a framework (or library) in JavaScript when you have so many choices – like React, Vue, Angular, Svelte, NextJS, Meteor, and more.</p>
<p>Meanwhile, PHP offers one simple solution: Laravel. It's a one-stop shop with many frameworks and libraries combined into one. But is it the right option for you?</p>
<p>In this article, I'll dive deeper into why the JavaScript ecosystem doesn't have a Laravel-like framework and whether you should switch to PHP for a one-stop solution like Laravel.</p>
<p>I will discuss the advantages and disadvantages of using JavaScript frameworks compared to a one-stop solution like Laravel and end it with a comparison table to help you decide.</p>
<h2 id="heading-what-well-cover-in-this-article">What we'll cover in this article:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-why-javascript-vs-laravel">Why JavaScript vs Laravel</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-javascript-lacks-an-all-in-one-solution">Why JavaScript lacks an all-in-one solution</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-javascript-frameworks-and-libraries">Benefits of JavaScript frameworks and libraries</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-laravel">Benefits of Laravel</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-differences-between-laravel-and-js-tools">Differences between Laravel and JS tools</a></p>
</li>
</ol>
<h2 id="heading-why-javascript-vs-laravel">Why JavaScript vs Laravel</h2>
<p>When beginners decide to learn Web Development, they often choose between JavaScript/TypeScript and PHP. Other solutions require more expertise, so most beginners gravitate towards these two languages.</p>
<p>Many JavaScript beginners eventually find its vast ecosystem overwhelming, whereas PHP learners often feel constrained by the limited frameworks and lack of advanced capabilities.</p>
<p>To deal with that confusion, I am comparing the tools available for JavaScript and PHP, not the languages directly. Since PHP has an all-in-one solution most developers use, I'll use Laravel for the comparison.</p>
<p>JavaScript lacks a single all-in-one solution or a specific representative framework. Therefore, I'll refer to tools like React, Vue, Angular, and so on as "JavaScript Frameworks."</p>
<p>Let's begin by understanding why the JavaScript ecosystem lacks a Laravel-like framework.</p>
<h2 id="heading-why-javascript-lacks-an-all-in-one-solution">Why JavaScript Lacks an All-in-One Solution</h2>
<p>I'll begin by acknowledging that JavaScript developers have tried in the past to make a Laravel for JavaScript.</p>
<p>Start with RedwoodJS. It's an open-source full-stack JavaScript framework started by Tom Preston-Werner, the co-founder and former CEO of GitHub. It helps developers build their applications and ship faster.</p>
<p>Redwood has many innovative solutions packed into one framework. Sounds familiar? That's precisely like Laravel. Do you see anyone shipping using Redwood? Most likely not.</p>
<p>The next option is Blitz. It makes the same promise as Redwood: a replacement for NextJS. Blitz is an all-in-one solution with custom built-in solutions for authentication, database, and so on.</p>
<p>But Blitz doesn't allow developers to choose what they want to use, like Laravel. It has custom solutions crafted by the core team. Developers cannot replace any solution piece with some other recommended solution.</p>
<p>Customization is a point worth noting in the JavaScript ecosystem, and I'll explain why as we uncover more frameworks. But Blitz didn't succeed either.</p>
<p>Then, there's AdonisJS. But, I think you get the point. Each all-in-one solution in JavaScript has tried to implement their version of Laravel – and none of them really took hold.</p>
<p>Blitz, Adonis, and Redwood followed a theme: a full-stack framework of ready-made non-customizable solutions for JavaScript developers to build applications.</p>
<p>These frameworks didn't gain popularity because they didn't meet the needs of the developers they aimed to serve.</p>
<p>The JavaScript ecosystem is unique, offering many solutions for the same problem. There's a reason for this diversity, and why those frameworks didn't succeed.</p>
<p>Let's discuss the reasons and benefits of the current popular JavaScript tools, as they directly impact the choice between JavaScript frameworks and Laravel. As I outline each benefit, consider whether it aligns with your needs.</p>
<h2 id="heading-benefits-of-javascript-frameworks-and-libraries">Benefits of JavaScript Frameworks and Libraries</h2>
<p>JavaScript frameworks and libraries are collections of pre-written resources, techniques, and functions created by developers to save time and effort.</p>
<p>These frameworks began emerging in the 2000s, with notable ones like React, Svelte, Angular, NextJS, and Vue standing out over time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-80.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Source:</em> <a target="_blank" href="https://raygun.com/blog/popular-javascript-frameworks/"><em>Raygun</em></a></p>
<p>However, having more options doesn't necessarily mean greater effectiveness. Below, we’ll explore the benefits of using JavaScript frameworks and why all-in-one solutions haven't succeeded in the JavaScript ecosystem.</p>
<h3 id="heading-versatility">Versatility</h3>
<p>It's easier to switch or choose one solution over the other in the JavaScript ecosystem. For example, to implement authentication, we have Clerk, Auth0, AuthJS, NextAuth, Supabase, and more.</p>
<p>The JavaScript ecosystem always had multiple options for a specific solution and startups dedicated to providing bespoke solutions. If you didn't like Clerk, you could use NextAuth for more customization.</p>
<p>JavaScript frameworks are highly versatile. When there are ten solutions for one problem, developers can easily switch and see what works for them.</p>
<p>Blitz and other frameworks couldn't succeed for this reason. If a developer disliked Blitz's approach to authentication, they would just switch to another solution.</p>
<h3 id="heading-npm">NPM</h3>
<p>Unlike JavaScript, other languages don't have an ecosystem around building tools for others, like libraries or packages. It's not as easy in those languages.</p>
<p>The JavaScript ecosystem has an entire registry filled with packages and libraries for the smallest solutions, like emails. JavaScript has many packages, frameworks, and libraries for each solution.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-81.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Source:</em> <a target="_blank" href="https://kinsta.com/knowledgebase/what-is-npm/"><em>Kinsta</em></a></p>
<p>Ironically, even the NPM registry has competitors. There are multiple other registries with unique packages and features. JavaScript follows the versatility rule everywhere, but does that indicate reliability? Maybe not.</p>
<h3 id="heading-startups">Startups</h3>
<p>Each solution for a specific problem is likely a startup funded by VC firms and Angel Investors.</p>
<p>JavaScript-based startups have the capital and the resources to conduct in-depth research and hire the best talent to build bespoke solutions for a targeted problem.</p>
<p>With unlimited resources allocated to a specific topic or problem, the solutions are specific and cater to different needs to their developers.</p>
<p>In return, JavaScript developers pay these startups for those solutions and startups are incentivised to create better solutions for the ecosystem.</p>
<h3 id="heading-the-common-dev">The Common Dev</h3>
<p>Any developer in the JavaScript ecosystem can identify a problem during their experience using a specific technology, build a solution, and offer it as a product.</p>
<p>JavaScript developers have the liberty to build and sell their solutions because startups push the idea of paying for bespoke solutions and developers have no problem doing that.</p>
<p>Take <a target="_blank" href="https://x.com/marc_louvion">Marc Lou</a> as an example. He built <a target="_blank" href="https://shipfa.st/">ShipFast</a> and other products that churn over $100,000 per month. He is an example of the possibilities in the field.</p>
<p>But perhaps these benefits and reasons seem intangible to you. Don’t worry—the web development domain offers more than one alternative. Let’s explore Laravel.</p>
<h2 id="heading-laravel">Laravel</h2>
<p>Laravel is a framework for the PHP programming language. Taylor Otwell introduced Laravel in 2011.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-45.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Source: Cloudways</em></p>
<p>Laravel offers many solutions packed into one tool, and can be a one-stop solution for everything. It offers authentication, debugging, serverless deployment, user-friendly form systems, starter kits, and so on.</p>
<p>The solutions provided by Laravel are many, but without the core language, they don't mean anything. Laravel is all about PHP.</p>
<p>PHP is a general-purpose scripting language. Rasmus Lardof introduced it to developers in the 1990s. Since then, the language has evolved with significant updates in 2004, 2015, and 2020.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-46.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Source: Gecko Dynamics</em></p>
<p>Before JavaScript frameworks had taken off, developers used PHP with JavaScript.</p>
<p>Laravel is a back-end framework and PHP focuses on the server-side. JavaScript without a framework is a client-side language at its core.</p>
<p>Developers traditionally used JavaScript on the client-side and PHP on the server-side to create full-fledged applications.</p>
<p>However, the tech stack landscape shifted when Laravel gained popularity and frameworks like React began to dominate the JavaScript ecosystem.</p>
<p>Many developers worldwide, especially from countries like India, are doubling down on PHP because of its robust and reliable nature.</p>
<p>Government websites and large infrastructures use Laravel (PHP) because it supports a variety of solutions in one place. Many developers don't want to combine and switch technologies.</p>
<p>Companies and individuals using PHP or Laravel want to ship quickly, make changes, and avoid learning a new library or technology at every step of the way like JavaScript.</p>
<p>PHP only has JavaScript as its direct rival that challenges its existence. However, JavaScript isn't the best for many situations. Let's see how Laravel performs better.</p>
<h2 id="heading-benefits-of-laravel">Benefits of Laravel</h2>
<h3 id="heading-one-stop-solution">One-Stop Solution</h3>
<p>Laravel is an all-in-one framework. It contains everything a developer would reasonably require to build compact, reliable, and robust solutions.</p>
<p>You don't need to spend time searching for various libraries and frameworks that may or may not integrate well with your foundational technology—it's all built-in for you.</p>
<p>If speed of deployment is your priority, Laravel is the ideal choice.</p>
<p>Moreover, for PHP developers using Laravel, there's less need to learn new libraries, syntax, and more because everything is integrated seamlessly.</p>
<h3 id="heading-less-wasted-time">Less Wasted Time</h3>
<p>The PHP ecosystem has fewer solutions for specific problems. It's difficult to identify a specific solution for a specific problem.</p>
<p>Developers rather choose an all-in-one solution than invest time in finding bespoke solutions for specific problems.</p>
<p>That's where the PHP ecosystem shines because developers don't spend a lot of time trying out different options. They already have everything in one place.</p>
<p>If there's a new project, their go-to choice is Laravel. In the JavaScript land, if a developer isn't using an existing solution or tech stack, they are likely finding a new one or creating one.</p>
<h3 id="heading-stable-and-robust">Stable and Robust</h3>
<p>Web Applications created using PHP are robust because the framework is battle-tested. You can create anything using it.</p>
<p>You could sabotage the entire application if you mess up with one choice of library or framework in a JavaScript project. But it's not the same with Laravel.</p>
<p>Since Laravel has fewer solutions, the chances of sabotaging are reduced to the bare minimum.</p>
<h3 id="heading-no-catching-up">No Catching Up</h3>
<p>With Laravel, you don't need to catch up with new technologies and frameworks frequently. You can learn one framework, library, or skill and consider it useful for a long time.</p>
<p>It's the opposite in JavaScript. Unlike JS, Laravel has more stable and less frequent updates.</p>
<p>The PHP ecosystem receives fewer updates so developers can reliably use the existing features without worrying about another framework or library providing a better solution or another version.</p>
<p>A new framework or library doesn't pop up each week. You don't need to follow up on versions frequently.</p>
<h3 id="heading-a-massive-community">A Massive Community</h3>
<p>Laravel was first launched a decade ago. It has gained popularity over time and invited many developers to create communities and share ideas.</p>
<p>Every PHP developer uses Laravel. If you ever need any help, you always have developers helping you.</p>
<h3 id="heading-free-solutions">Free Solutions</h3>
<p>Most PHP frameworks and libraries are open-source and free to access. Startups are rare in the PHP ecosystem.</p>
<p>If a developer is offering any bespoke solutions, most are public, especially free.</p>
<p>Any PHP developer can refer to those open-source free solutions, contribute to them, and extend those solutions further.</p>
<h2 id="heading-differences-between-laravel-and-js-tools">Differences Between Laravel and JS Tools</h2>
<p>You can create any type of application using both of these technologies. But the experience won't be exactly the same, of course. Here are some of the differences:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Features</td><td>JavaScript Frameworks</td><td>Laravel</td></tr>
</thead>
<tbody>
<tr>
<td>Beginner Friendly</td><td>They often use TypeScript, which is beginner-friendly and beneficial for developers switching from C, C#, Java, and C++</td><td>Laravel is easier to adopt if you have a grasp of PHP or other JavaScript frameworks.</td></tr>
<tr>
<td>Flexibility</td><td>High flexibility with the ability to choose different tools and libraries.</td><td>Less flexibility but provides a cohesive, opinionated structure.</td></tr>
<tr>
<td>Salary</td><td>Developers using JavaScript frameworks have higher salaries.</td><td>Laravel developers get a generally lower salary than JavaScript/TypeScript developers.</td></tr>
<tr>
<td>Availability of tools</td><td>JavaScript frameworks are widely available and ready-to-use. You can download them from the NPM registry and start immediately.</td><td>Laravel offers solutions for a wide range of problems, but if you encounter issues with their built-in solutions, you have fewer alternatives.</td></tr>
<tr>
<td>Security</td><td>Depends on the back-end setup of the respective framework, but there are options to choose from if the security measures lack.</td><td>It has built-in security features like CSRF and encryption.</td></tr>
<tr>
<td>Documentation</td><td>Depends on the framework. React, NextJS, and a few other frameworks have the most detailed documentations with code snippets and explanations.</td><td>Laravel has a structured and organised documentation with code snippets and explanations.</td></tr>
<tr>
<td>Testing &amp; Debugging</td><td>JavaScript frameworks have multiple testing libraries, like Jest.</td><td>Laravel has built-in tools, like PHPUnit intergration for testing.</td></tr>
<tr>
<td>Learning Curve</td><td>JavaScript frameworks have a higher learning curve due to frequent and significant updates, as well as the regular release of new frameworks.</td><td>Laravel has a gentle learning curve because its built-in solutions are easy to adopt, even for those familiar with JavaScript frameworks.</td></tr>
<tr>
<td>Scalability</td><td>JavaScript frameworks are highly scalable, allowing you to swap libraries as needed to maximize performance.</td><td>Laravel has limited options to modify and swap solutions. Hence, it is less scalable, but reliable.</td></tr>
<tr>
<td>Deployment &amp; Hosting</td><td>JavaScript frameworks can be deployed with any VPS, or using Vercel with a few clicks. However, it requires NodeJS on the server.</td><td>Laravel projects can be hosted on any server supporting PHP.</td></tr>
<tr>
<td>Customization</td><td>JavaScript frameworks allow for easy swapping and customization, enabling you to tailor libraries or frameworks to suit your needs.</td><td>Laravel solutions are limited and cannot be modified.</td></tr>
<tr>
<td>Performance</td><td>JavaScript frameworks have high-performance for dynamic, interactive Uls.</td><td>Laravel has high-performance for back-end processes and server-side rendering.</td></tr>
<tr>
<td>Future</td><td>JavaScript frameworks continue to grow, with more startups being founded and developers launching their own solutions.</td><td>Laravel continuously evolves, releasing new sub-frameworks and solutions to address problems in the most effective way possible.</td></tr>
</tbody>
</table>
</div><p>And that's it! I hope you enjoyed the article and learned something new. If you want, you can also follow me on <a target="_blank" href="https://x.com/whyafan">Twitter</a> (X) or <a target="_blank" href="https://www.linkedin.com/in/khanafan/">LinkedIn</a>. Also, I've built a <a target="_blank" href="https://store.afankhan.com/l/codenexus">Notion Dashboard</a> for Software Engineers to ace their coding interviews.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Real-Time Chat App with Laravel Reverb ]]>
                </title>
                <description>
                    <![CDATA[ In March of 2024, Laravel 11 was released. And with it arrived a new tool in the Laravel ecosystem: Laravel Reverb. Reverb is a separate open-source package that's a first-party WebSocket server for Laravel applications. It helps facilitate real-time... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/laravel-reverb-realtime-chat-app/</link>
                <guid isPermaLink="false">66ba2af546ffa10237d2bc86</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ San B ]]>
                </dc:creator>
                <pubDate>Wed, 27 Mar 2024 18:13:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/laravel-reverb-react-chat-boolfalse.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In March of 2024, <a target="_blank" href="https://blog.laravel.com/laravel-11-now-available">Laravel 11 was released</a>. And with it arrived a new tool in the Laravel ecosystem: <a target="_blank" href="https://reverb.laravel.com/">Laravel Reverb</a>.</p>
<p>Reverb is a separate open-source package that's a first-party WebSocket server for Laravel applications. It helps facilitate real-time communication between client and server.</p>
<p>Before this new package, Laravel had event broadcasting, but basically it didn't have a built-in way to set up a self-hosted WebSocket server. Fortunately, Reverb now gives us that option.</p>
<p>Laravel Reverb has a few key features: it's written in PHP, it's fast, and and it's scalable. It was developed in particular to be horizontally scalable. </p>
<p>Reverb basically allows you to run an application on a single server – but if the application starts to outgrow that server, you can add multiple additional servers. Then those servers can all communicate with each other to distribute the messages between themselves.</p>
<p>In this article you will learn how to build a real-time chat application using Laravel Reverb. This will let you easily implement WebSocket communications between your backend and frontend. </p>
<p>For a frontend technology, you can use anything you want – but in this case we'll use React.js with the Vite.js build tool.</p>
<p>By the end of this article, you'll have a full-stack, real-time app in your local machine, which will work like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/article-video-1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Demo of the app showing messaging between two logged in users</em></p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-general-steps">General Steps</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-laravel">How to Install Laravel</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-model-and-migration">How to Create the Model and Migration</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-authentication">How to Add Authentication</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-routes">How to Set Up Routes</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-a-laravel-event">How to Set Up a Laravel Event</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-a-laravel-queue-job">How to Set Up a Laravel Queue Job</a></li>
<li><a class="post-section-overview" href="#heading-how-to-write-the-controller-methods">How to Write the Controller Methods</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-laravel-reverb">How to Install Laravel Reverb</a></li>
<li><a class="post-section-overview" href="#heading-how-to-setup-websocket-channels">How to Setup WebSocket Channels</a></li>
<li><a class="post-section-overview" href="#heading-how-to-customize-laravel-views">How to Customize Laravel Views</a></li>
<li><a class="post-section-overview" href="#lets-work-on-the-front-end">Let's Work on the Front End</a></li>
<li><a class="post-section-overview" href="#heading-running-the-application">Running the Application</a></li>
<li><a class="post-section-overview" href="#heading-useful-reverb-resources">Useful Reverb Resources</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You'll need the following tools for the app that we'll build in this article:</p>
<ul>
<li><strong>PHP</strong>: version 8.2 or above (run <code>php -v</code> to check the version)</li>
<li><strong>Composer</strong> (run <code>composer</code> to check that it exists)</li>
<li><strong>Node.js</strong>: version 20 or above (run <code>node -v</code> to check the version)</li>
<li><strong>MySQL</strong>: version 5.7 or above (run <code>mysql --version</code> to check if it exists, or follow the <a target="_blank" href="https://dev.mysql.com/doc/refman/5.7/en/linux-installation.html">docs</a> to install it)</li>
</ul>
<h2 id="heading-general-steps">General Steps</h2>
<p>The main steps in this article will be:</p>
<ul>
<li>Installing Laravel 11.</li>
<li>Adding authentication flow to it (authentication scaffolding). Laravel provides a basic starting point for this using Bootstrap with React / Vue.</li>
<li>Installing Reverb.</li>
<li>React.js components and event listening in the frontend.</li>
</ul>
<h2 id="heading-how-to-install-laravel">How to Install Laravel</h2>
<p>To start, install Laravel 11 by using the composer command:</p>
<pre><code class="lang-shell">composer create-project laravel/laravel:^11.0 laravel-reverb-react-chat &amp;&amp; cd laravel-reverb-react-chat/
</code></pre>
<p>At this point, you can check out the app by running the <code>serve</code> command:</p>
<pre><code class="lang-shell">php artisan serve
</code></pre>
<h2 id="heading-how-to-create-the-model-and-migration">How to Create the Model and Migration</h2>
<p>You can generate a model and a migration for the messages by using this single command:</p>
<pre><code class="lang-shell">php artisan make:model -m Message
</code></pre>
<p>Then you'll need to set up the Message's model with the following code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Factories</span>\<span class="hljs-title">HasFactory</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Relations</span>\<span class="hljs-title">BelongsTo</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Message</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">HasFactory</span>;

    <span class="hljs-keyword">public</span> $table = <span class="hljs-string">'messages'</span>;
    <span class="hljs-keyword">protected</span> $fillable = [<span class="hljs-string">'id'</span>, <span class="hljs-string">'user_id'</span>, <span class="hljs-string">'text'</span>];

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">user</span>(<span class="hljs-params"></span>): <span class="hljs-title">BelongsTo</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;belongsTo(User::class, <span class="hljs-string">'user_id'</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTimeAttribute</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
        <span class="hljs-keyword">return</span> date(
            <span class="hljs-string">"d M Y, H:i:s"</span>,
            strtotime(<span class="hljs-keyword">$this</span>-&gt;attributes[<span class="hljs-string">'created_at'</span>])
        );
    }
}
</code></pre>
<p>As you can see, there's a <code>getTimeAttribute()</code> accessor that will format the message creation timestamp into a human-readable date and time format. It will show it on the top of each message in the chat box.</p>
<p>Next, set up the migration for the <code>messages</code> database table with this code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Migrations</span>\<span class="hljs-title">Migration</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Schema</span>\<span class="hljs-title">Blueprint</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Schema</span>;

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Migration</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> </span>{
        Schema::create(<span class="hljs-string">'messages'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();
            $table-&gt;foreignId(<span class="hljs-string">'user_id'</span>)-&gt;constrained();
            $table-&gt;text(<span class="hljs-string">'text'</span>)-&gt;nullable();
            $table-&gt;timestamps();
        });
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">down</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> </span>{
        Schema::dropIfExists(<span class="hljs-string">'messages'</span>);
    }
};
</code></pre>
<p>This migration creates a <code>messages</code> table in the database. The table contains columns for an auto-incrementing primary key (<code>id</code>), a foreign key (<code>user_id</code>) referencing the <code>id</code> column of the <code>users</code> table, a <code>text</code> column for storing the message content, and <code>timestamps</code> to automatically track the creation and modification times of each record. </p>
<p>The migration also includes a rollback method (<code>down()</code>) to drop the <code>messages</code> table if needed.</p>
<p>In this article, we'll use the MySQL database, but you can go with SQLite as the default one if you prefer. Just make sure to set up your database credentials in <code>.env</code> file correctly:</p>
<pre><code class="lang-env">DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=username
DB_PASSWORD=password
</code></pre>
<p>After setting up the environment variables, optimize the cache:</p>
<pre><code class="lang-shell">php artisan optimize
</code></pre>
<p>Run migrations to recreate the database tables as well as to add the <code>messages</code> table:</p>
<pre><code class="lang-shell">php artisan migrate:fresh
</code></pre>
<h2 id="heading-how-to-add-authentication">How to Add Authentication</h2>
<p>Now, you can add authentication scaffolding to your app. You can use Laravel's UI package to import some asset files. First you'll need to install the appropriate package:</p>
<pre><code class="lang-shell">composer require laravel/ui
</code></pre>
<p>Then import the React-related assets into the application:</p>
<pre><code class="lang-shell">php artisan ui react --auth
</code></pre>
<p>It may ask to overwrite the <code>app/Http/Controllers/Controller.php</code>, and you can go ahead and allow it:</p>
<pre><code class="lang-shell">The [Controller.php] file already exists. Do you want to replace it? (yes/no) [no]
</code></pre>
<p>This will do all of the authentication scaffolding compiled and installed, including routes, controllers, views, vite configurations, and a simple React-specific sample.<br>At this point, you're just one step away from the app being ready to go.</p>
<p><strong>NOTE:</strong> Make sure you have <strong>Node.js</strong> (with <strong>npm</strong>) version 20 or above installed. You can check that by running the <code>node -v</code> command. Otherwise, just go ahead and install it using the <a target="_blank" href="https://nodejs.org/en/download">official page</a>.</p>
<pre><code class="lang-shell">npm install &amp;&amp; npm run build
</code></pre>
<p>The command above will install NPM packages and build frontend assets. Now you can start the Laravel application and check out your fully ready app sample:</p>
<pre><code class="lang-shell">php artisan optimize &amp;&amp; php artisan serve
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/article-image-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>A screenshot of the Register page</em></p>
<p>It's also important to note that you can separately run the <code>dev</code> command instead of using <code>build</code> every time when you're making changes to frontend files:</p>
<pre><code class="lang-shell">npm run dev
</code></pre>
<p>See the details in the <code>package.json</code> file, in the <code>scripts</code> field.</p>
<h2 id="heading-how-to-set-up-routes">How to Set Up Routes</h2>
<p>In this real-time chat app, you'll need to have a few routes:</p>
<ul>
<li><code>home</code> for the home page (already should be added)</li>
<li><code>message</code> for adding a new message</li>
<li><code>messages</code> to get all the existing messages</li>
</ul>
<p>You'll have these kind of routes in the <code>web.php</code> file:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Auth</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Route</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>\<span class="hljs-title">HomeController</span>;

Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">return</span> view(<span class="hljs-string">'welcome'</span>); });

Auth::routes();

Route::get(<span class="hljs-string">'/home'</span>, [HomeController::class, <span class="hljs-string">'index'</span>])
    -&gt;name(<span class="hljs-string">'home'</span>);
Route::get(<span class="hljs-string">'/messages'</span>, [HomeController::class, <span class="hljs-string">'messages'</span>])
    -&gt;name(<span class="hljs-string">'messages'</span>);
Route::post(<span class="hljs-string">'/message'</span>, [HomeController::class, <span class="hljs-string">'message'</span>])
    -&gt;name(<span class="hljs-string">'message'</span>);
</code></pre>
<p>After setting up those routes, let's use Laravel Events and Queue Jobs advantages.</p>
<h2 id="heading-how-to-set-up-a-laravel-event">How to Set Up a Laravel Event</h2>
<p>You need to create a <code>GotMessage</code> event for listening for a specific event:</p>
<pre><code class="lang-shell">php artisan make:event GotMessage
</code></pre>
<blockquote>
<p>Laravel's events provide a simple observer pattern implementation, allowing you to subscribe and listen for various events that occur within your application. Event classes are typically stored in the <code>app/Events</code> directory. (<a target="_blank" href="https://laravel.com/docs/11.x/events">Docs</a>)</p>
</blockquote>
<p>Set up a private WebSocket channel in the <code>broadcastOn</code> method for all the authenticated users to receive messages in real time. In this case, we will call it <code>"channel_for_everyone"</code>, but you can also make it dynamic, depending on the user, like <code>"App.Models.User.{$this-&gt;message['user_id']}"</code>.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Events</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Broadcasting</span>\<span class="hljs-title">InteractsWithSockets</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Broadcasting</span>\<span class="hljs-title">PrivateChannel</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Contracts</span>\<span class="hljs-title">Broadcasting</span>\<span class="hljs-title">ShouldBroadcast</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Events</span>\<span class="hljs-title">Dispatchable</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Queue</span>\<span class="hljs-title">SerializesModels</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GotMessage</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ShouldBroadcast</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">Dispatchable</span>, <span class="hljs-title">InteractsWithSockets</span>, <span class="hljs-title">SerializesModels</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> <span class="hljs-keyword">array</span> $message</span>) </span>{
        <span class="hljs-comment">//</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">broadcastOn</span>(<span class="hljs-params"></span>): <span class="hljs-title">array</span> </span>{
        <span class="hljs-comment">// $this-&gt;message is available here</span>
        <span class="hljs-keyword">return</span> [
            <span class="hljs-keyword">new</span> PrivateChannel(<span class="hljs-string">"channel_for_everyone"</span>),
        ];
    }
}
</code></pre>
<p>As you can see, there's a public <code>$massage</code> property as a constructor argument, so you can get message infromation in the front end.</p>
<p>We've already used the channel name in the channels file, and we'll use it in the front end as well for real-time message updates.</p>
<p>Don't forget to implement the <code>ShouldBroadcast</code> interface in the event's class.</p>
<h2 id="heading-how-to-set-up-a-laravel-queue-job">How to Set Up a Laravel Queue Job</h2>
<p>Now it's time to create the <code>SendMessage</code> job for sending messages:</p>
<pre><code class="lang-shell">php artisan make:job SendMessage
</code></pre>
<blockquote>
<p>Laravel allows you to easily create queued jobs that may be processed in the background. By moving time intensive tasks to a queue, your application can respond to web requests with blazing speed and provide a better user experience to your customers. (<a target="_blank" href="https://laravel.com/docs/11.x/queues">Docs</a>)</p>
</blockquote>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Jobs</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Events</span>\<span class="hljs-title">GotMessage</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Message</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Bus</span>\<span class="hljs-title">Queueable</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Contracts</span>\<span class="hljs-title">Queue</span>\<span class="hljs-title">ShouldQueue</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Bus</span>\<span class="hljs-title">Dispatchable</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Queue</span>\<span class="hljs-title">InteractsWithQueue</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Queue</span>\<span class="hljs-title">SerializesModels</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SendMessage</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ShouldQueue</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">Dispatchable</span>, <span class="hljs-title">InteractsWithQueue</span>, <span class="hljs-title">Queueable</span>, <span class="hljs-title">SerializesModels</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> Message $message</span>) </span>{
        <span class="hljs-comment">//</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> </span>{
        GotMessage::dispatch([
            <span class="hljs-string">'id'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;message-&gt;id,
            <span class="hljs-string">'user_id'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;message-&gt;user_id,
            <span class="hljs-string">'text'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;message-&gt;text,
            <span class="hljs-string">'time'</span> =&gt; <span class="hljs-keyword">$this</span>-&gt;message-&gt;time,
        ]);
    }
}
</code></pre>
<p>The <code>SendMessage.php</code> queue job is responsible for dispatching the <code>GotMessage</code> event with information about a newly sent message. It receives a <code>Message</code> object upon construction, representing the message to be sent. </p>
<p>In its <code>handle()</code> method, it dispatches the <code>GotMessage</code> event with details such as the message ID, user ID, text, and timestamp. This job is designed to be queued for asynchronous processing, enabling efficient handling of message sending tasks in the background.</p>
<p>As you can see, there's a public <code>$massage</code> property as a constructor argument, which we'll use to attach a message information to the queue job.</p>
<h2 id="heading-how-to-write-the-controller-methods">How to Write the Controller Methods</h2>
<p>For the defined routes, here are the appropriate controller methods:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Jobs</span>\<span class="hljs-title">SendMessage</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Message</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">User</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">JsonResponse</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">$this</span>-&gt;middleware(<span class="hljs-string">'auth'</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>) </span>{
        $user = User::where(<span class="hljs-string">'id'</span>, auth()-&gt;id())-&gt;select([
            <span class="hljs-string">'id'</span>, <span class="hljs-string">'name'</span>, <span class="hljs-string">'email'</span>,
        ])-&gt;first();

        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'home'</span>, [
            <span class="hljs-string">'user'</span> =&gt; $user,
        ]);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">messages</span>(<span class="hljs-params"></span>): <span class="hljs-title">JsonResponse</span> </span>{
        $messages = Message::with(<span class="hljs-string">'user'</span>)-&gt;get()-&gt;append(<span class="hljs-string">'time'</span>);

        <span class="hljs-keyword">return</span> response()-&gt;json($messages);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">message</span>(<span class="hljs-params">Request $request</span>): <span class="hljs-title">JsonResponse</span> </span>{
        $message = Message::create([
            <span class="hljs-string">'user_id'</span> =&gt; auth()-&gt;id(),
            <span class="hljs-string">'text'</span> =&gt; $request-&gt;get(<span class="hljs-string">'text'</span>),
        ]);
        SendMessage::dispatch($message);

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Message created and job dispatched."</span>,
        ]);
    }
}
</code></pre>
<ul>
<li>In the <code>home</code> method, we'll get the logged in user's data from the database using the <code>User</code> model and send it to the blade view.</li>
<li>In the <code>messages</code> method, we'll retrieve all the messages from the database using the <code>Message</code> model, attach the <code>user</code> relationship data to it, append the <code>time</code> field (accessor) to each item, and send all that to the view.</li>
<li>In the <code>message</code> method, a new message will be created in the database table by using the <code>Message</code> model, and the <code>SendMessage</code> queue job will be dispatched.</li>
</ul>
<h2 id="heading-how-to-install-laravel-reverb">How to Install Laravel Reverb</h2>
<p>Now we've come to the most important moment: it's time to install <a target="_blank" href="https://laravel.com/docs/11.x/reverb#installation">Reverb</a> in your Laravel app.</p>
<p>It's so easy. All the necessary packaging and configuration setup can be done using this single command:</p>
<pre><code class="lang-shell">php artisan install:broadcasting
</code></pre>
<p>It will ask you to install Laravel Reverb as well as install and build the Node dependencies required for broadcasting. Just press enter to continue.</p>
<p>After the command execution, make sure you've automatically added reverb-specific environment variables to the <code>.env</code> file, like:</p>
<pre><code class="lang-env">BROADCAST_CONNECTION=reverb

###

REVERB_APP_ID=795051
REVERB_APP_KEY=s3w3thzezulgp5g0e5bs
REVERB_APP_SECRET=gncsnk3rzpvczdakl6pz
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
</code></pre>
<p>You'll also have two new configration files in the <code>config</code> directory:</p>
<ul>
<li><code>reverb.php</code></li>
<li><code>broadcasting.php</code></li>
</ul>
<h2 id="heading-how-to-setup-websocket-channels">How to Setup WebSocket Channels</h2>
<p>Lastly, you'll need to add a channel in the <code>channels.php</code> file. It should already be created after installing Reverb.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Broadcast</span>;

Broadcast::channel(<span class="hljs-string">'channel_for_everyone'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">$user</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
});
</code></pre>
<p>You'll have only one channel. You can change the channel's name and make it dynamic – it's up to you. In the closure of the channel, we'll always return true, but you can modify it later to make some restrictions regarding the channel's subscription.</p>
<p>Optimize caches one more time:</p>
<pre><code class="lang-shell">php artisan optimize
</code></pre>
<h2 id="heading-how-to-customize-laravel-views">How to Customize Laravel Views</h2>
<p>Now your back end should be ready at this point, so you can switch to the front end.</p>
<p>Before working on the React stuff, you'll want to set up Laravel <code>*.blade.php</code> views. In the <code>home</code> blade view, make sure to have the root div with an ID of <code>main</code> to render all the React components there.</p>
<pre><code class="lang-php">@<span class="hljs-keyword">extends</span>(<span class="hljs-string">'layouts.app'</span>)

@section(<span class="hljs-string">'content'</span>)
    &lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">container</span>"&gt;
        &lt;<span class="hljs-title">div</span> <span class="hljs-title">id</span>="<span class="hljs-title">main</span>" <span class="hljs-title">data</span>-<span class="hljs-title">user</span>="</span>{{ json_encode($user) }}<span class="hljs-string">"&gt;&lt;/div&gt;
    &lt;/div&gt;
@endsection</span>
</code></pre>
<p>The div with ID of <code>main</code> gets a data property for holding the <code>$user</code> info sent from the controller's <code>home</code> method.</p>
<p>I won't put the whole <code>resources/views/welcome.blade.php</code> content here, but you can just make the following small changes to it:</p>
<ul>
<li>Replace <code>url('/dashboard')</code> with <code>url('/home')</code>;</li>
<li>Replace <code>Dashboard</code> with <code>Home</code>;</li>
<li>Remove <code>main</code> and <code>footer</code> sections.</li>
</ul>
<h2 id="heading-lets-work-on-the-front-end">Let's Work on the Front End</h2>
<p>In Reverb, event broadcasting is done by a server-side broadcasting driver that broadcasts your Laravel events so that the front end can receive them within the browser client. </p>
<p>In the front end, <a target="_blank" href="https://github.com/laravel/echo">Laravel Echo</a> does that job under the hood. Echo a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver.</p>
<p>You can find the WebSocket configurations setup with Echo in the <code>rources/js/echo.js</code> file, but you don't need to do anything there for this project.</p>
<p>Let's create a few React components so that we have a refactored and more readable project.</p>
<p>Create a <code>Main.jsx</code> component in the new <code>components</code> folder:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/client'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../../css/app.css'</span>;
<span class="hljs-keyword">import</span> ChatBox <span class="hljs-keyword">from</span> <span class="hljs-string">"./ChatBox.jsx"</span>;

<span class="hljs-keyword">if</span> (<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'main'</span>)) {
    <span class="hljs-keyword">const</span> rootUrl = <span class="hljs-string">"http://127.0.0.1:8000"</span>;

    ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'main'</span>)).render(
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ChatBox</span> <span class="hljs-attr">rootUrl</span>=<span class="hljs-string">{rootUrl}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>
    );
}
</code></pre>
<p>Here we'll check if there's an element with the id <code>'main'</code>. If it exists, it proceeds with rendering the React application.</p>
<p>As you can see, there's a <code>ChatBox</code> component. We'll learn more about it soon.</p>
<p>Remove the <code>resources/js/components/Example.jsx</code> file, and import the <code>Main.jsx</code> component in the <code>app.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">'./bootstrap'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./components/Main.jsx'</span>;
</code></pre>
<p>Create <code>Message.jsx</code> and <code>MessageInput.jsx</code> files so you can use them in the <code>ChatBox</code> component.</p>
<p>The <code>Message</code> component will get <code>userId</code> and <code>message</code> arguments (fields) to show each message in the chat box.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> Message = <span class="hljs-function">(<span class="hljs-params">{ userId, message }</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">row</span> ${
        <span class="hljs-attr">userId</span> === <span class="hljs-string">message.user_id</span> ? "<span class="hljs-attr">justify-content-end</span>" <span class="hljs-attr">:</span> ""
        }`}&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col-md-6"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">small</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-muted"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>{message.user.name} | <span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">small</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">small</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-muted float-right"</span>&gt;</span>
                    {message.time}
                <span class="hljs-tag">&lt;/<span class="hljs-name">small</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">alert</span> <span class="hljs-attr">alert-</span>${
                <span class="hljs-attr">userId</span> === <span class="hljs-string">message.user_id</span> ? "<span class="hljs-attr">primary</span>" <span class="hljs-attr">:</span> "<span class="hljs-attr">secondary</span>"
                }`} <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
                    {message.text}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Message;
</code></pre>
<p>The <code>Message.jsx</code> component renders individual messages within the chat interface. It receives the <code>userId</code> and <code>message</code> props. Based on whether the message sender matches the current user, it aligns the message to the appropriate side of the screen. </p>
<p>Each message includes the sender's name, timestamp, and the message content itself, styled differently based on whether the message is sent by the current user or another user.</p>
<p>The <code>MessageInput</code> component will care about creating a new message:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> MessageInput = <span class="hljs-function">(<span class="hljs-params">{ rootUrl }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">const</span> messageRequest = <span class="hljs-keyword">async</span> (text) =&gt; {
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">`<span class="hljs-subst">${rootUrl}</span>/message`</span>, {
                text,
            });
        } <span class="hljs-keyword">catch</span> (err) {
            <span class="hljs-built_in">console</span>.log(err.message);
        }
    };

    <span class="hljs-keyword">const</span> sendMessage = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
        e.preventDefault();
        <span class="hljs-keyword">if</span> (message.trim() === <span class="hljs-string">""</span>) {
            alert(<span class="hljs-string">"Please enter a message!"</span>);
            <span class="hljs-keyword">return</span>;
        }

        messageRequest(message);
        setMessage(<span class="hljs-string">""</span>);
    };

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"input-group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setMessage(e.target.value)}
                   autoComplete="off"
                   type="text"
                   className="form-control"
                   placeholder="Message..."
                   value={message}
            /&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"input-group-append"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> sendMessage(e)}
                        className="btn btn-primary"
                        type="button"&gt;Send<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MessageInput;
</code></pre>
<p>The <code>MessageInput</code> component provides a form input field for users to type messages and send them in the chat interface. By clicking the button, it triggers a function to send the message to the server via an Axios POST request to the specified <code>rootUrl</code> that it got from the parent <code>ChatBox</code> component. It also handles validation to ensure that users cannot send empty messages. You can customize it later if you want. </p>
<p>Now create a <code>ChatBox.jsx</code> component to have the front end ready:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Message <span class="hljs-keyword">from</span> <span class="hljs-string">"./Message.jsx"</span>;
<span class="hljs-keyword">import</span> MessageInput <span class="hljs-keyword">from</span> <span class="hljs-string">"./MessageInput.jsx"</span>;

<span class="hljs-keyword">const</span> ChatBox = <span class="hljs-function">(<span class="hljs-params">{ rootUrl }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> userData = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'main'</span>)
        .getAttribute(<span class="hljs-string">'data-user'</span>);

    <span class="hljs-keyword">const</span> user = <span class="hljs-built_in">JSON</span>.parse(userData);
    <span class="hljs-comment">// `App.Models.User.${user.id}`;</span>
    <span class="hljs-keyword">const</span> webSocketChannel = <span class="hljs-string">`channel_for_everyone`</span>;

    <span class="hljs-keyword">const</span> [messages, setMessages] = useState([]);
    <span class="hljs-keyword">const</span> scroll = useRef();

    <span class="hljs-keyword">const</span> scrollToBottom = <span class="hljs-function">() =&gt;</span> {
        scroll.current.scrollIntoView({ <span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span> });
    };

    <span class="hljs-keyword">const</span> connectWebSocket = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">window</span>.Echo.private(webSocketChannel)
            .listen(<span class="hljs-string">'GotMessage'</span>, <span class="hljs-keyword">async</span> (e) =&gt; {
                <span class="hljs-comment">// e.message</span>
                <span class="hljs-keyword">await</span> getMessages();
            });
    }

    <span class="hljs-keyword">const</span> getMessages = <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> m = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">`<span class="hljs-subst">${rootUrl}</span>/messages`</span>);
            setMessages(m.data);
            <span class="hljs-built_in">setTimeout</span>(scrollToBottom, <span class="hljs-number">0</span>);
        } <span class="hljs-keyword">catch</span> (err) {
            <span class="hljs-built_in">console</span>.log(err.message);
        }
    };

    useEffect(<span class="hljs-function">() =&gt;</span> {
        getMessages();
        connectWebSocket();

        <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-built_in">window</span>.Echo.leave(webSocketChannel);
        }
    }, []);

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row justify-content-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col-md-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card-header"</span>&gt;</span>Chat Box<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card-body"</span>
                         <span class="hljs-attr">style</span>=<span class="hljs-string">{{height:</span> "<span class="hljs-attr">500px</span>", <span class="hljs-attr">overflowY:</span> "<span class="hljs-attr">auto</span>"}}&gt;</span>
                        {
                            messages?.map((message) =&gt; (
                                <span class="hljs-tag">&lt;<span class="hljs-name">Message</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{message.id}</span>
                                         <span class="hljs-attr">userId</span>=<span class="hljs-string">{user.id}</span>
                                         <span class="hljs-attr">message</span>=<span class="hljs-string">{message}</span>
                                /&gt;</span>
                            ))
                        }
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{scroll}</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card-footer"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">MessageInput</span> <span class="hljs-attr">rootUrl</span>=<span class="hljs-string">{rootUrl}</span> /&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ChatBox;
</code></pre>
<p>The <code>ChatBox</code> component manages a chat interface within the application. It fetches and displays messages from a server using WebSocket and HTTP requests.</p>
<p>The component renders a list of messages, a message input field, and automatically scrolls to the bottom when new messages arrive.</p>
<p>It defines a WebSocket channel for real-time message updates. You need to set up that channel by using the same name as it was written in the <code>routes/hannels.php</code> and in the <code>app/Events/GotMessage.php</code> queue job.</p>
<p>Also, the <code>leave()</code> function is called within the <code>useEffect</code> cleanup function to unsubscribe from the WebSocket channel when the component unmounts. This prevents memory leaks and unnecessary network connections by stopping the component from listening to updates on the WebSocket channel after it's no longer needed.</p>
<h2 id="heading-running-the-application">Running the Application</h2>
<p>Now, everything's ready and it's time to check out the app. Follow these instructions:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/article-image-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>A screenshot from the terminal with all the necessary commands</em></p>
<ul>
<li>Build frontend assets (this is not a "forever" running command):<br><code>npm run build</code></li>
<li>Start listening to the Laravel events:<br><code>php artisan queue:listen</code></li>
<li>Start the WebSocket server:<br><code>php artisan reverb:start</code></li>
<li>Start the server (you may use an alternative for your app like a local running server):<br><code>php artisan serve</code></li>
</ul>
<p>After all the necessary commands are running, you can check out the app by visiting the default URL: <code>http://127.0.0.1:8000</code>.</p>
<p>For testing, you can register two different users, have those users log in, send messages from each of them, and see the chat box.</p>
<h2 id="heading-useful-reverb-resources">Useful Reverb Resources</h2>
<p>Now that we've reached the end of this article, it's worth listing some useful resources about Reverb:</p>
<ul>
<li><a target="_blank" href="https://laravel.com/docs/11.x/broadcasting">Laravel Broadcasting</a> (official documentation)</li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=0g7HqfsCX4Y">Taylor Otwel - Laravel Update</a> (talk on Laracon EU 2024)</li>
<li><a target="_blank" href="https://twitter.com/_joedixon">Joe Dixon on X</a> (creator of Reverb)</li>
<li><a target="_blank" href="https://laracasts.com/series/lukes-larabits/episodes/17">Laracast episode</a> (practical example with Reverb)</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now you know how to build real-time applications with Laravel Reverb in the new version of Laravel. With this, you can implement WebSocket communications in your full-stack app and avoid using any additional 3rd-party services (like Pusher and Socket.io).</p>
<p>If you want to have a clear idea of how to integrate React.js into your Laravel app without using any additional Laravel tools (like Inertia), you can read through my previous <a target="_blank" href="https://www.freecodecamp.org/news/use-react-with-laravel/">freeCodeCamp article</a>, where you can build a single-page, full-stack Tasklist app.</p>
<p>The complete code for this article is here on my <a target="_blank" href="https://github.com/boolfalse/laravel-reverb-react-chat"><strong>GitHub</strong></a>⭐, where I actively publicize much of my work about various modern technologies.</p>
<p>For more information, you can visit my website: <a target="_blank" href="https://boolfalse.com/"><strong>boolfalse.com</strong></a></p>
<p>Feel free to share this article. 😇</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Web Apps with Nuxt and Laravel ]]>
                </title>
                <description>
                    <![CDATA[ The Laravel framework is one of the most widely used technologies in the web development ecosystem. It's relatively straightforward, and it's easy to use for building websites.  Laravel is built upon PHP, a popular web programming language that’s use... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-web-apps-with-nuxt-and-laravel/</link>
                <guid isPermaLink="false">66b9d9c738655574e9f95267</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Applications ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Abdulrahman Yusuf ]]>
                </dc:creator>
                <pubDate>Wed, 07 Feb 2024 10:59:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/pexels-pixabay-270557.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The Laravel framework is one of the most widely used technologies in the web development ecosystem. It's relatively straightforward, and it's easy to use for building websites. </p>
<p>Laravel is built upon PHP, a popular web programming language that’s used in over 75% of websites on the web. So knowing how to use a framework like Laravel can help make you a sought-after developer – and it also makes building websites and applications more seamless. </p>
<p>Nuxt is a Vue.js framework used for building rich and interactive web applications. It lets you choose between different rendering modes depending on the application requirements you want to build. You can choose between building a fully server-rendered app or client-rendered app. Nuxt also offers a mixture of both rendering modes, making applications much more powerful, efficient, and interactive.</p>
<p>In this article, you'll learn how to build full-stack applications using Nuxt and Laravel by building a Book Library App. The app will comprise a library API that we'll build using Laravel and a frontend using Nuxt. </p>
<p>We’ll talk about:</p>
<ul>
<li>Installing and setting up Laravel</li>
<li>Creating database models</li>
<li>Migrations</li>
<li>Controllers</li>
<li>API Testing</li>
<li>Form Validations</li>
<li>Data Fetching in Nuxt</li>
</ul>
<p>And more. Get ready, and let's dive in.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-laravel-on-your-machine">How to set up Laravel on your machine</a></li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-book-library-api">How to build the Book Library API</a></p>
<ul>
<li><a class="post-section-overview" href="#setting-up-our-database">Setting up our database</a></li>
<li><a class="post-section-overview" href="#creating-our-book-model">Creating our Book Model</a></li>
<li><a class="post-section-overview" href="#creating-our-book-controller">Creating our Book Controller</a></li>
<li><a class="post-section-overview" href="#defining-our-api-routes">Defining our API Routes</a></li>
<li><a class="post-section-overview" href="#testing-our-api">Testing our API</a><ul>
<li><a class="post-section-overview" href="#heading-creating-a-new-book-1">Creating a new book</a></li>
<li><a class="post-section-overview" href="#getting-our-list-of-books">Getting our list of books</a></li>
<li><a class="post-section-overview" href="#editing-our-book">Editing our book</a></li>
</ul>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-nuxt-frontend">How to Build the Nuxt Frontend</a></p>
</li>
<li><a class="post-section-overview" href="#heading-how-to-integrate-the-laravel-api-in-the-frontend">How to Integrate the Laravel API in the Frontend</a><ul>
<li><a class="post-section-overview" href="#getting-all-books-in-our-library">Getting all books in our library</a></li>
<li><a class="post-section-overview" href="#heading-creating-a-new-book-1">Creating a new book</a></li>
<li><a class="post-section-overview" href="#heading-editing-a-book">Editing a book</a></li>
<li><a class="post-section-overview" href="#heading-deleting-a-book">Deleting a book</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
<li><a class="post-section-overview" href="#heading-resources">Resources</a></li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<ol>
<li><a target="_blank" href="https://www.php.net">PHP</a> and <a target="_blank" href="https://getcomposer.org">Composer</a> are installed on your local machine.</li>
<li><a target="_blank" href="https://nodejs.org/">Node.js</a> is installed on your local machine.</li>
<li><a target="_blank" href="https://yarnpkg.com/">yarn</a> or <a target="_blank" href="https://www.npmjs.com">npm</a> installed on your local machine (npm comes pre-installed with Node).</li>
<li>A text editor installed, like <a target="_blank" href="https://code.visualstudio.com">VSCode</a>.</li>
<li>Basic knowledge of HTML, CSS, JavaScript, and the terminal.</li>
<li>Basic knowledge of PHP, Vue.js, and TypeScript.</li>
</ol>
<h2 id="heading-how-to-set-up-laravel-on-your-machine"><strong>How to Set Up Laravel on Your Machine</strong></h2>
<p>To begin the Laravel installation, open your terminal and bootstrap a new Laravel project using the command below: </p>
<pre><code class="lang-bash">composer create-project laravel/laravel library-api &amp;&amp; <span class="hljs-built_in">cd</span> library-api &amp;&amp; code .
</code></pre>
<p>This command creates a new Laravel project in your directory. <code>cd</code>'s into it, and open up VSCode using the <code>code .</code> command. (If you happen to be using a different editor, you can remove that command and open the directory manually).</p>
<p>To test your Laravel server and ensure everything works, let’s test the server by using the <code>php artisan serve</code> command in the terminal. This should make your API available on port 8000 and accessible in your browser.</p>
<p><img src="https://lh7-us.googleusercontent.com/Wd1K8FLTnBwThyGwMl8lYeTmpO2886t4IN2lJ6Nv2tShwvw0HGxNWhl3cneeAEVVdVL_Gvf9sB0feecPHqpRpXYbPz-dBRPPAnBxJAabWyMySW-FqSwJkUi1_bTOX7fLqo1luWJIRi1iEPlbSkVxh3U" alt="Image" width="1600" height="865" loading="lazy">
<em>Laravel Starter Application</em></p>
<h2 id="heading-how-to-build-the-book-library-api"><strong>How to Build the Book Library API</strong></h2>
<p>We will be creating a simple CRUD endpoint for our book library, that is the API should be able to achieve the following:</p>
<ul>
<li>Create new book entries</li>
<li>Get a list of all the book entries in the database</li>
<li>Edit previously added book entries</li>
<li>Delete book entries from the database</li>
</ul>
<h3 id="heading-setting-up-the-database">Setting up the database</h3>
<p>Laravel comes with a variety of databases you can use during development. By default, it automatically creates a configuration setup for MySQL. But for simplicity, we’ll be making use of a <a target="_blank" href="https://www.sqlite.org/index.html">SQLite</a> database in this tutorial. </p>
<p>Open the <code>.env</code> file in your root directory, change the <code>DB_CONNECTION</code> value from <code>mysql</code> to <code>sqlite</code>, and comment the remaining database configurations as you can see below:</p>
<p><img src="https://lh7-us.googleusercontent.com/NoxsSV5jfFxxzAsKDX04xAfWzUAESGajpJnam3zTA3LpBIil9-esqfxk4I1CJXjlK_jyjEk3twDTGu_Bi_Efw1DUXaU0yUcdCFcdKECCZ-V-hT4Y_03YKfeQCz8JQ5MVSznXcC3dYei8vBb9wTCo1_c" alt="Image" width="600" height="400" loading="lazy">
<em>Environmental Variables (available in .env file)</em></p>
<p>Laravel automatically helps create the SQLite database when the migrations command is run. We’ll do that in a bit after creating our database models.</p>
<h3 id="heading-creating-the-book-model"><strong>Creating the Book model</strong></h3>
<p>When you're building backend APIs, a model acts as a template that’s used to set up your database tables. It contains high-level instructions for how the tables and columns should be created. </p>
<p>In Laravel, you can use the command below to create a <code>Book</code> model:</p>
<pre><code class="lang-bash">php artisan make:model Book -m
</code></pre>
<p>This creates your Book model in the <code>app/models</code> directory and also creates a new migration file for you in the <code>database/migrations</code> directory.</p>
<p>Navigate to the migrations directory and open the newly created migrations file. It should be in the format <code>current_date_create_books_table.php</code> like you can see below:</p>
<p><img src="https://lh7-us.googleusercontent.com/zW84PQRgiGuLMX9yvmCJ_NvDS4xZfraKtxh7g0Du6rhOoqSpuXGlWnxkCSlTT9yhqT3c6N_jfPAsiuKakLG_MULYgYD6_Qe-Eycxo4hxaozonEVzbjVe64ddSu2FOpdOGMexms1jPuSZc43HB-N0-hY" alt="Image" width="1600" height="1016" loading="lazy">
<em>Default Migrations</em></p>
<p>Edit the <code>up</code> function and add the following content:</p>
<pre><code class="lang-php">$table-&gt;string(<span class="hljs-string">'title'</span>);
$table-&gt;string(<span class="hljs-string">'author'</span>);
$table-&gt;string(<span class="hljs-string">'isbn'</span>);
$table-&gt;date(<span class="hljs-string">'published_date'</span>);
$table-&gt;text(<span class="hljs-string">'cover_image'</span>)-&gt;nullable();
$table-&gt;foreignIdFor(User::class);
</code></pre>
<p>Your migrations file should look like this now:</p>
<p><img src="https://lh7-us.googleusercontent.com/sgSaSbourHadcXleC2Jxn8DHAkulytaRIQfcAZC7ZUPvNTJJITJDr9rDEeYSNf-mNjJxxW285Kz1C5MQlFWBNlSMG7Gk0VEIShz8-AtaA6hRHChRuYYkrYQkIl9IwZ_I0-tO7cXMFd8f3OLen-bAm9o" alt="Image" width="1600" height="1240" loading="lazy">
<em>Updated Migrations</em></p>
<p>In summary, you added new fields to be created for the Book tables. <code>Title</code>, <code>author</code>, and <code>isbn</code> all have a data type of string, while <code>published_date</code> and <code>cover_image</code> have data types of date and text, respectively. </p>
<p>You also made the <code>cover_image</code> nullable, that is the field can be left out whenever a new book entry is created. </p>
<p>Lastly, you imported the <code>User</code> class and made it a <a target="_blank" href="https://www.educative.io/blog/what-is-foreign-key-database">foreign key</a> to your Book table, creating a many-to-one relationship with the User table.</p>
<p>Now, let’s create your tables by running the migrations file with the command below:</p>
<pre><code class="lang-php">php artisan migrate
</code></pre>
<p>You should get a prompt in the terminal asking if you want to create your SQLite database before the migrations. Select yes and press enter to proceed with the migrations.</p>
<p>Once the migration has been run successfully, navigate to the Book model and paste in the following code:</p>
<pre><code class="lang-php"> <span class="hljs-comment">/**
* The attributes that are mass assignable.
*
* <span class="hljs-doctag">@var</span> array&lt;int, string&gt;
*/</span>

<span class="hljs-keyword">protected</span> $fillable = [
    <span class="hljs-string">'user_id'</span>,
    <span class="hljs-string">'title'</span>,
    <span class="hljs-string">'author'</span>,
    <span class="hljs-string">'isbn'</span>,
    <span class="hljs-string">'published_date'</span>,
    <span class="hljs-string">'cover_image'</span>
];
</code></pre>
<p>This will allow mass assignment when passing data into the model (passing the data as a whole instead of individually). Your Book model should look like this now:</p>
<p><img src="https://lh7-us.googleusercontent.com/zjYcbtyiQXKFRJubxnDuL5M6PRyEI5u5PaHGHUAfYgju5A8lQqOJAxRSLdsaIkg0bjFCXOOUTBGHlQXuJhpuEfOW5K6XbPDl2aEQoUyNvjs-p-m5TfRQhF2dlnwnZKHDb0EXsR6JneIGIXCw0ol1plI" alt="Image" width="1600" height="779" loading="lazy">
<em>Updated Book Model</em></p>
<h3 id="heading-creating-the-book-controller"><strong>Creating the Book controller</strong></h3>
<p>The controller is responsible for housing the business logic for any API being created. The implementation logic for how we want to perform any CRUD operations with the library API will live here. </p>
<p>Laravel also provides a command for you to make this easier for developers. Paste the command below in your terminal to create our book controller:</p>
<pre><code class="lang-php">php artisan make:controller BookController
</code></pre>
<p>This should be accessible at <code>app\Http\Controllers\BookController.php</code>. Replace the content of your BookController class with the code snippet below:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Book</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Carbon</span>\<span class="hljs-title">Carbon</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Log</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Storage</span>;


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BookController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span>(<span class="hljs-params">Request $request</span>)
    </span>{
        $data = $request-&gt;validate([
            <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">'required|string'</span>,
            <span class="hljs-string">'author'</span> =&gt; <span class="hljs-string">'required|string'</span>,
            <span class="hljs-string">'isbn'</span> =&gt; <span class="hljs-string">'required|string|unique:books'</span>,
            <span class="hljs-string">'published_date'</span> =&gt; <span class="hljs-string">'required|string'</span>,
            <span class="hljs-string">'cover_image'</span> =&gt; <span class="hljs-string">'nullable|image|max:2048'</span>,
        ]);

        <span class="hljs-keyword">try</span> {
            $published_date = Carbon::parse($data[<span class="hljs-string">'published_date'</span>])-&gt;toDateString();
            $data[<span class="hljs-string">'published_date'</span>] = $published_date;
            $data[<span class="hljs-string">'user_id'</span>] = <span class="hljs-number">1</span>; <span class="hljs-comment">// Making the assumption the id of the user is 1 </span>
        } <span class="hljs-keyword">catch</span> (\<span class="hljs-built_in">Exception</span> $e) {
            <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'Error'</span> =&gt; <span class="hljs-string">'Bad Request'</span>], <span class="hljs-number">400</span>);
        }

        $book = <span class="hljs-keyword">new</span> Book($data);

        <span class="hljs-keyword">if</span> ($request-&gt;hasFile(<span class="hljs-string">'cover_image'</span>)) {
            $coverImage = $request-&gt;file(<span class="hljs-string">'cover_image'</span>);
            $coverImageName = time() . <span class="hljs-string">'.'</span> . $coverImage-&gt;getClientOriginalExtension();
            $coverImage-&gt;move(public_path(<span class="hljs-string">'images'</span>), $coverImageName);
            $book-&gt;cover_image = $coverImageName;
        }

        $book-&gt;save();

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Book added successfully'</span>], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>)
    </span>{
        $books = Book::all();

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'books'</span> =&gt; $books], <span class="hljs-number">200</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span>(<span class="hljs-params">Request $request, $id</span>)
    </span>{
        $book = Book::findOrFail($id);
        $data = $request-&gt;all();

        <span class="hljs-keyword">try</span> {
            $published_date = Carbon::parse($data[<span class="hljs-string">'published_date'</span>])-&gt;toDateString();
            $data[<span class="hljs-string">'published_date'</span>] = $published_date;
            $data[<span class="hljs-string">'user_id'</span>] = $book-&gt;user_id;
        } <span class="hljs-keyword">catch</span> (\<span class="hljs-built_in">Throwable</span> $th) {
            Log::error(<span class="hljs-string">'Error '</span>, $th);
            <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'Error'</span> =&gt; <span class="hljs-string">'Bad Request'</span>], <span class="hljs-number">400</span>);
        }

        $book-&gt;update($data);

        <span class="hljs-keyword">if</span> ($request-&gt;hasFile(<span class="hljs-string">'cover_image'</span>)) {
            <span class="hljs-comment">// Delete the previous cover image if it exists</span>
            <span class="hljs-keyword">if</span> ($book-&gt;cover_image) {
                Storage::delete(<span class="hljs-string">'public/images/'</span> . $book-&gt;cover_image);
            }

            $coverImage = $request-&gt;file(<span class="hljs-string">'cover_image'</span>);
            $coverImageName = time() . <span class="hljs-string">'.'</span> . $coverImage-&gt;getClientOriginalExtension();
            $coverImage-&gt;storeAs(<span class="hljs-string">'public/images'</span>, $coverImageName);
            $book-&gt;cover_image = $coverImageName;
            $book-&gt;save();
        }

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Book updated successfully'</span>], <span class="hljs-number">200</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">destroy</span>(<span class="hljs-params">$id</span>)
    </span>{
        $book = Book::findOrFail($id);

        <span class="hljs-comment">// Delete the cover image if it exists</span>
        <span class="hljs-keyword">if</span> ($book-&gt;cover_image) {
            Storage::delete(<span class="hljs-string">'public/images/'</span> . $book-&gt;cover_image);
        }

        $book-&gt;delete();

        <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Book deleted successfully'</span>], <span class="hljs-number">200</span>);
    }
}
</code></pre>
<h3 id="heading-defining-the-api-routes"><strong>Defining the API Routes</strong></h3>
<p>Routing is one of the most important parts of building an API. Routes serve as access points through which developers, clients, or any other services can access an API. </p>
<p>The Laravel router allows you to register any routes that correspond to any HTTP verb being requested.</p>
<pre><code class="lang-php">Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
</code></pre>
<p>Navigate to the <code>routes/api.php</code> file and create the following routes for your API:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/books'</span>, [BookController::class, <span class="hljs-string">'store'</span>]);
Route::get(<span class="hljs-string">'/books'</span>, [BookController::class, <span class="hljs-string">'index'</span>]);
Route::put(<span class="hljs-string">'/books/{id}'</span>, [BookController::class, <span class="hljs-string">'update'</span>]);
Route::delete(<span class="hljs-string">'/books/{id}'</span>, [BookController::class, <span class="hljs-string">'destroy'</span>]);
</code></pre>
<h3 id="heading-testing-the-api"><strong>Testing the API</strong></h3>
<p>Now, it’s time to test your API and ensure all your endpoints are accessible and working as expected. </p>
<p>Run the Laravel server using the command below:</p>
<pre><code class="lang-php">php artisan serve
</code></pre>
<p>You can test the API using any API client of your choice. We have quite a number of popular options to choose from – <a target="_blank" href="https://www.postman.com">Postman</a>, <a target="_blank" href="https://insomnia.rest">Insomnia</a>, <a target="_blank" href="https://www.thunderclient.com">Thunder Client</a>, and so on. I’ll be using Thunder Client here.</p>
<p>We’ll test for all CRUD operations in your API client by confirming if all your objectives for your API are met.</p>
<h3 id="heading-creating-a-new-book">Creating a new book</h3>
<p><img src="https://lh7-us.googleusercontent.com/pUy0u6hCbOEihAsLhLTtSS6p0l3a42PIKHceWRnq5m4-0MJHwwG2r4mL5Y8MCr_EG3g7AuvvmW6nEx9SZ4-GtHTQNFO5Som8f4PYBPjntJSTNJXzrV0sbjL9RYRGhILUWHo6eWFb9tX82XWmrwH70yI" alt="Image" width="1600" height="1096" loading="lazy">
<em>Create book endpoint</em></p>
<h3 id="heading-getting-a-list-of-books">Getting a list of books</h3>
<p><img src="https://lh7-us.googleusercontent.com/9QNUQRrxFzime9Dsl9bKuh82o0XzD6AY8fUaAmQB_mHP0-pyqviLOVPfesLUsINl9n54tYqbjPNOWYEkJwfoXkaVuREbjGhqhghYI5ifi8e7TTQrNSIIm4jU3rM_8akLtc4so-4AzeegJicMpL8IGZw" alt="Image" width="1600" height="1095" loading="lazy">
<em>Get books endpoint</em></p>
<h3 id="heading-editing-a-book">Editing a book</h3>
<p><img src="https://lh7-us.googleusercontent.com/ZTC04dUEXPF2BdxNH-l9RbfzjhkMqO338pQSHjcoEl_hfa3EZbRUtWS2Stgk7slAlwjXpl1_ypvALbuQX3AvLaDPO0gj2rzh5BpqO-uwK6yjSehztm1wJnrjWLzKM0-asFqOU2wMOTp1xffqVroewec" alt="Image" width="1600" height="939" loading="lazy">
<em>Edit book endpoint</em></p>
<h2 id="heading-how-to-build-the-nuxt-frontend">How to Build the Nuxt Frontend</h2>
<p>Setting up a new Nuxt app is as simple as running the command below:</p>
<pre><code class="lang-bash">npx nuxi@latest init nuxt-library-frontend
</code></pre>
<p>Open the newly created project when you’re done installing all the dependencies and start the development server using this command:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Your Nuxt app should now be accessible on port 3000:</p>
<p><img src="https://lh7-us.googleusercontent.com/ZOV-VAcrLLTW7i6SAmFNh5q8hRcxhY05wC4vZUXbxTwgR5tTsKop-An1SYlFdXEiquu1zCz1tx5TTwrY9WlDEPuiuNpHh4vDsVWwlUVGvZFWljmsTGgRYST3478Pk79jNcNeXJoGa7lQcAdbTx3YitI" alt="Image" width="1600" height="873" loading="lazy">
<em>Nuxt Starter Page</em></p>
<p>This is what your frontend should look like at the end of the tutorial:</p>
<p><img src="https://lh7-us.googleusercontent.com/FbnijAJw1D-VJTnT8VNPQBfl694-fW2PhYTqoEokTMxceEnsN5Oa-mBTmSAcoeU8xcqRFyDceWTQjElj43oidAg2xtW2KWSVamDbL0kmVDuCZktQc1mER5pbCbGkshNsYm0WJKqbgQy8GVymZ8-Bo5I" alt="Image" width="600" height="400" loading="lazy">
<em>Sample of our completed application</em></p>
<p>I’ve created some <a target="_blank" href="https://github.com/Young-Einstein10/laravel-nuxt-app">boilerplate code</a> to help with the Nuxt frontend setup. This will enable us to focus on the implementation logic for consuming the Laravel API rather than spending time setting things up and styling. </p>
<p>For knowledge's sake, I created the UI components for the app using <a target="_blank" href="https://www.shadcn-vue.com">shadcn-vue</a>. It consists of an amazing collection of accessible, reusable UI components that you can customize to your taste. Check out the <a target="_blank" href="https://www.shadcn-vue.com/docs/installation/nuxt.html">installation</a> steps for more details on how easy it was to set up for the Nuxt app. </p>
<p>It also comes with an installation of the <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a> utility library that’ll we'll use for styling in this tutorial.</p>
<p>Clone the boilerplate setup from the GitHub repo <a target="_blank" href="https://github.com/Young-Einstein10/laravel-nuxt-app">here</a> so we can get started.</p>
<p>Navigate to <code>pages/index.vue</code> and replace the content of the index page with this:</p>
<pre><code class="lang-vue">&lt;template&gt;
 &lt;main class="bg-white p-10 min-h-screen"&gt;
   &lt;div class="max-w-4xl mx-auto"&gt;
     &lt;header class="flex justify-between items-center mb-20"&gt;
       &lt;h1 class="font-semibold text-4xl"&gt;My Library&lt;/h1&gt;
       &lt;Button&gt;Add New Book&lt;/Button&gt;
     &lt;/header&gt;


     &lt;div v-if="isLoading"&gt;
       &lt;p class="italic text-2xl font-medium text-center"&gt;Loading...&lt;/p&gt;
     &lt;/div&gt;
     &lt;div class="mt-8"&gt;
       &lt;div class="flex flex-col gap-8"&gt;
         &lt;div class="flex gap-4" v-for="book in books"&gt;
           &lt;div
             v-if="book.cover_image"
             class="rounded-lg w-32 h-44 flex items-center justify-center"
           &gt;
             &lt;NuxtImg
               :src="`${API_BASE_URL}/images/${book.cover_image}`"
               :alt="book.title"
               class="w-full h-auto"
             /&gt;
           &lt;/div&gt;
           &lt;div v-else class="bg-slate-300 rounded-lg w-32 h-44"&gt;&lt;/div&gt;
           &lt;div class="flex items-center justify-between flex-1"&gt;
             &lt;div&gt;
               &lt;h3 class="font-medium text-xl mb-4"&gt;{{ book.title }}&lt;/h3&gt;
               &lt;p class="mb-2"&gt;
                 &lt;span&gt;by:&lt;/span&gt;
                 &lt;span class="italic"&gt; {{ book.author }} &lt;/span&gt;
               &lt;/p&gt;
               &lt;p&gt;
                 &lt;span&gt;Published:&lt;/span&gt;
                 {{ book.published_date }}
               &lt;/p&gt;
             &lt;/div&gt;
             &lt;div class="actions flex gap-4"&gt;
               &lt;Button&gt;Edit&lt;/Button&gt;
               &lt;Button variant="destructive"&gt; Delete &lt;/Button&gt;
             &lt;/div&gt;
           &lt;/div&gt;
         &lt;/div&gt;
       &lt;/div&gt;
     &lt;/div&gt;
   &lt;/div&gt;
 &lt;/main&gt;
&lt;/template&gt;


&lt;script lang="ts" setup&gt;
import { API_BASE_URL } from "@/utils/constants";
import type { BookProps } from "@/utils/types";


const isLoading = ref(false);
const books = ref&lt;BookProps[]&gt;([]);
&lt;/script&gt;
</code></pre>
<p>We’ll need to create a new folder called <code>utils</code> in your root project which will contain two files: <code>constants.ts</code> and <code>types.ts</code>. Paste the following code snippet in your <code>constants.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> API_BASE_URL = <span class="hljs-string">"http://localhost:8000"</span>;
</code></pre>
<p>And the following in your <code>types.ts</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> BookProps {
     id?: <span class="hljs-built_in">number</span>;
     title: <span class="hljs-built_in">string</span>;
     author: <span class="hljs-built_in">string</span>;
     isbn: <span class="hljs-built_in">string</span>;
     published_date: <span class="hljs-built_in">string</span>;
     cover_image?: <span class="hljs-built_in">string</span>;
     user_id?: <span class="hljs-built_in">number</span>;
     created_at?: <span class="hljs-built_in">string</span>;
     updated_at?: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p>Save the <code>pages/index.vue</code> file. You should be able to see this result in your browser:</p>
<p><img src="https://lh7-us.googleusercontent.com/1M7J0_i-u7rUaHr6vX5na1sReyIh1e1s5VGFh1mZteE0j7sXM5lFUWBmPXZNDbLuobYM9nkJtodCvUTZig22KnJS471YHdOowjXmlmMBwkBuYpQ0J10rgi_xmJt8Vyfm_tHqsTEh6wdtqLx1q1X7KDc" alt="Image" width="1600" height="871" loading="lazy">
<em>Scaffolding the library frontend with Nuxt</em></p>
<p>Now, we need to create a side drawer and a dialog for adding new books and deleting added books from the database, respectively. </p>
<p>Create a new file called <code>BookDrawer.vue</code> in the component's root and add the following code in there:</p>
<pre><code class="lang-vue">&lt;script setup lang="ts"&gt;
import type { BookProps } from "@/utils/types";


type PickedProps = "title" | "author" | "isbn" | "published_date";
interface CustomBookProps extends Pick&lt;BookProps, PickedProps&gt; {
 cover_image?: string;
}


const props = defineProps&lt;{ open: boolean; book?: BookProps }&gt;();
const emit = defineEmits(["update:open", "refresh-data"]);


const isSubmitting = ref(false);
let form = reactive&lt;CustomBookProps&gt;({
 title: "",
 author: "",
 isbn: "",
 published_date: "",
 cover_image: undefined,
});


const onFileChange = async (e: any) =&gt; {
 form.cover_image = e.target.files[0];
};


const onSubmit = async () =&gt; {};


const closeDrawer = (openState: boolean) =&gt; emit("update:open", openState);
&lt;/script&gt;


&lt;template&gt;
 &lt;Sheet :open="open" @update:open="closeDrawer"&gt;
   &lt;SheetContent class="w-full bg-white"&gt;
     &lt;SheetHeader&gt;
       &lt;SheetTitle&gt;
         &lt;template v-if="book"&gt; Edit Book &lt;/template&gt;
         &lt;template v-else&gt;Add New Book&lt;/template&gt;
       &lt;/SheetTitle&gt;
       &lt;SheetDescription&gt;
         Make changes to your profile here. Click save when you're done.
       &lt;/SheetDescription&gt;
     &lt;/SheetHeader&gt;
     &lt;div class="grid gap-4 py-4"&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="bookTitle" class="text-right"&gt; Book Title &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.title"
           type="text"
           id="bookTitle"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="author" class="text-right"&gt; Author &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.author"
           type="text"
           id="author"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="isbn" class="text-right"&gt; ISBN &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.isbn"
           type="text"
           id="isbn"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="published_date" class="text-right"&gt;
           Published Date
         &lt;/Label&gt;
         &lt;Input
           v-model:model-value="form.published_date"
           type="date"
           id="published_date"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
       &lt;div class="grid grid-cols-4 items-center gap-4"&gt;
         &lt;Label for="cover_image" class="text-right"&gt; Book Cover &lt;/Label&gt;


         &lt;Input
           type="file"
           @change="onFileChange"
           id="cover_image"
           class="col-span-3"
           required
         /&gt;
       &lt;/div&gt;
     &lt;/div&gt;
     &lt;SheetFooter&gt;
       &lt;Button type="submit" @click="onSubmit"&gt;
         &lt;template v-if="isSubmitting"&gt;Saving...&lt;/template&gt;
         &lt;template v-else&gt; Save changes &lt;/template&gt;
       &lt;/Button&gt;
     &lt;/SheetFooter&gt;
   &lt;/SheetContent&gt;
 &lt;/Sheet&gt;
&lt;/template&gt;
</code></pre>
<p>Create another file called <code>DeleteBookDrawer.vue</code> and add the following code content to it: </p>
<pre><code class="lang-vue">&lt;script lang="ts" setup&gt;
const emit = defineEmits(["refresh-data", "update:open"]);
const props = defineProps&lt;{ open: boolean; book?: BookProps }&gt;();
&lt;/script&gt;


&lt;template&gt;
 &lt;AlertDialog
   :open="open"
   @update:open="(openState) =&gt; $emit('update:open', openState)"
 &gt;
   &lt;AlertDialogContent&gt;
     &lt;AlertDialogHeader&gt;
       &lt;AlertDialogTitle&gt;Are you absolutely sure?&lt;/AlertDialogTitle&gt;
       &lt;AlertDialogDescription&gt;
         This action cannot be undone. This will permanently delete your book
         and remove your data from the server.
       &lt;/AlertDialogDescription&gt;
     &lt;/AlertDialogHeader&gt;
     &lt;AlertDialogFooter&gt;
       &lt;AlertDialogCancel @click="$emit('update:open', false)"
         &gt;Cancel&lt;/AlertDialogCancel
       &gt;
       &lt;AlertDialogAction&gt;Continue&lt;/AlertDialogAction&gt;
     &lt;/AlertDialogFooter&gt;
   &lt;/AlertDialogContent&gt;
 &lt;/AlertDialog&gt;
&lt;/template&gt;
</code></pre>
<p>Import the newly created files in your index page as you can see below:</p>
<pre><code class="lang-vue">&lt;template&gt;
 &lt;main class="bg-white p-10 min-h-screen"&gt;
    ...

   &lt;BookDrawerDialog
     v-if="isBookDrawerOpen"
     :open="isBookDrawerOpen"
     @update:open="(open: boolean) =&gt; isBookDrawerOpen = open"
     :book="currentBook"
     @refresh-data="fetchBooks"
   /&gt;
   &lt;DeleteBookDialog
     v-if="isDeleteBookDialogOpen"
     :open="isDeleteBookDialogOpen"
     @update:open="(open: boolean) =&gt; isDeleteBookDialogOpen = open"
     :book="currentBook"
     @refresh-data="fetchBooks"
   /&gt;
 &lt;/main&gt;
&lt;/template&gt;
</code></pre>
<p>Now, we need to create a system for opening and closing the drawer and dialog, respectively. We’ll be creating a <a target="_blank" href="https://vuejs.org/guide/essentials/reactivity-fundamentals.html#ref">ref</a> to track the open/closed state for the drawer and dialog components in your index page. Paste the following code content in the script stage to enable that:</p>
<pre><code class="lang-vue">&lt;script lang="ts" setup&gt;

...

const currentBook = ref&lt;BookProps&gt;();


const isBookDrawerOpen = ref(false);
const isDeleteBookDialogOpen = ref(false);


const toggleAddBookDrawer = () =&gt; {
 isBookDrawerOpen.value = !isBookDrawerOpen.value;
};


const toggleEditBookDrawer = (book: BookProps) =&gt; {
 currentBook.value = book;
 isBookDrawerOpen.value = !isBookDrawerOpen.value;
};


const toggleDeleteDialog = (book: BookProps) =&gt; {
 currentBook.value = book;
 isDeleteBookDialogOpen.value = !isDeleteBookDialogOpen.value;
};
&lt;/script&gt;
</code></pre>
<p>Update the <code>Edit</code> and <code>Delete</code> buttons in your index page to include the toggle handler:</p>
<pre><code class="lang-vue">    ...

             &lt;div class="actions flex gap-4"&gt;
               &lt;Button @click="toggleEditBookDrawer(book)"&gt;Edit&lt;/Button&gt;
               &lt;Button @click="toggleDeleteDialog(book)" variant="destructive"&gt;
                 Delete
               &lt;/Button&gt;
             &lt;/div&gt;

           ...
</code></pre>
<p>Include the <code>toggleAddDrawer</code> in your Add Book button too:</p>
<pre><code class="lang-vue">&lt;Button @click="toggleAddBookDrawer"&gt;Add New Book&lt;/Button&gt;
</code></pre>
<p>Save the file changes and view in your browser. You should have the drawer active now:  </p>
<p><img src="https://lh7-us.googleusercontent.com/82AHDkwCX-oTW6pOpcwi735-8i5HluzloFnwDGnidH5gNKP8dkj3pXxr-Oa6gpMcQGZA-FnzsCP5nzpP0uGPDMhyTMkNqvxLXnVaK0TWFTBG-IrQPGU74-vhPpqCz1y7CcRULG7AUWcJsmejfdR5O2k" alt="Image" width="600" height="400" loading="lazy">
<em>Add New Book Drawer</em></p>
<p>Moving on, we need to be sure the EditDrawer and Delete Dialog work. We’ll create a temporary array of mock book data to test that so you can see the result on your page. Update the value of the <code>books</code> ref to the data you have below (and don’t forget to update the books ref value to an empty array – that is <code>books = ref&lt;BookProps&gt;([])</code>):</p>
<pre><code class="lang-vue">const books = ref&lt;BookProps[]&gt;([
     {
       title: "Atomic Habits",
       author: "James Clear",
       isbn: "XYEOUIUEHEJ2902",
       published_date: "2020-09-01",
       cover_image: "",
     },
     {
       title: "The Power of Habits",
       author: "Charles Duhigg",
       isbn: "WIUIQUEWHSDBSD28",
       published_date: "2012-02-28",
       cover_image: "",
     },
]);
</code></pre>
<p>Save the changes and check out the EditDrawer and DeleteDialog in action:</p>
<p><img src="https://lh7-us.googleusercontent.com/_kiROvQBeLBq1UV9Zj4n0tVYOdW9nLUUjb5qlxGtgFGvAJtHsGJ0CvoEyYHxB4BIQ31SznXfwkiXhg_5VowECEMuSCYnIhfEHWeiHAt-hhA9dETiyTOgqNjsxxAenIhx0gl2koYaLVGYnohdpTV13KU" alt="Image" width="1519" height="824" loading="lazy">
<em>Delete dialogue</em></p>
<p><img src="https://lh7-us.googleusercontent.com/YSTKDAEOpLp45EXn61OlRYTTW3hYmOF-ur3SES7emDWMyigINQNvpAozgscEF84-RgiZrgCkTIxtZehFYDMIntzWmy589khRcP7FV4OpdacfXvsi8xRlJG9qWBuXc8Csbal0C_Tr-_lrDt6AatSnVIg" alt="Image" width="1513" height="821" loading="lazy">
<em>EditDrawer</em></p>
<h2 id="heading-how-to-integrate-the-laravel-api-in-the-frontend">How to Integrate the Laravel API in the Frontend</h2>
<h3 id="heading-getting-all-books-in-the-library">Getting all books in the library</h3>
<p>So far, we only have the UI of our frontend working, and the data being displayed is not from the API. To make the app fully dynamic, you'll need to make an HTTP request to the server to get all the data you need for your page. </p>
<p>Update the body of the <code>fetchBooks</code> function in the index page to the following code:</p>
<pre><code class="lang-vue">const fetchBooks = async () =&gt; {
 try {
   isLoading.value = true;
   const response = await $fetch&lt;{ books: BookProps[] }&gt;(
     `${API_BASE_URL}/api/books`
   );


   books.value = response.books.sort(
     (a, b) =&gt;
       Number(new Date(b.created_at as string)) -
       Number(new Date(a.created_at as string))
   );


 } catch (error) {
   console.log(error);
 } finally {
   isLoading.value = false;
 }
};


onMounted(() =&gt; {
 fetchBooks();
});
</code></pre>
<p>Basically, you're calling your <code>fetchBooks</code> function when the page mounts using one of the <a target="_blank" href="https://vuejs.org/api/composition-api-lifecycle.html#onmounted">lifecycle hooks</a> available in Vue.js. When the function is called, it executes the instruction you created in the body:</p>
<ul>
<li>Set the loading state of the app to true.</li>
<li>Make a GET HTTP request to the books endpoint of your API and set the response to the books ref you created earlier.</li>
<li>When the request-response has been completed, set the loading state of the app back to false to indicate to the user the process has been completed.</li>
</ul>
<p>Save the changes, and you should see the results in the browser:</p>
<p><img src="https://lh7-us.googleusercontent.com/SGyND7gBGa7NmUXM4k-7Jn8pyuRizWhMJOOGnwZMMe7NIz6Jzt_qC0D3JUf-7X9IlGJMdm8XLbzGHG8cuKcJ6z83XpxOrrSGdAgOFaaUd6tDmo6-bDY-3PZG1lLRW4mywF77A86ABoIggR6NGwDDK5A" alt="Image" width="1509" height="818" loading="lazy">
<em>Fetching the book list</em></p>
<h3 id="heading-creating-a-new-book-1">Creating a new book</h3>
<p>For your book creation to be successful, you need to include the logic for creating a book in your <code>BookDrawer</code>. Add the following code in the <code>BookDrawer.vue</code> file:</p>
<pre><code class="lang-vue">...

const isSubmitting = ref(false);


const addNewBook = async (data: any) =&gt; {
 const response = await $fetch(`${API_BASE_URL}/api/books`, {
   method: "POST",
   body: data,
   headers: {
     Accept: "application/json",
   },
 });
 return response;
};


const onSubmit = async () =&gt; {
 try {
   const formData = new FormData();
   Object.keys(form).forEach((key) =&gt; {
     // @ts-ignore
     if (form[key]) {
       formData.append(key, form[key as never]);
     }
   });
   isSubmitting.value = true;
   const data = await addNewBook(formData);
   closeDrawer(false);
   emit("refresh-data");
 } catch (error) {
   console.log(error);
 } finally {
   isSubmitting.value = false;
 }
};

...
</code></pre>
<p>Similar to the implementation for fetching your books, the only difference here is that you're making a POST HTTP request instead. You use this method whenever you need to mutate data on the server.</p>
<p><img src="https://lh7-us.googleusercontent.com/7zQhG_V35mPWJW9sxMyTP8QjnezzoVg1eX08pdJ3Mw7EiXPwcHiTLdYHUD8HWqXg54SjvXymxrBCVWUjXbiuVNCAdj5vccdPyFl6KFJuuKAFTC9sYUQkkGYnESKr3aVHgUONnyOqAf1-fkafeqgyifE" alt="Image" width="1600" height="778" loading="lazy">
<em>Creating a new book</em></p>
<h3 id="heading-editing-a-book-1">Editing a book</h3>
<p>Update your <code>BookDrawer</code> with the following code to enable editing of the books that have been added:</p>
<pre><code class="lang-vue">&lt;script setup lang="ts"&gt;

...

onMounted(() =&gt; {
 if (props.book) {
   form = props.book;
 }
});


const editBook = async () =&gt; {
 try {
   isSubmitting.value = true;
   const resp = await $fetch(`${API_BASE_URL}/api/books/${props?.book?.id}`, {
     method: "PUT",
     body: formData,
   });
   closeDrawer(false);
   emit("refresh-data");
 } catch (error) {
   console.log(error);
 } finally {
   isSubmitting.value = false;
 }
};

...
&lt;/script&gt;
</code></pre>
<p>Update the save button with the toggle handler also:</p>
<pre><code class="lang-vue">&lt;Button
  type="submit"
  @click="() =&gt; (book?.id ? editBook() : onSubmit())"
&gt;
  &lt;template v-if="isSubmitting"&gt;Saving...&lt;/template&gt;
  &lt;template v-else&gt; Save changes &lt;/template&gt;
&lt;/Button&gt;
</code></pre>
<p>And here's the result:</p>
<p><img src="https://lh7-us.googleusercontent.com/aUGh8K6y9u8qXRwckj_PRphjvbnTR2DXf8jnHVehD2aaKtaCdCK1SVPjW-AbWwp_CXZROabKsTk0JBCaUjLAAzEbDHlFCbG6sjkpcVdW9VaKPqoYkVp2aoEtec5vPpjxFtNVAdFFWp3E3T_ZWNdxtYc" alt="Image" width="600" height="400" loading="lazy">
<em>Updating a book</em></p>
<h3 id="heading-deleting-a-book">Deleting a book</h3>
<p>Paste the following in the <code>DeleteBookDialog.vue</code> file:</p>
<pre><code class="lang-vue">const refreshData = () =&gt; emit("refresh-data");
const closeDialog = () =&gt; emit("update:open", false);


const deleteBook = async () =&gt; {
 await $fetch(`${API_BASE_URL}/api/books/${props?.book?.id}`, {
   method: "DELETE",
 });
 closeDialog();
 refreshData();
};
</code></pre>
<p>This works fine also:</p>
<p><img src="https://lh7-us.googleusercontent.com/KyP9Y8L7qzkvvQCfyuFSBCQvrV30hE-rpQ72Q2Z1-DL-6rzkXnFAuwWEAL0Y1t110slYV813RPXyTKlxxKG5gHFQitUdVuT_7a-gkPxpJXtYOtRsxHWI0JSt9nFDeM6ywNf3ReoCxeIsKzguzZosw_E" alt="Image" width="1600" height="816" loading="lazy">
<em>Deleting a book</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building full-stack web apps with Laravel and Nuxt is a very rewarding experience as you can enjoy the benefits and rewards from the best of both worlds. </p>
<p>Since Laravel is a feature-rich backend framework, it comes with many modules and packages that you can install whenever a feature or implementation needs to be worked on. </p>
<p>And Nuxt, as a Vue framework, gives you the superpower of building single-page applications that are fast, accessible, and responsive, giving end-users a delightful experience.</p>
<p>In this tutorial, we went over how we can build full-stack applications using Nuxt and the Laravel framework. I hope you were able to learn something new and are excited to explore this further. Feel free to reach out if you have any questions or ideas. </p>
<p>The full code for the tutorial can be found in the GitHub repo <a target="_blank" href="https://github.com/Young-Einstein10/laravel-nuxt-app/tree/final">here</a>.</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><a target="_blank" href="https://laravel.com/docs/10.x">Laravel Docs</a></li>
<li><a target="_blank" href="https://nuxt.com/docs/getting-started/installation">Nuxt Documentation</a></li>
<li><a target="_blank" href="https://www.shadcn-vue.com">Shadcn-vue</a>, ready-made UI components that can be copy-pasted anytime</li>
<li><a target="_blank" href="https://vuejs.org">Vue Docs</a>  </li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use React.js with Laravel to Build a Draggable Tasklist App ]]>
                </title>
                <description>
                    <![CDATA[ You may have seen tutorials that help you build a simple React.js app that use some third-party API or a Node.js server as a backend. You could also use Laravel for this purpose and integrate it with React. As a backend framework, Laravel actually of... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/use-react-with-laravel/</link>
                <guid isPermaLink="false">66ba2afae059e7f2fa5d3276</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ San B ]]>
                </dc:creator>
                <pubDate>Fri, 26 Jan 2024 00:09:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/freecodecamp-boolfalse-laravel-react-vite-draggable-tasklist.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You may have seen tutorials that help you build a simple React.js app that use some third-party API or a Node.js server as a backend. You could also use Laravel for this purpose and integrate it with React.</p>
<p>As a backend framework, Laravel actually offers a tool to help you do this, called <a target="_blank" href="https://laravel.com/docs/10.x/frontend#inertia">Inertia</a>. Here's what the docs say about it:</p>
<blockquote>
<p>It bridges the gap between your Laravel application and your modern Vue or React frontend, allowing you to build full-fledged, modern frontends using Vue or React while leveraging Laravel routes and controllers for routing, data hydration, and authentication — all within a single code repository.</p>
</blockquote>
<p>But what if you don't want to use such a tool? And instead, you just want to use React.js as a frontend library and have a simple Laravel-powered backend?</p>
<p>Well, in this article, you will learn how to use React.js with Laravel as a backend by building a draggable tasklist app.</p>
<p>For this full-stack single-page app, you'll use <a target="_blank" href="https://vitejs.dev/">Vite.js</a> as your frontend build tool and the <a target="_blank" href="https://www.npmjs.com/package/react-beautiful-dnd">react-beautiful-dnd</a> package for draggable items.</p>
<p>By the end of this article, you will have a single-page app for managing tasks, which will look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/tasklist.png" alt="Image" width="600" height="400" loading="lazy">
<em>Captured from a local working project</em></p>
<p>In this article, we'll create a dynamic page that will have a list of tasks, each of which will belong to a specific project. This way, the user will be able to select a project, and only the tasks of the selected project will be shown on the page. The user can also create a new task for the current project, as well as edit, delete and reorder tasks by dragging and dropping them.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-the-backend-how-to-install-laravel">The Backend: How to Install Laravel</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-models-and-migrations">How to Create Models and Migrations</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-seeders">How to Create Seeders</a></li>
<li><a class="post-section-overview" href="#heading-how-to-connect-to-the-mysql-database">How to Connect to the MySQL Database</a></li>
<li><a class="post-section-overview" href="#heading-service-injection">Service Injection</a></li>
<li><a class="post-section-overview" href="#heading-web-and-api-routes-in-laravel">Web and API Routes in Laravel</a></li>
<li><a class="post-section-overview" href="#heading-validation-requests-in-laravel">Validation Requests in Laravel</a></li>
<li><a class="post-section-overview" href="#heading-how-to-write-a-controller-that-uses-services">How to Write a Controller that Uses Services</a></li>
<li><a class="post-section-overview" href="#heading-how-to-test-the-api-routes">How to Test the API Routes</a></li>
<li><a class="post-section-overview" href="#heading-the-frontend-how-to-install-the-packages">The Frontend: How to Install the Packages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-vitejs">How to Configure Vite.js</a></li>
<li><a class="post-section-overview" href="#heading-reactjs-initial-integration">React.js – Initial Integration</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-css">How to Add CSS</a></li>
<li><a class="post-section-overview" href="#heading-a-service-for-the-api-requests">A Service for the API requests</a></li>
<li><a class="post-section-overview" href="#heading-reactjs-components">React.js Components</a></li>
<li><a class="post-section-overview" href="#heading-final-results">Final Results</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before following along, it would be helpful to have a basic understanding of React.js, Laravel, and familiarity with fundamental web development concepts.</p>
<p>You'll need the following tools for the app that we'll build in this article:</p>
<ul>
<li><strong>PHP</strong> 8.1 or above (run <code>php -v</code> to check the version)</li>
<li><strong>Composer</strong> (run <code>composer</code> to check that it exists)</li>
<li><strong>Node.js</strong> 18 or above (run <code>node -v</code> to check the version)</li>
<li><strong>MySQL</strong> 5.7 or above (run <code>mysql --version</code> to check if it exists, or follow the <a target="_blank" href="https://dev.mysql.com/doc/mysql-windows-excerpt/5.7/en/windows-testing.html">docs</a>)</li>
</ul>
<p>Additional (optional) tools that you can use:</p>
<ul>
<li><strong>Postman</strong> – a program with a UI for testing the API routes</li>
<li><strong>curl</strong> – a CLI command for testing the API routes</li>
</ul>
<p>We'll start by building out the backend, and then move to the frontend.</p>
<h2 id="heading-the-backend-how-to-install-laravel">The Backend: How to Install Laravel</h2>
<p>First, if you don't have it already, you'll need to install the Laravel framework on your local machine.</p>
<p>One way to install Laravel is by using a popular dependency manager for PHP called Composer. Here's the command to do so:</p>
<pre><code class="lang-shell">composer create-project laravel/laravel tasklist
</code></pre>
<p>This will install the latest stable version of Laravel in your local machine (currently it's version 10).</p>
<p>The <em>tasklist</em> in the command is the app's root folder name, which you can set to whatever you want.</p>
<p>At this point, you can <code>cd</code> into the project's folder and run the backend app without needing to have a virtual server set up:</p>
<pre><code class="lang-shell">cd tasklist/ &amp;&amp; php artisan serve
</code></pre>
<p>The <code>artisan</code> in the above command is a CLI tool included in Laravel. It exists at the root of your Laravel application as the <code>artisan</code> script file, which provides a number of helpful commands that can assist you while you build your application.<br>We'll use it in this article often.</p>
<p>Visit <a target="_blank" href="http://127.0.0.1:8000"><code>http://127.0.0.1:8000</code></a> in your browser to see the default page. It should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/laravel.png" alt="Image" width="600" height="400" loading="lazy">
<em>Laravel welcome page</em></p>
<h2 id="heading-how-to-create-models-and-migrations">How to Create Models and Migrations</h2>
<p>Now, let's create <em>Project</em> and <em>Task</em> models, as well as migrations for them.</p>
<p>Models are the way your app entities should be defined, and migrations are like schema definitions for storing the records of those entities in the database. </p>
<p>You can create model and migration files manually as well as generate them using the <code>artisan</code> command:</p>
<pre><code class="lang-shell">php artisan make:model Project -m
php artisan make:model Task -m
</code></pre>
<p>The <code>-m</code> argument will automatically generate a migration file using the provided model name.</p>
<p>Keep the command execution sequence as it is, so the Project's migration later can run before the Task's migration.</p>
<p>This is important, because the <code>projects</code> and <code>tasks</code> tables should have a one-to-many relationship (1-N): each task will refer to a single project, or, in other words, each project can have multiple tasks.</p>
<p>Set the <code>Project</code> model's <code>$fillable</code> fields and the <code>task()</code> relationship method as below:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Factories</span>\<span class="hljs-title">HasFactory</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Relations</span>\<span class="hljs-title">HasMany</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Project</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">HasFactory</span>;

    <span class="hljs-keyword">protected</span> $table = <span class="hljs-string">'projects'</span>;
    <span class="hljs-keyword">public</span> $timestamps = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">protected</span> $fillable = [
        <span class="hljs-string">'id'</span>, <span class="hljs-comment">// primary key, auto-increment, integer</span>
        <span class="hljs-string">'name'</span>, <span class="hljs-comment">// string</span>
    ];

    <span class="hljs-comment">// a project can have multiple tasks</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tasks</span>(<span class="hljs-params"></span>): <span class="hljs-title">HasMany</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;hasMany(Task::class);
    }
}
</code></pre>
<p>By default, the <code>$timestamps</code> public property has a <code>true</code> value, which is coming from the parent <code>Model</code> class. This means that the <code>created_at</code> and <code>updated_at</code> columns in your database table will be maintained automatically by <em>Eloquent</em> (the ORM included in Laravel).</p>
<p>But you can customize it by changing its value to <code>false</code>. We don't need to have <code>created_at</code> and <code>updated_at</code> fields in the <code>projects</code> table, so we'll set the <code>$timestamps</code> to <code>false</code>.</p>
<p>Set the <code>Task</code> model's <code>$fillable</code> fields, <code>project()</code> relationship method, and <code>created</code> accessor. An <a target="_blank" href="https://laravel.com/docs/10.x/eloquent-mutators#defining-an-accessor">accessor</a> in Laravel is like a function between the database and your code, that can access the already fetched database record and modify it.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Factories</span>\<span class="hljs-title">HasFactory</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Relations</span>\<span class="hljs-title">BelongsTo</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">SoftDeletes</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Task</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">HasFactory</span>, <span class="hljs-title">SoftDeletes</span>;

    <span class="hljs-keyword">protected</span> $table = <span class="hljs-string">'tasks'</span>;
    <span class="hljs-keyword">protected</span> $fillable = [
        <span class="hljs-string">'id'</span>, <span class="hljs-comment">// primary key, auto-increment, integer</span>
        <span class="hljs-string">'project_id'</span>, <span class="hljs-comment">// foreign key, integer</span>

        <span class="hljs-string">'priority'</span>, <span class="hljs-comment">// integer</span>
        <span class="hljs-string">'title'</span>, <span class="hljs-comment">// string</span>
        <span class="hljs-string">'description'</span>, <span class="hljs-comment">// text</span>
    ];
    <span class="hljs-keyword">protected</span> $appends = [
        <span class="hljs-string">'created'</span>,
    ];

    <span class="hljs-comment">// each task belongs to a single project</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">project</span>(<span class="hljs-params"></span>): <span class="hljs-title">BelongsTo</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;belongsTo(Project::class);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCreatedAttribute</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;created_at-&gt;diffForHumans();
    }
}
</code></pre>
<p>Above, in the <code>Task</code> model, there is an accessor called <code>created</code>. For having an accessor, we have the <code>created</code> field in the <code>$appends</code> array, and also a public function <code>getCreatedAttribute()</code>.</p>
<p>In <code>get**&lt;WordsInCamelCase&gt;**Attribute()</code> function there is logic that will run to modify the already fetched database record.</p>
<p>In our case the <code>getCreatedAttribute()</code> function will return a human-friendly and readable time difference between the current time and the given time. For example, <em>"3 mins ago"</em> or <em>"4 months ago"</em>.</p>
<p>Now that the models are ready, let's set up the migrations.</p>
<p>First, set up a migration for the <code>projects</code> table:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Migrations</span>\<span class="hljs-title">Migration</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Schema</span>\<span class="hljs-title">Blueprint</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Schema</span>;

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Migration</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        Schema::create(<span class="hljs-string">'projects'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();
            $table-&gt;string(<span class="hljs-string">'name'</span>);
        });
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">down</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        Schema::dropIfExists(<span class="hljs-string">'projects'</span>);
    }
};
</code></pre>
<p>Then set up a migration for the <code>tasks</code> table:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Migrations</span>\<span class="hljs-title">Migration</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Schema</span>\<span class="hljs-title">Blueprint</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Schema</span>;

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Migration</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        Schema::create(<span class="hljs-string">'tasks'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();

            $table-&gt;foreignId(<span class="hljs-string">'project_id'</span>)-&gt;nullable()-&gt;constrained();

            $table-&gt;integer(<span class="hljs-string">'priority'</span>);
            $table-&gt;string(<span class="hljs-string">'title'</span>);
            $table-&gt;text(<span class="hljs-string">'description'</span>)-&gt;nullable();

            $table-&gt;timestamps();
            $table-&gt;softDeletes();
        });
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">down</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-comment">// drop existing foreign keys</span>
        Schema::table(<span class="hljs-string">'tasks'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            <span class="hljs-keyword">if</span> (Schema::hasColumn(<span class="hljs-string">'tasks'</span>, <span class="hljs-string">'project_id'</span>)) {
                $table-&gt;dropForeign([<span class="hljs-string">'project_id'</span>]);
            }
        });

        <span class="hljs-comment">// drop the table</span>
        Schema::dropIfExists(<span class="hljs-string">'tasks'</span>);
    }
};
</code></pre>
<p>The <code>tasks</code> table has a foreign key <code>project_id</code>, which is a reference to the <code>projects</code> table. So it's a good practice to update the <code>down()</code> method too, to be sure that the <code>project_id</code> foreign will be dropped before dropping the actual <code>projects</code> table.</p>
<p>There is also a <code>priority</code> field, which will be a non-nullable natural number for ordering the tasks. And optionally, you can add a soft deletion feature to the <code>Task</code> model.</p>
<h2 id="heading-how-to-create-seeders">How to Create Seeders</h2>
<p>Now we need to add dummy data to the <code>projects</code> and <code>tasks</code> tables. To seed some data in the database, you can use Laravel <em>seeders</em>. This allows you to create dummy data to use in your database. </p>
<p>If you want to read more about how this works, you can <a target="_blank" href="https://laravel.com/docs/10.x/seeding">check out the docs here</a>.</p>
<p>Laravel provides a way to generate those files by using <code>make:seeder</code> artisan command:</p>
<pre><code class="lang-shell">php artisan make:seeder ProjectsSeeder
php artisan make:seeder TasksSeeder
</code></pre>
<p>So with the above commands, you'll have <code>database/seeders/ProjectsSeeder.php</code> and <code>database/seeders/TasksSeeder.php</code> files created.</p>
<p>At first, you'll need to set up the <code>ProjectsSeeder</code> to add a few projects to the <code>projects</code> table. Then you can set up the <code>TasksSeeder</code> to add tasks to the <code>tasks</code> table.</p>
<p>As I mentioned at the beginning, each task will belong to a specific project. From a relational database perspective, this means that each entry in the <code>tasks</code> table will link to a specific entry in the <code>projects</code> table. Here's the importance of having a <code>project_id</code> foreign key in the <code>tasks</code> table to be able to relate each task to a specific project as well as retrieve the specific project's tasks.</p>
<p>You can imagine the database structure by looking at the following visuals:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/laravel_react_tasklist-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>generated by PHPStorm IDE</em></p>
<p>Using the example below, you can generate 3 projects:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Seeders</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Project</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Seeder</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProjectsSeeder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Seeder</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">1</span>; $i &lt;= <span class="hljs-number">3</span>; $i++) {
            Project::create([
                <span class="hljs-string">'name'</span> =&gt; <span class="hljs-string">"Project <span class="hljs-subst">$i</span>"</span>,
            ]);
        }
    }
}
</code></pre>
<p>Next, set up <code>TasksSeeder</code>. You'll run all the seeder files after setting them up, and they will run one by one. That being said, at this point your <code>ProjectsSeeder</code> is ready to create a few projects.</p>
<p>By imagining it, the next step will be generating the tasks, each of them will have a reference to one of the already existing projects by its <code>project_id</code> field.</p>
<p>Using the example below, you can generate 10 projects:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Seeders</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Project</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Task</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Seeder</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TasksSeeder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Seeder</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        $project_ids = Project::all()-&gt;pluck(<span class="hljs-string">'id'</span>)-&gt;toArray();

        $now = now();
        $tasks = [];
        $project_priorities = [];
        <span class="hljs-keyword">foreach</span> ($project_ids <span class="hljs-keyword">as</span> $project_id) {
            $project_priorities[$project_id] = <span class="hljs-number">0</span>;
        }

        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">1</span>; $i &lt;= <span class="hljs-number">10</span>; $i++) {
            $project_id = $project_ids[array_rand($project_ids)];
            $project_priorities[$project_id]++;

            $tasks[] = [
                <span class="hljs-string">'project_id'</span> =&gt; $project_id,
                <span class="hljs-string">'priority'</span> =&gt; $project_priorities[$project_id],
                <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">"Task "</span> . $project_priorities[$project_id],
                <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">"Description for Task "</span> . $project_priorities[$project_id],

                <span class="hljs-string">'created_at'</span> =&gt; $now,
                <span class="hljs-string">'updated_at'</span> =&gt; $now,
            ];
        }

        Task::insert($tasks);
    }
}
</code></pre>
<p>The above code just grabs all the project IDs, then randomly chooses a project for each task. In the end, it inserts all the tasks into the <code>tasks</code> table. </p>
<p>As you may have noticed, we're inserting <code>$tasks</code> into the <code>tasks</code> table using the <code>insert()</code> static function, which allows us to insert all the items into the database table with a single query. </p>
<p>But it has a downside as well: it doesn't manage <code>created_at</code> and <code>updated_at</code> fields. That's why there's a need to set up those fields manually by assigning them the same <code>$now</code> timestamp.</p>
<p>Now, when you have all the seeders ready, you need to register them into the <code>DatabaseSeeder</code>.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Seeders</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Seeder</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseSeeder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Seeder</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;call([
            ProjectsSeeder::class,
            TasksSeeder::class,
        ]);
    }
}
</code></pre>
<h2 id="heading-how-to-connect-to-the-mysql-database">How to Connect to the MySQL Database</h2>
<p>Before running migrations and seeds, create a MySQL database and set up the appropriate credentials in the <code>.env</code> file. If there is not a <code>.env</code>, then create it and paste the <code>.env.example</code> file's content into it.</p>
<p>After setting up the database credentials, you'll have these kinds of environment variables:</p>
<pre><code class="lang-env">DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE="&lt;DATABASE_NAME&gt;"
DB_USERNAME="&lt;USERNAME&gt;"
DB_PASSWORD="&lt;PASSWORD&gt;"
</code></pre>
<p>After setting up environment variables, optimize the cache:</p>
<pre><code class="lang-shell">php artisan optimize
</code></pre>
<p>Now you'll be able to create <code>projects</code> and <code>tasks</code> tables in the MySQL database, setup their structure, and add initial records with a single command:</p>
<pre><code class="lang-shell">php artisan migrate:fresh --seed
</code></pre>
<p>In the above command, the <code>migrate:fresh</code> argument will drop all tables from the database. Then it will execute the <code>migrate</code> command, which will run your migrations to create <code>projects</code> and <code>tasks</code> tables appropriately. </p>
<p>With the <code>--seed</code> argument, it will run <code>ProjectsSeeder</code> and <code>TasksSeeder</code> after the migrations. That being said, it will empty your database for you, and will create all the tables and fill all the necessary dummy data.</p>
<p>After running the command, you'll have these kinds of database records:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-74.png" alt="Image" width="600" height="400" loading="lazy">
<em>A screenshot from the PHPStorm IDE</em></p>
<h2 id="heading-service-injection">Service Injection</h2>
<p>Now let's create a controller and a service classes to manage all the task features, such as listing, creating, updating, deleting, and reordering the tasks.</p>
<p>At first, use the below command to generate a controller.</p>
<pre><code class="lang-shell">php artisan make:controller TaskController
</code></pre>
<p>In order not to place all the code in the controller, you can keep only the main logic in it, and move the other logic implementations to another class file. </p>
<p>Those classes are generally called <em>services</em>, and using service implementations in a controller method is called <strong>service injection</strong> (it comes from the term <em>dependency injection</em>).</p>
<p>One of the main advantages of using services is that it helps you create a maintainable codebase.</p>
<p>You can inject your service class into the controller's construction method as an argument, so after each controller execution (when a controller's <code>__construct()</code> method runs) you can initialize an object of service. This means that you can access your service's functions right in your controller.</p>
<p>Now, let's create two separate service classes, which will be used in the <code>TaskController</code>.</p>
<p>Manually create a <code>app/Services/ProjectService.php</code> service class, which will be responsible for the project-related logic.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Project</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Collection</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProjectService</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAll</span>(<span class="hljs-params"></span>): <span class="hljs-title">Collection</span>
    </span>{
        <span class="hljs-keyword">return</span> Project::all();
    }
}
</code></pre>
<p>The second service class will be the <code>app/Services/TaskService.php</code>, which will be responsible for doing task manipulations:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Task</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">DB</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskService</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">list</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $projectId</span>)
    </span>{
        <span class="hljs-keyword">return</span> Task::with(<span class="hljs-string">'project'</span>)-&gt;where(<span class="hljs-string">'project_id'</span>, $projectId)
            -&gt;orderBy(<span class="hljs-string">'priority'</span>)-&gt;get();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getById</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>)
    </span>{
        <span class="hljs-keyword">return</span> Task::where(<span class="hljs-string">'id'</span>, $id)-&gt;with(<span class="hljs-string">'project'</span>)-&gt;first();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span>(<span class="hljs-params">$data</span>): <span class="hljs-title">void</span>
    </span>{
        $count = Task::where(<span class="hljs-string">'project_id'</span>, $data[<span class="hljs-string">'project_id'</span>])-&gt;count();
        $data[<span class="hljs-string">'priority'</span>] = $count + <span class="hljs-number">1</span>;

        Task::create($data);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id, <span class="hljs-keyword">array</span> $data</span>): <span class="hljs-title">void</span>
    </span>{
        $task = <span class="hljs-keyword">$this</span>-&gt;getById($id);
        <span class="hljs-keyword">if</span> (!$task) { <span class="hljs-keyword">return</span>; }

        $task-&gt;update($data);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">delete</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">void</span>
    </span>{
        $task = <span class="hljs-keyword">$this</span>-&gt;getById($id);
        <span class="hljs-keyword">if</span> (!$task) { <span class="hljs-keyword">return</span>; }

        $task-&gt;delete();

        $tasks = Task::where(<span class="hljs-string">'project_id'</span>, $task-&gt;project_id)
            -&gt;where(<span class="hljs-string">'priority'</span>, <span class="hljs-string">'&gt;'</span>, $task-&gt;priority)-&gt;get();
        <span class="hljs-keyword">if</span> ($tasks-&gt;isEmpty()) {
            <span class="hljs-keyword">return</span>;
        }

        $when_then = <span class="hljs-string">""</span>;
        $where_in = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">foreach</span> ($tasks <span class="hljs-keyword">as</span> $task) {
            $when_then .= <span class="hljs-string">"WHEN "</span>.$task-&gt;id
                .<span class="hljs-string">" THEN "</span>.($task-&gt;priority - <span class="hljs-number">1</span>).<span class="hljs-string">" "</span>;
            $where_in .= $task-&gt;id.<span class="hljs-string">","</span>;
        }

        $table_name = (<span class="hljs-keyword">new</span> Task())-&gt;getTable();
        $bulk_update_query = <span class="hljs-string">"UPDATE `"</span>.$table_name
            .<span class="hljs-string">"` SET `priority` = (CASE `id` "</span>.$when_then.<span class="hljs-string">"END)"</span>
            .<span class="hljs-string">" WHERE `id` IN("</span>.substr($where_in, <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>).<span class="hljs-string">");"</span>;

        <span class="hljs-comment">// there is no way to be SQL injected here</span>
        <span class="hljs-comment">// because all the values are not provided by the user</span>
        DB::update($bulk_update_query);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reorder</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $project_id, <span class="hljs-keyword">int</span> $start, <span class="hljs-keyword">int</span> $end</span>): <span class="hljs-title">void</span>
    </span>{
        $items = Task::where(<span class="hljs-string">'project_id'</span>, $project_id)
            -&gt;orderBy(<span class="hljs-string">'priority'</span>)-&gt;pluck(<span class="hljs-string">'priority'</span>, <span class="hljs-string">'id'</span>)-&gt;toArray();

        <span class="hljs-keyword">if</span> ($start &gt; count($items) || $end &gt; count($items)) {
            <span class="hljs-keyword">return</span>;
        }

        $ids = [];
        $priorities = [];
        <span class="hljs-keyword">foreach</span> ($items <span class="hljs-keyword">as</span> $id =&gt; $priority) {
            $ids[] = $id;
            $priorities[] = $priority;
        }

        $out_priority = array_splice($priorities, $start - <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
        array_splice($priorities, $end - <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, $out_priority);

        $when_then = <span class="hljs-string">""</span>;
        $where_in = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">foreach</span> ($priorities <span class="hljs-keyword">as</span> $out_k =&gt; $out_v) {
            $id = $ids[$out_v - <span class="hljs-number">1</span>];
            $when_then .= <span class="hljs-string">"WHEN "</span>.$id.<span class="hljs-string">" THEN "</span>.($out_k + <span class="hljs-number">1</span>).<span class="hljs-string">" "</span>;
            $where_in .= $id.<span class="hljs-string">","</span>;
        }

        $table_name = (<span class="hljs-keyword">new</span> Task())-&gt;getTable();
        $bulk_update_query = <span class="hljs-string">"UPDATE `"</span>.$table_name
            .<span class="hljs-string">"` SET `priority` = (CASE `id` "</span>.$when_then.<span class="hljs-string">"END)"</span>
            .<span class="hljs-string">" WHERE `id` IN("</span>.substr($where_in, <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>).<span class="hljs-string">")"</span>
            .<span class="hljs-string">" AND `deleted_at` IS NULL;"</span>; <span class="hljs-comment">// soft delete</span>

        DB::update($bulk_update_query);
    }
}
</code></pre>
<p>In the above <code>TaskService</code> class, you'll use the following functions in the <code>TaskController</code>.</p>
<ul>
<li><strong>list</strong>: fetches tasks for a given project ID, including the related project, and orders them by <em>priority</em>.</li>
<li><strong>getById</strong>: retrieves a specific task by its ID, including the related project.</li>
<li><strong>store</strong>: stores a new task, calculating the <em>priority</em> based on existing tasks for the same project.</li>
<li><strong>update</strong>: updates an existing task by its ID.</li>
<li><strong>delete</strong>: deletes a task by its ID and adjusts the priorities of remaining tasks in the same project.</li>
<li><strong>reorder</strong>: changes the priorities of tasks within a project, (handles soft delete as well with <code>deleted_at IS NULL</code>).</li>
</ul>
<h2 id="heading-web-and-api-routes-in-laravel">Web and API Routes in Laravel</h2>
<p>Now you can add routes to test the methods you've already written. In this project, we have a stateless app on the frontend which requests API routes for getting JSON data, so it will follow RESTful principles (GET, POST, PUT, DELETE methods). Only the initial HTML page will be retrieved as a whole web page.</p>
<p>So now, set up a route in <code>routes/web.php</code> for the initial single-page:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Route</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>\<span class="hljs-title">TaskController</span>;

Route::group([<span class="hljs-string">'prefix'</span> =&gt; <span class="hljs-string">'/'</span>, <span class="hljs-string">'as'</span> =&gt; <span class="hljs-string">'tasks.'</span>], <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    Route::get(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'index'</span>])-&gt;name(<span class="hljs-string">'index'</span>);
});
</code></pre>
<p>Set up API routes in <code>routes/api.php</code> like this:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Route</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>\<span class="hljs-title">TaskController</span>;

Route::group([<span class="hljs-string">'prefix'</span> =&gt; <span class="hljs-string">'/tasks'</span>, <span class="hljs-string">'as'</span> =&gt; <span class="hljs-string">'tasks.'</span>], <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    Route::get(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'list'</span>]);
    Route::get(<span class="hljs-string">'/{id}'</span>, [TaskController::class, <span class="hljs-string">'get'</span>])
        -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'[1-9][0-9]*'</span>);
    Route::post(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'store'</span>]);
    Route::put(<span class="hljs-string">'/{id}'</span>, [TaskController::class, <span class="hljs-string">'update'</span>])
        -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'[1-9][0-9]*'</span>);
    Route::delete(<span class="hljs-string">'/{id}'</span>, [TaskController::class, <span class="hljs-string">'delete'</span>])
        -&gt;where(<span class="hljs-string">'id'</span>, <span class="hljs-string">'[1-9][0-9]*'</span>);
    Route::put(<span class="hljs-string">'/'</span>, [TaskController::class, <span class="hljs-string">'reorder'</span>]);
});
</code></pre>
<p>We have all the API routes in the <code>routes/api.php</code> instead of the <code>routes/web.php</code> because in the <em>web.php</em> file, all the routes by default are <a target="_blank" href="https://laravel.com/docs/10.x/routing#csrf-protection">CSRF protected</a>. So, in a stateless app, usually you won't need that – that's why <em>api.php</em> was invented in Laravel.</p>
<p>As you can see, there is a <em>"task"</em> prefix for all API routes. It's optional to have a prefix, but it's just a good practice. And for the specific API routes, there are regex validations for accepting only natural numbers as project IDs.</p>
<p>Don't forget to refresh route caches after the above changes. It's important to remember that Laravel (version 10 in this case) reads routes from the cached <code>bootstrap/cache/routes-v7.php</code> file, and they won't be updated automatically right after your changes. It just generates one if it hasn't cached yet.</p>
<p>Use the below command to refresh Laravel caches as well as the route caches:</p>
<pre><code class="lang-shell">php artisan optimize
</code></pre>
<h2 id="heading-validation-requests-in-laravel">Validation Requests in Laravel</h2>
<p>Before writing controller methods, you'll need to add some validation request files. You can do that manually or by just using the <code>artisan</code> command:</p>
<pre><code class="lang-shell">php artisan make:request Task/CreateTaskRequest
php artisan make:request Task/ListTasksRequest
php artisan make:request Task/ReorderTasksRequest
php artisan make:request Task/UpdateTaskRequest
</code></pre>
<p>After creating them, you'll need to set up validation rules for each request.</p>
<p>Validation rules in Laravel are a way to describe how to expect to get incoming HTTP data. If the data matches the rules, then it passes the validation – otherwise, Laravel will return a failure.</p>
<p>Laravel provides a <a target="_blank" href="https://laravel.com/docs/10.x/validation#available-validation-rules">set of rules</a> you can use to check incoming data. A field of the incoming request can have multiple rules. </p>
<p>One way to write validation rules for a single field is concatenating those rules by a "|" character.</p>
<p>Below are the validation rules for creating a new task:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'project_id'</span> =&gt; <span class="hljs-string">'required|integer|exists:projects,id'</span>,
    <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">'required|string|max:255'</span>,
    <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'nullable|string'</span>,
];
</code></pre>
<p>Below is the validation rule for listing project tasks:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'project_id'</span> =&gt; <span class="hljs-string">'required|integer|exists:projects,id'</span>,
];
</code></pre>
<p>Below are the validation rules for tasks reordering:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'project_id'</span> =&gt; <span class="hljs-string">'required|integer|exists:projects,id'</span>,
    <span class="hljs-string">'start'</span> =&gt; <span class="hljs-string">'required|integer'</span>,
    <span class="hljs-string">'end'</span> =&gt; <span class="hljs-string">'required|integer|different:start'</span>,
];
</code></pre>
<p>Below are the validation rules for updating a task:</p>
<pre><code class="lang-php"><span class="hljs-keyword">return</span> [
    <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">'required|string|max:255'</span>,
    <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'nullable|string'</span>,
];
</code></pre>
<p>Don't forget to return <code>true</code> in the <code>authorize()</code> method in all validation classes:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">authorize</span>(<span class="hljs-params"></span>): <span class="hljs-title">bool</span>
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<p>This function is usually designed to determine if the user is authorized to make the request. As we don't use authentication as well as authorization stuff in the app, it should return <code>true</code> for all the cases.</p>
<h2 id="heading-how-to-write-a-controller-that-uses-services">How to Write a Controller that Uses Services</h2>
<p>As the last step in the backend part, it's time to write controller methods for each API route, which will use service functions.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">CreateTaskRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">ListTasksRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">ReorderTasksRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Requests</span>\<span class="hljs-title">Task</span>\<span class="hljs-title">UpdateTaskRequest</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>\<span class="hljs-title">ProjectService</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Services</span>\<span class="hljs-title">TaskService</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">JsonResponse</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaskController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">protected</span> ?TaskService $taskService = <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">TaskService $taskService</span>)
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService = $taskService;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>)
    </span>{
        $projects = (<span class="hljs-keyword">new</span> ProjectService())-&gt;getAll();

        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'tasks.index'</span>, [
            <span class="hljs-string">'projects'</span> =&gt; $projects,
        ]);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">list</span>(<span class="hljs-params">ListTasksRequest $request</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        $tasks = <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;list($request-&gt;get(<span class="hljs-string">'project_id'</span>));

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'tasks'</span> =&gt; $tasks,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Tasks retrieved successfully."</span>,
        ]); <span class="hljs-comment">// 200</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">store</span>(<span class="hljs-params">CreateTaskRequest $request</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;store($request-&gt;all());

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task created successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">get</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        $task = <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;getById($id);

        <span class="hljs-keyword">if</span> ($task) {
            <span class="hljs-keyword">return</span> response()-&gt;json([
                <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
                <span class="hljs-string">'task'</span> =&gt; $task,
                <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task retrieved successfully."</span>,
            ]); <span class="hljs-comment">// 200</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> response()-&gt;json([
                <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">false</span>,
                <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task not found!"</span>,
            ], <span class="hljs-number">404</span>);
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">update</span>(<span class="hljs-params">UpdateTaskRequest $request, <span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;update($id, $request-&gt;all());

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task updated successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">delete</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> $id</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;delete($id);

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Task deleted successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reorder</span>(<span class="hljs-params">ReorderTasksRequest $request</span>): <span class="hljs-title">JsonResponse</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;taskService-&gt;reorder(
            $request-&gt;get(<span class="hljs-string">'project_id'</span>),
            $request-&gt;get(<span class="hljs-string">'start'</span>),
            $request-&gt;get(<span class="hljs-string">'end'</span>)
        );

        <span class="hljs-keyword">return</span> response()-&gt;json([
            <span class="hljs-string">'success'</span> =&gt; <span class="hljs-literal">true</span>,
            <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">"Tasks reordered successfully."</span>,
        ], <span class="hljs-number">201</span>);
    }
}
</code></pre>
<p>As you can see in the <code>TaskController:</code></p>
<ul>
<li><code>TaskService</code> is injected into the constructor method as an argument. In the constructor body, an instance of the <code>TaskService</code> class is created, and the <code>$taskService</code> property is initialized. So in the custom methods, you'll be able to access that <code>$taskService</code> and its functions.</li>
<li>The <code>index</code> method is for returning the HTML.</li>
<li>All the other custom methods ( <code>list</code>, <code>store</code>, <code>get</code>, <code>update</code>, <code>delete</code>, <code>reorder</code>) are using the <code>TaskService</code> functions through the already initialized <code>$taskService</code> property. So, all the logic implementation goes to the service, and this way, you just call a service function and return the response.</li>
</ul>
<h2 id="heading-how-to-test-the-api-routes">How to Test the API Routes</h2>
<p>At this point, you can test the API routes by requesting them via Postman or any similar tool. Just run (or rerun) the backend:</p>
<pre><code class="lang-shell">php artisan serve
</code></pre>
<p>Here's the published <a target="_blank" href="https://documenter.getpostman.com/view/1747137/2s9YsJArg7">Postman collection</a> with all the requests.</p>
<p>Instead of using Postman, you can use a command line tool such as <em>curl</em> right from your terminal.</p>
<p>Below are all the sample commands that you can run to test out the API routes:</p>
<ul>
<li>Create a new task for a specific project:</li>
</ul>
<pre><code class="lang-shell">curl --location '127.0.0.1:8000/api/tasks?project_id=1' \
--header 'Content-Type: application/json' \
--data '{
    "title": "Title",
    "description": "Description"
}'
</code></pre>
<ul>
<li>List project tasks:</li>
</ul>
<pre><code class="lang-shell">curl --location 'http://127.0.0.1:8000/api/tasks?project_id=1'
</code></pre>
<ul>
<li>Get a task by ID:</li>
</ul>
<pre><code class="lang-shell">curl --location 'http://127.0.0.1:8000/api/tasks/1'
</code></pre>
<ul>
<li>Update a task by ID:</li>
</ul>
<pre><code class="lang-shell">curl --location --request PUT 'http://127.0.0.1:8000/api/tasks/11' \
--header 'Content-Type: application/json' \
--data '{
    "title": "Title edited",
    "description": "Description edited"
}'
</code></pre>
<ul>
<li>Reorder project tasks:</li>
</ul>
<pre><code class="lang-shell">curl --location --request PUT 'http://127.0.0.1:8000/api/tasks' \
--header 'Content-Type: application/json' \
--data '{
    "project_id": "1",
    "start": "1",
    "end": "2"
}'
</code></pre>
<ul>
<li>Delete a task by ID:</li>
</ul>
<pre><code class="lang-shell">curl --location --request DELETE 'http://127.0.0.1:8000/api/tasks/11'
</code></pre>
<p>In the below screenshot, there is an example of getting project tasks by its ID using the <em>curl</em> command (at the bottom right):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-84.png" alt="Image" width="600" height="400" loading="lazy">
<em>An example of using <strong>curl</strong> command to retrieve the existing tasks from the database</em></p>
<h2 id="heading-the-frontend-how-to-install-the-packages">The Frontend: How to Install the Packages</h2>
<p>Now it's time to switch to the frontend. We'll use TypeScript for React.js. After completing this part, you'll be able to integrate React.js (with Vite) in your Laravel app.</p>
<p>First, make sure you have Node.js version 18 or above by using this command:</p>
<pre><code class="lang-shell">node -v
</code></pre>
<p>Install these necessary npm packages:</p>
<pre><code class="lang-shell">npm i react-dom dotenv react-beautiful-dnd react-responsive-modal react-toastify @vitejs/plugin-react
</code></pre>
<ul>
<li><strong><code>react-dom</code></strong> is a library from the React team for rendering React components in the DOM (Document Object Model)</li>
<li><strong><code>dotenv</code></strong> is for loading environment variables from the <code>.env</code> file into the process environment</li>
<li><strong><code>react-beautiful-dnd</code></strong> is a library from Atlassian for creating drag-and-drop interfaces with animations</li>
<li><strong><code>react-responsive-modal</code></strong> is for creating simple and responsive modal dialogs</li>
<li><strong><code>react-toastify</code></strong> is for displaying notifications or toasts</li>
<li><strong><code>@vitejs/plugin-react</code></strong> is a plugin for the Vite build tool that enables seamless integration of React with fast development and optimized production builds</li>
</ul>
<p>Install the development dependencies with this command:</p>
<pre><code class="lang-shell">npm i -D @types/react-dom @types/react-beautiful-dnd
</code></pre>
<ul>
<li><strong><code>@types/react-dom</code></strong> is TypeScript type definitions for the <code>react-dom</code> package</li>
<li><strong><code>@types/react-beautiful-dnd</code></strong> is TypeScript type definitions for the <code>react-beautiful-dnd</code> package</li>
</ul>
<h2 id="heading-how-to-configure-vitejs">How to Configure Vite.js</h2>
<p>As Laravel v10 already has <code>vite.config.js</code>, you'll want to set up any React-related stuff there. Or if you still don't have this file, create one like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>;
<span class="hljs-keyword">import</span> laravel <span class="hljs-keyword">from</span> <span class="hljs-string">'laravel-vite-plugin'</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">'@vitejs/plugin-react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dotenv/config'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
    <span class="hljs-attr">build</span>: {
        <span class="hljs-attr">minify</span>: process.env.APP_ENV === <span class="hljs-string">'production'</span> ? <span class="hljs-string">'esbuild'</span> : <span class="hljs-literal">false</span>,
        <span class="hljs-attr">cssMinify</span>: process.env.APP_ENV === <span class="hljs-string">'production'</span>,
    },
    <span class="hljs-attr">plugins</span>: [
        laravel({
            <span class="hljs-attr">input</span>: [<span class="hljs-string">'resources/react/app.tsx'</span>],
            <span class="hljs-attr">refresh</span>: <span class="hljs-literal">true</span>,
        }),
        react(),
    ],
});
</code></pre>
<p>As you can see in the Vite configuration file, there is a reference to the <code>resources/react/app.tsx</code>, which will be the entry point for Laravel to use React resources.</p>
<p>For the initial HTML page, create a <code>resources/views/tasks/index.blade.php</code> blade file, so all the frontend assets will be injected there in the <code>div</code> with ID <code>app</code>:</p>
<pre><code class="lang-php">&lt;!DOCTYPE html&gt;
&lt;html lang=<span class="hljs-string">"{{ str_replace('_', '-', app()-&gt;getLocale()) }}"</span>&gt;
&lt;head&gt;
    &lt;meta charset=<span class="hljs-string">"utf-8"</span>&gt;
    &lt;meta name=<span class="hljs-string">"viewport"</span> content=<span class="hljs-string">"width=device-width, initial-scale=1"</span>&gt;
    &lt;title&gt;{{ config(<span class="hljs-string">"app.name"</span>) }}&lt;/title&gt;
    &lt;link rel=<span class="hljs-string">"shortcut icon"</span> href=<span class="hljs-string">"{{ asset('favicon.ico') }}"</span> /&gt;
    &lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"https://fonts.googleapis.com/css?family=Montserrat"</span>&gt;
    &lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"https://fonts.googleapis.com/icon?family=Material+Icons"</span>&gt;
    @viteReactRefresh
    @vite(<span class="hljs-string">'resources/react/app.tsx'</span>)
&lt;/head&gt;
&lt;body&gt;
&lt;div id=<span class="hljs-string">"app"</span> data-projects=<span class="hljs-string">"{{ json_encode(<span class="hljs-subst">$projects</span>) }}"</span>&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>As you can see in the blade file, there is a <code>$projects</code> variable passed from the backend. It's the whole project data that will be used to filter tasks in the frontend.</p>
<h2 id="heading-reactjs-initial-integration">React.js – Initial Integration</h2>
<p>In this article, we'll just have a basic React.js app working with Laravel.</p>
<p>At first, it's a good idea to delete unnecessary resources, like default <code>resources/css</code> and <code>resources/js</code> directories.</p>
<p>Create a <code>resources/react/app.tsx</code> file like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/client'</span>;
<span class="hljs-keyword">import</span> Main <span class="hljs-keyword">from</span> <span class="hljs-string">"./Main"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./index.css'</span>

ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'app'</span>)).render(
    &lt;Main /&gt;
);
</code></pre>
<p>So, the <code>resources/react</code> folder will be the root directory for all the upcoming React stuff.</p>
<p>Create an <code>index.css</code> with some temporary content:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.test-class</span> {
  <span class="hljs-attribute">color</span>: red;
}
</code></pre>
<p>Also create a <code>Main.tsx</code> with some temporary content:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            &lt;h2 className=<span class="hljs-string">"test-class"</span>&gt;React App&lt;/h2&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Main;
</code></pre>
<p>To check the result in the browser, make sure you have backend running and build the assets via the <code>vite</code> tool:</p>
<pre><code class="lang-shell">npm run build
</code></pre>
<p>Or, if you want to watch React file changes and automatically build assets, you can keep this command running:</p>
<pre><code class="lang-shell">npm run dev
</code></pre>
<p>The two <code>npm run</code> commands above refer to <code>vite</code>, which builds the assets.<br>You can see this by checking the <code>package.json</code> file, <em>"scripts"</em> field:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"vite build"</span>
}
</code></pre>
<p>Now you can open <em><a target="_blank" href="http://localhost:8000">http://localhost:8000</a></em> to see the initial rendered view:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/image-85.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot from browser</em></p>
<h2 id="heading-how-to-add-css">How to Add CSS</h2>
<p>Now, once you've set up Vite and have React integrated into your Laravel app, you can work on the React part.</p>
<p>We won't spend too much time on styles, so you can paste this CSS into your <code>index.css</code>:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> { <span class="hljs-attribute">background-color</span>: whitesmoke; <span class="hljs-attribute">color</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.7</span>); <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Montserrat"</span>, sans-serif; <span class="hljs-attribute">cursor</span>: default; <span class="hljs-attribute">margin</span>: auto <span class="hljs-number">0</span>; }

<span class="hljs-comment">/* MODAL start */</span>
<span class="hljs-selector-class">.modal-content</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">flex-direction</span>: column; <span class="hljs-attribute">justify-content</span>: center; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">width</span>: <span class="hljs-number">500px</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; }
<span class="hljs-selector-class">.modal-header</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.modal-input-header</span> { <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>; }
<span class="hljs-selector-class">.modal-input</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.modal-textarea</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">resize</span>: vertical; }
<span class="hljs-selector-class">.modal-actions</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; }
<span class="hljs-selector-class">.modal-btn</span> { <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">cursor</span>: pointer; }
<span class="hljs-selector-class">.modal-btn-cancel</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e53e3e</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.modal-btn-cancel</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#c53030</span>; }
<span class="hljs-selector-class">.modal-btn-submit</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.modal-btn-submit</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4a5568</span>; }
<span class="hljs-selector-class">.modal-question</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-comment">/* MODAL end */</span>

<span class="hljs-comment">/* LEFT &amp; RIGHT SIDE start */</span>
<span class="hljs-selector-class">.left-side</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">50%</span>; <span class="hljs-attribute">float</span>: left; }
<span class="hljs-selector-class">.right-side</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">50%</span>; <span class="hljs-attribute">float</span>: left; }
<span class="hljs-comment">/* LEFT &amp; RIGHT SIDE end */</span>

<span class="hljs-comment">/* LEFT SIDE start */</span>
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.no-tasks</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">text-align</span>: center; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-item</span> { <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>; <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span>; <span class="hljs-attribute">min-height</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">min-width</span>: <span class="hljs-number">200px</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">list-style-type</span>: none; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-item-content</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-project</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">flex-direction</span>: column; <span class="hljs-attribute">justify-content</span>: center; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">10px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-project-name</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-time</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.8rem</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-title</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-actions</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-edit-btn</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">10px</span>; <span class="hljs-attribute">cursor</span>: pointer; <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-edit-btn</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4a5568</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-delete-btn</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e53e3e</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span> <span class="hljs-number">10px</span>; <span class="hljs-attribute">cursor</span>: pointer; <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.left-side</span> <span class="hljs-selector-class">.task-delete-btn</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#c53030</span>; }
<span class="hljs-comment">/* LEFT SIDE end */</span>

<span class="hljs-comment">/* RIGHT SIDE start */</span>
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.projects</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.projects-select</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.no-project-selected</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.right-side-wrapper</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">flex-direction</span>: column; <span class="hljs-attribute">justify-content</span>: center; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#2d3748</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-header</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>; <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-input-header</span> { <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-input</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-textarea</span> { <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>; <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>; <span class="hljs-attribute">resize</span>: vertical; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-actions</span> { <span class="hljs-attribute">display</span>: flex; <span class="hljs-attribute">justify-content</span>: space-between; <span class="hljs-attribute">align-items</span>: center; <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn</span> { <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>; <span class="hljs-attribute">cursor</span>: pointer; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-cancel</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#e53e3e</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-cancel</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#c53030</span>; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-submit</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2d3748</span>; <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>; <span class="hljs-attribute">border</span>: none; }
<span class="hljs-selector-class">.right-side</span> <span class="hljs-selector-class">.add-task-btn-submit</span><span class="hljs-selector-pseudo">:hover</span> { <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4a5568</span>; }
<span class="hljs-comment">/* RIGHT SIDE end */</span>
</code></pre>
<p>Later you'll attach the <code>index.css</code> file in your main component.</p>
<h2 id="heading-a-service-for-the-api-requests">A Service for the API Requests</h2>
<p>As you did in backend, here in the frontend you also can move all the logic implementations into a different file, so your code will be more readable and maintainable. We can name that file <code>utils.ts</code>, as there will be utilities in it we need.</p>
<p>Before that, just create <code>axiosConfig.ts</code> for the global Axios configuration, which you'll use in <code>utils.ts</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> axios.create({ baseURL: <span class="hljs-string">'/api'</span> });
</code></pre>
<p>Using the above setup, you can be sure that all the HTTP requests will have the <code>/api</code> prefix.</p>
<p>For example, if you use <code>axiosConfig.get('/example')</code>, it will send a GET request to the <code>/api/example</code>. This is an optional configuration, but it's a recommended way to have non-repetitive code.</p>
<p>As you'll have a few use cases for sending HTTP requests to the server, you can have separate utilities file for those operations:</p>
<ul>
<li>Create a new task for a project</li>
<li>Update a task</li>
<li>List project's tasks</li>
<li>Delete a task</li>
<li>Reorder project's tasks</li>
</ul>
<p>So below is the <code>utils.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> axiosConfig <span class="hljs-keyword">from</span> <span class="hljs-string">'./axiosConfig'</span>;
<span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-toastify'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getErrorMessage = <span class="hljs-function">(<span class="hljs-params">error: unknown</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span>) <span class="hljs-keyword">return</span> error.message;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">String</span>(error)
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getTasks = <span class="hljs-keyword">async</span> (projectId) =&gt; {
    <span class="hljs-keyword">if</span> (!projectId) {
        toast.error(<span class="hljs-string">"Project is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.get(<span class="hljs-string">`/tasks?project_id=<span class="hljs-subst">${projectId}</span>`</span>);
        <span class="hljs-keyword">const</span> { success, tasks, message } = response.data;

        <span class="hljs-keyword">if</span> (success) {
            <span class="hljs-keyword">return</span> tasks;
        } <span class="hljs-keyword">else</span> {
            toast.error(message);
            <span class="hljs-keyword">return</span> [];
        }
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
        <span class="hljs-keyword">return</span> [];
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> reorderTasks = <span class="hljs-keyword">async</span> (projectId, start, end) =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.put(<span class="hljs-string">'/tasks'</span>, {
            project_id: projectId,
            start,
            end,
        });
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> editTask = <span class="hljs-keyword">async</span> (task) =&gt; {
    <span class="hljs-keyword">if</span> (!task.id) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">if</span> (!task.title) {
        toast.error(<span class="hljs-string">"Title is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.put(<span class="hljs-string">`/tasks/<span class="hljs-subst">${task.id}</span>`</span>, {
            title: task.title,
            description: task.description,
        });
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deleteTask = <span class="hljs-keyword">async</span> (id) =&gt; {
    <span class="hljs-keyword">if</span> (!id) {
        toast.error(<span class="hljs-string">"Invalid task!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.delete(<span class="hljs-string">`/tasks/<span class="hljs-subst">${id}</span>`</span>);
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> createTask = <span class="hljs-keyword">async</span> (task, projectId) =&gt; {
    <span class="hljs-keyword">if</span> (!projectId) {
        toast.error(<span class="hljs-string">"Project is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">if</span> (!task.title) {
        toast.error(<span class="hljs-string">"Title is required!"</span>);
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axiosConfig.post(<span class="hljs-string">`/tasks?project_id=<span class="hljs-subst">${projectId}</span>`</span>, {
            title: task.title,
            description: task.description,
        });
        <span class="hljs-keyword">const</span> { success, message } = response.data;

        toast[success ? <span class="hljs-string">'success'</span> : <span class="hljs-string">'error'</span>](message);
    } <span class="hljs-keyword">catch</span> (err) {
        toast.error(getErrorMessage(err));
    }
}
</code></pre>
<p>In the above file, you'll find the following functions:</p>
<ul>
<li><strong>getErrorMessage</strong>: Returns the error message if the input is an instance of Error – otherwise, converts it to a string.</li>
<li><strong>getTasks</strong>: Retrieves tasks for a given project ID using Axios. Displays an error toast if the project ID is missing or if the API request is unsuccessful.</li>
<li><strong>reorderTasks</strong>: Sends a PUT request to reorder tasks within a project based on start and end positions. Displays a success or error toast based on the API response.</li>
<li><strong>editTask</strong>: Sends a PUT request to update task information. Validates that the task has an ID and a title before making the request. Displays a success or error toast based on the API response.</li>
<li><strong>deleteTask</strong>: Sends a DELETE request to delete a task by its ID. Displays a success or error toast based on the API response.</li>
<li><strong>createTask</strong>: Sends a POST request to create a new task for a given project ID. Validates that the project ID is present, and the task has a title before making the request. Displays a success or error toast based on the API response.</li>
</ul>
<h2 id="heading-reactjs-components">React.js Components</h2>
<p>Now, since you have utilities ready, in the <code>resources/react/components</code> folder, you can create the components you need to use in your <code>Main.tsx</code>.</p>
<p>First, create <code>SelectProject.tsx</code>, which will be responsible for choosing the current project:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {getTasks} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SelectProject</span>(<span class="hljs-params">{projectId, projects, setProjectId, setTasks}</span>) </span>{
    <span class="hljs-keyword">const</span> selectProject = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> value = e.target.value;
        setProjectId(value);
        <span class="hljs-keyword">if</span> (value === <span class="hljs-string">''</span>) {
            setTasks([]);
        } <span class="hljs-keyword">else</span> {
            getTasks(value).then(<span class="hljs-function">(<span class="hljs-params">tasksData</span>) =&gt;</span> setTasks(tasksData));
        }
    };

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"projects"</span>&gt;
            &lt;select className=<span class="hljs-string">"projects-select"</span>
                    value={projectId}
                    onChange={selectProject}&gt;
                &lt;option value=<span class="hljs-string">""</span> defaultValue&gt;Choose a project&lt;/option&gt;
                {projects.map(<span class="hljs-function">(<span class="hljs-params">project</span>) =&gt;</span> (
                    &lt;option key={project.id}
                            value={project.id}&gt;{project.name}&lt;/option&gt;
                ))}
            &lt;/select&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SelectProject;
</code></pre>
<p>The <code>SelectProject</code> component renders a dropdown menu allowing the user to select a project. When a project is selected, it updates the state with the selected project ID, fetches tasks for that project using the <code>getTasks</code> utility function, and updates the state with the retrieved tasks, providing dynamic interaction with project selection and task loading.</p>
<p>Then create <code>TaskList.tsx</code>, which will be responsible for rendering the project's tasks and for their drag and drop manipulations:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {DragDropContext, Draggable, Droppable} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-beautiful-dnd"</span>;
<span class="hljs-keyword">import</span> Task <span class="hljs-keyword">from</span> <span class="hljs-string">"./Task"</span>;
<span class="hljs-keyword">import</span> {reorderTasks} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-keyword">const</span> getItemStyle = <span class="hljs-function">(<span class="hljs-params">isDragging, draggableStyle</span>) =&gt;</span> ({
    background: isDragging ? <span class="hljs-string">'lightgreen'</span> : <span class="hljs-string">'grey'</span>,
    ...draggableStyle,
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TaskList</span>(<span class="hljs-params">{ tasks, setIsModalEditOpen, setModalEditTask, setIsModalDeleteOpen, setModalDeleteTaskId, projectId, setTasks }</span>)
</span>{
    <span class="hljs-keyword">const</span> handleDragEnd = <span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (!result.destination || result.destination.index === result.source.index) {
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">const</span> items = <span class="hljs-built_in">Array</span>.from(tasks);
        <span class="hljs-keyword">const</span> [reorderedItem] = items.splice(result.source.index, <span class="hljs-number">1</span>);
        items.splice(result.destination.index, <span class="hljs-number">0</span>, reorderedItem);
        reorderTasks(projectId, result.source.index + <span class="hljs-number">1</span>, result.destination.index + <span class="hljs-number">1</span>);

        setTasks(items);
    };

    <span class="hljs-keyword">return</span> (
        &lt;DragDropContext onDragEnd={handleDragEnd}&gt;
            &lt;Droppable droppableId=<span class="hljs-string">"droppable"</span>&gt;
                {<span class="hljs-function">(<span class="hljs-params">provided</span>) =&gt;</span> (
                    &lt;ul {...provided.droppableProps} ref={provided.innerRef}&gt;
                        {tasks.map(<span class="hljs-function">(<span class="hljs-params">task, index</span>) =&gt;</span> (
                            &lt;Draggable key={task.id.toString()} draggableId={task.id.toString()} index={index}&gt;
                                {<span class="hljs-function">(<span class="hljs-params">provided, snapshot</span>) =&gt;</span> (
                                    &lt;li ref={provided.innerRef}
                                        {...provided.draggableProps}
                                        {...provided.dragHandleProps}
                                        className=<span class="hljs-string">"task-item"</span>
                                        style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
                                    &gt;
                                        &lt;Task task={task}
                                              setIsModalEditOpen={setIsModalEditOpen}
                                              setModalEditTask={setModalEditTask}
                                              setIsModalDeleteOpen={setIsModalDeleteOpen}
                                              setModalDeleteTaskId={setModalDeleteTaskId}
                                        /&gt;
                                    &lt;/li&gt;
                                )}
                            &lt;/Draggable&gt;
                        ))}
                        {provided.placeholder}
                    &lt;/ul&gt;
                )}
            &lt;/Droppable&gt;
        &lt;/DragDropContext&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TaskList;
</code></pre>
<p>The <code>TaskList</code> component utilizes the <code>react-beautiful-dnd</code> library to implement a draggable task list. It renders a list of tasks, allowing users to drag and drop tasks to reorder them, with drag-and-drop functionality triggering a function (<code>handleDragEnd</code>) that updates the task order both visually and in the backend using the <code>reorderTasks</code> utility function.</p>
<p>Now, create <code>Task.tsx</code>, which will be responsible for a single Task from a list:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Task</span>(<span class="hljs-params">{ task, setIsModalEditOpen, setModalEditTask, setIsModalDeleteOpen, setModalDeleteTaskId }</span>)
</span>{
    <span class="hljs-keyword">const</span> handleEdit = <span class="hljs-function">() =&gt;</span> {
        setModalEditTask(task);
        setIsModalEditOpen(<span class="hljs-literal">true</span>);
    };

    <span class="hljs-keyword">const</span> handleDelete = <span class="hljs-function">() =&gt;</span> {
        setModalDeleteTaskId(task.id);
        setIsModalDeleteOpen(<span class="hljs-literal">true</span>);
    };

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"task-item-content"</span>&gt;
            &lt;span className=<span class="hljs-string">"task-project"</span>&gt;
                &lt;span className=<span class="hljs-string">"task-project-name"</span>&gt;
                    {task.project.name}
                &lt;/span&gt;
                &lt;span className=<span class="hljs-string">"task-time"</span>&gt;
                    Created {task.created}
                &lt;/span&gt;
               &lt;/span&gt;
            &lt;span className=<span class="hljs-string">"task-title"</span>&gt;{task.title}&lt;/span&gt;
            &lt;div className=<span class="hljs-string">"task-actions"</span>&gt;
                &lt;button className=<span class="hljs-string">"task-edit-btn"</span>
                    onClick={handleEdit}&gt;Edit&lt;/button&gt;
                &lt;button className=<span class="hljs-string">"task-delete-btn"</span>
                    onClick={handleDelete}&gt;Delete&lt;/button&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Task;
</code></pre>
<p>The <code>Task</code> component represents a single task item. It displays task details including the project name, creation time, and title, and provides buttons to trigger actions such as editing and deleting the task, with corresponding handlers (<code>handleEdit</code> and <code>handleDelete</code>).</p>
<p>Next, create <code>AddTaskForm.tsx</code>, which will be responsible for the task form for adding tasks to the current selected project:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {createTask} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AddTaskForm</span>(<span class="hljs-params">{newTask, setNewTask, projectId, reloadTasks }</span>)
</span>{
    <span class="hljs-keyword">const</span> clearTaskCreate = <span class="hljs-function">() =&gt;</span> {
        setNewTask({title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});
    };
    <span class="hljs-keyword">const</span> submitTaskCreate = <span class="hljs-function">() =&gt;</span> {
        createTask(newTask, projectId).then(<span class="hljs-function">() =&gt;</span> {
            setNewTask({title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});
            reloadTasks();
        });
    };

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;h2 className=<span class="hljs-string">"add-task-header"</span>&gt;Add Task&lt;/h2&gt;

            &lt;h3 className=<span class="hljs-string">"add-task-header"</span>&gt;Title&lt;/h3&gt;
            &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
                   className=<span class="hljs-string">"add-task-input"</span>
                   onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setNewTask({
                       ...newTask,
                    title: e.target.value
                   })}
                   value={newTask.title}
            /&gt;
            &lt;h3 className=<span class="hljs-string">"add-task-input-header"</span>&gt;Description&lt;/h3&gt;
            &lt;textarea className=<span class="hljs-string">"add-task-textarea"</span>
                      onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setNewTask({
                          ...newTask,
                        description: e.target.value
                      })}
                      value={newTask.description || <span class="hljs-string">''</span>}
            /&gt;
            &lt;div className=<span class="hljs-string">"add-task-actions"</span>&gt;
                &lt;button className=<span class="hljs-string">"add-task-btn add-task-btn-cancel"</span>
                        onClick={clearTaskCreate}&gt;Clear
                &lt;/button&gt;
                &lt;button className=<span class="hljs-string">"add-task-btn add-task-btn-submit"</span>
                        onClick={submitTaskCreate}&gt;Add&lt;/button&gt;
            &lt;/div&gt;
        &lt;/&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AddTaskForm;
</code></pre>
<p>The <code>AddTaskForm</code> component provides a form for adding new tasks. It includes input fields for the task title and description, with buttons to clear the form or submit the task creation, and it utilizes the <code>createTask</code> utility function to handle the task creation process, triggering a reload of tasks upon success.</p>
<p>Then, create <code>ModalEdit.tsx</code>, which will be responsible for the modal popup for editing and submitting changes to a task:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Modal} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-responsive-modal"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {editTask} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalEdit</span>(<span class="hljs-params">{
    isModalEditOpen, setIsModalEditOpen, setModalEditTask,
    modalEditTask, reloadTasks
}</span>) </span>{
    <span class="hljs-keyword">const</span> submitTaskEdit = <span class="hljs-function">() =&gt;</span> {
        setIsModalEditOpen(<span class="hljs-literal">false</span>);
        editTask(modalEditTask).then(<span class="hljs-function">() =&gt;</span> {
            reloadTasks();
        });
    };

    <span class="hljs-keyword">return</span> (
        &lt;Modal open={isModalEditOpen} center
            onClose={<span class="hljs-function">() =&gt;</span> setIsModalEditOpen(<span class="hljs-literal">false</span>)}&gt;
            &lt;div className=<span class="hljs-string">"modal-content"</span>&gt;
                &lt;h2 className=<span class="hljs-string">"modal-header"</span>&gt;Edit Task&lt;/h2&gt;
                &lt;h3 className=<span class="hljs-string">"modal-input-header"</span>&gt;Title&lt;/h3&gt;
                &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span> value={modalEditTask.title}
                       className=<span class="hljs-string">"modal-input"</span>
                       onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setModalEditTask({
                           ...modalEditTask,
                        title: e.target.value
                       })}
                /&gt;
                &lt;h3 className=<span class="hljs-string">"modal-input-header"</span>&gt;Description&lt;/h3&gt;
                &lt;textarea className=<span class="hljs-string">"modal-textarea"</span>
                          onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setModalEditTask({
                              ...modalEditTask,
                            description: e.target.value
                          })}
                          value={modalEditTask.description || <span class="hljs-string">''</span>}
                /&gt;
                &lt;div className=<span class="hljs-string">"modal-actions"</span>&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-cancel"</span>
                            onClick={<span class="hljs-function">() =&gt;</span> setIsModalEditOpen(<span class="hljs-literal">false</span>)}
                    &gt;Close
                    &lt;/button&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-submit"</span>
                            onClick={submitTaskEdit}
                    &gt;Save&lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/Modal&gt;
    )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ModalEdit;
</code></pre>
<p>The <code>ModalEdit</code> component displays a modal for editing task details. It includes input fields for modifying the task title and description, and buttons to close the modal or save the changes, using the <code>editTask</code> utility function to handle the task editing process and triggering a reload of tasks upon successful editing.</p>
<p>Next, create <code>ModalDelete.tsx</code>, which will be responsible for submitting a task deletion:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {Modal} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-responsive-modal"</span>;
<span class="hljs-keyword">import</span> {deleteTask} <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalDelete</span>(<span class="hljs-params">{
    isModalDeleteOpen, setIsModalDeleteOpen,
    modalDeleteTaskId, reloadTasks
}</span>) </span>{
    <span class="hljs-keyword">const</span> submitTaskDelete = <span class="hljs-function">() =&gt;</span> {
        setIsModalDeleteOpen(<span class="hljs-literal">false</span>);
        deleteTask(modalDeleteTaskId).then(<span class="hljs-function">() =&gt;</span> {
            reloadTasks();
        });
    };

    <span class="hljs-keyword">return</span> (
        &lt;Modal open={isModalDeleteOpen} onClose={<span class="hljs-function">() =&gt;</span> setIsModalDeleteOpen(<span class="hljs-literal">false</span>)} center&gt;
            &lt;div className=<span class="hljs-string">"modal-content"</span>&gt;
                &lt;h2 className=<span class="hljs-string">"modal-header"</span>&gt;Delete Task&lt;/h2&gt;
                &lt;p className=<span class="hljs-string">"modal-question"</span>&gt;
                    Are you sure you want to <span class="hljs-keyword">delete</span> <span class="hljs-built_in">this</span> task?
                &lt;/p&gt;
                &lt;div className=<span class="hljs-string">"modal-actions"</span>&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-cancel"</span>
                            onClick={<span class="hljs-function">() =&gt;</span> setIsModalDeleteOpen(<span class="hljs-literal">false</span>)}
                    &gt;Cancel&lt;/button&gt;
                    &lt;button className=<span class="hljs-string">"modal-btn modal-btn-submit"</span>
                            onClick={submitTaskDelete}
                    &gt;Yes&lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/Modal&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ModalDelete;
</code></pre>
<p>The <code>ModalDelete</code> component displays a modal for confirming the deletion of a task. It provides options to either cancel the deletion or proceed with deleting the task, utilizing the <code>deleteTask</code> utility function and triggering a reload of tasks upon successful deletion.</p>
<p>And lastly, set up the <code>Main.tsx</code> by using the above-defined components.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {getTasks} <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"react-responsive-modal/styles.css"</span>;
<span class="hljs-keyword">import</span> { ToastContainer } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-toastify'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'react-toastify/dist/ReactToastify.css'</span>;
<span class="hljs-keyword">import</span> ModalEdit <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/ModalEdit"</span>;
<span class="hljs-keyword">import</span> ModalDelete <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/ModalDelete"</span>;
<span class="hljs-keyword">import</span> TaskList <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/TaskList"</span>;
<span class="hljs-keyword">import</span> SelectProject <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/SelectProject"</span>;
<span class="hljs-keyword">import</span> AddTaskForm <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/AddTaskForm"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Main</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [projectId, setProjectId] = useState(<span class="hljs-string">''</span>);
    <span class="hljs-keyword">const</span> projectsData = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'app'</span>).getAttribute(<span class="hljs-string">'data-projects'</span>);
    <span class="hljs-keyword">const</span> projects = <span class="hljs-built_in">JSON</span>.parse(projectsData);
    <span class="hljs-keyword">const</span> [tasks, setTasks] = useState([]);
    <span class="hljs-keyword">const</span> [isModalEditOpen, setIsModalEditOpen] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [modalEditTask, setModalEditTask] = useState({id: <span class="hljs-string">''</span>, title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});
    <span class="hljs-keyword">const</span> [isModalDeleteOpen, setIsModalDeleteOpen] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [modalDeleteTaskId, setModalDeleteTaskId] = useState(<span class="hljs-string">''</span>);
    <span class="hljs-keyword">const</span> [newTask, setNewTask] = useState({title: <span class="hljs-string">''</span>, description: <span class="hljs-string">''</span>});

    <span class="hljs-keyword">const</span> reloadTasks = <span class="hljs-function">() =&gt;</span> {
        getTasks(projectId).then(<span class="hljs-function">(<span class="hljs-params">tasksData</span>) =&gt;</span> setTasks(tasksData));
    };

    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            &lt;ToastContainer autoClose={<span class="hljs-number">2000</span>} /&gt;
            &lt;ModalEdit isModalEditOpen={isModalEditOpen}
                       setIsModalEditOpen={setIsModalEditOpen}
                       modalEditTask={modalEditTask}
                       setModalEditTask={setModalEditTask}
                       reloadTasks={reloadTasks}
            /&gt;
            &lt;ModalDelete isModalDeleteOpen={isModalDeleteOpen}
                         setIsModalDeleteOpen={setIsModalDeleteOpen}
                         modalDeleteTaskId={modalDeleteTaskId}
                         reloadTasks={reloadTasks}
            /&gt;
            &lt;div className=<span class="hljs-string">"left-side"</span>&gt;
                {tasks.length &gt; <span class="hljs-number">0</span> ? (
                    &lt;TaskList tasks={tasks}
                              setIsModalEditOpen={setIsModalEditOpen}
                              setModalEditTask={setModalEditTask}
                              setIsModalDeleteOpen={setIsModalDeleteOpen}
                              setModalDeleteTaskId={setModalDeleteTaskId}
                              projectId={projectId}
                              setTasks={setTasks}
                    /&gt;
                ) : (
                    &lt;div className=<span class="hljs-string">"no-tasks"</span>&gt;
                        {projectId === <span class="hljs-string">''</span> ? (
                            &lt;p&gt;Choose a project to see its tasks.&lt;/p&gt;
                        ) : (
                            &lt;p&gt;This project has no tasks.&lt;/p&gt;
                        )}
                    &lt;/div&gt;
                )}
            &lt;/div&gt;
            &lt;div className=<span class="hljs-string">"right-side"</span>&gt;
                &lt;div className=<span class="hljs-string">"right-side-wrapper"</span>&gt;
                    &lt;SelectProject projectId={projectId}
                                   projects={projects}
                                   setProjectId={setProjectId}
                                   setTasks={setTasks}
                    /&gt;
                    {projectId === <span class="hljs-string">''</span> ? (
                        &lt;div className=<span class="hljs-string">"no-project-selected"</span>&gt;
                            &lt;p&gt;Please select a project.&lt;/p&gt;
                        &lt;/div&gt;
                    ) : (
                        &lt;AddTaskForm newTask={newTask}
                                     setNewTask={setNewTask}
                                     projectId={projectId}
                                     reloadTasks={reloadTasks}
                        /&gt;
                    )}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Main;
</code></pre>
<p>The <code>Main</code> component is a central component that managing project and task-related functionalities. It includes modals for editing and deleting tasks, a task list with dynamic updates, a project selection dropdown, and a form for adding new tasks, leveraging state management and utility functions for smooth user interaction.</p>
<h2 id="heading-final-results">Final Results</h2>
<p>At this point, all the components are ready to interact with each other. So you can build the frontend assets and run the server:</p>
<pre><code class="lang-shell">npm run build &amp;&amp; php artisan serve
</code></pre>
<p>By visiting <code>http://127.0.0.1:8000</code> you'll get this kind of result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/tasklist.gif" alt="Image" width="600" height="400" loading="lazy">
<em>GIF generated from a local working project</em></p>
<p><strong>That's it!</strong></p>
<p>Now you can easily integrate React.js into your Laravel app without using any additional Laravel tools (like Inertia). And as a result, you can continue to maintain your Laravel app to build more scalable APIs with its authentication and other stuff.</p>
<p>So, this can be just an example app for your next full-stack Laravel and React.js project.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>With this article, you built a single-page, full-stack Tasklist app using React.js (with TypeScript) with Vite.js as frontend technologies, Laravel as a backend framework, and <code>react-beautiful-dnd</code> package for having draggable items.<br>Now you know how to manually integrate React.js in your Laravel app and maintain it.</p>
<p>You can find the complete code of the project here on my <a target="_blank" href="https://github.com/boolfalse/laravel-react-tasklist"><strong>GitHub</strong>⭐</a>, where I actively publicize much of my work about various modern technologies.<br>For more information, you can visit my website: <a target="_blank" href="https://boolfalse.com/"><strong>boolfalse.com</strong></a></p>
<p>Feel free to share this article. 😇</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build a Google Drive Clone with Laravel, PHP, and Vue.js ]]>
                </title>
                <description>
                    <![CDATA[ Building a file manager application is a great way to learn many important programming concepts. We just published a 14-hour course on the freeCodeCamp.org YouTube channel that will teach you how to build and deploy a feature-rich file manager applic... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-google-drive-clone-with-laravel-php-vuejs/</link>
                <guid isPermaLink="false">66b200ddeea9870582e16c3d</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Mon, 21 Aug 2023 14:48:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/googeldrive.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building a file manager application is a great way to learn many important programming concepts.</p>
<p>We just published a 14-hour course on the freeCodeCamp.org YouTube channel that will teach you how to build and deploy a feature-rich file manager application using Laravel (the PHP framework), Inertia, and Vue.js.</p>
<p>This comprehensive course was created by experienced developer Zura Sekhniashvili, also known as The Codeholic. He has created many popular courses. By the end of this course, you'll be equipped with the skills and knowledge to tackle a range of file management challenges, all while harnessing the potential of Laravel, PHP, Inertia, and Vue.js.</p>
<h3 id="heading-what-youll-learn">What You'll Learn</h3>
<p>While building a file management application, you will gain expertise in various functionalities, including:</p>
<ul>
<li>Creating folders to maintain a structured hierarchy.</li>
<li>Seamlessly uploading files and folders to the application.</li>
<li>Efficiently deleting and downloading files as needed.</li>
<li>Implementing robust search capabilities for locating files quickly.</li>
<li>Enabling file sharing to collaborate with others effectively.</li>
<li>Integrating Amazon Web Services (AWS) S3 for secure file storage.</li>
<li>Deploying your finished project on a custom domain for the world to see.</li>
</ul>
<p>Here are all the sections in the course:</p>
<ul>
<li>Project Setup</li>
<li>Install Laravel Breeze</li>
<li>Email Verification</li>
<li>DB Overview, Models &amp; Migrations</li>
<li>Explore Intertia Folder Structure</li>
<li>Authentication Layout</li>
<li>Define Main Routes</li>
<li>Create Root Folder On User Registration</li>
<li>Creating Folder</li>
<li>Save Folder Path</li>
<li>Show Files into table</li>
<li>Navigate into Second Level of Folders</li>
<li>Create Folders Inside Subfolder</li>
<li>Implement Breadcrumbs</li>
<li>Uploading Files</li>
<li>Show Human Readable File size</li>
<li>Show File Icons</li>
<li>Show File Progress</li>
<li>Display Error When File Already Exists</li>
<li>Implement Load More</li>
<li>Increase max_file_uploads</li>
<li>Deleting Files</li>
<li>Implement Downloading Files</li>
<li>Show Success Notifications</li>
<li>Implement Trash</li>
<li>Implement Favourites</li>
<li>Sharing Files to Others</li>
<li>Implement Shared With Me Page</li>
<li>Implement Shared By Me Page</li>
<li>Searching for Files</li>
<li>Connect to AWS S3</li>
<li>Download Files from S3</li>
<li>Project Deployment &amp; Bug Fixing</li>
</ul>
<p>Whether you're a seasoned developer looking to expand your skill set or a beginner eager to dive into the world of web development, this course has something for everyone. You can watch the full course on the freeCodeCamp.org YouTube channel (14-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Wn3IPX_ax-0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Introduction to Laravel APIs – Security, Features, and More ]]>
                </title>
                <description>
                    <![CDATA[ By Abdulwahab Ashimi Laravel is the most popular PHP framework, with its elegant syntax, extensive feature set, and developer-friendly environment. And it's gained significant traction in recent years. The Laravel ecosystem is constantly being update... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/introduction-to-laravel-apis/</link>
                <guid isPermaLink="false">66d45d99787a2a3b05af4376</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 05 Jun 2023 23:27:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/pexels-klas-tauberman-128362.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Abdulwahab Ashimi</p>
<p>Laravel is the most popular PHP framework, with its elegant syntax, extensive feature set, and developer-friendly environment. And it's gained significant traction in recent years.</p>
<p>The Laravel ecosystem is constantly being updated with new features. <a target="_blank" href="https://laravel.com/docs/10.x/releases">Laravel 10</a> is exciting!</p>
<p>Among its many strengths is the robust support for APIs (Application Programming Interfaces). This enables seamless communication and integration between different software applications.</p>
<p>In this article, we will delve deeper into the world of Laravel APIs. We'll explore their capabilities, benefits, and how to leverage Laravel's features to build powerful and secure APIs.</p>
<h2 id="heading-what-is-an-api">What is an API?</h2>
<p>To understand Laravel's API capabilities, you'll want to make sure you know what APIs are really all about and why they're so important.</p>
<p>APIs serve as a connector or a bridge that lets one application communicate with the other and exchange data with each other.</p>
<p>There are rules and protocols that govern how these applications interact. The API provider sets some rules and the Developer (API consumer) sets their own rules. </p>
<h3 id="heading-how-do-apis-work">How Do APIs Work?</h3>
<p>Paystack, for example, has what they call a Paystack gateway. It provides this as an API (that is, the interface to interact with. Remember the acronym for API?) that developers can integrate into their website.</p>
<p>When a user tries to make a purchase on the website, the website tells the gateway to handle the payment and the Paystack gateway process the payment. It then tells the site that the user has paid, and the site will allow the users to complete and receive their purchase. </p>
<h3 id="heading-why-are-apis-important">Why are APIs Important?</h3>
<p>The vital role APIs play is that they let developers leverage the functionalities of a different application without having to build those features themselves.</p>
<p>APIs also make it possible for two independent applications to interact and exchange data. You can think of it as two applications chatting 😀. Irrespective of the software system they operate on, APIs let them communicate.</p>
<p>The entire web today operates through various chains of communication. They power everything from social media login mechanisms to payment gateways, geolocation services, and much more.</p>
<p>APIs offer some advantages, such as:</p>
<ol>
<li><strong>Modularity</strong>: APIs promote modularity by allowing applications to be built in separate components that can be developed, tested, and maintained independently.</li>
<li><strong>Reusability</strong>: APIs enable code and functionality to be reused, saving development time and effort.</li>
<li><strong>Scalability</strong>: APIs allow applications to scale by handling increased traffic and distributing tasks across multiple systems.</li>
<li><strong>Integration</strong>: APIs facilitate integration with external services, enabling developers to leverage the power of other applications and services.</li>
<li><strong>Flexibility</strong>: APIs provide the flexibility to adapt and evolve applications by adding new features and integrating them with emerging technologies.</li>
</ol>
<p>Now that you hopefully understand the significance of APIs, let's explore how Laravel simplifies the process of building APIs with its elegant syntax and powerful features.</p>
<h2 id="heading-apis-in-laravel">APIs in Laravel</h2>
<p>Laravel has expressive syntax, powerful tools, and comprehensive support for building robust APIs. </p>
<p>It's routing system makes it easier for developers to expose their application's functionalities and data to other applications. You can enable interactions and still preserve security. </p>
<p>Laravel's powerful routing system lets developers easily define routes that are exclusive to APIS, permitting them to handle incoming requests and give proper responses with data. </p>
<p>These routes are configured to reply using request methods called HTTP methods that specify the action to be performed. These Methods (GET, POST, PUT, and DELETE) let developers create RESTful APIs that follow the principles of resource-based routing.</p>
<p>In order to handle these API requests, Laravel's controllers contain the logic for processing these requests. Controllers serve as intermediaries between the routes and the underlying application.</p>
<p>Controllers allow developers to organize and structure their API logic effectively. Laravel's controllers can fetch data from databases, perform business logic operations, and return responses in a specified format, such as JSON or XML.</p>
<h2 id="heading-authentication-and-security-concerns">Authentication and Security Concerns</h2>
<p>Ensuring the security of your APIs is very important as it helps protect sensitive data and limit user access based on their level of authorization. </p>
<p>Laravel provides various systems for authentication through tokens or API keys. Authenticating API requests ensures that only authorized users utilize certain API endpoints and Laravel's robust mechanisms makes this easy.</p>
<p>Laravel's middleware adds another layer of security. It serves as a series of filters that intercept requests and responses. These interceptions are ways of performing various validations, checks, and transformations.</p>
<p>You can use middleware to enforce rate limiting, validate incoming data, or modify the response format.</p>
<h2 id="heading-common-api-features">Common API Features</h2>
<p>Laravel simplifies the implementation of common API features, making it easier for developers to build robust and scalable APIs. Let's explore some of these features:</p>
<h3 id="heading-rate-limiting">Rate Limiting</h3>
<p>API rate limiting allows you to control the number of requests that can be made to your API within a specific time frame.</p>
<p>This helps you prevent abuse and ensure fair usage of your API resources. Laravel provides built-in rate limiting capabilities, enabling you to define rate limits based on IP addresses, authenticated users, or other criteria.</p>
<h3 id="heading-caching">Caching</h3>
<p>Caching is a technique that can significantly improve the performance of your API by storing the responses to frequently requested data.</p>
<p>Laravel offers powerful caching mechanisms that allow you to cache API responses, reducing the load on your server and enhancing the overall user experience.</p>
<p>With Laravel's caching capabilities, you can store the responses to API requests in memory or on disk.</p>
<h3 id="heading-api-versioning">API Versioning</h3>
<p>API versioning is a crucial aspect of API development, as it allows you to manage changes to your API over time while ensuring backward compatibility for existing consumers.</p>
<p>Laravel offers tools and conventions for versioning your APIs, enabling you to introduce changes and enhancements without breaking the existing functionality for your API consumers.</p>
<p>By incorporating versioning into your API development workflow, you can effectively manage and control the evolution of your API, provide stability to existing consumers, and introduce new features or improvements in a controlled manner.</p>
<h2 id="heading-how-to-extend-laravels-api-support">How to Extend Laravel's API Support</h2>
<p>There are some third-party packages available within the Laravel ecosystem that allow you to extend and enhance Laravel's API capabilities.</p>
<p>These packages provide additional functionality, such as advanced authentication mechanisms, request validation, API documentation generation, and more.</p>
<p>One such popular package is Laravel Passport, which simplifies the implementation of OAuth2 authentication for your API.</p>
<p>OAuth2 is a heavily adopted authentication protocol that enables secure authorization and authentication between applications.</p>
<p><a target="_blank" href="https://laravel.com/docs/10.x/passport">Laravel Passport</a> seamlessly integrates with Laravel's authentication system, making it a breeze to add OAuth2 authentication to your API and integrate with external applications that rely on OAuth2.</p>
<p>Another notable package is <a target="_blank" href="https://github.com/dingo/api">DingoAPI</a>, which provides a robust set of tools for building RESTful APIs in Laravel.</p>
<p>DingoAPI offers features like request and response transformation, API versioning, rate limiting, and error handling that complements Laravel's existing capabilities.</p>
<p>It provides additional flexibility and control over your API development process.</p>
<p>By leveraging these third-party packages, you can extend Laravel's API support according to your specific project requirements, saving development time and effort while benefiting from the expertise and contributions of the Laravel community.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Laravel's API support makes it an exceptional choice for developing robust and secure APIs in PHP.</p>
<p>You can leverage Laravel's powerful routing system, expressive controllers, and built-in authentication mechanisms to create APIs that seamlessly integrate with other applications and services.</p>
<p>Whether you are building a web application that requires integration with external services or developing a standalone API for others to consume.   </p>
<p>Laravel provides the necessary tools, conventions, and ecosystem support to create robust, secure, and highly functional APIs.</p>
<p>Embrace Laravel's API capabilities, and unlock the potential to build powerful and interconnected web applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Configure a Laravel Project with a Custom Domain Name on Windows with XAMPP ]]>
                </title>
                <description>
                    <![CDATA[ By Abdulwahab Ashimi Laravel's simplicity and MVC architecture make it an ideal PHP framework for building web applications.  In this article, I will show you how to set up Laravel on your Windows machine and configure it to run on a custom domain na... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/configure-a-laravel-project-with-custom-domain-name/</link>
                <guid isPermaLink="false">66d45d974bc8f441cb6df807</guid>
                
                    <category>
                        <![CDATA[ apache ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 14 Feb 2023 02:58:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/cover--2-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Abdulwahab Ashimi</p>
<p>Laravel's simplicity and MVC architecture make it an ideal PHP framework for building web applications. </p>
<p>In this article, I will show you how to set up Laravel on your Windows machine and configure it to run on a custom domain name.</p>
<p>This guide is best suited for a beginner trying to get Laravel up and running quickly and easily. But even as an advanced programmer, you'll likely find fresh insights into how you can simplify the process of configuring a Laravel project. So let's dive in!</p>
<h2 id="heading-how-to-install-and-start-xampp">How to Install and Start Xampp</h2>
<p>Xampp is an open-source tool that allows you to run an Apache server, MySQL database, and other tools from a single interface for development. </p>
<p>You can download and install Xampp from here: <a target="_blank" href="https://www.apachefriends.org/download.html">https://www.apachefriends.org/download.html</a>.</p>
<p>First, launch your Xampp Interface and start your Apache and MySQL Server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/XAMPP-Control-Panel-v3.3.0-----Compiled_-Apr-6th-2021---2_8_2023-12_12_47-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>The Xampp Interface</em></p>
<p>Next, click on <code>Explorer</code> to launch your Xampp <code>htdocs</code> folder. Delete the files and folders inside the folder. Now you can setup your Laravel application.</p>
<h2 id="heading-how-to-set-up-laravel">How to Set Up Laravel</h2>
<p>Inside the <code>htdocs</code> folder, you can clone your existing Laravel application or set up a fresh installation using <code>composer create-project laravel/laravel example-app</code>. In this case, "example-app" is your project name but you can replace it with your preferred name for the project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/htdocs-2_8_2023-12_25_22-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>The Laravel Directory Structure on htdocs</em></p>
<p>Open the htdocs folder in your preferred code editor. I will be using VScode for my example.</p>
<p>Replace the <code>APP_URL</code> value in the <code>.env</code> file of your Laravel project with the custom domain name:</p>
<pre><code class="lang-env">APP_URL=https://project.test
</code></pre>
<p>You can replace "project.test" with your prefered test domain name.</p>
<h2 id="heading-how-to-configure-your-hosts-file">How to Configure Your Hosts File</h2>
<p>In your Windows file explorer, navigate to the "hosts" file located at <code>C:\Windows\System32\drivers\etc\hosts</code> and open it with VSCode (or whatever editor you're using). I'd advise that you use VSCode with admin privileges.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/1.-Host-File.png" alt="Image" width="600" height="400" loading="lazy">
<em>etc directory containing the hosts file and other files</em></p>
<p>Add the following line to the file:</p>
<pre><code><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span> project.test
</code></pre><p>This will map the hostname "project.test" to the local IP address "127.0.0.1".</p>
<p>Now, if you launch your Apache server and visit project.test on your browser, it loads the "index of" project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675857587334/7a426d88-8963-4c75-a347-49054a33b8da.png" alt="Image" width="1600" height="644" loading="lazy">
<em>Index of' The Laravel Directory on Browser</em></p>
<p>This is because for your Laravel application to work, it needs to load the public folder. If you can load public.test/public on your browser, you will be redirected to the Laravel project. To fix that, you can configure the Apache root directory.</p>
<h2 id="heading-how-to-configure-your-apache-root-directory">How to Configure Your Apache Root Directory</h2>
<p>In your Windows file explorer, navigate to and open the "httpd.conf" file which contains the Apache configuration. It's located at <code>C:\xampp\apache\conf\httpd.conf</code> . You should also use VSCode with admin privileges in this case.</p>
<p>Right below <code># Virtual hosts</code>, add the following:</p>
<pre><code class="lang-conf">&lt;VirtualHost *:80&gt;
    ServerName project.test
    DocumentRoot "C:/xampp/htdocs/project/public"
    &lt;Directory "C:/xampp/htdocs/project/public"&gt;
        Options Indexes FollowSymLinks Includes ExecCGI
        AllowOverride All
        Require all granted
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</code></pre>
<p>Note: Replace <code>project.test</code> with your custom domain name and <code>C:/xampp/htdocs/project/public</code> with the path to your public folder.</p>
<p>Stop and restart the Apache server from your Xampp interface and try visiting "<a target="_blank" href="http://decmark.test"><strong>http://project.test</strong></a>" on your browser to see the Laravel project's homepage.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You can have multiple projects with their own custom domains by setting them up in different directories inside the htdocs directory and specifying their individual Apache configurations.</p>
<p>If this article was helpful to you. Share it with friends or drop me a shout out on Twitter <a target="_blank" href="https://twitter.com/Adebowale1st">@adebowale1st</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Laravel Valet Performance – How to Prevent 504 Errors and Speed Up Valet ]]>
                </title>
                <description>
                    <![CDATA[ Last week, I decided to install Laravel Valet on my Mac. But after the installation, the performance of the microservice architecture application I had it on was quite slow.  I wondered if it was an M1 issue or because I had yet to shut the machine d... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/speed-up-performance-in-laravel-valet/</link>
                <guid isPermaLink="false">66ba2fcfde9370f66eeb0a9d</guid>
                
                    <category>
                        <![CDATA[ error ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sule-Balogun Olanrewaju ]]>
                </dc:creator>
                <pubDate>Mon, 02 Jan 2023 23:12:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/pexels-jonathan-petersson-399636.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Last week, I decided to install Laravel Valet on my Mac. But after the installation, the performance of the microservice architecture application I had it on was quite slow. </p>
<p>I wondered if it was an M1 issue or because I had yet to shut the machine down. I shut down, and the problem persisted. And I couldn't find anything online indicating that it was an issue with M1. So how could I fix it?</p>
<p><strong>In this tutorial, you'll learn:</strong></p>
<ul>
<li>Why does the Error 504 occur?</li>
<li>What is Laravel Valet and how does it work?</li>
<li>Valet commands you should know</li>
<li>How to fix the 504 error and speed up performance in Valet</li>
</ul>
<h2 id="heading-why-does-the-error-504-occur">Why does the Error 504 occur?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/image-145.png" alt="Image" width="600" height="400" loading="lazy">
<em>Error 504 Nginx</em></p>
<p>Error 504, also known as "Gateway timeout", is an error that occurs when a server takes longer than usual to respond to an HTTP request. This makes it unable to complete the request cycle. </p>
<p>The Gateway timeout is a server-side error caused by several things. It can result from network connectivity issues when the server exceeds the default limit of <strong>256M</strong> or an execution time of <strong>60 seconds</strong>, an overloaded server, firewall, and so on. </p>
<p>This error happens with local servers, too, such as XAMPP, WAMP and Valet, during the local development lifecycle. </p>
<p>This article will help you fix this problem on Valet by adding some configurations to speed up the server request lifecycle.</p>
<h2 id="heading-what-is-laravel-valet-and-how-does-it-work">What is Laravel Valet and How Does It Work?</h2>
<p>Laravel Valet is a development environment for macOS, Windows, and other operating systems. Once installed, Valet runs Nginx processes in the background when your laptop comes on.</p>
<p>Unlike other development environments like XAMPP and WAMP, you'll have to manually start your server each time you set out to work. Valet then uses DnsMasq to proxy all parked applications to a <code>.test</code> domain. </p>
<p>So, for instance, you would access on XAMPP server <code>http://localhost/application</code> but on valet, you would do <code>http://application.test</code>, and it will point to where the application is installed. </p>
<p>Working with Valet means you don't have to put all applications in an htdocs or www directory. Instead, any random directory you create will work just perfectly in Valet. </p>
<h2 id="heading-helpful-valet-commands-you-should-know">Helpful Valet Commands You Should Know:</h2>
<ul>
<li><strong><code>valet park</code>:</strong> Registers all applications/sites in a directory and exposes them with the .test domain.</li>
</ul>
<pre><code>cd ~/project_directory
valet park
</code></pre><ul>
<li><strong><code>valet parked</code>:</strong> Gives a tabular breakdown of all registered sites. Information like site name, SSL, URL, and path is available.</li>
</ul>
<pre><code>cd ~/project_directory
valet parked
</code></pre><ul>
<li><strong><code>valet secure</code>:</strong> Secures your application with an SSL certificate and makes the site accessible over HTTPS.</li>
</ul>
<pre><code>cd ~<span class="hljs-regexp">/project_directory/</span>site
valet secure
</code></pre><ul>
<li><strong><code>valet unsecure</code>:</strong> Use this command to unsecure your site and revert to serving over HTTP.</li>
</ul>
<pre><code>cd ~<span class="hljs-regexp">/project_directory/</span>site
valet unsecure
</code></pre><ul>
<li><strong><code>valet isolate</code>:</strong> Isolates a particular site and makes it run on a different PHP version that's not the globally installed version. You can run <code>php -v</code> on the terminal to see the version. But if some of your applications want to downgrade or upgrade, you should use the isolate command and specify the version you need. The isolate command below, enforce the site to use PHP version 7.4:</li>
</ul>
<pre><code>cd ~<span class="hljs-regexp">/project_directory/</span>site
valet isolate @php7<span class="hljs-number">.4</span>
</code></pre><ul>
<li><strong><code>valet unisolate</code>:</strong> Reverts a site back to the globally installed PHP version.</li>
</ul>
<pre><code>cd ~<span class="hljs-regexp">/project_directory/</span>site
valet unisolate
</code></pre><ul>
<li><strong><code>valet restart</code>:</strong> The restart command ensures all valet services are restarted. It's useful when configurations are modified, updated, and installed.</li>
</ul>
<pre><code>cd ~<span class="hljs-regexp">/project_directory/</span>site
valet restart
</code></pre><ul>
<li><strong><code>valet -v</code>:</strong> This command helps check the the current Valet version. Not only that, but the command also shows a list of all available commands and descriptions of what they do in Valet.</li>
</ul>
<pre><code>~<span class="hljs-regexp">/project_directory/</span>site
valet -v
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-26-at-11.33.10.png" alt="Image" width="600" height="400" loading="lazy">
<em>Valet available commands</em></p>
<h2 id="heading-how-to-speed-up-performance-in-valet">How to Speed Up Performance in Valet</h2>
<p>Over in the terminal, we will need to create a file <code>www.conf</code> in a directory and then add the configuration settings we need.</p>
<p>Get your PHP global version and copy it like this:</p>
<pre><code>php -v
</code></pre><p>Navigate to the directory and replace the 7.4 with the PHP version you copied earlier.</p>
<pre><code>cd /opt/homebrew/etc/php/<span class="hljs-number">7.4</span>/php-fpm.d
</code></pre><p>Create a <code>www.conf</code> file like this:</p>
<pre><code>touch www.conf
</code></pre><p>Open the file so you can add the valet configuration settings:</p>
<pre><code>open -a TextEdit www.conf
</code></pre><p>The command opens up the <code>www.conf</code> file in your text editor and you can update the file with these settings:</p>
<pre><code>pm.max_children = <span class="hljs-number">200</span>
pm.start_servers = <span class="hljs-number">20</span>
pm.min_spare_servers = <span class="hljs-number">10</span>
pm.max_spare_servers = <span class="hljs-number">20</span>
pm.process_idle_timeout = <span class="hljs-number">10</span>s
pm.max_requests = <span class="hljs-number">500</span>
</code></pre><p>Save and close the file from the text editor.</p>
<p>Let's go through each line we added to the <code>www.conf</code> file:</p>
<ul>
<li><strong><code>pm</code></strong> is an acronym for process manager, and the setting will impact how the process manager controls every child process. The possible values available to us include static, on-demand, and dynamic.</li>
<li><strong>pm.max_children</strong> is a static option, indicating the maximum number of the child processes, which we have set to 200.</li>
<li><strong><code>pm.start_servers, pm.max_spare_servers</code></strong> and <code>pm.min_spare_servers</code>: These are dynamic values, and the child processes are set dynamically based on the server directives – that is, start_servers = 20, min_spare_servers = 10 and max_spare_servers = 20.</li>
<li><strong>pm.process_idle_timeout</strong>: the total time taken for an idle request not processed to be killed/terminated is set to a default value of 10 seconds (s). Other units can be estimated in minutes (m), hours (h) or days (d).</li>
<li><strong>pm.max_requests</strong>: This refers to the maximum number of requests a child process can handle at a given time before it's killed/terminated. If the request executes the maximum, it becomes idle, and the pm gets rid of it.</li>
</ul>
<p>Finally, restart all services from the terminal using the sudo brew command. Notice again the 7.4 – that's because of my global PHP version.</p>
<pre><code>sudo brew services restart php@<span class="hljs-number">7.4</span>
</code></pre><p>You can now say bye-bye to the 504 error and slow Valet performance in your local environment.</p>
<h2 id="heading-wrapping-up">Wrapping up</h2>
<p>In this article, you have learned about working with Laravel Valet and how to get all Valet configurations set up. You also learned how to ensure a fast development environment and get rid of persistent 504 gateway timeout errors. </p>
<p>Keep learning and Happy Coding!</p>
<p>You can find me on <a target="_blank" href="https://www.linkedin.com/in/suleolanrewaju/">LinkedIn</a> and <a target="_blank" href="https://twitter.com/bigdevlarry">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Cross-Site Request Forgery (CSRF)? Laravel Web Security Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you'll learn about Laravel web security and how to secure your web applications and protect them from Cross-Site Request Forgery, or CSRF attacks. CSRF is a malicious activity that involves an attacker performing actions on behalf o... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/laravel-web-security-csrf/</link>
                <guid isPermaLink="false">66ba2fccc346e93df556b001</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sule-Balogun Olanrewaju ]]>
                </dc:creator>
                <pubDate>Tue, 04 Oct 2022 21:40:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/10/Dark-Neon-Simple-Futuristic-UIUX-Designer-LinkedIn-Banner--7---1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you'll learn about Laravel web security and how to secure your web applications and protect them from Cross-Site Request Forgery, or CSRF attacks.</p>
<p>CSRF is a malicious activity that involves an attacker performing actions on behalf of an authenticated user. Fortunately, Laravel provides out-of-the-box measures to prevent this type of vulnerability.</p>
<p><strong>In this tutorial, you'll learn:</strong></p>
<ul>
<li>What is CSRF?</li>
<li>How to prevent a CSRF request</li>
<li>How and where CSRF verification happens</li>
</ul>
<h2 id="heading-what-is-csrf">What is CSRF?</h2>
<p>CSRF attacks hijack user sessions. They do this by tricking a user into sending a request through hidden form tags or malicious URLs (images or links) without the user's knowledge. </p>
<p>This attack leads to a change in the state of the user session, data leaks, and attackers can sometimes manipulate end-users data in an application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-2022-10-03-at-14.59.14-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>CSRF Explainer</em></p>
<p>The image above illustrates this scenario where an Actor (User) sends a request from <strong>malicious.xyz</strong> through the <strong>webserver</strong> to <strong>application.xyz</strong>. They then realize that their information has been manipulated by <strong>updating</strong> their <strong>password</strong>.</p>
<h2 id="heading-how-to-prevent-csrf-requests">How to Prevent CSRF Requests</h2>
<p>For each user session, Laravel generates secured tokens that it uses to ensure that the authenticated user is the one requesting the application. </p>
<p>Since this token changes each time a user session is regenerated, a malicious attacker can not access it. </p>
<p>Each time there’s a request to modify user information on the server-side (back end) like <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, and <code>DELETE</code>, you need to include a <code>@csrf</code> in the HTML form request. The <code>@csrf</code> is thus a Blade directive used to generate a hidden token validated by the application. </p>
<p><strong>Blade directive</strong> is the syntax used within the Laravel templating engine called <strong>Blade</strong>. To create a blade file you give it a name – in our case form – followed by the blade extension. This means that the file will have the name <code>form.blade.php</code>.</p>
<p>You use the blade file to render views to users on the webpage. There are a couple of default directives or blade shorthand syntaxes you can use. For example, <code>@if</code> checks if a condition is met, <code>@empty</code> checks if records are not empty, <code>@auth</code> checks if a user is authenticated, and so on. </p>
<p>But here we are more interested with the <code>@csrf</code> directive. Here's how you use it:</p>
<pre><code class="lang-php">&lt;form method=<span class="hljs-string">"POST"</span> action=<span class="hljs-string">"{{route('pay')}}"</span>&gt;

    @csrf

&lt;/form&gt;
</code></pre>
<p>Earlier Laravel releases used to look somewhat like this – both work and do the same thing behind the scenes.</p>
<pre><code class="lang-php">&lt;form method=<span class="hljs-string">"POST"</span> action=<span class="hljs-string">"{{route('pay')}}"</span>&gt;

    &lt;input type=<span class="hljs-string">"hidden"</span> name=<span class="hljs-string">"_token"</span> value=<span class="hljs-string">"{{ csrf_token() }}"</span> /&gt;

&lt;/form&gt;
</code></pre>
<p>When the CSRF token is not present in the form request that gets sent or if it appears invalid, Laravel throws an error message "Page Expired" with a status code 419.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-2022-10-03-at-15.11.43-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Laravel 419 Page Expired</em></p>
<h2 id="heading-how-and-where-csrf-verification-happens">How and Where CSRF Verification Happens</h2>
<p>The <code>VerifyCsrfToken</code> middleware handles CSRF verification within the Laravel application. The middleware is registered in the Kernel.php, and found within the application's web route middleware group. This means the middleware is triggered for requests within the Web, not related to APIs.</p>
<pre><code class="lang-php"><span class="hljs-keyword">protected</span> $middlewareGroups = [
        <span class="hljs-string">'web'</span> =&gt; [
           .
           .
           .
           .
           .
            \App\Http\Middleware\VerifyCsrfToken::class,
        ],
    ];
</code></pre>
<p>The VerifyCsrfToken middleware extends the <code>Illuminate\Foundation\Http\Middleware\VerifyCsrfToken</code> class. This means that the CSRF verification is housed within the class. </p>
<p>Let's dive deeper to learn how Laravel handles the CSRF verification. </p>
<p>Within the class, we have the <code>tokensMatch</code> function.</p>
<pre><code class="lang-php"><span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tokensMatch</span>(<span class="hljs-params">$request</span>)
    </span>{
        $token = <span class="hljs-keyword">$this</span>-&gt;getTokenFromRequest($request);

        <span class="hljs-keyword">return</span> is_string($request-&gt;session()-&gt;token()) &amp;&amp;
               is_string($token) &amp;&amp;
               hash_equals($request-&gt;session()-&gt;token(), $token);
    }
</code></pre>
<p>The function does two things:</p>
<ol>
<li><code>$this-&gt;getTokenFromRequest</code> gets the token from the incoming request attached via a hidden field or the request's header. The token is decrypted and then returned to the token variable.</li>
</ol>
<pre><code class="lang-php"><span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTokenFromRequest</span>(<span class="hljs-params">$request</span>)
    </span>{
        $token = $request-&gt;input(<span class="hljs-string">'_token'</span>) ?: $request-&gt;header(<span class="hljs-string">'X-CSRF-TOKEN'</span>);

        <span class="hljs-keyword">if</span> (! $token &amp;&amp; $header = $request-&gt;header(<span class="hljs-string">'X-XSRF-TOKEN'</span>)) {
            <span class="hljs-keyword">try</span> {
                $token = CookieValuePrefix::remove(<span class="hljs-keyword">$this</span>-&gt;encrypter-&gt;decrypt($header, <span class="hljs-built_in">static</span>::serialized()));
            } <span class="hljs-keyword">catch</span> (DecryptException $e) {
                $token = <span class="hljs-string">''</span>;
            }
        }

        <span class="hljs-keyword">return</span> $token;
    }
</code></pre>
<ol start="2">
<li>Cast both request token and session to a string and then use the PHP built-in hash_equals to compare if both strings are equal using the same time. The result of this operation is always a <strong>bool (true) or (false)</strong>.</li>
</ol>
<h2 id="heading-wrapping-up">Wrapping up</h2>
<p>In this article, you have learned about CSRF, how to handle and protect against it, and the behind-the-scenes of how Laravel does the verification.</p>
<p>You can read more about this in the <a target="_blank" href="https://laravel.com/docs/9.x/csrf">Laravel documentation</a>. And you can read more about <a target="_blank" href="https://www.php.net/manual/en/function.hash-equals.php">PHP hash equals in the docs here</a>.</p>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Full-Stack Single Page Application with Laravel 9, MySQL, Vue.js, Inertia, Jetstream and Docker ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific In this tutorial, you will learn how to build a single page application. I'll take you through the process step by step, using cutting edge technologies like Laravel 9, Jetstream, Vuejs, Inertiajs, MySQL, Tailwind CSS, and Docker. Le... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-full-stack-single-page-application-with-laravel-mysql-vue-and-docker/</link>
                <guid isPermaLink="false">66d45ee3052ad259f07e4ad4</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 16 May 2022 13:55:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/02/EP__3__LiveCoding.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<p>In this tutorial, you will learn how to build a single page application. I'll take you through the process step by step, using cutting edge technologies like Laravel 9, Jetstream, Vuejs, Inertiajs, MySQL, Tailwind CSS, and Docker.</p>
<p>Let's get started.</p>
<h2 id="heading-what-you-need-to-follow-this-guide">What you need to follow this guide:</h2>
<p>To follow along you will need: </p>
<ul>
<li>a computer</li>
<li>to know how to install software </li>
<li>a basic understanding of HTML, CSS, JavaScript, and PHP </li>
<li>knowledge of at least one JavaScript framework and an understanding of the MVC design pattern.</li>
</ul>
<p>This guide is organized into 10 chapters and is based off a live coding series that I record. The live coding series is completely unscripted, so there will be bugs and gotchas there that you won't find in this guide.</p>
<p>You can find the complete playlist at the end of this article.</p>
<p>Everything here should just work, but if it doesn't feel free to ask for help by joining my community on Slack. There you can share code snippets and chat with me directly. </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-tech-are-we-using">What Tech Are We Using?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-machine">How to Setup Your Machine</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-app-with-laravel-9-laravel-sail-jetstram-inertia-and-vue3">How to build the app with Laravel 9, Laravel Sail, Jetstram, Inertia and Vue3</a></li>
<li><a class="post-section-overview" href="#heading-how-to-refactor-the-admin-dashboard-and-create-new-admin-pages">How to Refactor the Admin Dashboard and Create New Admin Pages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-submit-forms-with-files">How to Submit Forms with Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-the-form-to-the-component">How to Add the Form to the Component</a></li>
<li><a class="post-section-overview" href="#heading-how-to-store-data">How to Store Data</a></li>
<li><a class="post-section-overview" href="#heading-how-to-update-operations">How to Update Operations</a></li>
<li><a class="post-section-overview" href="#heading-how-to-delete-a-resource">How to Delete a Resourse</a></li>
<li><a class="post-section-overview" href="#heading-wrapup-and-whats-next">Wrap up and what's next</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-tech-are-we-using">What Tech Are We Using?</h2>
<p>First, let's go over the different tools we'll be using in this project.</p>
<h3 id="heading-docker">Docker</h3>
<p>Docker is a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers. </p>
<p>To simplify this concept, Docker lets you package applications and dependencies in a container. </p>
<p>A containerized application allows you to have a flexible development environment so that you can run different applications without worrying about dependencies, their requirements, and conflicts between different versions. You can easily run applications that, for instance, require two different versions of PHP and MySQL. </p>
<p>Each team member can quickly reproduce the same environment of your application by simply running the same container's configuration.</p>
<p>If you want to learn more about Docker, its <a target="_blank" href="https://www.docker.com/">Documentation</a> is a great place to start.</p>
<p>Here's a <a target="_blank" href="https://www.freecodecamp.org/news/the-docker-handbook/">Handbook on Docker essentials</a>, as well, so you can practice your skills.</p>
<h3 id="heading-mysql">Mysql</h3>
<p>MySQL is an open-source relational database management system. You can use it to organize data into one or more tables with data that may be related to each other.</p>
<p>We need to store data somewhere and here is where MySQL comes into play.</p>
<p>Here are the <a target="_blank" href="https://www.mysql.com/">Docs</a> if you want to read up more. Here's a <a target="_blank" href="https://www.freecodecamp.org/news/learn-to-use-the-mysql-database/">full free course on MySQL</a> if you want to dive deeper.</p>
<h3 id="heading-laravel">Laravel</h3>
<p>Laravel is a free, open-source PHP web framework that helps you develop web applications following the model–view–controller architectural pattern.</p>
<p>Laravel is an amazing PHP framework that you can use to create bespoke web applications.</p>
<p>Here's the Laravel <a target="_blank" href="https://laravel.com/">Documentation</a> for more info, and here's a <a target="_blank" href="https://www.freecodecamp.org/news/laravel-full-course/">full project-based course</a> to help you learn Laravel.</p>
<h3 id="heading-laravel-sail">Laravel Sail</h3>
<p>Laravel Sail is a lightweight command-line interface for interacting with Laravel's default Docker development environment. </p>
<p>Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience.</p>
<p>Usually, creating a development environment to build such applications means you have to install software, languages, and frameworks on your local machine – and that is time-consuming. Thanks to Docker and Laravel Sail we will be up and running in no time!</p>
<p><strong>Laravel Sail is supported on macOS, Linux, and Windows <a target="_blank" href="https://docs.microsoft.com/en-us/windows/wsl/about">via WSL2</a>.</strong></p>
<p>Here's the <a target="_blank" href="https://laravel.com/docs/9.x/sail">Documentation</a> if you want to read up on it.</p>
<h3 id="heading-laravel-jetstream">Laravel Jetstream</h3>
<p>When building web applications, you likely want to let users register and log in to use your app. That is why we will use Jetstream.</p>
<p>Laravel Jetstream is a beautifully designed application starter kit for Laravel and provides the perfect starting point for your next Laravel application.</p>
<p>It uses Laravel Fortify to implement all the back end authentication logic.
Here are the <a target="_blank" href="https://jetstream.laravel.com/2.x/introduction.html">Docs</a>.</p>
<h3 id="heading-vuejs">Vuejs</h3>
<p>Vue.js is an open-source model–view–ViewModel front end JavaScript framework for building user interfaces and single-page applications.</p>
<p>Vue is a fantastic framework that you can use as a stand-alone to build single-page applications, but you can also use it with Laravel to build something amazing.</p>
<p>Here's the Vue <a target="_blank" href="https://vuejs.org/">Documentation</a> if you want to read up. And here's a <a target="_blank" href="https://www.freecodecamp.org/news/vue-3-full-course/">great Vue course</a> to get you started.</p>
<h3 id="heading-inertia-js">Inertia JS</h3>
<p>Inertia is the glue between Laravel and Vuejs that we will use to build modern single-page applications using classic server-side routing.</p>
<p>You can learn more about it in the <a target="_blank" href="https://inertiajs.com/">Documentation here</a>.</p>
<h3 id="heading-tailwind">Tailwind</h3>
<p>Tailwind CSS is a utility-first CSS framework packed with classes like flex, pt-4, text-center, and rotate-90 that you can use to build any design, directly in your markup</p>
<p>We'll use it in this project to build our design. Here's a <a target="_blank" href="https://www.freecodecamp.org/news/get-started-with-tailwindcss/">quick guide to get you up and running</a> if you aren't familiar with Tailwind.</p>
<h2 id="heading-how-to-set-up-your-machine">How to Set Up Your Machine</h2>
<p>To follow along with my live coding (and this tutorial), you will need to install Docker desktop on your machine. If you are using Windows, you will also need to enable WSL in your system settings.</p>
<p>Visit the Docker <a target="_blank" href="https://www.docker.com/get-started">getting started page</a> to install Docker Desktop.</p>
<p>If you are on Windows, enable WSL2 by following the steps <a target="_blank" href="https://docs.microsoft.com/en-us/windows/wsl/about">here</a>.</p>
<p>If you have any trouble, feel free to reach out or join my community on Slack to get help.</p>
<h2 id="heading-laravel-installation-with-sail">Laravel Installation with Sail</h2>
<p>If you have successfully installed Docker Desktop on your machine, we can open the terminal and install Laravel 9. </p>
<p>Open a terminal window and browse to a folder where you want to keep your project. Then run the command below to download the latest Laravel files. The command will put all files inside a folder called my-example-app, which you can tweak as you like.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Download laravel</span>
curl -s <span class="hljs-string">"https://laravel.build/my-example-app"</span> | bash
<span class="hljs-comment"># Enter the laravel folder</span>
<span class="hljs-built_in">cd</span> my-example-app
</code></pre>
<h3 id="heading-deploy-laravel-on-docker-using-the-sail-up-command">Deploy Laravel on Docker using the <code>sail up</code> command</h3>
<p>With Docker Desktop up and running, the next step is to start Laravel sail to build all the containers required to run our application locally.</p>
<p>Run the following command from the folder where all Laravel files have been downloaded:</p>
<pre><code class="lang-bash">vendor/bin/sail up
</code></pre>
<p>It will take a minute. Then visit <a target="_blank" href="http://localhost">http://localhost</a> and you should see your Laravel application.</p>
<p>If you run <code>sail up</code> and you get the following error, it is likely that you need to update Docker Desktop:</p>
<pre><code class="lang-bash">ERROR: Service <span class="hljs-string">'laravel.test'</span> failed to build:
</code></pre>
<h2 id="heading-how-to-build-the-app-with-laravel-9-laravel-sail-jetstram-inertia-and-vue3">How to Build the App with Laravel 9, Laravel Sail, Jetstram, Inertia and Vue3</h2>
<p>In this section, we will define a basic roadmap, install Laravel 9 with Laravel Sail, Run sail, and build the containers. </p>
<p>I will also take you on a tour of Laravel Sail and the sail commands. </p>
<p>Then we will install Jetstream and scaffold Vue and Inertia files and have a look at the files and available features.</p>
<p>Next, we will populate our database and add the front end provided by Jetstream to register an account and log into a fresh Laravel application.</p>
<p>Finally, we will have a look at the Jetstream dashboard, and the Inertia/Vue Components and then start playing around.</p>
<p>Along the way, we'll disable the registration, enable the Jetstream user profile picture feature, and then add our first Inertia page where we'll render some data taken from the database.</p>
<p>Here's the live coding video if you want to follow along that way:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/c0ibec9dhZA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>And if you prefer following along in this written tutorial, here are all the steps.</p>
<p>Just a reminder – you should have Laravel installed with Sail and have Docker set up on your machine. You can follow the steps above to do so if you haven't already.</p>
<h3 id="heading-laravel-sail-overview-sail-commands">Laravel Sail Overview – Sail Commands</h3>
<p>With Laravel Sail installed, our usual Laravel commands have sligtly changed.</p>
<p>For instance, instead of running the Laravel artisan command using PHP like <code>php artisan</code>, we now have to use Sail, like so: <code>sail artisan</code>.</p>
<p>The <code>sail artisan</code> command will return a list of all available Laravel commands.</p>
<p>Usually, when we work with Laravel, we also have to run the <code>npm</code> and <code>composer</code> commands.</p>
<p>Again, we need to prefix our commands with <code>sail</code> to make them run inside the container.</p>
<p>Below you'll find a list of some commands you will likely have to run:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Interact with the database - run the migrations</span>
sail artisan migrate <span class="hljs-comment"># It was: php artisan migrate</span>
<span class="hljs-comment"># Use composer commands</span>
sail composer require &lt;packageName&gt; <span class="hljs-comment"># it was: composer require &lt;packageName&gt;</span>
<span class="hljs-comment"># Use npm commands</span>
sail npm run dev <span class="hljs-comment"># it was: npm run dev</span>
</code></pre>
<p>You can read more in the <a target="_blank" href="https://laravel.com/docs/9.x/sail#executing-sail-commands">Sail documentation</a>.</p>
<h3 id="heading-install-jetstream-and-scaffold-vue-and-inertia">Install Jetstream and Scaffold Vue and Inertia</h3>
<p>Let's now install the Laravel Jetstream authentication package and use the Inertia scaffolding with Vue3.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> my-example-app
sail composer require laravel/jetstream
</code></pre>
<p>Remember to prefix the composer command with <code>sail</code>.</p>
<p>The command above has added a new command to Laravel. Now we need to run it to install all the Jetstream components:</p>
<pre><code class="lang-bash">sail artisan jetstream:install inertia
</code></pre>
<p>Next we need to compile all static assets with npm:</p>
<pre><code class="lang-bash">sail npm install
sail npm run dev
</code></pre>
<p>Before we can actually see our application, we will need to run the database migrations so that the session table, required by Jetstream, is present.</p>
<pre><code class="lang-bash">sail artisan migrate
</code></pre>
<p>Done! Jetstream is now installed in our application. If you visit <code>http://localhost</code> in your browser you should see the Laravel application with two links at the top to register and log in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/welcome-page.png" alt="welcome-page" width="600" height="400" loading="lazy"></p>
<h3 id="heading-populate-the-database-and-create-a-user-account">Populate the Database and Create a User Account</h3>
<p>Before creating a new user, let's have a quick look at the database configuration that Laravel Sail has created for us in the <code>.env</code> file.</p>
<pre><code class="lang-env">DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=my-example-app
DB_USERNAME=sail
DB_PASSWORD=password
</code></pre>
<p>As you can see, Laravel Sail configures everything we need to access the database container that is running on Docker. The <code>DB_DATABASE</code> is the name of the database and it is the same as the project folder. This is why in the previous step we were able to run the <code>migrate</code> command without issues.</p>
<p>Since we already migrated all database tables, we can now use the Laravel built-in user factory to create a new user then use its details to log in our user dashboard.</p>
<p>Let's open artisan tinker to interact with our application.</p>
<pre><code class="lang-bash">sail artisan tinker
</code></pre>
<p>The command above will open a command line interface that we can use to interact with our application. Let's create a new user.</p>
<pre><code class="lang-php">User::factory()-&gt;create()
</code></pre>
<p>The command above will create a new user and save its data in our database. Then it will render the user data onto the screen. Make sure to copy the user email so we can use it later to log in. Then exit by typing <code>exit;</code>.</p>
<p>The default password for every user created with a factory is <code>password</code>.</p>
<p>Let's visit the login page and access our application dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/loginpage.png" alt="loginpage" width="600" height="400" loading="lazy"></p>
<h3 id="heading-jetstream-dashboard">Jetstream Dashboard</h3>
<p>After login you are redirected to the Jetstream dashboard, which looks amazing by default. We can customize it as we like, but it is just a starting point.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/dashboard.png" alt="dashboard" width="600" height="400" loading="lazy"></p>
<h3 id="heading-jetstreamvue-components-and-inertia-overview">Jetstream/Vue Components and Inertia Overview</h3>
<p>The first thing you may notice after installing Jetstram is that there are a number of Vue components registered in our application. Not only that, also Inertia brings in Vue components. </p>
<p>To use Inertia, we need to get familiar with it when defining routes.</p>
<p>When we installed Jetstream, it created inside the <code>resources/js</code> directory a number of subfolders where all our Vue components live. There are not just simple components but also Pages components rendered by inertia as our Views.</p>
<p>The Jetstream inertia scaffolding created:</p>
<ul>
<li><code>resources/js/Jetstream</code> Here we have 27 components used by Jetstream, but we can use them in our application too if we want.</li>
<li><code>resources/js/Layouts</code> In this folder there is the layout component used by inertia to render the dashboard page</li>
<li><code>resources/js/Pages</code> This is where we will place all our Pages (views) components. You will find the Dashboard page as well as the Laravel Welcome page components here.</li>
</ul>
<p>The power of Inertia mostly comes from how it connects Vue and Laravel, letting us pass data (Database Models and more) as props to our Vue Pages components.</p>
<p>When you open the <code>routes/web.php</code> file you will notice that we no longer return a view but instead we use <code>Inertia</code> to render a Page component.</p>
<p>Let's examine the <code>/</code> homepage route that renders the Welcome component.</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Welcome'</span>, [
        <span class="hljs-string">'canLogin'</span> =&gt; Route::has(<span class="hljs-string">'login'</span>),
        <span class="hljs-string">'canRegister'</span> =&gt; Route::has(<span class="hljs-string">'register'</span>),
        <span class="hljs-string">'laravelVersion'</span> =&gt; Application::VERSION,
        <span class="hljs-string">'phpVersion'</span> =&gt; PHP_VERSION,
    ]);
});
</code></pre>
<p>It looks like our usual Route definition, exept that in the closure we are returning an <code>\Inertia\Response</code> by calling the <code>render</code> method of the Inertia class <code>Inertia::render()</code>. </p>
<p>This method accepts two parameters. The first is a component name. Here we passed the <code>Welcome</code> Page component, while the second parameter is an associative array that will turn into a list of <code>props</code> to pass to the component. Here is where the magic happens.</p>
<p>Looking inside the Welcome component, you will notice that in its script section, we simply define four props matching with the keys of our associative array. Then inertia will do the rest.</p>
<pre><code class="lang-vue">&lt;script&gt;
    import { defineComponent } from 'vue'
    import { Head, Link } from '@inertiajs/inertia-vue3';

    export default defineComponent({
        components: {
            Head,
            Link,
        },
        // 👇 Define the props 
        props: {
            canLogin: Boolean, 
            canRegister: Boolean,
            laravelVersion: String,
            phpVersion: String,
        }
    })
&lt;/script&gt;
</code></pre>
<p>We can then just call the props inside the template. If you look at the template section you will notice that <code>laravelVersion</code> and <code>phpVersion</code> are referenced in the code as you normally would do with props in Vuejs.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0"</span>&gt;</span>
  Laravel v{{ laravelVersion }} (PHP v{{ phpVersion }})
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The dashboard component is a little different. In fact it uses the Layout defined under <code>Layouts/AppLayout.vue</code> and uses the <code>Welcome</code> component to render the Dashboard page content, which is the same as the laravel Welcome page.</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>
                Dashboard
            <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white overflow-hidden shadow-xl sm:rounded-lg"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">welcome</span> /&gt;</span> 
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>Inside the layout component you will notice the two inertia components <code>Head</code> and <code>Link</code>.</p>
<p>We can use the <code>Head</code> component to add head elements to our page, like meta tags, page title, and so on. The <code>Link</code> component is a wrapper aroud a standard anchor tag that incercepts click events and prevents full page reload as you can read in the Inertia documentation.</p>
<p><a target="_blank" href="https://inertiajs.com/links">Link Component</a>
<a target="_blank" href="https://inertiajs.com/title-and-meta#head-component">Head Component</a></p>
<h3 id="heading-disable-the-registration-feature">Disable the Registration Feature</h3>
<p>If you are following along, the next step I'll take is to disable one on the features Jetstream provides – register an account. </p>
<p>To do that, we can navigate to <code>config/fortify.php</code> and comment out line 135 <code>Features::registration()</code> from the features array.</p>
<pre><code class="lang-php"><span class="hljs-string">'features'</span> =&gt; [
        <span class="hljs-comment">//Features::registration(),</span>
        Features::resetPasswords(),
        <span class="hljs-comment">// Features::emailVerification(),</span>
        Features::updateProfileInformation(),
        Features::updatePasswords(),
        Features::twoFactorAuthentication([
            <span class="hljs-string">'confirmPassword'</span> =&gt; <span class="hljs-literal">true</span>,
        ]),
    ],
</code></pre>
<p>If we visit the welcome page we will notice that the <code>register</code> link is gone. Also, the route is no longer listed when we run <code>sail artisan route:list</code>.</p>
<h3 id="heading-enable-jetstream-user-profile-picture">Enable Jetstream User Profile Picture</h3>
<p>Now let's try to enable the Jetstream feature called ProfilePhotos. As you can guess, this will allow the user to add a profile picture.</p>
<p>To do that we need to visit <code>config/jetstream.php</code> and uncomment line 59 <code>Features::profilePhoto</code>.</p>
<pre><code class="lang-php">    <span class="hljs-string">'features'</span> =&gt; [
        <span class="hljs-comment">// Features::termsAndPrivacyPolicy(),</span>
        Features::profilePhotos(), <span class="hljs-comment">// 👈</span>
        <span class="hljs-comment">// Features::api(),</span>
        <span class="hljs-comment">// Features::teams(['invitations' =&gt; true]),</span>
        Features::accountDeletion(),
    ],
</code></pre>
<p>If you log in you will see that in the user profile, a new section is available to upload a profile picture.</p>
<p>But before doing anything else we need to run <code>sail artisan storage:link</code> so that Laravel creates a symlink to the <code>storage/app/public</code> folder where we will save all user profile images.</p>
<p>Now try to visit the user profile and update the profile picture. If you get a 404 on the image this is because by default Laravel sail assumes we are using Laravel valet and sets the app URL like so <code>APP_URL=http://my-example-app.test</code> in the <code>.env</code> file. Let's change it and use localhost instead.</p>
<pre><code class="lang-env">APP_URL=http://localhost
</code></pre>
<p>Now we should be good to go and be able to see and change our profile image!🥳</p>
<h3 id="heading-how-to-add-our-first-inertia-page-and-render-records-from-the-db">How to Add our First Inertia Page and Render Records from the DB</h3>
<p>Since we are rendering Vue components instead of blade views, it is wise to start <code>sail npm run watch</code> to watch and recompile our Vue components as we create or edit them. Next let's add a new Photos page.</p>
<p>I will start by creating a new Route inside web.php:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//dd(Photo::all());</span>
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Guest/Photos'</span>);
});
</code></pre>
<p>In the code above I defined a new GET route and then rendered a component that I will place inside the <code>resources/js/Pages/Guest</code> and call <code>Photos</code>. Let's create it.</p>
<p>Create a Guest folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> resources/js/Pages
mkdir Guest
<span class="hljs-built_in">cd</span> Guest
touch Photos.vue
</code></pre>
<p>Then let's define a basic component:</p>
<pre><code class="lang-vue">&lt;template&gt;
  &lt;h1&gt;Photos Page&lt;/h1&gt;
&lt;/template&gt;
</code></pre>
<p>If we visit <code>http://localhost/photos/</code> we will see our new page, cool! Let's copy over the page structure from the Welcome page so that we get the login and dashboard links as well.</p>
<p>The component will change to this:</p>
<pre><code class="lang-vue">&lt;template&gt;
    &lt;Head title="Phots" /&gt;

    &lt;div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0"&gt;
        &lt;div v-if="canLogin" class="hidden fixed top-0 right-0 px-6 py-4 sm:block"&gt;
            &lt;Link v-if="$page.props.user" :href="route('admin.dashboard')" class="text-sm text-gray-700 underline"&gt;
                Dashboard
            &lt;/Link&gt;

            &lt;template v-else&gt;
                &lt;Link :href="route('login')" class="text-sm text-gray-700 underline"&gt;
                    Log in
                &lt;/Link&gt;

                &lt;Link v-if="canRegister" :href="route('register')" class="ml-4 text-sm text-gray-700 underline"&gt;
                    Register
                &lt;/Link&gt;
            &lt;/template&gt;
        &lt;/div&gt;

        &lt;div class="max-w-6xl mx-auto sm:px-6 lg:px-8"&gt;
            &lt;h1&gt;Photos&lt;/h1&gt;

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
    import { defineComponent } from 'vue'
    import { Head, Link } from '@inertiajs/inertia-vue3';

    export default defineComponent({
        components: {
            Head,
            Link,
        },

        props: {
            canLogin: Boolean,
            canRegister: Boolean,

        }
    })
&lt;/script&gt;
</code></pre>
<p>The next step is to render a bunch of data onto this new page. For that we will build a Model and add some records to the database.</p>
<pre><code class="lang-bash">saild artisan make:model Photo -mfcr
</code></pre>
<p>This command creates a Model called <code>Photo</code>, plus a database migration table class, a factory, and a resource controller.</p>
<p>Now let's define the database table inside the migration we just creted. Visit the <code>database/migrations</code> folder and you should see a file with a name similar to this: <code>2022_02_13_215119_create_photos_table</code> (yours will be sligly different).</p>
<p>Inside the migration file we can define a basic table like the following:</p>
<pre><code class="lang-php"> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>)
    </span>{
        Schema::create(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();
            $table-&gt;string(<span class="hljs-string">'path'</span>);
            $table-&gt;text(<span class="hljs-string">'description'</span>);
            $table-&gt;timestamps();
        });
    }
</code></pre>
<p>For our table we defined just two new columns, <code>path</code> and <code>description</code>, plus the <code>id</code>, <code>created_at</code> and <code>updated_at</code> that will be created by the <code>$table-&gt;id()</code> and by the <code>$table-&gt;timestamps()</code> methods.</p>
<p>After the migration we will define a seeder and then run the migrations and seed the database.</p>
<p>At the top of the <code>database/seeders/PhotoSeeder.php</code> file we will import our Model and Faker:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Photo</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Faker</span>\<span class="hljs-title">Generator</span> <span class="hljs-title">as</span> <span class="hljs-title">Faker</span>;
</code></pre>
<p>Next we will implement the run method using a for loop to create 10 records in the database.</p>
<pre><code class="lang-php">

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params">Faker $faker</span>)
    </span>{
        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">10</span>; $i++) {
            $photo = <span class="hljs-keyword">new</span> Photo();
            $photo-&gt;path = $faker-&gt;imageUrl();
            $photo-&gt;description = $faker-&gt;paragraphs(<span class="hljs-number">2</span>, <span class="hljs-literal">true</span>);
            $photo-&gt;save();
        }
    }
</code></pre>
<p>We are ready to run the migrations and seed the database.</p>
<pre><code class="lang-php">
sail artisan migrate
sail artisan db:seed --<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PhotoSeeder</span></span>
</code></pre>
<p>We are now ready to show the data on the <code>Guest/Photos</code> page component.
First update the route and pass a collection of Photos as props to the rendered component:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//dd(Photo::all());</span>
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Guest/Photos'</span>, [
        <span class="hljs-string">'photos'</span> =&gt; Photo::all(), <span class="hljs-comment">## 👈 Pass a collection of photos, the key will become our prop in the component</span>
        <span class="hljs-string">'canLogin'</span> =&gt; Route::has(<span class="hljs-string">'login'</span>),
        <span class="hljs-string">'canRegister'</span> =&gt; Route::has(<span class="hljs-string">'register'</span>),
    ]);
});
</code></pre>
<p>Second, pass the prop to the props in the script section of the Guest/Photos component:</p>
<pre><code class="lang-js">
<span class="hljs-attr">props</span>: {
    <span class="hljs-attr">canLogin</span>: <span class="hljs-built_in">Boolean</span>,
    <span class="hljs-attr">canRegister</span>: <span class="hljs-built_in">Boolean</span>,
    <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span> <span class="hljs-comment">// 👈 Here</span>
}
</code></pre>
<p>Finally loop over the array and render all photos in the template section, just under the h1:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"photos"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"photo.id"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span> &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>Done! if you visit the <code>/photos</code> page you should see ten photos. 🥳</p>
<h2 id="heading-how-to-refactor-the-admin-dashboard-and-create-new-admin-pages">How to Refactor the Admin Dashboard and Create New Admin Pages</h2>
<p>In this chapter we will Re-route the Jetstream dashboard and make a route group for all admin pages. </p>
<p>Then we will see how to add a new link to the dashboard and add a new admin page. </p>
<p>Finally we will take a collection of data from the db and render them in a basic table. The default table isn't cool enough, so for those reading this article, I decided to add a Tailwind table component.</p>
<h3 id="heading-re-route-the-jetstream-dashboard">Re-route the Jetstream Dashboard</h3>
<p>If we look at the <code>config/fortify.php</code> file we can see that around line 64 there is a key called home. It is calling the <code>Home</code> constant of the Route service provider.</p>
<p>This means that we can tweek the constant and redirect the authenticated user to a different route.</p>
<p>Lets go through it step-by-step:</p>
<ul>
<li>update the HOME Constant</li>
<li>make a route group and redirect logged in users to <code>admin/</code> instead of '/dashboard'</li>
</ul>
<p>Our application will have only a single user, so once they're logged in it is clearly the site admin – so makes sense to redirect to an <code>admin</code> URI.</p>
<p>Change the HOME constant in <code>app/Providers/RouteServiceProvider.php</code> around line 20 to match the following:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> HOME = <span class="hljs-string">'/admin'</span>;
</code></pre>
<h3 id="heading-how-to-add-an-admin-pages-route-group">How to Add an Admin Pages Route Group</h3>
<p>Next let's update our route inside web.php. We will change the route registered by Jetstream from this:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);
</code></pre>
<p>To this:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;prefix(<span class="hljs-string">'admin'</span>)-&gt;name(<span class="hljs-string">'admin.'</span>)-&gt;group(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{

    Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);

    <span class="hljs-comment">// other admin routes here</span>
});
</code></pre>
<p>The route above is a route group that uses the <code>auth:sanctum</code> middleware for all routes within the group, a prefix of <code>admin</code>, and adds a <code>admin</code> suffix to each route name.</p>
<p>The end result is that we will be able to refer to the dashboard route by name, which now will be <code>admin.dashboard</code>. When we log in, we will be redirected to the <code>admin</code> route. Our dashboard route will respond since it's URI is just <code>/</code> but the goup prefix will prefix every route in the group and make their URI start with <code>admin</code>.</p>
<p>If you now run <code>sail artisan route:list</code> you will notice that the dashboard route has changed as we expected.</p>
<p>Before moving to the next step we need to update both the <code>/layouts/AppLayout.vue</code> and <code>/Pages/Welcome.vue</code> components.</p>
<p>Do you remeber that the dashboard route name is now <code>admin.dashboard</code> and not just <code>dashboard</code>?</p>
<p>Let's inspect the two components and update every reference of <code>route('dahsboard')</code> to this:</p>
<pre><code class="lang-js">route(<span class="hljs-string">'admin.dahsboard'</span>)
</code></pre>
<p>and also every reference of <code>route().current('dashboard')</code> to this:</p>
<pre><code class="lang-js">route().current(<span class="hljs-string">'admin.dashboard'</span>)
</code></pre>
<p>After all the changes, make sure to recompile the Vue components and watch changes by running <code>sail npm run watch</code>. Then visit the home page to check if everything is working.</p>
<h3 id="heading-how-to-add-a-new-link-to-the-dashboard">How to Add a New Link to the Dashboard</h3>
<p>Now, to add a new admin page where we can list all photos stored in the database, we need to add a new route to the group we created earlier. Let's hit the <code>web.php</code> file and make our changes.</p>
<p>In the Route group we will add a new route:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;prefix(<span class="hljs-string">'admin'</span>)-&gt;name(<span class="hljs-string">'admin.'</span>)-&gt;group(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{

    Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);

    <span class="hljs-comment">// 👇 other admin routes here 👇</span>

    Route::get(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/Photos'</span>);
    })-&gt;name(<span class="hljs-string">'photos'</span>); <span class="hljs-comment">// This will respond to requests for admin/photos and have a name of admin.photos</span>

});
</code></pre>
<p>In the new route above we used the <code>inertia()</code> helper function that does the same exact thing – returns an Inertia/Response and renders our Page component. We placed the component under an <code>Admin</code> folder inside <code>Pages</code> and we will call it <code>Photos.vue</code>.</p>
<p>Before we create the component, let's add a new link to the dashboard that points to our new route.</p>
<p>Inside <code>AppLayout.vue</code>, find the <code>Navigation Links</code> comment and copy/paste the <code>jet-nav-link</code> component that is actually displaing a link to the dashboard and make it point to our new route.</p>
<p>You will end up having something like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Navigation Links --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hidden space-x-8 sm:-my-px sm:ml-10 sm:flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">jet-nav-link</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.dashboard')"</span> <span class="hljs-attr">:active</span>=<span class="hljs-string">"route().current('admin.dashboard')"</span>&gt;</span>
        Dashboard
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-nav-link</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 👇 here it is our new link --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">jet-nav-link</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.photos')"</span> <span class="hljs-attr">:active</span>=<span class="hljs-string">"route().current('admin.photos')"</span>&gt;</span>
        Photos
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-nav-link</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our link above uses <code>route('admin.photos')</code> to point to the correct route in the admin group.</p>
<p>If you visit <code>localhost/dashboard</code> and open the inspector, you should see an error:</p>
<pre><code class="lang-js"><span class="hljs-built_in">Error</span>: Cannot find <span class="hljs-built_in">module</span> <span class="hljs-string">`./Photos.vue`</span>
</code></pre>
<p>It is fine – we haven't created the Photos page component yet. So let's do it now!</p>
<h3 id="heading-how-to-add-a-new-admin-page-component">How to Add a New Admin Page Component</h3>
<p>Make a file named <code>Photos.vue</code> inside the <code>Pages/Admin</code> folder. Below are the bash commands to create the folder and the file via terminal, but you can do the same using your IDE's graphical interface.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> resources/js/Pages
mkdir Admin
touch Admin/Photos.vue
</code></pre>
<p>To make this new page look like the Dashboard page, we will copy over its content. You should end up having something like this:</p>
<pre><code class="lang-vue">
&lt;template&gt;
  &lt;app-layout title="Dashboard"&gt; &lt;!-- 👈 if you want you can update the page title --&gt;
    &lt;template #header&gt;
      &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
    &lt;/template&gt;

    &lt;div class="py-12"&gt;
      &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
        &lt;div class="bg-white overflow-hidden shadow-xl sm:rounded-lg"&gt;
          &lt;!-- 👇  All photos for the Admin page down here --&gt;
          &lt;h1 class="text-2xl"&gt;Photos&lt;/h1&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/app-layout&gt;
&lt;/template&gt;

&lt;script&gt;
import { defineComponent } from "vue";
import AppLayout from "@/Layouts/AppLayout.vue";

export default defineComponent({
  components: {
    AppLayout,
  },
});
&lt;/script&gt;
</code></pre>
<p>I removed a few pieces from the Dashboard template so make sure to double check the code above. The <code>welcome</code> component was removed from the template as it is not required in this page, and also its reference in the script section. The rest is identical.</p>
<p>Feel free to update the page title referenced as prop on the <code>&lt;app-layout title="Dashboard"&gt;</code>.</p>
<p>Now when you visit <code>localhost/admin</code> you can click on the Photos menu item and see our Photos page component content. It's not much for now, just an <code>h1</code>.</p>
<h3 id="heading-how-to-render-records-in-the-admin-page-as-a-table">How to Render Records in the Admin Page as a Table</h3>
<p>Now it's time to render the data onto a table. To make things work let's first add our markup and fake that we already have access to as an array of objects and loop over them inside our table. Than we will figure out how to make things work for real.</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table-auto w-full text-left"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Photo<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Desciption<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Actions<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ photo.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"60"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{photo.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>View - Edit - Delete<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<p>Ok, since we assumed that our component has access to a list of Photos, let's pass a new prop to the component from the Route.</p>
<p>Update the route in web.php and pass to the <code>inertia()</code> function a second argument that will be an associative array. It will have its keys passed as props to the Vue Page component. </p>
<p>In it we will call <code>Photo::all()</code> to have a collection to assign to a <code>photos</code> key, but you can use other eloquent methods if you want to paginate the results, for example.</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/Photos'</span>, [
        <span class="hljs-string">'photos'</span> =&gt; Photo::all()
    ]);
})-&gt;name(<span class="hljs-string">'photos'</span>);
</code></pre>
<p>To connect the prop to our Page component we need to define the prop also inside the component.</p>
<pre><code class="lang-js">&lt;script&gt;
<span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
  <span class="hljs-attr">components</span>: {
    AppLayout,
  },
  <span class="hljs-comment">/* 👇 Pass the photos array as a props 👇 */</span>
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span>,
  },
});
&lt;/script&gt;
</code></pre>
<h4 id="heading-extra-how-to-use-a-tailwind-table-component">Extra: How to use a Tailwind table component</h4>
<p>Tailwind is a CSS framework similar to Bootstrap. There are a number of free to use components that we can grab from the documentation, tweak, and use.</p>
<p>This table component is free and looks nice:<a target="_blank" href="https://tailwindui.com/components/application-ui/lists/tables">https://tailwindui.com/components/application-ui/lists/tables</a>.</p>
<p>We can tweek the Photos page template and use the Tailwind table component to get a nice looking table like so:</p>
<pre><code class="lang-vue">
&lt;template&gt;
    &lt;app-layout title="Dashboard"&gt;
        &lt;template #header&gt;
            &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
        &lt;/template&gt;

         &lt;div class="py-12"&gt;
            &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
              &lt;!-- All posts goes here --&gt;
              &lt;h1 class="text-2xl"&gt;Photos&lt;/h1&gt;
              &lt;a class="px-4 bg-sky-900 text-white rounded-md" href&gt;Create&lt;/a&gt;
              &lt;div class="flex flex-col"&gt;
                  &lt;div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"&gt;
                      &lt;div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"&gt;
                          &lt;div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"&gt;
                              &lt;table class="min-w-full divide-y divide-gray-200"&gt;
                                  &lt;thead class="bg-gray-50"&gt;
                                      &lt;tr&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;ID&lt;/th&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;Photos&lt;/th&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;Description&lt;/th&gt;
                                          &lt;th scope="col" class="relative px-6 py-3"&gt;
                                              &lt;span class="sr-only"&gt;Edit&lt;/span&gt;
                                          &lt;/th&gt;
                                      &lt;/tr&gt;
                                  &lt;/thead&gt;
                                  &lt;tbody class="bg-white divide-y divide-gray-200"&gt;
                                      &lt;tr v-for="photo in photos" :key="photo.id"&gt;
                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div
                                                  class="text-sm text-gray-900"
                                              &gt;{{ photo.id }}&lt;/div&gt;
                                          &lt;/td&gt;

                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div class="flex items-center"&gt;
                                                  &lt;div class="flex-shrink-0 h-10 w-10"&gt;
                                                      &lt;img
                                                          class="h-10 w-10 rounded-full"
                                                          :src="photo.path"
                                                          alt
                                                      /&gt;
                                                  &lt;/div&gt;
                                              &lt;/div&gt;
                                          &lt;/td&gt;

                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div class="text-sm text-gray-900"&gt;
                                                {{ photo.description.slice(0, 100) + '...' }}
                                              &lt;/div&gt;
                                          &lt;/td&gt;
                                        &lt;!-- ACTIONS --&gt;
                                          &lt;td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"&gt;
                                              &lt;a href="#" class="text-indigo-600 hover:text-indigo-900"&gt;
                                              View - Edit - Delete
                                              &lt;/a&gt;
                                          &lt;/td&gt;
                                      &lt;/tr&gt;
                                  &lt;/tbody&gt;
                              &lt;/table&gt;
                          &lt;/div&gt;
                      &lt;/div&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/app-layout&gt;
&lt;/template&gt;
</code></pre>
<h2 id="heading-how-to-submit-forms-with-files">How to Submit Forms with Files</h2>
<p>For the next section we will look into how to submit a form so that we can add a new photo to the database.</p>
<ul>
<li>Add a create button</li>
<li>Add a create route</li>
<li>Define the PhotosCreate component</li>
<li>Add a form</li>
<li>Validate data</li>
<li>Show validation errors</li>
<li>Save the file to the filesystem</li>
<li>Save the model</li>
</ul>
<h3 id="heading-how-to-create-a-new-photo">How to Create a New Photo</h3>
<p>Add a link that points to a create route:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 bg-sky-900 text-white rounded-md"</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.photos.create')"</span>&gt;</span>Create<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Create the route within the admin group:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/photos/create'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/PhotosCreate'</span>);
})-&gt;name(<span class="hljs-string">'photos.create'</span>);
</code></pre>
<p>Let's add also the route that will handle the form submission for later:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    dd(<span class="hljs-string">'I will handle the form submission'</span>)   
})-&gt;name(<span class="hljs-string">'photos.store'</span>);
</code></pre>
<p>Create the <code>Admin/PhotosCreate.vue</code> component:</p>
<pre><code class="lang-vue">
    &lt;template&gt;
    &lt;app-layout title="Dashboard"&gt;
        &lt;template #header&gt;
            &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
        &lt;/template&gt;

         &lt;div class="py-12"&gt;
            &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
                &lt;h1 class="text-2xl"&gt;Add a new Photo&lt;/h1&gt;
                &lt;!-- 👇 Photo creation form goes here --&gt;

            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/app-layout&gt;
&lt;/template&gt;


&lt;script&gt;
import { defineComponent } from "vue";
import AppLayout from "@/Layouts/AppLayout.vue";

export default defineComponent({
  components: {
    AppLayout,
  },

});
&lt;/script&gt;
</code></pre>
<h2 id="heading-how-to-add-the-form-to-the-component">How to Add the Form to the Component</h2>
<p>The next step is to add the form to the page and figure out how to submit it.</p>
<p>If you hit the Inertia documentation you will find out that there is a useForm class that we can use to simplify the process.</p>
<p>First, import the module inside the script tag of the Admin/PhotosCreate.vue component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
</code></pre>
<p>Next we can use it in the setup function (Vue 3 composition API):</p>
<pre><code class="lang-js">setup () {
    <span class="hljs-keyword">const</span> form = useForm({
      <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
    })

    <span class="hljs-keyword">return</span> { form }
  }
</code></pre>
<p>In the code above we defined the function called <code>setup()</code> then a constant called <code>form</code> to have the <code>useForm()</code> class assigned to it.</p>
<p>Inside its parentheses we defined two properties, <code>path</code> and <code>description</code> which are the column names of our photos model.</p>
<p>Finally we returned the <code>form</code> variable for the setup function. This is to make the variable available inside our template.</p>
<p>Next we can add the form markup:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"form.post(route('admin.photos.store'))"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span> Description <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"lorem ipsum"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"form.description"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-sm text-gray-500"</span>&gt;</span>Brief description for your photo<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.description"</span>&gt;</span>{{form.errors.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span> Photo <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-1 text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mx-auto h-12 w-12 text-gray-400"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 48 48"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex text-sm text-gray-600"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Upload a file<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span> @<span class="hljs-attr">input</span>=<span class="hljs-string">"form.path = $event.target.files[0]"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-1"</span>&gt;</span>or drag and drop<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>PNG, JPG, GIF up to 10MB<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.path"</span>&gt;</span>{{form.errors.path}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">:disabled</span>=<span class="hljs-string">"form.processing"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>&gt;</span>Save<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>The code above uses the Vue v-on directive short end syntax <code>@submit.prevent="form.post(route('admin.photos.store'))"</code> on the form tag, and the dom event <code>submit</code> with the <code>prevent</code> modifier. </p>
<p>Then it uses the <code>form</code> variable that we created earlier and a <code>post</code> method. This is available because we are using the <code>useForm</code> class. </p>
<p>Next we point the form to the route named admin.photos.store that we created earlier.</p>
<p>Inside the form we have two groups of inputs. First, we have the textarea that uses the v-model to bind it to the property <code>form.description</code> that we declared before.</p>
<p>The second group uses the <code>form.path</code> in a Tailwind component (showing the markup for a drop file area).</p>
<p>Right now we are allowing users to upload only a single photo using the v-on directive on the input DOM event <code>@input="form.path = $event.target.files[0]"</code>.</p>
<p>The last two things to notice are the error handling done via <code>&lt;div class="text-red-500" v-if="form.errors.path"&gt;{{form.errors.path}}&lt;/div&gt;</code> for the path and also for the description.</p>
<p>Finally we use <code>form.processing</code> to disable the submit button while the form is processing.</p>
<p>The next step is to define the logic to save the data inside the database.</p>
<h2 id="heading-how-to-store-data">How to Store Data</h2>
<p>To store the data, we can edit the route we defined earlier like so:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Request $request</span>) </span>{
    <span class="hljs-comment">//dd('I will handle the form submission')  </span>

    <span class="hljs-comment">//dd(Request::all());</span>
    $validated_data = $request-&gt;validate([
        <span class="hljs-string">'path'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'image'</span>, <span class="hljs-string">'max:2500'</span>],
        <span class="hljs-string">'description'</span> =&gt; [<span class="hljs-string">'required'</span>]
    ]);
    <span class="hljs-comment">//dd($validated_data);</span>
    $path = Storage::disk(<span class="hljs-string">'public'</span>)-&gt;put(<span class="hljs-string">'photos'</span>, $request-&gt;file(<span class="hljs-string">'path'</span>));
    $validated_data[<span class="hljs-string">'path'</span>] = $path;
    <span class="hljs-comment">//dd($validated_data);</span>
    Photo::create($validated_data);
    <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
})-&gt;name(<span class="hljs-string">'photos.store'</span>);
</code></pre>
<p>The code above uses dependency injection to allow us to use the parameter <code>$request</code> inside the callback function. </p>
<p>We first validate the request and save the resulting array inside the variable <code>$validated_data</code>. Then we use the <code>Storage</code> facades to save the file in the filesystem and obtain the file path that we store inside the <code>$path variable</code>.</p>
<p>Finally we add a <code>path</code> key to the associative array and pass to it the <code>$path</code> variable. Next we create the resource in the database using the <code>Photo::create</code> method and redirect the user to the <code>admin.photos</code> page using the new <code>to_route()</code> helper function.</p>
<p>Make sure to import the <code>Request</code> class and the <code>Storage</code> facades at the top of the web.php file like so:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Storage</span>;
</code></pre>
<p>Now we can add a new photo in the database and show a list of photos for both the admin and standard visitors.</p>
<p>Next we need to complete the CRUD operations and allow the user to edit/update a photo and delete it.</p>
<h2 id="heading-how-to-update-operations">How to Update Operations</h2>
<p>Let's start by adding the routes responsible for showing the forms used to edit the resource and update its values onto the database.</p>
<p>Just under the other routes in the Admin group, let's add the following code:</p>
<pre><code class="lang-php">
Route::get(<span class="hljs-string">'/photos/{photo}/edit'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Photo $photo</span>)</span>{
     <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/PhotosEdit'</span>, [
            <span class="hljs-string">'photo'</span> =&gt; $photo
        ]);
})-&gt;name(<span class="hljs-string">'photos.edit'</span>);
</code></pre>
<p>The route above uses dependency injection to inject inside the function the current post, selected by the URI <code>/photos/{photo}/edit</code>. </p>
<p>Next it returns the Inertia response via the <code>inertia()</code> function that accepts the Component name <code>'Admin/PhotosEdit'</code> as its first parameter and an associative array as its second.</p>
<p>Doing <code>['photo' =&gt; $photo]</code> will allow us to pass the <code>$photo</code> model as a prop to the component later.</p>
<p>Next let's add the new Page component under <code>resources/js/Pages/Admin/PhotosEdit.vue</code></p>
<p>This will be its template:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Edit Photo"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>Edit Photo<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"form.post(route('admin.photos.update', photo.id))"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                            <span class="hljs-attr">for</span>=<span class="hljs-string">"description"</span>
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>
                        &gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
                                <span class="hljs-attr">id</span>=<span class="hljs-string">"description"</span>
                                <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span>
                                <span class="hljs-attr">rows</span>=<span class="hljs-string">"3"</span>
                                <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"</span>
                                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"lorem ipsum"</span>
                                <span class="hljs-attr">v-model</span>=<span class="hljs-string">"form.description"</span>
                            /&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-sm text-gray-500"</span>&gt;</span>Brief description for your photo<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>
                            <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.description"</span>
                        &gt;</span>{{ form.errors.description }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-cols-2"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"preview p-4"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"'/storage/' + photo.path"</span> <span class="hljs-attr">alt</span> /&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span>Photo<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                                <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"</span>
                            &gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-1 text-center"</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
                                        <span class="hljs-attr">class</span>=<span class="hljs-string">"mx-auto h-12 w-12 text-gray-400"</span>
                                        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                                        <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
                                        <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 48 48"</span>
                                        <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>
                                    &gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                                            <span class="hljs-attr">d</span>=<span class="hljs-string">"M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"</span>
                                            <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>
                                            <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span>
                                            <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span>
                                        /&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex text-sm text-gray-600"</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                                            <span class="hljs-attr">for</span>=<span class="hljs-string">"path"</span>
                                            <span class="hljs-attr">class</span>=<span class="hljs-string">"relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"</span>
                                        &gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Upload a file<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                                                <span class="hljs-attr">id</span>=<span class="hljs-string">"path"</span>
                                                <span class="hljs-attr">name</span>=<span class="hljs-string">"path"</span>
                                                <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span>
                                                <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>
                                                @<span class="hljs-attr">input</span>=<span class="hljs-string">"form.path = $event.target.files[0]"</span>
                                            /&gt;</span>
                                        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-1"</span>&gt;</span>or drag and drop<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>PNG, JPG, GIF up to 10MB<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.path"</span>&gt;</span>{{ form.errors.path }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
                        <span class="hljs-attr">:disabled</span>=<span class="hljs-string">"form.processing"</span>
                        <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>
                    &gt;</span>Update<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>The template is actually identical to the Create component, except for a few things. The form points to a route that expects a paramenter that we pass as the second argument to the funtion <code>route</code>. It looks like this: <code>&lt;form @submit.prevent="form.post(route('admin.photos.update', photo.id))"&gt;</code>.</p>
<p>There is a section where we can see the original photo next to the upload form group:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"preview p-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"'/storage/' + photo.path"</span> <span class="hljs-attr">alt</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The rest is identical, and here we have the script section:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
    <span class="hljs-attr">components</span>: {
        AppLayout,
    },
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">photo</span>: <span class="hljs-built_in">Object</span>
    },
    setup(props) {
        <span class="hljs-keyword">const</span> form = useForm({
            <span class="hljs-attr">_method</span>: <span class="hljs-string">"PUT"</span>,
            <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">description</span>: props.photo.description,
        })

        <span class="hljs-keyword">return</span> { form }
    },

});
</code></pre>
<p>Notice that we are passing a props object with the photo key, which allows us to reference the model in the template.</p>
<p>Next, this <code>_method: "PUT",</code> line of code is required to be able to submit a <code>PUT</code> request instead of the <code>POST</code> request called on the form tag.</p>
<p>Now let's implement the logic to handle the form submission inside the Route below.</p>
<p>In web.php just under the previous route, let's add one that responds to the PUT request submitted by our form.</p>
<pre><code class="lang-php">Route::put(<span class="hljs-string">'/photos/{photo}'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Request $request, Photo $photo</span>)
    </span>{
        <span class="hljs-comment">//dd(Request::all());</span>

        $validated_data = $request-&gt;validate([
            <span class="hljs-string">'description'</span> =&gt; [<span class="hljs-string">'required'</span>]
        ]);

        <span class="hljs-keyword">if</span> ($request-&gt;hasFile(<span class="hljs-string">'path'</span>)) {
            $validated_data[<span class="hljs-string">'path'</span>] = $request-&gt;validate([
                <span class="hljs-string">'path'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'image'</span>, <span class="hljs-string">'max:1500'</span>],

            ]);

            <span class="hljs-comment">// Grab the old image and delete it</span>
            <span class="hljs-comment">// dd($validated_data, $photo-&gt;path);</span>
            $oldImage = $photo-&gt;path;
            Storage::delete($oldImage);

            $path = Storage::disk(<span class="hljs-string">'public'</span>)-&gt;put(<span class="hljs-string">'photos'</span>, $request-&gt;file(<span class="hljs-string">'path'</span>));
            $validated_data[<span class="hljs-string">'path'</span>] = $path;
        }

        <span class="hljs-comment">//dd($validated_data);</span>

        $photo-&gt;update($validated_data);
        <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
    })-&gt;name(<span class="hljs-string">'photos.update'</span>);
</code></pre>
<p>The route logic is straigthforward. First we validate the description, next we check if a file was uploaded and if so we validate it. </p>
<p>Then we delete the previously uploaded image <code>Storage::delete($oldImage);</code> before storing the new image onto the datadabse and update the resource using <code>$photo-&gt;update($validated_data);</code>.</p>
<p>As before with the store route, we redirect to the <code>admin.photos</code> route using <code>return to_route('admin.photos');</code>.</p>
<h2 id="heading-how-to-delete-a-resource">How to Delete a Resource</h2>
<p>The last step we need to take is to write the logic to delete the photo. Let's start by adding the route.</p>
<p>Right below the previous route we can write:</p>
<pre><code class="lang-php">Route::delete(<span class="hljs-string">'/photos/{photo}'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Photo $photo</span>)
</span>{
    Storage::delete($photo-&gt;path);
    $photo-&gt;delete();
    <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
})-&gt;name(<span class="hljs-string">'photos.delete'</span>);
</code></pre>
<p>This route is also using a wildcard in its URI to identify the resource. Next, its second paramenter is the callback that uses the dependency injection as before. Inside the callback we first delete the image from the filesystem using <code>Storage::delete($photo-&gt;path);</code>.</p>
<p>Then we remove the resource from the database <code>$photo-&gt;delete();</code> and redirect the user back <code>return to_route('admin.photos');</code> like we did in the previous reoute.</p>
<p>Now we need to add a delete button to the table we created in one of the previous steps to show all photos.</p>
<p>Inside the template section of the component <code>Admin/Photos.vue</code> within the <code>v-for</code>, we can add this Jetstream button:</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
    Delete
<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
</code></pre>
<p>Find the table cell that has the <code>ACTIONS</code> comment and replace the <code>DELETE</code> text with the button above.</p>
<p>So the final code will be:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
    View - Edit - 

    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
        Delete
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
</code></pre>
<p>As you can see there is a <code>@click</code> event listener on the button. It calls a method <code>delete_photo(photo)</code> that we need to define along with a bunch of other methods to have a nice modal opening to ask for confirmation from the user.</p>
<p>First import the Inertia helper function useForm:</p>
<pre><code class="lang-js"><span class="hljs-comment">// 0. Import the useForm class at the top of the script section along with all required components</span>
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> JetDangerButton <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DangerButton.vue'</span>
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
</code></pre>
<p>Remember to register the component <code>JetDangerButton</code> inside the components object before moving forward.</p>
<p>Next add the <code>setup()</code> function in the script section and implement the logic required to submit the form and show a modal. The comments in the code will guide you thorought all the steps.</p>
<pre><code class="lang-js"><span class="hljs-comment">// 1. add the setup function</span>
setup() {
    <span class="hljs-comment">// 2. declare a form variable and assign to it the Inertia useForm() helper function </span>
    <span class="hljs-keyword">const</span> form = useForm({
        <span class="hljs-comment">// 3. override the form method to make a DELETE request</span>
        <span class="hljs-attr">_method</span>: <span class="hljs-string">"DELETE"</span>,
    });
    <span class="hljs-comment">// 4. define a reactive object with show_modal and photo property</span>
    <span class="hljs-comment">// this will be used to figure out when to show the modal and the selected post values</span>
    <span class="hljs-keyword">const</span> data = ref({
        <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">photo</span>: {
            <span class="hljs-attr">id</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
        }
    })

    <span class="hljs-comment">// 5. define the delete_photo function and update the values of the show_modal and photo properties</span>
    <span class="hljs-comment">// of the reactive object defined above. This method is called by the delete button and will record the details </span>
    <span class="hljs-comment">// of the selected post</span>
    <span class="hljs-keyword">const</span> delete_photo = <span class="hljs-function">(<span class="hljs-params">photo</span>) =&gt;</span> {
        <span class="hljs-comment">//console.log(photo);</span>
        <span class="hljs-comment">//console.log(photo.id, photo.path, photo.description);</span>
        data.value = {
            <span class="hljs-attr">photo</span>: {
                <span class="hljs-attr">id</span>: photo.id,
                <span class="hljs-attr">path</span>: photo.path,
                <span class="hljs-attr">description</span>: photo.description
            },
            <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">true</span>
        };
    }
    <span class="hljs-comment">// 6. define the method that will be called when our delete form is submitted</span>
    <span class="hljs-comment">// the form will be created next</span>
    <span class="hljs-keyword">const</span> deleting_photo = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
        form.post(route(<span class="hljs-string">'admin.photos.delete'</span>, id))
        closeModal();
    }
    <span class="hljs-comment">// 7. delare a method to close the modal by setting the show_modal to false</span>
    <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
        data.value.show_modal = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-comment">// 8. remember to return from the setup function the all variables and methods that you want to expose </span>
    <span class="hljs-comment">// to the template.</span>
    <span class="hljs-keyword">return</span> { form, data, closeModal, delete_photo, deleting_photo }

}
</code></pre>
<p>Finally outside the <code>v-for</code> loop add the modal using the following code. You can place this where you want but not inside the loop.</p>
<pre><code class="lang-html">
 <span class="hljs-tag">&lt;<span class="hljs-name">JetDialogModal</span> <span class="hljs-attr">:show</span>=<span class="hljs-string">"data.show_modal"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">title</span>&gt;</span>
        Photo {{ data.photo.description.slice(0, 20) + '...' }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">content</span>&gt;</span>
        Are you sure you want to delete this photo?

    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">footer</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"closeModal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 py-2"</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"deleting_photo(data.photo.id)"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Yes, I am sure!<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">JetDialogModal</span>&gt;</span>
</code></pre>
<p>This is our final JavaScript code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;
<span class="hljs-keyword">import</span> TableComponent <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Components/TableComponent.vue"</span>;
<span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> JetDialogModal <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DialogModal.vue'</span>;
<span class="hljs-keyword">import</span> JetDangerButton <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DangerButton.vue'</span>
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
    <span class="hljs-attr">components</span>: {
        AppLayout,
        Link,
        TableComponent,
        JetDialogModal,
        JetDangerButton
    },
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span>,
    },

    setup() {

        <span class="hljs-keyword">const</span> form = useForm({
            <span class="hljs-attr">_method</span>: <span class="hljs-string">"DELETE"</span>,
        });
        <span class="hljs-keyword">const</span> data = ref({
            <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">photo</span>: {
                <span class="hljs-attr">id</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
            }

        })


        <span class="hljs-keyword">const</span> delete_photo = <span class="hljs-function">(<span class="hljs-params">photo</span>) =&gt;</span> {
            <span class="hljs-comment">//console.log(photo);</span>
            <span class="hljs-built_in">console</span>.log(photo.id, photo.path, photo.description);
            data.value = {
                <span class="hljs-attr">photo</span>: {
                    <span class="hljs-attr">id</span>: photo.id,
                    <span class="hljs-attr">path</span>: photo.path,
                    <span class="hljs-attr">description</span>: photo.description
                },
                <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">true</span>
            };
        }
        <span class="hljs-keyword">const</span> deleting_photo = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
            form.post(route(<span class="hljs-string">'admin.photos.delete'</span>, id))
            closeModal();
        }

        <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
            data.value.show_modal = <span class="hljs-literal">false</span>;


        }

        <span class="hljs-keyword">return</span> { form, data, closeModal, delete_photo, deleting_photo }

    }
});
&lt;/script&gt;
</code></pre>
<p>And here we have the HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

         <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
              <span class="hljs-comment">&lt;!-- All posts goes here --&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl"</span>&gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 bg-sky-900 text-white rounded-md"</span> <span class="hljs-attr">href</span>&gt;</span>Create<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-col"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"</span>&gt;</span>
                              <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"min-w-full divide-y divide-gray-200"</span>&gt;</span>
                                  <span class="hljs-tag">&lt;<span class="hljs-name">thead</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-50"</span>&gt;</span>
                                      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative px-6 py-3"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>&gt;</span>Edit<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                                  <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
                                  <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white divide-y divide-gray-200"</span>&gt;</span>
                                      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"photo.id"</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                                                  <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm text-gray-900"</span>
                                              &gt;</span>{{ photo.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
                                                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-shrink-0 h-10 w-10"</span>&gt;</span>
                                                      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                                                          <span class="hljs-attr">class</span>=<span class="hljs-string">"h-10 w-10 rounded-full"</span>
                                                          <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span>
                                                          <span class="hljs-attr">alt</span>
                                                      /&gt;</span>
                                                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm text-gray-900"</span>&gt;</span>
                                                {{ photo.description.slice(0, 100) + '...' }}
                                              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                        <span class="hljs-comment">&lt;!-- ACTIONS --&gt;</span>
                                         <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
                                            View - Edit - 

                                            <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
                                                Delete
                                            <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
                                            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                                  <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
                              <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
                          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">JetDialogModal</span> <span class="hljs-attr">:show</span>=<span class="hljs-string">"data.show_modal"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">title</span>&gt;</span>
                Photo {{ data.photo.description.slice(0, 20) + '...' }}
            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">content</span>&gt;</span>
                Are you sure you want to delete this photo?

            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">footer</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"closeModal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 py-2"</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"deleting_photo(data.photo.id)"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Yes, I am sure!<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">JetDialogModal</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>That's it. If you did everything correctly you should be able to see all photos, create new photos as well as edit and delete them.</p>
<p>I will leave you some home work. Can you figure out how to implement the view and edit links before the delete button in the section below?</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- ACTIONS --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
    View - Edit - 

    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
        Delete
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
</code></pre>
<h2 id="heading-wrapup-and-whats-next">Wrapup and What's next</h2>
<p>During this guide we took our first steps and learned how to build a single page application using Laravel as our backend framework and Vue3 for the front end. We glued them together with Inertia js and built a simple photo application that lets a user manage photos. </p>
<p>We are just at the beginning of a fantastic journey. Learning new technologies isn't easy, but thanks to their exaustive documentations we can keep up and build awesome applications. </p>
<p>Your next step to master Laravel, Vue3, Inertia and all the tech we have been using so far is to hit their documentation and keep learning. Use the app we have build if you want, and improve it or start over from scratch. </p>
<p>Just keep that in mind, coding is fun so relax and enjoy it.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This is just an overview of how I'd build a single page application using these technologies.</p>
<p>If you are familiar with server-side routing and Vuejs then you will enjoy bulding a single page application with Laravel, Inertia, and Vuejs. The learning curve isn't that steep plus you have great documentation to help you out.</p>
<p>I hope you've enjoyed this guide. If so, let me know and consider subscribing to my YouTube channel and following me on Twitter. And if you get stuck, get in touch for help.</p>
<p>You can find the source code for this guide <a target="_blank" href="https://bitbucket.org/fbhood/spa-with-laravel-9/src/master/">here</a>.</p>
<p><a target="_blank" href="https://twitter.com/Fab_Sky_Walker">Follow me on Twitter</a>
<a target="_blank" href="https://join.slack.com/t/fabiopacificicom/shared_invite/zt-rf4vwvcm-esx1RkokwrJ93yyr1rPpVQ">Join me on slack</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add a MetaMask Login to Your Laravel Web Application ]]>
                </title>
                <description>
                    <![CDATA[ By Darren Chowles Logging into a website via third parties is ubiquitous online. Almost every other member-based website allows you to log in with accounts like Facebook, Twitter, and Google. If you’ve ever visited NFT marketplaces like OpenSea or Ra... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/add-a-metamask-login-to-your-laravel-app/</link>
                <guid isPermaLink="false">66d45e0055db48792eed3f57</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Metamask ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web3 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 23 Mar 2022 18:38:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/03/photo.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Darren Chowles</p>
<p>Logging into a website via third parties is ubiquitous online. Almost every other member-based website allows you to log in with accounts like Facebook, Twitter, and Google.</p>
<p>If you’ve ever visited NFT marketplaces like OpenSea or Rarible, you would have noticed they allow you to sign in with a crypto wallet like MetaMask. </p>
<p>This login process affirms you’re the owner of the Ethereum address in question and allows the system to authenticate your access. Very similar to how a username and password would allow you access to a gated part of a website.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before starting this tutorial, I’ll assume you have a basic understanding of Laravel, and that you can initialise a new project in your environment. Even though this tutorial is Laravel-focused, with some tweaking you can apply this to any other PHP project. The concepts remain the same.</p>
<p>I’ve tried to keep this as generic as possible. I only focus on the MetaMask signing and validation, without restricting you to using it with specific front-end technologies (like React or Vue) or authentication scaffolding (like Breeze or Jetstream). This gives you the freedom to implement it with minimal effort into an existing project.</p>
<p>We’ll need the following before we start:</p>
<ul>
<li>A new or existing Laravel project.</li>
<li><a target="_blank" href="https://metamask.io/">MetaMask</a> installed in your browser.</li>
</ul>
<h2 id="heading-boilerplate">Boilerplate</h2>
<p>We’ll start out with some boilerplate code by importing <a target="_blank" href="https://getbootstrap.com/">Bootstrap 5</a> and creating a simple “Log in with MetaMask” button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/image-1-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>MetaMask Login<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-12 text-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary mt-5"</span>&gt;</span>Log in with MetaMask<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Easy enough. 😀</p>
<p>We’re also importing the <a target="_blank" href="https://docs.ethers.io/">ethers.js library</a> that will allow us to interact with the Ethereum blockchain via MetaMask, which in this case acts as the interface to the provider (<a target="_blank" href="https://infura.io/">Infura</a> by default).</p>
<h3 id="heading-quick-tip">Quick tip:</h3>
<p>Providers allow us to interact with the Ethereum blockchain. To connect to the network, you need access to a node. Depending on the type of node, it could require a large amount of disk space and bandwidth. Running a node can also be a complex process, especially if you want to focus on development rather than maintaining and operating a node. </p>
<p>Enter, the provider! Companies like Infura provide these nodes as a service, so you don’t need to worry about running your own. Instead, you can access this functionality via their APIs.</p>
<p>You may run into older tutorials that state MetaMask injects web3.js (a library providing similar functionality to ethers.js) into the page by default. This is <a target="_blank" href="https://docs.metamask.io/guide/provider-migration.html#summary-of-breaking-changes">no longer the case</a>.</p>
<h2 id="heading-detect-the-provider">Detect the Provider</h2>
<p>We’ll start off our new <code>web3Login()</code> function by checking that the browser has a provider available. This would be the case if you have MetaMask installed. You can also test this code where MetaMask is not installed (for example, an incognito window) to confirm the detection works.</p>
<p>Add the click event to the button:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary mt-5"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"web3Login();"</span>&gt;</span>Log in with MetaMask<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>And start off the function with our detection snippet in our <code>&lt;head&gt;</code> below the ethers.js import:</p>
<pre><code class="lang-js">&lt;script&gt;
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">web3Login</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">window</span>.ethereum) {
            alert(<span class="hljs-string">'MetaMask not detected. Please install MetaMask first.'</span>);
            <span class="hljs-keyword">return</span>;
        }
    }
&lt;/script&gt;
</code></pre>
<p>Go ahead and test this in a browser with no MetaMask installed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/image-2-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-install-laravel-dependencies">Install Laravel Dependencies</h2>
<p>Before we continue with the front-end login process, we need to put some endpoints in place. Our login script will need these so the user can sign a message with their wallet, and our system can then verify their signature.</p>
<p>We need to install two dependencies via Composer to help us perform hashing and use elliptic curve cryptography:</p>
<pre><code class="lang-bash">composer require kornrunner/keccak
composer require simplito/elliptic-php
</code></pre>
<h2 id="heading-add-laravel-routes">Add Laravel Routes</h2>
<p>Open your <strong>routes/web.php</strong> file and add the following routes:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/web3-login-message'</span>, <span class="hljs-string">'Web3LoginController@message'</span>);
Route::post(<span class="hljs-string">'/web3-login-verify'</span>, <span class="hljs-string">'Web3LoginController@verify'</span>);
</code></pre>
<p>The first route will return the message that needs to be signed, and the second route will verify the signed message.</p>
<h2 id="heading-create-the-login-controller">Create the Login Controller</h2>
<p>Now it’s time to create the controller that will generate the message and perform the verification.</p>
<p>Create a new file called <strong>Web3LoginController.php</strong> in <strong>app/Http/Controllers</strong> and add the following code to it:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Elliptic</span>\<span class="hljs-title">EC</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Str</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">kornrunner</span>\<span class="hljs-title">Keccak</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Web3LoginController</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">message</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span>
    </span>{
        $nonce = Str::random();
        $message = <span class="hljs-string">"Sign this message to confirm you own this wallet address. This action will not cost any gas fees.\n\nNonce: "</span> . $nonce;

        session()-&gt;put(<span class="hljs-string">'sign_message'</span>, $message);

        <span class="hljs-keyword">return</span> $message;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verify</span>(<span class="hljs-params">Request $request</span>): <span class="hljs-title">string</span>
    </span>{
        $result = <span class="hljs-keyword">$this</span>-&gt;verifySignature(session()-&gt;pull(<span class="hljs-string">'sign_message'</span>), $request-&gt;input(<span class="hljs-string">'signature'</span>), $request-&gt;input(<span class="hljs-string">'address'</span>));
        <span class="hljs-comment">// If $result is true, perform additional logic like logging the user in, or by creating an account if one doesn't exist based on the Ethereum address</span>
        <span class="hljs-keyword">return</span> ($result ? <span class="hljs-string">'OK'</span> : <span class="hljs-string">'ERROR'</span>);
    }

    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifySignature</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $message, <span class="hljs-keyword">string</span> $signature, <span class="hljs-keyword">string</span> $address</span>): <span class="hljs-title">bool</span>
    </span>{
        $hash = Keccak::hash(sprintf(<span class="hljs-string">"\x19Ethereum Signed Message:\n%s%s"</span>, strlen($message), $message), <span class="hljs-number">256</span>);
        $sign = [
            <span class="hljs-string">'r'</span> =&gt; substr($signature, <span class="hljs-number">2</span>, <span class="hljs-number">64</span>),
            <span class="hljs-string">'s'</span> =&gt; substr($signature, <span class="hljs-number">66</span>, <span class="hljs-number">64</span>),
        ];
        $recid = ord(hex2bin(substr($signature, <span class="hljs-number">130</span>, <span class="hljs-number">2</span>))) - <span class="hljs-number">27</span>;

        <span class="hljs-keyword">if</span> ($recid != ($recid &amp; <span class="hljs-number">1</span>)) {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }

        $pubkey = (<span class="hljs-keyword">new</span> EC(<span class="hljs-string">'secp256k1'</span>))-&gt;recoverPubKey($hash, $sign, $recid);
        $derived_address = <span class="hljs-string">'0x'</span> . substr(Keccak::hash(substr(hex2bin($pubkey-&gt;encode(<span class="hljs-string">'hex'</span>)), <span class="hljs-number">1</span>), <span class="hljs-number">256</span>), <span class="hljs-number">24</span>);

        <span class="hljs-keyword">return</span> (Str::lower($address) === $derived_address);
    }
}
</code></pre>
<p>There’s a lot going on in there, so let’s break it down:</p>
<h3 id="heading-create-the-message">Create the message</h3>
<p>The <code>message()</code> method creates the message we’ll supply to the front end. It also includes a random token to ensure the message to sign will be different each time.</p>
<p>This token is usually referred to as a nonce, or number used once. In this case, however, it’s a simple random string. </p>
<p>The purpose of this is to prevent <a target="_blank" href="https://en.wikipedia.org/wiki/Replay_attack">replay attacks</a> where, if a malicious user obtained your signature, they could use that to authenticate as you on the website.</p>
<p>The message is then saved to the session and returned to the front end.</p>
<h3 id="heading-verify-the-message">Verify the message</h3>
<p>Once you have signed the message with your private key via MetaMask, your Ethereum address as well as the signature is sent to the back end for verification.</p>
<p>If it passes the verification, we determine the Ethereum address that signed the message and ensure it matches the Ethereum address sent from the front end during the signing process.</p>
<p>If that passes, we send an <strong>OK</strong> or <strong>ERROR</strong> back to the front end.</p>
<p>It’s also at this point where you can add additional logic like logging the member in or creating a new member record if one doesn’t exist for the Ethereum address in question.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/Laravel-Metamask.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-finalise-the-front-end">Finalise the Front End</h2>
<p>Now that the backend is ready, we can complete the rest of the front end. This will involve launching MetaMask, asking the user to sign the message, and then verifying the signature by using our back-end route.</p>
<p>Below is the full <code>web3Login()</code> function:</p>
<pre><code class="lang-js">&lt;script&gt;
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">web3Login</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">window</span>.ethereum) {
            alert(<span class="hljs-string">'MetaMask not detected. Please install MetaMask first.'</span>);
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">const</span> provider = <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(<span class="hljs-built_in">window</span>.ethereum);

        <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/web3-login-message'</span>);
        <span class="hljs-keyword">const</span> message = <span class="hljs-keyword">await</span> response.text();

        <span class="hljs-keyword">await</span> provider.send(<span class="hljs-string">"eth_requestAccounts"</span>, []);
        <span class="hljs-keyword">const</span> address = <span class="hljs-keyword">await</span> provider.getSigner().getAddress();
        <span class="hljs-keyword">const</span> signature = <span class="hljs-keyword">await</span> provider.getSigner().signMessage(message);

        response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/web3-login-verify'</span>, {
            <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
            <span class="hljs-attr">headers</span>: {
                <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>
            },
            <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
                <span class="hljs-string">'address'</span>: address,
                <span class="hljs-string">'signature'</span>: signature,
                <span class="hljs-string">'_token'</span>: <span class="hljs-string">'{{ csrf_token() }}'</span>
            })
        });
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.text();

        <span class="hljs-built_in">console</span>.log(data);
    }
&lt;/script&gt;
</code></pre>
<p>Let’s break it down:</p>
<ul>
<li>We first set the provider to the MetaMask provided <code>window.ethereum</code>.</li>
<li>Next, we grab the message returned from our back end (refresh the page a few times to try this out, you will notice the random token changes every time).</li>
<li>Once we have the message, we obtain the user’s Ethereum address and ask them to sign the message.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/image-3-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>We then POST the address and signature to the back end (along with our Laravel CRSF token) for verification.</li>
<li>The result is either an <strong>OK</strong> or <strong>ERROR</strong> string which we output in the console.</li>
<li>At this point you may elect to show an error message (if applicable) or redirect the user if they were registered or logged in during the back-end verification.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial we covered the basics of adding a MetaMask login to your website. I hope this has proven useful! <a target="_blank" href="https://webdev.chowles.com/">Sign up to my newsletter</a> or <a target="_blank" href="https://www.chowles.com/">visit my blog</a> where I’ll share insightful web development articles to supercharge your skills.</p>
<p>Here are some ideas to take your integration one step further:</p>
<ul>
<li>Integrate a library like <a target="_blank" href="https://github.com/web3modal/web3modal">Web3Modal</a> to provide users with various wallet options instead of only MetaMask.</li>
<li>With the user’s Ethereum address validated, provide them with functions like displaying their ETH balance.</li>
</ul>
<h3 id="heading-resources">Resources</h3>
<ul>
<li>Download <a target="_blank" href="https://metamask.io/">MetaMask</a>.</li>
<li>View the <a target="_blank" href="https://docs.ethers.io/">ethers.js documentation</a>.</li>
<li>Latest <a target="_blank" href="https://laravel.com/docs">Laravel documentation</a>.</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Laravel 8 on Your Android Phone ]]>
                </title>
                <description>
                    <![CDATA[ By Precious Oladele Hey, how are you doing? In this article, I'm going to show you how you can install Laravel 8 on your phone.  To get the most out of this guide, you should have some knowledge of PHP and you should know what Laravel is. But if ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-setup-laravel-8-on-android-phone/</link>
                <guid isPermaLink="false">66d4608b55db48792eed3f99</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ android app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 11 Nov 2021 16:34:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/11/pexels-lisa-1092644--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Precious Oladele</p>
<p>Hey, how are you doing? In this article, I'm going to show you how you can install Laravel 8 on your phone. </p>
<p>To get the most out of this guide, you should have some knowledge of PHP and you should know what Laravel is. But if you don't, don't worry – I will explain the basics so you can get started.</p>
<h2 id="heading-what-is-laravel">What Is Laravel?</h2>
<p>Laravel is a web application framework with expressive, elegant syntax. It's built on PHP, which means that Laravel is PHP but it makes things easier to work with.</p>
<p>It comes with lots of packages for various features, like authentication, so we don't need to write authentication ourselves. To learn more about what Laravel can do, you can visit the site at <a target="_blank" href="https://www.freecodecamp.org/news/p/8cc4755b-3b0b-4751-991c-a7b4997c0335/laravel.com">laravel.com</a>.</p>
<h2 id="heading-why-i-wrote-this-tutorial">Why I wrote this tutorial</h2>
<p>I created this tutorial because I want people interested in programming who don't have a laptop or pc to be able to build things on their phones. </p>
<p>My <a target="_blank" href="https://www.freecodecamp.org/news/how-to-code-on-your-phone-python-pydroid-android-app-tutorial/">last post on freeCodeCamp</a> made me realize that people are interested in learning how the tech works, so that's why I'm making more guides like this.</p>
<p>So let's dive into it. In this tutorial I am going to show you how you can install composer.php and use it to set up Laravel 8 on your phone 🔥🔥. </p>
<p>I am Precious Oladele, and I'm almost 19 this month 🥴. I'm from Nigeria and I will be taking you through this process. And if you're wondering how I know so much about this, it's because I also don't have a laptop so I explore with my phone instead 😎.</p>
<h2 id="heading-requirements">Requirements</h2>
<p>To go through this tutorial, you'll need an Android phone with V6.0+.</p>
<h2 id="heading-set-up">Set up</h2>
<p>We need to head over to the Play Store and download <strong>Termux:</strong></p>
<p><img src="https://lh6.googleusercontent.com/cQlgYSj1hH3487jHevQn4eKEjC51xAn5vm0fo6tL8yfNV-nwTeLFW2kv480G5i-OjxtaKXCNQ2c5q36ywH5nqjitwKBjieL7NC4ZXmw7K3WbsuxrsKgAaQBslE9uFg_xnSOFdXc" alt="Image" width="720" height="1600" loading="lazy"></p>
<p><strong>Termux</strong> is a Linux-based system which we can use on our phones. It's as simple as using your regular Linux – you can install anything, even Kali, Ubuntu, or whatever you want. But for this tutorial we will be using it to set up Laravel 8 on our mobile phone.</p>
<h2 id="heading-download-composer">Download composer</h2>
<p>Before we download composer, we need to open up our <strong>Termux</strong> app and type in this command:</p>
<pre><code>termux-setup-storage
</code></pre><p>It'll ask you for storage permissions, so go ahead and click accept. Once you're done head over to <a target="_blank" href="https://getcomposer.org/download/">https://getcomposer.org/download/</a>.</p>
<p><img src="https://lh4.googleusercontent.com/6DrBqRU9swO3NdA0iEKhzhvK7bQtH3dLdKdx69YrCQboxPLIi-seYv84u_5I4dTAcAqAayNRv6wxlMxxLkivcLYi0N5-iLLYAwB9nzVKvxdMFRaY44ECp5duQLcX7j685RyK-K0" alt="Image" width="720" height="1600" loading="lazy"></p>
<p>We need to grab everything there. But before that we need to install <strong>PHP</strong> so we can use it in our app. To do that in your <strong>Termux</strong>, type in the following command:</p>
<pre><code>apt install php
</code></pre><p>and click enter. You should see this:</p>
<p><img src="https://lh6.googleusercontent.com/jamAoOq-pWlvWz9FfouVQVagFh1stoz-qvrCf4k_Aywd9pabMiDj8ygNR9lqCiXDmwF8M2nzHyrJoDlvrBoPAUSO4w7WY40vQlQqTWzdkDAvK8dlR9XMn19qVhLAu1iwv0E7R24" alt="Image" width="720" height="1600" loading="lazy"></p>
<p>Once that is done head over to the composer page and grab the code. We need to do this because Termux is Linux-based. If it was Windows there would be a simple button to download composer.exe there. </p>
<p>Copy the whole code and head over to Termux where you can paste it in. Then click enter.</p>
<p>When composer is installed you should see something like this:</p>
<p><img src="https://lh3.googleusercontent.com/Ou_2eDdSX0ZuA0KW29MF2xNMi0YHh3f159-w7ujzGeQtIUEtEZ4mFaq4WYJLFxeGeox7lHmxMswxJcRm1fY6a2C4fZSd0329DnjPcHJvaiUs-vKerof13s2qYMCEi650q-X_qqU" alt="Image" width="720" height="1600" loading="lazy"></p>
<h2 id="heading-how-to-install-laravel-8">How to Install Laravel 8</h2>
<p>Before we install Laravel 8, let's check to see if we have a <strong>composer.phar</strong> file. In your <strong>Termux</strong> type this command:</p>
<pre><code class="lang-php">ls
</code></pre>
<p>and hit enter. You will see the available files there.</p>
<p><img src="https://lh4.googleusercontent.com/wNOLq4c37i9ccupbMQm64jvOibFEEN2ZuPi_Lez_HO6PZZalo0eI1Lp_XWwvpZK8fWgRedfNsUqo2tYsC4lN54w7TDjjFB4C3k0yR2NVcXiKRY4sFOk6tpCGjVk8X3GaIuMIJEw" alt="Image" width="720" height="1600" loading="lazy"></p>
<p>You can see the <strong>composer.phar</strong> file and a <strong>storage</strong> folder. The storage folder grants access to your file manager. Remember the <strong>termux-setup-storage</strong> command you wrote first.</p>
<p>Now let's install Laravel 8. To do so we can either create a project or just install it globally. But it's a bit of a long process when installing it globally on your phone because you need to set a path and so on, and it can be pretty confusing. So in this guide we will create a project instead. </p>
<p>In your <strong>Termux</strong> go ahead and type this:</p>
<pre><code class="lang-php">php composer.phar create-project laravel/laravel myapp
</code></pre>
<p><code>myapp</code> is just the project name – you can change it to whatever you want. Then hit enter and wait for the magic to happen.</p>
<p>When you see the below, it means that Laravel has been installed:</p>
<p><img src="https://lh5.googleusercontent.com/e1T64FywzWaz7amrgF0E5aXE6YqJdjMqCm2ZbAMeVyq2GBJ3wPXpAIxevqulsoPa2LOyqfWTtb6PjbqK48-IcWAssx6Hk3cPHyd5-N6Lw2fFWFdWEM0qhbA6ivKHjt5GH_ED5aI" alt="Image" width="720" height="1600" loading="lazy"></p>
<p>Easy as pie. Now to test it, you can cd into <code>myapp</code> by typing <strong><code>cd myapp</code></strong>. Then you can run the Laravel server with <strong><code>php artisan serve</code>.</strong></p>
<p>Voilà – development has started 🔥</p>
<p><img src="https://lh5.googleusercontent.com/OmM_BEGUNTploSBEIItrnkn6S_yotTKJPo_q60PP7mx93Uo9V4Tb9p_ucHIiaHAOPbFGL36CWT-zWcGwc-a2FsiNbfyFpbnWu6IjT-MsS2X5TQhI1vAbhU3bGMgbM_tLC_nu3oU" alt="Image" width="720" height="1600" loading="lazy"></p>
<p>Now you can open <a target="_blank">http://127.0</a>.0.1:8000 in your browser and see that Laravel is running:</p>
<p><img src="https://lh5.googleusercontent.com/iG_uPbC4lzleqSPETYM6pRFsZ1BAj5PbAeTwSDNVWL2_r6V-AxpWk5iYpS-Gs2iBQUmvPZMnVoCjIk9ZtcOJj6QeCOi32dFJ-2zVJC420MIiFyN9pSKUb5sUGI2iSaJ0ITf9Wr4" alt="Image" width="720" height="1600" loading="lazy"></p>
<p> Also make sure you do this so your <strong>Termux</strong> app won't force close when you are coding: 😎</p>
<p><img src="https://lh5.googleusercontent.com/P6s9dGlmoHGeo2_vAmImYFSXrFgRZTUUlunlOwegzVw8QdLGKoigMhbm5lPlxsE-K5PraWkGlN6VYwzwk16FLi_A4GOGRJdCkPWB3rlc6bbZuJQN7d7s0WKkJTSu1QFTeGXABDM" alt="Image" width="720" height="1026" loading="lazy"></p>
<h2 id="heading-thats-it">That's it!</h2>
<p>Thanks for reading. I hope you learned something from this tutorial. You should now be able to install Laravel on your Android phone and start using it to build apps.</p>
<p>If you want more content from me, you can subscribe to my YouTube channel 🙏😁<br><a target="_blank" href="https://youtube.com/channel/UCLcHGKxbEO1XGVETXqzYXLA">DevStack</a></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VAh6A1SpZfo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
