<?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[ MySQL - 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[ MySQL - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 26 May 2026 04:44:08 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/mysql/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Install and Configure XAMPP Properly to Avoid Errors When You Close the App ]]>
                </title>
                <description>
                    <![CDATA[ XAMPP is popular software for those who use MySQL databases and PHP. It’s free software, and it supports Windows, Linux OS, and MacOS. This makes it quite popular among developers even though it can also present us with many challenges. One common is... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-install-and-configure-xampp-properly-to-avoid-errors-when-you-close-the-app/</link>
                <guid isPermaLink="false">67360e398a8e3a6a2f6c32cd</guid>
                
                    <category>
                        <![CDATA[ Xampp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Md. Fahim Bin Amin ]]>
                </dc:creator>
                <pubDate>Thu, 14 Nov 2024 14:50:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cckf4TsHAuw/upload/ab4deba46100e61a5425d817f6406742.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>XAMPP is popular software for those who use MySQL databases and PHP. It’s free software, and it supports Windows, Linux OS, and MacOS. This makes it quite popular among developers even though it can also present us with many challenges.</p>
<p>One common issue you may have faced is the errors you can get when you want to exit the application. You can easily mitigate that issue by opening the application launcher as Administrator. But opening the launcher via administrator correctly can also be troublesome. This means that you’ll need to configure a setting so that it asks for administrator access each time you want to launch it.</p>
<p>In this quick tutorial, I’ll walk you through this process so you don’t have to struggle with it.</p>
<h2 id="heading-video-guide">Video Guide</h2>
<p>I have prepared a complete video where I show you how to download and install the client. I also teach you how to make some changes so that you don’t encounter any issues when you want to quit the application.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/3viM71-ULAw" 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> </p>
<h2 id="heading-step-by-step-process">Step-By-Step Process</h2>
<p>If you keep the default installation directory during the installation, it will install it inside the <code>C</code> drive of your Windows operating system.</p>
<p>So simply enter into the “C” drive and go inside the “xampp” directory.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731566127539/9964e362-0f79-4a08-9799-7ea17bd3740e.png" alt="Find the &quot;xampp&quot; directory" class="image--center mx-auto" width="733" height="508" loading="lazy"></p>
<p>Scroll down until you find the “xampp-control.exe” file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731566176798/8a529579-5545-4a97-b32d-6fdbffe15a95.png" alt="Find the &quot;xampp-control.exe&quot; file" class="image--center mx-auto" width="835" height="625" loading="lazy"></p>
<p>Right-click after selecting the file and open the <strong>Properties</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731566236620/dfaa3a6c-795a-4dab-a03a-66b73a7d5de5.png" alt="Properties tab" class="image--center mx-auto" width="485" height="629" loading="lazy"></p>
<p>Next, go to the <strong>Compatibility</strong> section. Make sure to add a check mark by clicking on the checkbox by “Run the program as an administrator”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731566287073/53bf3e86-1a4b-47b3-83e9-96caf1a4195d.png" alt="Configure properties" class="image--center mx-auto" width="487" height="626" loading="lazy"></p>
<p>Click “Apply”, and “OK”.</p>
<p>That’s it!</p>
<p>Now, whenever you want to open the XAMPP launcher, it will always open with administrator access.</p>
<h3 id="heading-quitting-the-application">Quitting the application</h3>
<p>When you want to quit the application, you need to click the “Quit” button instead of using the “X” exit button at the upper right side of the window.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731566387255/316d1778-dd5b-4a61-9efe-cbbaf8fc7552.png" alt="Quit the application" class="image--center mx-auto" width="705" height="484" loading="lazy"></p>
<p>And now you shouldn’t get any errors when exiting the control panel anymore!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for reading this short tutorial. I hope it helped you interact more easily with XAMPP.</p>
<p>You can follow me on <a target="_blank" href="https://github.com/FahimFBA">GitHub</a>, <a target="_blank" href="https://github.com/FahimFBA">Linke</a><a target="_blank" href="https://www.linkedin.com/in/fahimfba/">dIn</a>, <a target="_blank" href="https://www.linkedin.com/in/fahimfba/">and You</a><a target="_blank" href="https://youtube.com/@FahimAmin">Tube</a> <a target="_blank" href="https://youtube.com/@FahimAmin">to get</a> more content like this. Also, my <a target="_blank" href="https://www.fahimbinamin.com/">website</a> <a target="_blank" href="https://www.fahimbinamin.com/">is alwa</a>ys available for you!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn MySQL – Beginner's Course ]]>
                </title>
                <description>
                    <![CDATA[ Databases have become an integral part of storing and managing data efficiently. MySQL, a widely used open-source relational database management system, empowers users to handle data in a structured manner, facilitating easier retrieval and manipulat... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-mysql-beginners-course/</link>
                <guid isPermaLink="false">66b204c5eea9870582e16c91</guid>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 25 Oct 2023 12:43:08 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/mysql.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Databases have become an integral part of storing and managing data efficiently. MySQL, a widely used open-source relational database management system, empowers users to handle data in a structured manner, facilitating easier retrieval and manipulation of information. Understanding MySQL is vital for developers, data analysts, and anyone aspiring to delve into the realm of databases.</p>
<p>We just published a course on the freeCodeCamp.org YouTube channel that will help you master MySQL. Josh from KeepItTechie teaches this course. He works as a Sql Server Database Administrator so he is the pefect person to teach this course.</p>
<p>MySQL is an open-source relational database management system that uses SQL (Structured Query Language) to interact with data. It is renowned for its speed, reliability, and ease of use. MySQL forms the database component of the popular LAMP (Linux, Apache, MySQL, PHP/Python/Perl) software stack, making it an essential tool for web development and application building.</p>
<p>This course is structured to provide a comprehensive and practical understanding of databases. Catering to both beginners and seasoned users looking to enhance their skills, the course covers a wide range of topics, from the basics to advanced functionalities.</p>
<p>Here are the sections covered in this course:</p>
<ul>
<li>Introduction</li>
<li>MySQL Overview</li>
<li>Installation</li>
<li>Login to MySQL</li>
<li>Exploring MySQL</li>
<li>Show Databases</li>
<li>Create Database</li>
<li>Use Database</li>
<li>Create Table</li>
<li>Describe Table</li>
<li>Drop Table</li>
<li>Show Table</li>
<li>Drop Database</li>
<li>Install Test Database</li>
<li>Create User Accounts</li>
<li>Change Account Password</li>
<li>List All Accounts</li>
<li>Drop User Accounts</li>
<li>Grant Privileges</li>
<li>Revoke Privileges</li>
<li>Show Granted Privileges</li>
<li>Query</li>
<li>Select Query</li>
<li>Where Clause</li>
<li>Order By</li>
<li>Like</li>
<li>Insert Query</li>
<li>Update Query</li>
<li>Delete Query</li>
<li>Primary Key</li>
<li>Foreign Key</li>
<li>Inner Join</li>
<li>Left Join</li>
<li>Right Join</li>
<li>View</li>
<li>Index</li>
<li>Sub Query</li>
<li>Stored Procedure</li>
<li>Trigger</li>
<li>Processlist</li>
<li>Backup Database</li>
<li>Restore Database</li>
<li>Outro</li>
</ul>
<p>This is a hands-on approach and will ensures that you gain practical knowledge that can be applied in real-world scenarios. You can watch the course on the <a target="_blank" href="https://www.youtube.com/watch?v=uWkcxasFWzQ">freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/uWkcxasFWzQ" 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 Install MySQL and MySQL Workbench on Windows ]]>
                </title>
                <description>
                    <![CDATA[ If you want to learn MySQL, starting with a good client is super helpful – especially when you are just beginning your journey. There are a lot of clients out there for your MySQL-based needs, like XAMPP, DataGrip, and others. Among all of them, I pr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-install-mysql-workbench-on-windows/</link>
                <guid isPermaLink="false">66b902ea3639976bd84355b9</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Windows ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Md. Fahim Bin Amin ]]>
                </dc:creator>
                <pubDate>Fri, 30 Jun 2023 18:23:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/boitumelo-phetla-0DJHJcpwN9Q-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you want to learn MySQL, starting with a good client is super helpful – especially when you are just beginning your journey.</p>
<p>There are a lot of clients out there for your MySQL-based needs, like XAMPP, DataGrip, and others. Among all of them, I prefer the <a target="_blank" href="https://www.mysql.com/products/workbench/">MySQL Workbench</a>. It is completely free, by the way.</p>
<p>In this tutorial, I will show you how you can install and configure your Windows machine for this MySQL and MySQL workbench from scratch. </p>
<p>If you enjoy learning from videos as well, then don't worry as I have also created a step-by-step video just for you:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/kZf_h-Phfds" 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-how-to-install-mysql-workbench">How to Install MySQL Workbench</h2>
<h4 id="heading-download-mysql-workbench">➡️ Download MySQL Workbench</h4>
<p>Make sure to visit only the <a target="_blank" href="https://www.mysql.com/products/workbench/">official website</a> for downloading the MySQL Workbench. You do not want to get into shoddy websites and download the wrong file that infects your favorite machine, right?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-32.png" alt="Image" width="600" height="400" loading="lazy">
<em>Find the official website for MySQL Workbench: https://www.mysql.com/products/workbench/</em></p>
<p>Now click on the "DOWNLOADS" tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-32_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Scroll down until you find <code>MySQL Community (GPL) Downloads »</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-32_2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on <code>MySQL Community (GPL) Downloads »</code>. After that, on the new page, click "MySQL installer for Windows".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-32_3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From the dropdown menu, select your operating system as "Microsoft Windows". Then download the file which is larger in size.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-33.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A <code>.msi</code> file will be downloaded. That is our installer file to install MySQL and MySQL workbench.</p>
<h4 id="heading-install-both-mysql-and-mysql-workbench">➡️ Install both MySQL and MySQL Workbench</h4>
<p>Simply double click on the installer file. It will reload the necessary components and open the installer GUI selection window. Choose the setup type as custom and click "Next".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-34_1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select Custom</em></p>
<p>A new page will appear. Make sure to select the latest "MySQL Server", "MySQL Workbench" and "MySQL Shell". Selecting and clicking on the right side arrow will take the product name in the "Products to be installed section". Then click "Next".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-34-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Install necessary components</em></p>
<p>Click "Execute" to install the three necessary components. The process might take some time depending on your internet speed and computer configuration. After it gets finished, simply click "Next".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-35.png" alt="Image" width="600" height="400" loading="lazy">
<em>Execute</em></p>
<p>In the Product Configuration window, simply click "Next". It will install the three selected components for us.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-37.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next</em></p>
<p>Keep everything as it is and simply click "Next". It will configure the MySQL Server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-37_1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next</em></p>
<p>Keep everything as it is and simply click "Next". It will apply the TCP/IP connectivity for our MySQL server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-37_2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next</em></p>
<p>Now give it a Root password. For testing purposes, I am using a very simple "1111" as my password, but I would recommend not doing the same. Also, make sure to remember the password as you will need it when you want to work in MySQL Workbench. Click "Next".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-37_3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next</em></p>
<p>Keep everything as it is, and simply click "Next". It will make sure to setup our root password for the MySQL workbench.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-38.png" alt="Image" width="600" height="400" loading="lazy">
<em>Root password</em></p>
<p>We want to run the service as a Standard System Account for our operating system. Therefore, keep everything as it is, and simply click "Next".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-38_1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next</em></p>
<p>Select the option to grant full access to the user running the Windows Service and then click "Next."</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-38_2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next</em></p>
<p>Then click "Execute". This will grant the full access to the user running the Windows service and the administrator group only, but the other users and groups will not have its access. </p>
<p>So if you have multiple user accounts in your computer, then they will not be able to access the MySQL server/Workbench. If you want then you can change the settings here based on your need.</p>
<p>As I have only one user account in my Windows machine, I can safely keep the first option selected.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-38_3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Execute</em></p>
<p>It might take some time. Then when you will receive a green check box in all configuration steps, simply click "Finish".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-38_4.png" alt="Image" width="600" height="400" loading="lazy">
<em>Finish</em></p>
<p>The configuration has beep applied successfully. Simply click "Next".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-39.png" alt="Image" width="600" height="400" loading="lazy">
<em>Next</em></p>
<p>Click "Finish" to complete the installation.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-39_1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Finish</em></p>
<p>It will open the MySQL Workbench and MySQL Shell. Simply close all of them now.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-39_1-L.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-configuration">➡️ Configuration</h2>
<p>Now we need to configure the path variables for our operating system. Go to the drive where you have installed your Windows operating system. Like others, I have also installed my operating system on the "C" drive. </p>
<p>Therefore, I am going to the "C" drive and opening the "Program Files" directory.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-40.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go to the "MySQL" folder.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-40_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then go to the MySQL Server folder.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-40_2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go to the "bin" folder.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-40_3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Copy the path/address. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-40_4.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now open the Environment Variables settings. Simply click on the Windows button and type "env". </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-40_5.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click "Environment Variables".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-41.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Select the "Path" and click "Edit".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-41_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click "New". A new blank box will appear. Paste the path/address that you copied earlier. Do not close the window now as we need to do the same thing for the MySQL Shell folder.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-41_2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, we need to do the same thing for the MySQL Shell also. Open the MySQL Shell folder now.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-41_3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go to the "bin" folder.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-41_4.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Copy the path/directory.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-42.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now apply the same process as you did earlier. Click "New" on the Edit environment variable window. Paste the path/directory in the new blank box.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-42_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now click "OK".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-42_2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click "OK" again.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-42_3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And click "OK" one more time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-42_4.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-finishing-up">➡️ Finishing Up</h2>
<p>Our task is now finished. You can now open the MySQL Workbench.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-43.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Simply click on the Local instance. It will ask for the root password. Enter the password. If you do not want to go into the same hassle of entering a password every time, check the box on save password in the vault. Click "OK".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-43_1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is your default MySQL Workbench workspace.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-43_2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Workbench workspace</em></p>
<p>If you want then you can also hide the SQL Additions tab by clicking on the colored box.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-43_3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For getting the Schemas, click on the "Schemas" tab from the navigator.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-43_4.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Your MySQL Workbench is also ready for any kind of development process. You can also use MySQL from your terminal as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_10-43_5.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/2023-06-28_11-32.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thank you for reading the entire article.</p>
<p>If you have any questions, feel free to reach out to me using <a target="_blank" href="https://twitter.com/Fahim_FBA">Twitter</a> or <a target="_blank" href="https://www.linkedin.com/in/fahimfba/">LinkedIn</a>.</p>
<p>Also, make sure to follow me on <a target="_blank" href="https://github.com/FahimFBA">GitHub</a>!</p>
<p>You can also <a target="_blank" href="https://www.youtube.com/@FahimAmin?sub_confirmation=1">subscribe to my YouTube channel</a> for more helpful video content.</p>
<p>If you are interested then you can also check my website: <a target="_blank" href="https://fahimbinamin.com/">https://fahimbinamin.com/</a></p>
<p>Have a great day! 😊</p>
<p>Cover: Photo by <a target="_blank" href="https://unsplash.com/@writecodenow?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Boitumelo Phetla</a> on <a target="_blank" href="https://unsplash.com/photos/0DJHJcpwN9Q?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Make a Database Read-Only in MySQL ]]>
                </title>
                <description>
                    <![CDATA[ If you are learning MySQL, then you are likely enjoying executing different commands and checking the results afterward.  But you may be working on a project where you have modified your database to an acceptable state and you're worried about alteri... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-make-a-database-read-only-in-mysql/</link>
                <guid isPermaLink="false">66b902f2d7a98b87028af8a1</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Md. Fahim Bin Amin ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jun 2023 22:49:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/altumcode-dMUt0X3f59Q-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you are learning MySQL, then you are likely enjoying executing different commands and checking the results afterward. </p>
<p>But you may be working on a project where you have modified your database to an acceptable state and you're worried about altering it by mistake. This could happen when you're working in the DB, or when others who have access to your computer get in there.</p>
<p>Well, fear not! In MySQL, you can restrict the database to ensure its safety. I assume you're a SQL beginner, so I am not going to bore you with the difficult stuff. The easiest thing is to make the database <strong>READ ONLY</strong>. After that, no one can modify the database in any way if they aren't familiar with certain MySQL commands.</p>
<h3 id="heading-if-youd-like-to-go-through-the-process-step-by-step-in-a-video-here-you-go">If you'd like to go through the process step-by-step in a video, here you go:</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/7kFzNo6tD-k" 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-how-to-make-a-database-read-only">How to Make a Database READ ONLY</h2>
<p>Let's assume that you're already in your SQL editor (from where you can execute your MySQL commands). Keep in mind that usually, when we create a database, it comes with read-write accessibility by default. </p>
<p>So for now, let's assume that the default status of a newly created database is READ-WRITE enabled. </p>
<p>To convert the database into a READ-ONLY state, use this command:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">DATABASE</span> database_name <span class="hljs-keyword">READ</span> <span class="hljs-keyword">ONLY</span> = <span class="hljs-number">1</span>;
</code></pre>
<p>Let me explain each part of the code for you now.</p>
<p>As you want to change something in the data, you are telling the database,<br>"hey, I want to alter something". So you used the <code>ALTER</code> command. </p>
<p>Then comes the part where you tell it which thing you want to alter, because there might be multiple tables or databases. So you need to tell it which thing you want to alter. So, you stated <code>DATABASE</code> to specify that you actually want to alter a database.</p>
<p>In your client (MySQL Workbench/XAMPP, and so on), there might be multiple databases. So wouldn't MySQL get confused about exactly which database you want to alter? You don't want to confuse it, right? This is why you need to specify the database name. </p>
<p>After that, you tell it exactly which modification you want it to make. You want to change the <code>READ ONLY</code> status to <code>1</code>. This means that <strong>READ ONLY</strong> is on/enabled.</p>
<p>After this, nobody will be able to make any kind of alteration or changes (update/delete/addition) to that specific READ ONLY database anymore. Nobody will even be able to delete the database!</p>
<h2 id="heading-how-to-make-a-database-read-write">How to Make a Database READ-WRITE</h2>
<p>What if you need to revert the changes so you can make updates to the database? You need to change the <strong>READ ONLY</strong> status to 0 to state that you want the <strong>READ ONLY</strong> status to be disabled (or no in short).</p>
<p>Simply use the following command to do that:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">DATABASE</span> database_name <span class="hljs-keyword">READ</span> <span class="hljs-keyword">ONLY</span> = <span class="hljs-number">0</span>;
</code></pre>
<p>After this, anyone will be able to make changes to the database or even delete the database if they want.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Isn't it a short but beautiful trick? Enjoy your coding journey!</p>
<p>Also, if you like programming-related content, then make sure to <a target="_blank" href="https://www.youtube.com/@FahimAmin?sub_confirmation=1">subscribe to my YouTube channel</a> where I publish programming-related content regularly!</p>
<p>Also, you can follow me on <a target="_blank" href="https://github.com/FahimFBA">GitHub</a> and <a target="_blank" href="https://twitter.com/Fahim_FBA">Twitter</a>. You can also check my website: <a target="_blank" href="https://fahimbinamin.com/">https://fahimbinamin.com/</a></p>
<p>If you want to endorse me for relevant skills, then do that using <a target="_blank" href="https://www.linkedin.com/in/fahimfba/">LinkedIn</a>.</p>
<p>Cover image: Photo by <a target="_blank" href="https://unsplash.com/@altumcode?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">AltumCode</a> on <a target="_blank" href="https://unsplash.com/photos/dMUt0X3f59Q?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use MySQL Stored Procedures to Simplify Database Operations ]]>
                </title>
                <description>
                    <![CDATA[ In the realm of database management, MySQL has emerged as one of the most popular and reliable choices.  MySQL not only offers robust data storage capabilities but also provides a powerful feature called "procedures" that allows developers to streaml... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-simplify-database-operations-using-mysql-stored-procedures/</link>
                <guid isPermaLink="false">66ba10d8228e16bed602a8a1</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Arunachalam B ]]>
                </dc:creator>
                <pubDate>Mon, 12 Jun 2023 22:05:52 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/How-to-use-Mysql-procedure-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the realm of database management, MySQL has emerged as one of the most popular and reliable choices. </p>
<p>MySQL not only offers robust data storage capabilities but also provides a powerful feature called "procedures" that allows developers to streamline complex database operations. </p>
<p>In this tutorial, we will delve into the concept of MySQL procedures and explore their benefits. Then I'll provide a step-by-step guide on how to use them effectively.</p>
<h2 id="heading-what-are-sql-procedures">What are SQL Procedures?</h2>
<p>SQL procedures are a set of SQL statements grouped together to form a logical unit of work. They are similar to functions or methods in programming languages, enabling you to encapsulate complex queries and operations into a single reusable entity. </p>
<p>Procedures enhance code modularity, readability, and maintainability, making it easier to manage and execute repetitive or intricate database tasks.</p>
<h2 id="heading-when-to-use-stored-procedures">When to Use Stored Procedures</h2>
<p>Let's consider an e-commerce website, where we have the functionality to generate sales reporting. We have a table called <code>sales</code> that we'll be working with for this example.</p>
<p>Generating sales reports in real time can be resource-intensive, especially when dealing with large datasets. By creating stored procedures that aggregate and summarize sales data, we can optimize the reporting process. </p>
<p>These procedures can calculate metrics like total sales, top-selling products, or revenue by category, making it easier to retrieve valuable insights quickly and efficiently.</p>
<p>Here's the schema of sales table:</p>
<table><thead><tr><th>Column</th><th>Type</th></tr><tr><td>sale_id</td><td>int</td></tr><tr><td>customer_id</td><td>int</td></tr><tr><td>saled_date</td><td>datetime</td></tr><tr><td>total_amount</td><td>decimal</td></tr><tr><td>status</td><td>varchar(50)</td></tr></thead></table>

<p>To illustrate a simple example, let's consider <code>sales</code> table is populated with 1 million rows of mock data.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-108.png" alt="Image" width="600" height="400" loading="lazy">
<em>Mock data for sales table</em></p>
<pre><code>select count(*) <span class="hljs-keyword">from</span> sales;
</code></pre><p>The goal is to get sales reports for a particular period of time.</p>
<pre><code>CREATE PROCEDURE GenerateSalesReport (
    IN start_date DATE,
    IN end_date DATE
)
BEGIN
    SELECT DATE_FORMAT(order_date, <span class="hljs-string">'%Y-%m-%d'</span>) AS <span class="hljs-built_in">Date</span>,
           COUNT(order_id) AS TotalOrders,
           SUM(total_amount) AS TotalSales
    FROM orders
    WHERE order_date BETWEEN start_date AND end_date
    GROUP BY DATE_FORMAT(order_date, <span class="hljs-string">'%Y-%m-%d'</span>);
END
</code></pre><p>The example stored procedure <code>GenerateSalesReport</code> takes two input parameters: <code>start_date</code> and <code>end_date</code>. These define the date range for the sales report. </p>
<p>The procedure selects the order date, counts the number of orders, and calculates the total sales amount within the specified date range. The result is grouped by the date, using the <code>DATE_FORMAT</code> function to display it in the desired format.</p>
<p>Now, you might have a question:</p>
<blockquote>
<p>"Can't we achieve the same outcome using a simple query instead of creating a stored procedure?"</p>
</blockquote>
<p>Well. It's true that using a simple query is a viable option. But there are several compelling reasons to consider utilizing a stored procedure.</p>
<p>Here are few reasons that feels promising to use stored procedures at some places. </p>
<ol>
<li>A stored procedure offers the advantage of code reusability. By encapsulating the query logic within a stored procedure, we can reuse it multiple times without duplicating the code.</li>
<li>Instead of rewriting the same query in different parts of the application, we can simply call the stored procedure whenever needed, streamlining the codebase and making it easier to manage and update.</li>
<li>Using a stored procedure can lead to improved performance in certain scenarios. When a stored procedure is executed, the database server can optimize the execution plan and cache it for subsequent invocations. This optimization can result in faster execution times, as the database engine leverages the cached plan.</li>
<li>Furthermore, stored procedures can minimize network round trips by combining multiple queries into a single call, reducing the overhead associated with individual query executions. This optimization can significantly enhance overall performance, especially when dealing with complex operations or large datasets.</li>
<li>Another significant advantage of stored procedures is enhanced security. By granting execution privileges only to the stored procedure and not directly to underlying tables, you can enforce access control and protect sensitive data.</li>
</ol>
<p>In summary, while a simple query can achieve the desired outcome, utilizing a stored procedure offers distinct benefits such as code reusability, improved performance through query optimization, reduced network overhead, and enhanced security.</p>
<h2 id="heading-building-blocks-of-stored-procedures">Building Blocks of Stored Procedures</h2>
<p>Let's break down the stored procedure and examine each component individually. We will understand creating and running the stored procedure in MySQL. </p>
<p>There are several MySQL IDEs available, and I recommend using MySQL Workbench. But you are free to choose any IDE that suits your preferences and needs.</p>
<h3 id="heading-procedure-name">Procedure Name</h3>
<p>Every stored procedure has a unique name that identifies it within the database. The name should be descriptive and relevant to the procedure's purpose.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-107.png" alt="Image" width="600" height="400" loading="lazy">
<em>Define a procedure</em></p>
<pre><code>CREATE PROCEDURE <span class="hljs-string">`GenerateSalesReport`</span>()
BEGIN
END
</code></pre><h3 id="heading-parameters">Parameters</h3>
<p>Stored procedures can have input parameters that allow you to pass values into the procedure at runtime. We define <code>start_date</code> and <code>end_date</code> as our input parameters. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-106.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sample parameters in stored procedure</em></p>
<pre><code>CREATE PROCEDURE <span class="hljs-string">`GenerateSalesReport`</span>(
    IN start_date DATE,
    IN end_date DATE
)
BEGIN
END
</code></pre><h3 id="heading-variables">Variables</h3>
<p>Variables are used to store and manipulate data within the stored procedure. They can be declared and assigned values as needed. </p>
<p>There are two types of variables in SQL. We'll look at each of them now.</p>
<h4 id="heading-session-variables">Session Variables</h4>
<p>Session variables in MySQL are prefixed with the <code>@</code> symbol (for example <code>@variable_name</code>). These variables are associated with the current session or connection and retain their values throughout the session until they are explicitly changed or the session ends.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-102.png" alt="Image" width="600" height="400" loading="lazy">
<em>Define Session variable in stored procedure</em></p>
<pre><code>
CREATE PROCEDURE <span class="hljs-string">`GenerateSalesReport`</span>(
    IN start_date DATE,
    IN end_date DATE
)
BEGIN
   SELECT @totalSales := <span class="hljs-number">0</span>;
   SELECT SUM(sales_amount) INTO @totalSales FROM sales;
   SELECT @totalSales As total_sales;
END
</code></pre><h4 id="heading-regular-variables">Regular Variables</h4>
<p>Regular variables, also known as local variables, are declared using the <code>DECLARE</code> keyword within the scope of a stored procedure. Unlike session variables, regular variables do not have the <code>@</code> prefix (for example <code>variable_name</code>). They are temporary and exist only within the block of code where they are declared.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-104.png" alt="Image" width="600" height="400" loading="lazy">
<em>Define Normal variable in stored procedure</em></p>
<pre><code>CREATE PROCEDURE <span class="hljs-string">`GenerateSalesReport`</span>(
    IN start_date DATE,
    IN end_date DATE
)
BEGIN
   DECLARE totalSales INT;
   SELECT SUM(sales_amount) INTO totalSales FROM sales;
END
</code></pre><h3 id="heading-sql-statements">SQL Statements</h3>
<p>The core functionality of a stored procedure is defined by SQL statements. These statements can include SELECT, INSERT, UPDATE, DELETE, and other SQL commands to interact with the database.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-105.png" alt="Image" width="600" height="400" loading="lazy">
<em>SQL statements in stored procedure</em></p>
<pre><code>CREATE PROCEDURE <span class="hljs-string">`GenerateSalesReport`</span>(
    IN start_date DATE,
    IN end_date DATE
)
BEGIN
    SELECT DATE_FORMAT(saled_date, <span class="hljs-string">'%d-%m-%Y'</span>) AS <span class="hljs-built_in">Date</span>,
           COUNT(sale_id) AS TotalOrders,
           SUM(total_amount) AS TotalSales
    FROM sales
    WHERE saled_date BETWEEN start_date AND end_date
    GROUP BY DATE_FORMAT(saled_date, <span class="hljs-string">'%d-%m-%Y'</span>);
END
</code></pre><h3 id="heading-procedure-call">Procedure Call</h3>
<p>To execute the stored procedure and generate a detailed sales report for a specific date range, we can use the following syntax:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CALL</span> &lt;procedure_name&gt;(&lt;parameter1&gt;, ...);
</code></pre>
<pre><code>CALL GenerateSalesReport(<span class="hljs-string">'2021-01-01'</span>, <span class="hljs-string">'2023-12-31'</span>);
</code></pre><p>The below screenshot shows the result of the stored procedure. The interesting part is that this query has processed around 1 million data in a second. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-109.png" alt="Image" width="600" height="400" loading="lazy">
<em>Result of sample procedure call to generate saled report</em></p>
<h2 id="heading-importance-of-using-mysql-stored-procedures">Importance of Using MySQL Stored Procedures</h2>
<h3 id="heading-improved-performance">Improved Performance</h3>
<p>Stored procedures offer a significant performance advantage over ad-hoc SQL queries. Once a stored procedure is created, it is compiled and stored in a pre-optimized form. </p>
<p>This compilation process eliminates the need for repetitive query parsing and optimization, resulting in faster execution times. By reducing the overhead associated with query processing, stored procedures enhance the overall performance of database operations.</p>
<h3 id="heading-enhanced-security">Enhanced Security</h3>
<p>Security is a critical aspect of database management. Stored procedures allow database administrators to define access rights and permissions for executing specific procedures. This fine-grained control ensures that only authorized users can interact with the database through the procedures, minimizing the risk of unauthorized data access or modifications. </p>
<p>By encapsulating sensitive operations within stored procedures, security vulnerabilities are reduced, strengthening the overall database security posture.</p>
<h3 id="heading-code-reusability-and-maintainability">Code Reusability and Maintainability</h3>
<p>Stored procedures promote code reusability, modularity, and maintainability. By encapsulating frequently used SQL statements and operations within a single procedure, you can avoid code duplication and ensure consistent execution across multiple instances. </p>
<p>This modularity makes it easier to maintain and update the database logic. Additionally, when modifications are required, changes can be made in a single location (the stored procedure) rather than in multiple places, simplifying the maintenance process.</p>
<h3 id="heading-transaction-control">Transaction Control</h3>
<p>Stored procedures enable transaction control within the database. Transactions ensure data integrity by grouping multiple database operations into a single logical unit. By executing a series of operations within a transaction, you can ensure that either all the operations are successfully completed, or none of them are applied. </p>
<p>This atomicity ensures data consistency and protects against data corruption. Stored procedures allow you to define transaction boundaries, ensuring that complex operations are handled reliably and consistently.</p>
<h3 id="heading-performance-optimization-and-query-plan-caching">Performance Optimization and Query Plan Caching</h3>
<p>Another advantage of using stored procedures is the ability to optimize query execution plans. </p>
<p>Since stored procedures are compiled and stored, the database engine can generate optimized execution plans based on the stored procedure's statistics and data distribution. These optimized plans can significantly improve query performance. </p>
<p>Furthermore, the query execution plans for stored procedures are cached, which further reduces the overhead of plan generation for subsequent executions.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Stored procedures are a valuable tool in database management, and you'll want to use them in specific scenarios. When dealing with complex business logic, aiming for performance optimization, enhancing security and access control, promoting code reusability and maintainability, handling complex transactions, or integrating with legacy systems, stored procedures can provide significant benefits. </p>
<p>By leveraging their power effectively, you can streamline your database operations, improve application performance, and simplify code maintenance, leading to a more efficient and scalable database environment.</p>
<p>If you wish to learn more about SQL and Stored Procedures, subscribe to my <a target="_blank" href="https://5minslearn.gogosoon.com/?ref=fcc_sql_stored_procedure">email newsletter</a> (<a target="_blank" href="https://5minslearn.gogosoon.com/?ref=fcc_sql_stored_procedure">https://5minslearn.gogosoon.com/</a>) and follow me on social media.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Create a SQL Database App with a Windows GUI ]]>
                </title>
                <description>
                    <![CDATA[ Are you interested in learning how to design and use databases for Windows applications? We just published a full course on the freeCodeCamp.org YouTube channel that will teach you how to create a SQL database application with a Windows graphical int... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/sql-database-app-with-windows-gui/</link>
                <guid isPermaLink="false">66b2067ba2135cc2539a21ed</guid>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 16 Mar 2023 14:09:53 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/03/sqlapp.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you interested in learning how to design and use databases for Windows applications?</p>
<p>We just published a full course on the freeCodeCamp.org YouTube channel that will teach you how to create a SQL database application with a Windows graphical interface.</p>
<p>Led by Shad Sluiter, a Professor Of Computer Science and Software Development at Grand Canyon University, this course will guide you through the process of creating a graphical Windows application with C# that can display and modify data from a mySQL database server.</p>
<p>Throughout the course, you'll gain practical experience with a range of topics, including creating a new database in mySQL, writing SQL queries, creating a GUI front-end app, and connecting to a database with a Data Access Object. You'll also learn how to query a database, insert new records, and perform compound select actions.</p>
<p>But that's not all – this course also covers more advanced topics such as creating a second table with a foreign key, performing table joins, designing with UML and ER diagrams, and even adding a video player to the app.</p>
<p>To follow along with the course, you'll need Microsoft Windows. With easy-to-follow video tutorials, this course is perfect for anyone looking to gain practical skills in database design and application development.</p>
<p>So, if you're ready to take your database skills to the next level, follow along with this course and start building your own Windows application with ease.</p>
<p>You can watch the full course <a target="_blank" href="https://youtu.be/VX4wl7qIcbA">on the freeCodeCamp.org YouTube channel</a> (3-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VX4wl7qIcbA" 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 Use SQL Triggers ]]>
                </title>
                <description>
                    <![CDATA[ MySQL Triggers are like JavaScript event listeners. They are not executed until an action that they have been told to listen for happens.  Here's a helpful description of them from the MySQL docs: A trigger is a named database object that is associa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/sql-triggers/</link>
                <guid isPermaLink="false">66c4c6ab99f22436b71945df</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Zubair Idris Aweda ]]>
                </dc:creator>
                <pubDate>Tue, 21 Feb 2023 20:13:01 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/pexels-tima-miroshnichenko-5640619.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>MySQL Triggers are like JavaScript event listeners. They are not executed until an action that they have been told to listen for happens. </p>
<p>Here's a helpful description of them from the MySQL docs:</p>
<blockquote>
<p>A trigger is a named database object that is associated with a table, and that activates when a particular event occurs for the table.   </p>
<p>Some uses for triggers are to perform checks of values to be inserted into a table or to perform calculations on values involved in an update. - <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/triggers.html">MySQL.com</a></p>
</blockquote>
<p>Triggers are useful for automating repetitive tasks. You can just set up a trigger to do some calculation after every specific database action. You can also set up a trigger to perform data validation tasks on a table.</p>
<p>A trigger gets fired when an <code>INSERT</code>, <code>UPDATE</code> or <code>DELETE</code> operation happens on a database table. A trigger is fired per row, so if multiple rows of data are being inserted or deleted, each one still fires the action setup by the trigger. A trigger can be set to fire before or after an action.</p>
<p>In this tutorial, you'll learn how to create triggers, how to drop them, and when they're useful.</p>
<h2 id="heading-how-to-create-a-trigger">How to Create A Trigger</h2>
<p>To create a new trigger, use the <code>CREATE TRIGGER</code> command. This command has the following structure:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TRIGGER</span> trigger_name
trigger_time 
trigger_event
<span class="hljs-keyword">ON</span> table_name 
<span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-keyword">ROW</span>
trigger_body
</code></pre>
<p>So here's a breakdown of every line:</p>
<ul>
<li>The keyword <code>CREATE TRIGGER</code> is mandatory and is followed by the name of the trigger. You'll use this name to refer to the trigger in future, and to delete the trigger if the need ever arises. This name should be unique per database.</li>
<li>The <code>trigger_time</code> is a variable value that can only be either <code>BEFORE</code> or <code>AFTER</code>. This determines whether the trigger will fire before or after the event has happened.</li>
<li>The <code>trigger_event</code> is another variable that has a limited number of possible options. This variable cannot be any value other than <code>INSERT</code>, <code>UPDATE</code>, or <code>DELETE</code>. It specifies what event to listen for.</li>
<li><code>table_name</code> is the name of the table the trigger should watch. This has to be the name of an existing table in your database, but it can be an empty table.</li>
<li>The <code>FOR EACH ROW</code> is the other mandatory part of the trigger definition.</li>
<li><code>trigger_body</code> is the SQL query that you want to be run when this trigger is fired.</li>
</ul>
<p>To create an example trigger, I will create a simple <code>users</code> table for practice.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span>
    <span class="hljs-keyword">users</span> (
        fullname <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">120</span>),
        email <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">120</span>),
        username <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">30</span>),
        <span class="hljs-keyword">password</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">60</span>)
    );
</code></pre>
<p>Now, we can create a simple trigger and attach it to this empty table. A trigger that encrypts string passwords before they are inserted using the <code>MD5</code> function would make sense.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TRIGGER</span> password_hasher <span class="hljs-keyword">BEFORE</span> <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">ON</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-keyword">ROW</span>
<span class="hljs-keyword">SET</span>
    NEW.password = <span class="hljs-keyword">MD5</span> (NEW.password);
</code></pre>
<p>This example is pretty straightforward and self-explanatory. But there's a <code>NEW</code> keyword there. This keyword gives you access to the new data being created and lets you use or modify the values as you like. </p>
<p>You can only modify these values if your set <code>event_time</code> is <code>BEFORE</code>. If the <code>event_time</code> is set to <code>AFTER</code>, the data has already been stored before getting to the trigger so it cannot be modified again.  </p>
<p>You can use the <code>NEW</code> keyword in <code>INSERT</code> and <code>UPDATE</code> events but not the <code>DELETE</code> event.</p>
<p>There's also the <code>OLD</code> keyword that you can use in <code>DELETE</code> and <code>UPDATE</code> event triggers that gives you access to the former values of the affected record. You can't use this keyword on an <code>INSERT</code> event because there is no previous record before new data is created.</p>
<p>To test this trigger, insert a row into the <code>users</code> table:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span>
    <span class="hljs-keyword">users</span>
<span class="hljs-keyword">VALUES</span>
    (
        <span class="hljs-string">'idris babu'</span>,
        <span class="hljs-string">'zubs@test.com'</span>,
        <span class="hljs-string">'zubby1'</span>,
        <span class="hljs-string">'password'</span>
    );
</code></pre>
<p>Check your table for the values. You should have something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-18-at-02.14.44.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The password value has been correctly encrypted. 🥳</p>
<h2 id="heading-how-to-drop-a-trigger">How to Drop a Trigger</h2>
<p>After creating a trigger, you might want to stop its execution for some reason. In this case, you can drop the trigger. </p>
<p>To drop the trigger, use the <code>DROP TRIGGER</code> command. The command only requires the name of the trigger. You can use the command like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TRIGGER</span> password_hasher;
</code></pre>
<p>Running this query will remove the trigger that we created above and every record inserted from now on will not have the password encrypted. </p>
<p>To test this, insert the same record as before, and check the result.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-18-at-02.24.21.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The newly created record doesn't have an encrypted password.</p>
<p>Something to keep in mind: if you drop the table completely, all associated triggers are also dropped automatically.</p>
<h2 id="heading-when-to-use-triggers">When to Use Triggers</h2>
<ol>
<li>Logging: You can have a trigger to automatically write to another table on insertion, update, or deletion of record from a table.</li>
<li>Data validation: You can write a trigger to ensure data is a certain type and correct values can be set when needed.</li>
<li>Data syncronisation: You can use a trigger to keep related tables updated. For example, in an ecommerce table, every time a sales record gets created, a trigger can update the vendor's balance. Or if the vendor's record is deleted, a trigger can remove all their products.</li>
</ol>
<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>I hope you now understand SQL triggers and when to use them so you can write better queries.</p>
<p>If you have any questions or relevant advice, please get in touch with me to share them.</p>
<p>To read more of my articles or follow my work, you can connect with me on <a target="_blank" href="https://www.linkedin.com/in/idris-aweda-zubair-5433121a3/">LinkedIn</a>, <a target="_blank" href="https://twitter.com/AwedaIdris">Twitter</a>, and <a target="_blank" href="https://github.com/Zubs">Github</a>. It’s quick, it’s easy, and it’s free!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use MySQL Common Table Expressions – with Example Queries ]]>
                </title>
                <description>
                    <![CDATA[ In your day to day job as a Software Engineer or Database Administrator, you'll likely have to write long complex queries, often with some subqueries.  These queries over time become less performant, difficult to read and understand, and even more di... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/mysql-common-table-expressions/</link>
                <guid isPermaLink="false">66c4c6a51b22d2d8d9040ecc</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Zubair Idris Aweda ]]>
                </dc:creator>
                <pubDate>Mon, 20 Feb 2023 17:52:56 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/Common-dining-table-eettafel-Esstisch-04-1280x854.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In your day to day job as a Software Engineer or Database Administrator, you'll likely have to write long complex queries, often with some subqueries. </p>
<p>These queries over time become less performant, difficult to read and understand, and even more difficult to manage. And no one wants to do the hard job of refactoring them, so they just live on. </p>
<p>Or you have probably had to fetch similar data based on a set of data or parameters. To achieve this, you write many similar, sometimes exactly identical subqueries and bring them together using the UNION keyword. </p>
<p>Well, you can make your life easier and solve these problems efficiently using a Common Table Expression.</p>
<blockquote>
<p>A common table expression (CTE) is a named temporary result set that exists within the scope of a single statement and that can be referred to later within that statement, possibly multiple times. – <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/with.html">MySQL.com</a></p>
</blockquote>
<p>Using a Common Table Expression, you can write more readable and more performant queries very easily. It's actually easier than it is to write multiple subqueries that could make your queries unreadable and less performant.</p>
<p>You'll primarily use a common table expression for two reasons:</p>
<ul>
<li>To write queries without using subqueries (or using fewer subqueries)</li>
<li>To write recursive functions</li>
</ul>
<p>In this tutorial, I'll show you how to write your own common table expressions.</p>
<h2 id="heading-how-to-create-a-common-table-expression">How to Create a Common Table Expression</h2>
<p>You can create a Common Table Expression (CTE) using the <code>WITH</code> keyword. You can specify multiple common table expressions at the same time by comma-separating the queries making up each common table expression.</p>
<p>The general shape of a Common Table Expression is like so:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span> cte_name <span class="hljs-keyword">AS</span> (<span class="hljs-keyword">query</span>)

<span class="hljs-comment">-- Multiple CTEs</span>
<span class="hljs-keyword">WITH</span>
    cte_name1 <span class="hljs-keyword">AS</span> (
        <span class="hljs-comment">-- Query here</span>
    ),
    cte_name2 <span class="hljs-keyword">AS</span> (
        <span class="hljs-comment">-- Query here</span>
    )
</code></pre>
<p>The <code>WITH</code> keyword is followed by the CTE name. After the name, you introduce the query to be run in the CTE using the <code>AS</code> keyword. You need to enclose the query must in parentheses. The CTE cannot be followed by a semicolon like other SQL queries. Instead it is followed by another query that uses it.</p>
<p>After creating a CTE, you can easily use the result of the queries run in the CTE by referencing the CTE in other queries, other CTEs, or even in itself.</p>
<h3 id="heading-cte-example">CTE Example</h3>
<p>If you have a table of world_cup players, for example, you can create a CTE like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span>
    barca_players <span class="hljs-keyword">AS</span> (
        <span class="hljs-keyword">SELECT</span>
            <span class="hljs-keyword">id</span>,
            player_name,
            nationality,
            <span class="hljs-keyword">position</span>,
            <span class="hljs-keyword">TIMESTAMPDIFF</span> (<span class="hljs-keyword">YEAR</span>, player_dob, <span class="hljs-keyword">CURRENT_DATE</span>) age
        <span class="hljs-keyword">FROM</span>
            wc_players
        <span class="hljs-keyword">WHERE</span>
            club = <span class="hljs-string">'Barcelona'</span>
    )
<span class="hljs-keyword">SELECT</span>
    *
<span class="hljs-keyword">FROM</span>
    barca_players;
</code></pre>
<p>Here, we've created a CTE named <code>barca_players</code>. This CTE will return the name, position, age, and nationality of every Barcelona player that was at the world cup. It contains the subquery:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span>
    <span class="hljs-keyword">id</span>,
    player_name,
    nationality,
    <span class="hljs-keyword">position</span>,
    <span class="hljs-keyword">TIMESTAMPDIFF</span> (<span class="hljs-keyword">YEAR</span>, player_dob, <span class="hljs-keyword">CURRENT_DATE</span>) age
<span class="hljs-keyword">FROM</span>
    wc_players
<span class="hljs-keyword">WHERE</span>
    club = <span class="hljs-string">'Barcelona'</span>;
</code></pre>
<p>This subquery is what produces the CTE result. Next, it is followed by a query that uses this result. You can see the result of selecting every record in the CTE below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-17-at-22.59.25.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can also select only specific fields from the CTE, for example:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span>
    barca_players <span class="hljs-keyword">AS</span> (
        <span class="hljs-keyword">SELECT</span>
            <span class="hljs-keyword">id</span>,
            player_name,
            nationality,
            <span class="hljs-keyword">position</span>,
            <span class="hljs-keyword">TIMESTAMPDIFF</span> (<span class="hljs-keyword">YEAR</span>, player_dob, <span class="hljs-keyword">CURRENT_DATE</span>) age
        <span class="hljs-keyword">FROM</span>
            wc_players
        <span class="hljs-keyword">WHERE</span>
            club = <span class="hljs-string">'Barcelona'</span>
    )
<span class="hljs-keyword">SELECT</span>
    player_name,
    <span class="hljs-keyword">position</span>
<span class="hljs-keyword">FROM</span>
    barca_players;
</code></pre>
<p>This query is almost the same as the first one, except that it selects only the player names and positions from the list.</p>
<h2 id="heading-how-to-use-common-table-expressions-with-parameters">How to Use Common Table Expressions With Parameters</h2>
<p>You can also pass arguments to the CTE. These are aliases you can use for referencing columns of the query results. The number of parameters passed into the CTE must be the same as the number of columns being selected in its subquery. This is because the columns get matched to the aliases one by one, one after the other.</p>
<p>For example, in the <code>barca_players</code> CTE created above, you can decide to refer to the <code>nationality</code> column as <code>country</code>, and <code>position</code> as <code>role</code>:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span>
    barca_players (<span class="hljs-keyword">id</span>, player_name, country, <span class="hljs-keyword">role</span>, age) <span class="hljs-keyword">AS</span> (
        <span class="hljs-keyword">SELECT</span>
            <span class="hljs-keyword">id</span>,
            player_name,
            nationality,
            <span class="hljs-keyword">position</span>,
            <span class="hljs-keyword">TIMESTAMPDIFF</span> (<span class="hljs-keyword">YEAR</span>, player_dob, <span class="hljs-keyword">CURRENT_DATE</span>) age
        <span class="hljs-keyword">FROM</span>
            wc_players
        <span class="hljs-keyword">WHERE</span>
            club = <span class="hljs-string">'Barcelona'</span>
    )
<span class="hljs-keyword">SELECT</span>
    player_name,
    <span class="hljs-keyword">role</span>
<span class="hljs-keyword">FROM</span>
    barca_players;
</code></pre>
<p>Notice that in the CTE subquery, you still use the correct column names. But in the outer <code>SELECT</code> query, you use the new aliases specified as parameters to the CTE.</p>
<h2 id="heading-recursive-common-table-expressions">Recursive Common Table Expressions</h2>
<p>When you reference a Common Table Expression within itself, it becomes a recursive Common Table Expression. </p>
<p>A Recursive Common Table Expression, as the name implies, is a common table expression that can run a subquery multiple times, as long as a condition is met. It iterates continuously until it reaches a break point, when the condition stops being true.</p>
<p>To define a recursive CTE, the <code>RECURSIVE</code> keyword must be in its name. Without this keyword, MySQL throws an error.</p>
<p>For example, you can write a common table expression that prints numbers 1 to 10 and their squares like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span> <span class="hljs-keyword">RECURSIVE</span>
    numbers_list (n, <span class="hljs-keyword">square</span>) <span class="hljs-keyword">AS</span> (
        <span class="hljs-keyword">SELECT</span>
            <span class="hljs-number">1</span>,
            <span class="hljs-number">1</span>
        <span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span>
        <span class="hljs-keyword">SELECT</span>
            n + <span class="hljs-number">1</span>,
            (n + <span class="hljs-number">1</span>) * (n + <span class="hljs-number">1</span>)
        <span class="hljs-keyword">FROM</span>
            numbers_list
        <span class="hljs-keyword">WHERE</span>
            n &lt; <span class="hljs-number">10</span>
    )
<span class="hljs-keyword">SELECT</span>
    *
<span class="hljs-keyword">FROM</span>
    numbers_list;
</code></pre>
<p>Let's examine what is happening here:</p>
<p>In the first two lines, the recursive common table expression is defined with two parameters, one representing the column for the number, and the other representing the column for the square:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span> <span class="hljs-keyword">RECURSIVE</span>
    numbers_list (n, <span class="hljs-keyword">square</span>) <span class="hljs-keyword">AS</span> (
</code></pre>
<p>Next, the subquery. The subquery is in two parts, joined by the <code>UNION ALL</code> keyword to form one. You can also join these subqueries by the <code>UNION</code> keyword if you don't need duplicate records. </p>
<p>The first part of the subquery is a key part of recursive common table expressions. It is the base query, the first result set, the initial iteration. This query is the starting point of all iterations. </p>
<p>In this example, it is static, as no records are being fetched.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span>
    <span class="hljs-number">1</span>,
    <span class="hljs-number">1</span>
</code></pre>
<p>After this first query, the result table has one row, and looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-17-at-23.55.27.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The second part of the subquery is where the iteration really happens. </p>
<p>In this query, the CTE is referenced within itself, and its columns can be used. When a column name is mentioned, the most recent value of that column is taken. </p>
<p>So at the start of the iteration, <code>n</code> is 1 and <code>square</code> is also 1. That means, <code>n + 1</code> is 2, and <code>(n + 1) * (n + 1)</code> is 2 *2 which is 4. 2 and 4 get added to the result table and then become the most recent values in the table. <code>n</code> becomes 2, and <code>square</code> becomes <code>4</code>. </p>
<p>This continues until the condition in the <code>WHERE</code> keyword is stops being true.</p>
<p>The <code>WHERE</code> keyword in the query specifies the breakpoint of the CTE. Until the condition specified is met, the query keeps getting run. In this case, after every iteration, the query checks if <code>n</code> is less than 10.</p>
<p>If a condition that will always evaluate to true is set, then this creates an endless loop and you get an error like <code>Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.</code></p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span>
    n + <span class="hljs-number">1</span>,
    (n + <span class="hljs-number">1</span>) * (n + <span class="hljs-number">1</span>)
<span class="hljs-keyword">FROM</span>
    numbers_list
<span class="hljs-keyword">WHERE</span>
    n &lt; <span class="hljs-number">10</span>
</code></pre>
<p>Now you might think, "If the condition checks for <code>n &lt; 10</code> , how come 10 is still in the final table?". </p>
<p>Well, the reason is because in SQL, the <code>WHERE</code> keyword part of a query is evaluated first before other parts. So, when <code>n = 9</code> is the last row, the query runs once more, and before insertion or anything, it checks if 9 is less than 10. Since 9 is less than 10, it adds <code>n + 1</code> which is 10 to the list. Then on the next iteration, 10 is the most recent record and it is not less than itself, so the loop ends. </p>
<p>Keep in mind that a Recursive Common Table Expression consists of a recursive <code>SELECT</code> query, and a non-recursive <code>SELECT</code> query.</p>
<h3 id="heading-simple-recursive-common-table-expression-rules">Simple Recursive Common Table Expression Rules</h3>
<ul>
<li>You can't use the <code>GROUP BY</code> keyword. This is because you can only group a collection, but in a recursive common table expression, records are handled and evaluated individually. Other keywords like <code>ORDER BY</code>, <code>DISTINCT</code>, and aggregate functions like <code>SUM</code> cannot be used either.</li>
<li>You can't use window functions.</li>
</ul>
<p>These rules apply to the recursive part of a recursive common table expression.</p>
<h3 id="heading-use-cases-for-recursive-ctes">Use Cases for Recursive CTEs</h3>
<h4 id="heading-fibonacci-sequence">Fibonacci Sequence</h4>
<blockquote>
<p>The Fibonacci sequence is a sequence in which each number is the sum of the two preceding ones. The sequence commonly starts from 0 and 1, although some authors start the sequence from 1 and 1 or sometimes from 1 and 2. (<a target="_blank" href="https://en.wikipedia.org/wiki/Fibonacci_number">source</a>)</p>
</blockquote>
<p>You can easily generate a Fibonacci sequence of any length using a recursive common table expression. For example, here's a query that will get the first 20 numbers of a Fibonacci sequence starting from 0 and 1.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span> <span class="hljs-keyword">RECURSIVE</span>
    fibonacci (n, fib_n, next_fib_n) <span class="hljs-keyword">AS</span> (
        <span class="hljs-comment">/*
        * n - Number of iterations
        * fib_n - Currennt Fibonnaci number. Starts at 0
        * next_fib_n - Next Fibonnaci number. Starts at 1
        */</span>
        <span class="hljs-keyword">SELECT</span>
            <span class="hljs-number">1</span>,
            <span class="hljs-number">0</span>,
            <span class="hljs-number">1</span>
        <span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span>
        <span class="hljs-keyword">SELECT</span>
            n + <span class="hljs-number">1</span>,
            next_fib_n,
            fib_n + next_fib_n
        <span class="hljs-keyword">FROM</span>
            fibonacci
        <span class="hljs-keyword">WHERE</span>
            n &lt; <span class="hljs-number">20</span>
    )
<span class="hljs-keyword">SELECT</span>
    *
<span class="hljs-keyword">FROM</span>
    fibonacci;
</code></pre>
<h4 id="heading-hierarchical-data-traversal">Hierarchical Data Traversal</h4>
<p>In many application databases, you will find that hierarchical data is stored in the same table.</p>
<p>For example, a <code>categories</code> table will usually contain main categories and sub-categories referencing their parent category. An <code>employees</code> table will contain regular employees with their <code>manager_id</code>, as well as their managers or supervisors, because they are also employees.</p>
<p>If you had a <code>categories</code> table like this, with 4 records, 1 main category, and a chain of  sub-categories:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span>
    categories (
        <span class="hljs-keyword">id</span> <span class="hljs-built_in">int</span>,
        cat_name <span class="hljs-built_in">varchar</span>(<span class="hljs-number">100</span>),
        parent_category_id <span class="hljs-built_in">int</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-literal">NULL</span>
    );

<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span>
    categories
<span class="hljs-keyword">VALUES</span>
    (<span class="hljs-number">1</span>, <span class="hljs-string">'Mens'</span>, <span class="hljs-literal">NULL</span>),
    (<span class="hljs-number">2</span>, <span class="hljs-string">'Tops'</span>, <span class="hljs-number">1</span>),
    (<span class="hljs-number">3</span>, <span class="hljs-string">'Jerseys'</span>, <span class="hljs-number">2</span>),
    (<span class="hljs-number">4</span>, <span class="hljs-string">'England'</span>, <span class="hljs-number">3</span>);
</code></pre>
<p>You can fetch each category, with its parent category attached easily like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">WITH</span> <span class="hljs-keyword">RECURSIVE</span>
    category_tree <span class="hljs-keyword">AS</span> (
        <span class="hljs-keyword">SELECT</span>
            <span class="hljs-keyword">id</span>,
            cat_name,
            parent_category_id,
            cat_name <span class="hljs-keyword">AS</span> full_name
        <span class="hljs-keyword">FROM</span>
            categories
        <span class="hljs-keyword">WHERE</span>
            parent_category_id <span class="hljs-keyword">IS</span> <span class="hljs-literal">NULL</span>
        <span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span>
        <span class="hljs-keyword">SELECT</span>
            c.id,
            c.cat_name,
            c.parent_category_id,
            <span class="hljs-keyword">CONCAT</span> (ct.full_name, <span class="hljs-string">' &gt; '</span>, c.cat_name)
        <span class="hljs-keyword">FROM</span>
            categories c
            <span class="hljs-keyword">JOIN</span> category_tree ct <span class="hljs-keyword">ON</span> c.parent_category_id = ct.id
    )
<span class="hljs-keyword">SELECT</span>
    full_name
<span class="hljs-keyword">FROM</span>
    category_tree;
</code></pre>
<p>In this example, the base query selects the root category, where <code>parent_category_id IS NULL</code>. Then it goes on to look for a category where the <code>parent_category_id</code> is the <code>id</code> of the current category by using a <code>JOIN</code>. It repeats this until it gets to the final category. The result of this query is the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-18-at-01.19.10.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>I hope you now understand how to use MySQL Common Table Expressions, their variations (regular and recursive), and when to use them so you can write better queries. You can find more about common table expressions in the docs <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/with.html">here</a>.</p>
<p>If you have any questions or relevant advice, please get in touch with me to share them.</p>
<p>To read more of my articles or follow my work, you can connect with me on <a target="_blank" href="https://www.linkedin.com/in/idris-aweda-zubair-5433121a3/">LinkedIn</a>, <a target="_blank" href="https://twitter.com/AwedaIdris">Twitter</a>, and <a target="_blank" href="https://github.com/Zubs">Github</a>. It’s quick, it’s easy, and it’s free!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use MySQL Transactions ]]>
                </title>
                <description>
                    <![CDATA[ By Aisha Bukar What is a Database Transaction and Why is it Important? A database transaction is a single area of the database where multiple data operations are carried out and written as a whole.  These operations can be create, read, update, or de... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-mysql-transactions/</link>
                <guid isPermaLink="false">66d45d599208fb118cc6cf85</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 10 Feb 2023 21:35:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/White-Minimalist-Dental-Clinic-Facebook-Cover--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Aisha Bukar</p>
<h2 id="heading-what-is-a-database-transaction-and-why-is-it-important">What is a Database Transaction and Why is it Important?</h2>
<p>A database transaction is a single area of the database where multiple data operations are carried out and written as a whole. </p>
<p>These operations can be create, read, update, or delete operations. </p>
<p>During the process of a transaction, the database is in an inconsistent state because there are ongoing operations that are making changes to the database. The DB returns to a more consistent state when the operations have been committed. </p>
<p>For a transaction to be successful, it means that every operation carried out has been committed.</p>
<p>Database transactions are very important in ensuring the consistency of your database when multiple operations are being performed at the same time. It also gives you a way to recover changes that may have occurred due to the failure or accidental misuse of an operation.</p>
<h2 id="heading-overview-of-mysql-and-its-transaction-support">Overview of MySQL and its Transaction Support</h2>
<p>MySQL databases offer support for database transactions by providing statements to initiate these transactions. It gives us the following in-built queries:</p>
<p>"<strong>START TRANSACTION / BEGIN</strong>": this query triggers the start of a transaction.</p>
<p>"<strong>COMMIT</strong>": this query allows the changes made to the database to become permanent. You can set your database to auto-commit changes by using the following query:</p>
<pre><code class="lang-mysql">SET autocommit = 1;
</code></pre>
<p>"<strong>SET</strong>": this query allows you to set your commit by enabling the operations to commit automatically or disabling the auto-commit. That is, your operations won't commit automatically until you call the "commit" query.</p>
<pre><code class="lang-mysql">/*Disabling the auto-commit  */
SET autocommit = 0;
/* OR */
SET autocommit = OFF;

/* Enabling the operations to automatically commit every operation*/
SET autocommit = 1;
/* OR */
SET autocommit = ON
</code></pre>
<p>"<strong>ROLLBACK</strong>": this query allows you to undo the changes you have made to the database, therefore returning the database to its previous (last commit) state.</p>
<h2 id="heading-acid-properties-of-transactions">ACID Properties of Transactions</h2>
<p>ACID is an acronym that stands for Atomicity, Consistency, Isolation, and Durability. Let's go through each term to understand how they relate to transactions.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Colorful-Villager-Bingo-Card-Instagram-Story.png" alt="ACID properties" width="600" height="400" loading="lazy"></p>
<h3 id="heading-atomicity">Atomicity</h3>
<p>Atomicity in a database transaction means that all the changes made during that transaction are treated as one "bundle" of changes. This means that when you are trying to modify your database, it's either all of the changes happen at the same time, or none of them happen at all. </p>
<p>It's like when you and your teammates are building an application. If one person writes a line of code, and then another person takes it off, it's like nothing happened. But if everyone keeps adding different lines of code and nobody takes any off, then the code base keeps getting bigger.</p>
<h3 id="heading-consistency">Consistency</h3>
<p>Consistency in databases means that the data stored in the database is always in a valid and consistent state. For example, if the database contains any constraints such as primary keys, foreign keys, and so on, it should always conform to the rules surrounding the constraint.</p>
<p>For example, let's say a table has a rule that says a specific column must be an integer value. Consistency ensures that this rule is always followed and data inserted into the column can only be of the integer value data type.</p>
<h3 id="heading-isolation">Isolation</h3>
<p>The ability of multiple transactions to execute without interfering with one another is known as Isolation. The isolation level of a transaction determines how the changes made by that transaction are visible to other transactions.</p>
<p>MYSQL supports the following isolation levels:</p>
<p><strong>i. READ UNCOMMITTED</strong>: In the READ UNCOMMITTED level, which is also the lowest isolation level, a transaction can read data that is yet to be committed by other transactions. This means that other transactions can alter the data that another transaction is currently reading, but these changes may not be visible until the operation is complete.</p>
<p><strong>ii. READ COMMITTED</strong>: This is the second-to-the-lowest isolation level. Here, a transaction is only able to read data that has already been committed by other transactions. This means that other transactions can alter the data that a transaction is currently reading but these changes will not be visible until the other transaction has been committed.</p>
<p><strong>iii. REPEATABLE READ</strong>: This is a higher level of isolation. A transaction at this level is only able to read data that has already been committed by other transactions, and it also restricts other transactions from altering the data that is currently being read. This means that even though other transactions have committed changes, if a transaction executes a SELECT statement again, it will always see the same data.</p>
<p><strong>iv. SERIALIZABLE</strong>: This is the highest level of isolation. At this level, a transaction is only able to read data that has already been committed by other transactions. It also prevents other transactions from altering the data that the transaction is reading and from adding new rows that would be visible to the current transaction.</p>
<p>MySQL uses the READ COMMITTED isolation level by default. However, it is possible to change the isolation level by using the "SET TRANSACTION ISOLATION LEVEL" statement.</p>
<h3 id="heading-durability">Durability</h3>
<p>Durability ensures that your data remains safe, even in the event of unforeseen circumstances. When a transaction is committed, its changes must remain in the database, even if it experiences a malfunction or a power outage. </p>
<p>But how does MySQL ensure durability? It makes use of write-ahead logging. This technique involves writing a log of the transaction to disk before making any changes to the database. </p>
<p>The log acts as a road map for the database and contains information about the changes that will be made in case of an unexpected system failure. In the event of this, the database can be recovered from the log, and the changes made in the transaction will be replayed to make sure that the database is still in a consistent state. </p>
<p>It's important to keep in mind that while write-ahead logging can have a performance impact, it's a small price to pay for the peace of mind that comes with knowing your data is safe.</p>
<h2 id="heading-locking-and-concurrency-in-mysql-transactions">Locking and Concurrency in MySQL Transactions</h2>
<p>Locking is a technique that is used to prevent race conditions. A race condition is a process where multiple transactions are trying to access the same data at the same time. </p>
<p>MySQL uses different types of locks to control access to data in a transaction. These include:</p>
<ol>
<li><strong>Shared locks</strong>: This allows multiple transactions to read the same data at the same time but restricts any of them from writing or making changes to it.</li>
<li><strong>Exclusive locks</strong>: This prevents different transactions from reading or writing the same data at the same time.</li>
<li><strong>Intent locks</strong>: This is used to specify that a transaction is planning to read or write a certain section of data.</li>
<li><strong>Row-level locks</strong>: This allows transactions to lock only the specific rows they need to access, rather than the entire table.</li>
</ol>
<p>Concurrency is a method where multiple transactions can run simultaneously without interfering with each other's data.</p>
<p>MySQL uses a multi-version concurrency control (MVCC) mechanism. This allows multiple transactions to read and write to the same data at the same time without conflict. </p>
<p>I'm sure you are wondering how this can be achieved. Well, each transaction sort of captures the data it is about to modify at the start of the transaction and writes its changes to an entirely different version of the data. This allows other transactions to continue working with the original version of the data without a conflict of interest.</p>
<p>To achieve high concurrency, it's important to keep the transactions as short as possible and avoid long-running transactions that hold locks for extended periods.</p>
<h2 id="heading-how-to-create-and-use-transactions-in-mysql">How to Create and Use Transactions in MySQL</h2>
<p>The first thing required is to start the transaction using the "START TRANSACTION" statement. Here is an example:</p>
<pre><code class="lang-mysql">START TRANSACTION;
    INSERT INTO users (name, email) VALUES ('John Doe', 'johndoe@example.com');
    UPDATE accounts SET balance = SUM(balance) WHERE name = 'John Doe';
</code></pre>
<p>In this example, a new transaction is started with the START TRANSACTION statement. The next two statements, an insert and an update, are executed within the transaction.</p>
<p>The next step is to commit the changes to make sure they are permanent. We do this by including the COMMIT statement in the query.</p>
<pre><code class="lang-mysql">START TRANSACTION;
    INSERT INTO users (name, email) VALUES ('John Doe', 'johndoe@example.com');
    UPDATE accounts SET balance = SUM(balance) WHERE name = 'John Doe';
COMMIT;
</code></pre>
<p>If by any chance there was an error during the transaction and you want to undo the changes, you can use the ROLLBACK statement. Then the transaction will be rolled back and the insert and update statements will not be executed. This means no change will take place in the database.</p>
<pre><code class="lang-mysql">START TRANSACTION;
    INSERT INTO users (name, email) VALUES ('John Doe', 'johndoe@example.com');
    UPDATE accounts SET balance = SUM(balance) WHERE user_id=15;
ROLLBACK;
</code></pre>
<h2 id="heading-how-to-use-the-innodb-storage-engine-for-transactions">How to Use the InnoDB Storage Engine for Transactions</h2>
<p>InnoDB is a storage engine for MySQL that has many functions that can improve your database performance. Some of these features include the ability to group and execute multiple SQL statements together, encrypt our data, create and drop indexes without affecting the database performance, handle CPU as well as large data, and many more.</p>
<p>To use InnoDB for transactions in MySQL, you will need to make sure that your tables are using the InnoDB storage engine. You can check this by running the following query:</p>
<pre><code class="lang-mysql">SHOW TABLE STATUS FROM your_database_name;
</code></pre>
<p>This will show you the storage engine used by each table in your database. It is also possible for you to set the default storage engine to InnoDB by modifying the <code>my.cnf</code> configuration file, or by running the following command:</p>
<pre><code class="lang-mysql">SET storage_engine=InnoDB;
</code></pre>
<p>After running this query, your database tables should be using the InnoDB storage engine. We can then begin to perform the functions we listed above.</p>
<pre><code class="lang-mysql">START TRANSACTION;
    UPDATE accounts SET balance = 50 WHERE user_id = 1;
    UPDATE accounts SET balance = 2000 WHERE user_id = 2;
COMMIT;
</code></pre>
<p>This is a simple example of a transaction that updates two rows in the "accounts" table. If any of the statements fails, the entire transaction will be rolled back, and no changes will be made to the database.</p>
<p>Additionally, InnoDB also provides some additional features like row-level locking, foreign key constraints, and crash recovery which makes it more robust and reliable than other storage engines, particularly for transactional workloads.</p>
<h2 id="heading-how-to-handle-errors-and-exceptions-in-transactions">How to Handle Errors and Exceptions in Transactions</h2>
<p>Handling errors and exceptions are important, especially when working with transactions. </p>
<p>A method of handling errors and exceptions in transactions is to use a try-catch block. In MySQL, you can use the SIGNAL and RESIGNAL statements to raise and handle exceptions within a transaction.</p>
<p>Here's an example of how you might use a try-catch block to handle an exception within a transaction:</p>
<pre><code class="lang-mysql">START TRANSACTION;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
        START TRANSACTION
            ROLLBACK;
            RESIGNAL;
        END;
    UPDATE accounts SET balance = 5000 WHERE user_id = 1;
    UPDATE accounts SET balance = 1000 WHERE user_id = 2;
    IF (SELECT balance FROM accounts WHERE user_id = 1) &lt; 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
    END IF;
COMMIT;
</code></pre>
<p>The DECLARE EXIT HANDLER FOR SQLEXCEPTION block is used to catch any SQL exceptions that occur within the transaction. </p>
<p>If an exception is caught, the transaction is rolled back using the ROLLBACK statement. Then the RESIGNAL statement raises the exception again so that it can be handled by an outer try-catch block, if any.</p>
<p>The IF statement checks if the balance of user_id =1 is less than zero. If true, the SIGNAL statement raises an exception with a specific SQLSTATE '45000' and a message "Insufficient balance".</p>
<p>It's worth knowing that if an exception occurs within a transaction, any changes that may have occurred during the transaction will be rolled back, regardless of whether or not the exception is handled.</p>
<h2 id="heading-how-to-use-savepoints-in-mysql-transactions">How to Use Savepoints in MySQL Transactions</h2>
<p>It's good practice to use the SAVEPOINT statement within a transaction to set savepoints. This makes it possible for you to rollback to a specific point in the transaction, rather than rolling back the entire transaction. </p>
<p>Here is an example of how you might use the SAVEPOINT statement within the previous example:</p>
<pre><code class="lang-mysql">START TRANSACTION;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
        START TRANSACTION
            ROLLBACK TO SAVEPOINT my_savepoint;
            RESIGNAL;
        END;
    UPDATE accounts SET balance = 5000 WHERE user_id = 1;
    UPDATE accounts SET balance = 1000 WHERE user_id = 2;
    IF (SELECT balance FROM accounts WHERE user_id = 1) &lt; 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
    END IF;
COMMIT;
</code></pre>
<p>You use the SAVEPOINT statement to set a savepoint named "my_savepoint" before the two updates. If an exception is caught, the ROLLBACK statement rolls back the transaction to the savepoint, using the clause "TO SAVEPOINT my_savepoint", rather than rolling back the entire transaction. </p>
<p>This will undo only the changes made after the savepoint, and leave the changes made before the savepoint intact.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Transaction operations are quite important. They help developers ensure that their database remains in a consistent state and makes it easy to reverse changes if necessary. </p>
<p>MySQL provides features such as commit, rollback, and savepoint to make the process much easier. It also provides robust engines like InnoDB which support these features as well. </p>
<p>For further information on MySQL transactions, you can check out the official <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/sql-transactional-statements.html">documentation</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ MySQL Date Functions – Explained with Example Queries ]]>
                </title>
                <description>
                    <![CDATA[ SQL is a programming language we use to interact with relational databases. SQL databases contain tables, which contain rows of data. These tables can contain a wide range of data types. In this article, you'll learn how MySQL functions help make dat... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/mysql-date-functions/</link>
                <guid isPermaLink="false">66c4c6a8bd556981b1bdc460</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Zubair Idris Aweda ]]>
                </dc:creator>
                <pubDate>Wed, 25 Jan 2023 17:15:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/image-162-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>SQL is a programming language we use to interact with relational databases. SQL databases contain tables, which contain rows of data. These tables can contain a wide range of data types.</p>
<p>In this article, you'll learn how MySQL functions help make date management very easy. </p>
<p>These functions help perform various tasks. Some perform simple tasks like adding days to dates, finding how many days are between two dates, or even more complicated tasks like how to tell how far into a year a date is by number of days.</p>
<p>Before proceeding, keep in mind that this article was written on <code>2023-01-24</code>. So your results on running the queries here might be slightly different based on when you read it.</p>
<h2 id="heading-how-to-use-the-currentdate-function-in-sql">How to Use the <code>CURRENT_DATE</code> Function in SQL</h2>
<p>This function returns today's date in the format '<strong>YYYY-MM-DD</strong>'. It is one of the simplest MySQL functions to use. It takes no arguments at all.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">CURRENT_DATE</span>;
<span class="hljs-comment">-- Returns 2023-01-24</span>
</code></pre>
<p>This function has synonymous functions that work just the way it does: <code>CUR_DATE</code> and <code>CURRENT_DATE()</code> will return the exact same result as <code>CURRENT_DATE</code>.</p>
<h2 id="heading-how-to-use-the-adddate-function-in-sql">How to Use the <code>ADDDATE</code> Function in SQL</h2>
<p>This functions performs additions, or subtractions, on date values. It takes an interval that can be in days, or months, or even years. This interval can be positive or negative. The function takes this format:</p>
<pre><code class="lang-sql">ADDDATE(date/expr, INTERVAL expr unit);
</code></pre>
<p>Here, the <code>date/expr</code> refers to the base date value to be added to or subtracted from. And the <code>INTERVAL</code> is a constant keyword that has to come before the <code>expr</code> that is used to set the value of the increment in numbers. Finally, you have the unit, which can be <code>day</code>, <code>week</code>,  <code>month</code>, <code>quarter</code> or even <code>year</code>.  The <code>unit</code> can also be a smaller value like <code>second</code> or even <code>microsecond</code>. Check the <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/expressions.html#temporal-intervals">MySQL docs</a> for more possible values.</p>
<p>This functions works exactly the same as the <code>DATE_ADD</code> and you can use them interchangeably.</p>
<p>Using ADDDATE, you can find the date of 45 days from today like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> ADDDATE(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">45</span> <span class="hljs-keyword">DAY</span>);
<span class="hljs-comment">-- Returns 2023-03-10</span>
</code></pre>
<p>To get the date of the day 7 months and 3 weeks ago, use the ADDDATE like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> ADDDATE(
    ADDDATE(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">-7</span> <span class="hljs-keyword">MONTH</span>), 
    <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">-3</span> <span class="hljs-keyword">WEEK</span>
);
<span class="hljs-comment">-- Returns 2022-06-03</span>
</code></pre>
<p>Here, we called the ADDDATE function twice. First, to get the date of 7 months ago. Then, we called it again to get the date of 3 weeks before that time.</p>
<p>A common use case of ADDDATE in real life applications is to get data values to be used in a WHERE clause as a range. </p>
<p>For example, if you had an <code>employees</code> table with a <code>hiredate</code> field that stores their resumption date. To see all employees that resumed in the past year (where <code>hiredate</code> &gt; the date of a year ago), use ADDDATE like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * 
<span class="hljs-keyword">FROM</span> employees 
<span class="hljs-keyword">WHERE</span> hiredate &gt; ADDDATE(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">-1</span> <span class="hljs-keyword">YEAR</span>);
</code></pre>
<p>Another common case would be when you have to filter by a time range. In a <code>songs</code> table with a <code>released</code> field, to fetch all songs released in the last three weeks except for the ones released this week, use ADDDATE like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * 
<span class="hljs-keyword">FROM</span> songs 
<span class="hljs-keyword">WHERE</span> released 
<span class="hljs-keyword">BETWEEN</span> ADDDATE(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">-3</span> <span class="hljs-keyword">WEEK</span>) 
<span class="hljs-keyword">AND</span> ADDDATE(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">-1</span> <span class="hljs-keyword">WEEK</span>);
</code></pre>
<h2 id="heading-how-to-use-the-datediff-function-in-sql">How to Use the <code>DATEDIFF</code> Function in SQL</h2>
<p>This function returns the number of days between two dates. It takes in the two dates to be subtracted. Let's use <code>DATEDIFF</code> to find the number of days between today and <code>2023-03-10</code>.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DATEDIFF</span>(<span class="hljs-string">'2023-03-10'</span>, <span class="hljs-keyword">CURRENT_DATE</span>);
<span class="hljs-comment">-- Returns 45</span>
</code></pre>
<p>Rearranging the dates and calling the function again results in a difference in the response:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DATEDIFF</span>(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-string">'2023-03-10'</span>);
<span class="hljs-comment">-- Returns -45</span>
</code></pre>
<p>You can use this function with the <code>ABS</code> function to get the absolute value and not have issues with the negative sign or value.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">ABS</span>(<span class="hljs-keyword">DATEDIFF</span>(<span class="hljs-keyword">CURRENT_DATE</span>, <span class="hljs-string">'2023-03-10'</span>));
<span class="hljs-comment">-- Returns 45</span>
</code></pre>
<p>This is very useful when you have to return data with respect to time. <code>For example, in many blogs, you see a part that says something like 'Posted 7 days ago'. You can use the</code>DATEDIFF` to get this value easily.</p>
<h2 id="heading-how-to-use-the-dateformat-function-in-sql">How to Use the <code>DATE_FORMAT</code> Function in SQL</h2>
<p>This function lets you present your data anyhow you want it. This is a very useful function. It takes in the date to be formatted, and also a string representing the desired format. The function takes this format:</p>
<pre><code class="lang-sql">DATE_FORMAT(date, format)
</code></pre>
<p>The format string can be of any length and each character in it defines a specific format and must be prefixed by the percentage symbol, <code>%</code>. For example, given the date <code>2023-03-10</code>, you can present this as <code>Fri 10th March, 2023</code> like so:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DATE_FORMAT</span>(<span class="hljs-string">'2023-03-10'</span>, <span class="hljs-string">'%a %D %M, %Y'</span>);
</code></pre>
<p>Here, we passed in the format string <code>'%a %D %M, %Y'</code>. But, what does this truly mean? Here's a few things to note:</p>
<ul>
<li>The provided format string, <code>'%a %D %M, %Y'</code>, is exactly the same shape as the result, <code>Fri 10th March, 2023</code>. This means you can shape the result anyhow you like – even the space characters matter. Every character in the format string is returned as part of the result, except it is prefixed using the percentage sign, then it is read as a format character. For example, rewriting the format string to <code>'45 days from today is %a, %D day of %M, %Y'</code> will result in <code>45 days from today is Fri, 10th day of March, 2023</code>.</li>
<li>The <code>a</code> used results in the abbreviated weekday name, Fri.</li>
<li>The <code>D</code> returned the day of the month with English suffix, 10th.</li>
<li>The <code>M</code> returned the name of the month, March.</li>
<li>The <code>Y</code> returned the year, 2023.</li>
</ul>
<p>There are many more characters that you can use in the format string, and you can find them <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format">here</a>.</p>
<h2 id="heading-how-to-use-the-max-and-min-functions-in-sql">How to Use the <code>MAX</code> and <code>MIN</code> Functions in SQL</h2>
<p>While these functions aren't limited or specific to date data type, they are very useful when working with dates. You can use the MAX to find the latest record in a table. You can use the MIN to find the oldest record in a table.</p>
<p>In a table of <code>employees</code>, with a <code>birthday</code> field storing their date of birth, you can find the oldest employee using the MAX function like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">WHERE</span> birthday = (<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MAX</span>(birthday) <span class="hljs-keyword">from</span> employees);
</code></pre>
<p>Or alternatively, like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> birthday <span class="hljs-keyword">DESC</span>
<span class="hljs-keyword">LIMIT</span> <span class="hljs-number">1</span>;
</code></pre>
<p>You could get the youngest employee using the MIN function:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">WHERE</span> birthday = (<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MIN</span>(birthday) <span class="hljs-keyword">from</span> employees);
</code></pre>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> birthday
<span class="hljs-keyword">LIMIT</span> <span class="hljs-number">1</span>;
</code></pre>
<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>I hope you now understand the MySQL date functions we discussed here, their variations and arguments, and when to use them so you can write better queries. You can find more of these functions <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html">here</a>.</p>
<p>If you have any questions or relevant advice, please get in touch with me to share them.</p>
<p>To read more of my articles or follow my work, you can connect with me on <a target="_blank" href="https://www.linkedin.com/in/idris-aweda-zubair-5433121a3/">LinkedIn</a>, <a target="_blank" href="https://twitter.com/AwedaIdris">Twitter</a>, and <a target="_blank" href="https://github.com/Zubs">Github</a>. It’s quick, it’s easy, and it’s free!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Upload Files to Azure using NestJS and typeORM with MySQL ]]>
                </title>
                <description>
                    <![CDATA[ Images and videos are examples of huge files that might be stored in your database. And this might impact the performance of your applications because of the amount of space that those files can take up. Also, it expands the database, which makes bac... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/upload-files-to-azure-using-nestjs-and-typeorm-with-mysql/</link>
                <guid isPermaLink="false">66b906bf77c23fa04d7098f5</guid>
                
                    <category>
                        <![CDATA[ Azure ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ nestjs ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Destiny Erhabor ]]>
                </dc:creator>
                <pubDate>Fri, 21 Oct 2022 23:13:08 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/10/pexels-panumas-nikhomkhai-1148820.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Images and videos are examples of huge files that might be stored in your database. And this might impact the performance of your applications because of the amount of space that those files can take up.</p>
<p>Also, it expands the database, which makes backups larger and slower. Because of this, it's not regarded as a best practice. Instead, using a distributed system to save files and adding a reference to such files to our database is a good choice.</p>
<p>In this article you will learn how to upload these files to Azure cloud distributed services and delete them from Azure using NestJS, a popular Node framework.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-cloud-distributed-system">What is a Cloud Distributed System</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-azure">What is Azure</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-started">Getting Started</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-connect-to-azure">How to Connect to Azure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-nestjs-and-mysql-db">How to Set up NestJS and MySQL DB</a></p>
</li>
<li><p><a class="post-section-overview" href="#how-to-connect-to-azure-blob-through-sdk">How to Connect to Azure Blob through SDK</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-upload-the-image-through-the-api">How to Upload the Image through the API</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-an-endpoint-for-uploading-images">How to Create an Endpoint for Uploading Images</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-delete-files">How to Delete Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-cloud-distributed-system">What is a Cloud Distributed System?</h2>
<p>A distributed system is made up of a number of independent parts that are spread across several devices. They communicate with one another via messages to accomplish shared objectives.</p>
<p>As a result, to the end user the distributed system will look like a single interface or computer. Together, the system is expected to maximize information and resource usage while preventing errors because, even if one system fails, the service will still be available.</p>
<h2 id="heading-what-is-azure">What is Azure?</h2>
<p>Azure is a platform for public cloud computing that offers solutions for analytics, virtual computing, storage, networking, and much more.</p>
<p>These solutions include Infrastructure as a Service (IaaS), Platform as a Service (PaaS), and Software as a Service (SaaS). You can supplement or replace your on-premise servers with it.</p>
<p>Blobs, tables, and queues are the three main data services provided by Azure. These services are all widely distributed, highly scalable, and reliable. We will be utilizing one of this services in this article.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Before you start following along with this tutorial, make sure you have the following things ready:</p>
<ul>
<li><p>An Azure Subscription – You can <a target="_blank" href="https://azure.microsoft.com/en-us/free/?WT.mc_id=academic-75638-bethanycheum">sign up</a> for a free Azure account if you don’t already have one.</p>
</li>
<li><p>Basic Knowledge and installations of NestJS and MySQL server database. Learn more from the <a target="_blank" href="https://docs.nestjs.com/">NestJS documentation</a>.</p>
</li>
</ul>
<h2 id="heading-how-to-connect-to-azure">How to Connect to Azure</h2>
<p>One of Microsoft's Cloud Storage's cloud-based object storage solution is called a Blob. Large-scale unstructured data storage is best suited for blob storage. Unstructured data, such as text or binary data, is data that does not follow a specific data model or description.</p>
<h3 id="heading-how-to-create-a-blob-storage">How to create a blob storage</h3>
<p><strong>Step 1</strong>: The Azure dashboard is visible once you create an account and log in to the Azure site. Select <code>Storage Accounts</code> from the menu or use the search bar.‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-20-52-47-2.png" alt="creating blob storage step 1‌" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 1‌</em></p>
<p><strong>Step 2:</strong> Choose <code>create</code> or <code>Create storage account</code> if you do not have an existing storage account from the menu in the following box.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-20-54-28-2.png" alt="creating blob storage step 2" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 2</em></p>
<p><strong>Step 3:</strong> In this window, we need to fill in subscription, resource group, storage account name, region, performance, and redundancy.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/screencapture-portal-azure-2022-10-15-20_55_56-4.png" alt="creating blob storage step 3" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 3</em></p>
<ul>
<li><p>The <code>Subscription</code> allows Azure keep track of where to charge for the resource used. You can use your free subscription here.</p>
</li>
<li><p>A <code>resource group</code> is a central grouping for your resource(s). It helps you structure and organize your Azure resources based on your wants.</p>
</li>
<li><p>The <code>Storage account name</code> must be a <strong>unique</strong> name globally.</p>
</li>
<li><p><code>Performance</code> gives you different storage types such as HDD and SSD. Here, we are using Standard.</p>
</li>
<li><p><code>Redundancy</code> helps protect your storage from data center or region failures by duplicating your resource to other regions.</p>
</li>
</ul>
<p><strong>Step 4</strong>: Then click <code>review</code> to validate your options. After completing validation, you can click the <code>Create</code> button to create the Storage account. (Note here that we left every other option as default.)</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-20-59-35-3.png" alt="creating blob storage step 4" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 4</em></p>
<p>‌When successfully created, the following windows should show up:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/screencapture-portal-azure-2022-10-15-21_01_08-2.png" alt="creating blob storage step 5" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 5</em></p>
<p><strong>Step 5:</strong> Next, select <code>Go to resource</code> to be sent to the storage account dashboard. The left sidebar is then visible and has a number of options. Choose the <code>containers</code> option there, which is in the Data Storage section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-21-02-50-3.png" alt="creating blob storage step 5" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 5</em></p>
<p>‌<strong>Step 6:</strong> in the Containers dashboard, now click on <code>+ Container</code>, then a form will appear on the right side. Fill out the form by giving the <strong>name</strong> and <strong>public access level</strong> (you can use any option according to your requirements) for the container. You can create any number of containers under one storage account</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-21-04-07-3.png" alt="creating blob storage step 6" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 6</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-21-04-57-2.png" alt="creating blob storage step 6" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 6</em></p>
<p>Click the create button once you've finished filling out the form.</p>
<p><strong>Step 7:</strong> Copy the Credentials from the Azure portal.</p>
<p>You should have authorization before sending requests to Azure storage. Azure offers two keys for that purpose, each of which contains a connection string. As a result, you will need these credentials as a connection string to the NestJS application.</p>
<p>One of the connection strings is available for copying in the Access keys area of the <code>Security + networking</code> menu on the left. (We will add these to our NestJS .env file.)</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-21-57-01-2.png" alt="creating blob storage step 7" width="600" height="400" loading="lazy"></p>
<p><em>Creating blob storage step 7</em></p>
<p>Cool! You've configured your Azure blob storage. The next step is to setup and link your NestJS application with the blob storage.</p>
<h2 id="heading-how-to-set-up-nestjs-and-mysql-db">How to Set Up NestJS and MySQL DB</h2>
<p>As mentioned earlier, we will be using NestJS as our server and the MySQL database to save a reference to the file saved on the Azure distributed system.</p>
<p>Firstly you need to have NestJS and MySQL server installed on your system. Then run the following NestJS command to start a new project. Let's call our project <code>nestjs-file-upload-azure</code>:</p>
<pre><code class="lang-js">nest <span class="hljs-keyword">new</span> nestjs-file-upload-azure
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-05-18-05-3.png" alt="How to create a new project in nestjs‌" width="600" height="400" loading="lazy"></p>
<p><em>How to create a new project in Nestjs‌</em></p>
<p>Before we get started in creating out resources, let's install the necessary dependencies needed:</p>
<pre><code class="lang-js">yarn add mysql2 @nestjs/typeorm @nestjs/config typeorm
</code></pre>
<p>To setup your MySQL database with NestJS, open the <code>app.module.ts</code> inside the <code>src</code> folder and add the following code:</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> { Module } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>; 
<span class="hljs-keyword">import</span> { ConfigModule, ConfigService } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/config'</span>; 
<span class="hljs-keyword">import</span> { TypeOrmModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/typeorm'</span>; 
<span class="hljs-keyword">import</span> { FileModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'./modules/files/file.module'</span>; 
<span class="hljs-keyword">import</span> { UserModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'./modules/users/user.module'</span>; 

@Module({ 
    <span class="hljs-attr">imports</span>: [ 
        ConfigModule.forRoot({ 
            <span class="hljs-attr">envFilePath</span>: <span class="hljs-string">'.env'</span>, 
            <span class="hljs-attr">isGlobal</span>: <span class="hljs-literal">true</span>, 
        }), 
        TypeOrmModule.forRootAsync({ 
            <span class="hljs-attr">imports</span>: [ConfigModule], 
            <span class="hljs-attr">inject</span>: [ConfigService], 
            <span class="hljs-attr">useFactory</span>: <span class="hljs-function">(<span class="hljs-params">config: ConfigService</span>) =&gt;</span> ({ 
                <span class="hljs-attr">type</span>: <span class="hljs-string">'mysql'</span>, 
                <span class="hljs-attr">host</span>: <span class="hljs-string">'localhost'</span>, 
                <span class="hljs-attr">port</span>: <span class="hljs-number">3306</span>, 
                <span class="hljs-attr">username</span>: config.get(<span class="hljs-string">'DB_USERNAME'</span>), 
                <span class="hljs-attr">password</span>: config.get(<span class="hljs-string">'DB_PASSWORD'</span>), 
                <span class="hljs-attr">database</span>: <span class="hljs-string">'azure_upload'</span>, 
                <span class="hljs-attr">entities</span>: [__dirname + <span class="hljs-string">'/**/*.entity{.ts,.js}'</span>], 
                <span class="hljs-attr">synchronize</span>: <span class="hljs-literal">true</span>, 
            }), 
        }), 
        UserModule, 
        FileModule, 
    ], 
    <span class="hljs-attr">controllers</span>: [], 
    <span class="hljs-attr">providers</span>: [], 
}) 

<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppModule</span> </span>{}
</code></pre>
<p>Inside the <code>imports</code>, configure a simple MySQL database with the <code>TypeOrmModule.forRootAsync()</code> called <code>azure_upload</code> locally. It injects <code>ConfigService</code> to allow us to use environment variables (in this case your database name and password).</p>
<p>For a production-based application, you should set <code>synchronize</code> to false and use <code>migration</code> so as to keep your database data safe.</p>
<p>Now the database is connected successfully, thanks to the TypeORM package we installed. You can check by running <code>yarn start:dev</code> on your terminal or <code>npm run start:dev</code> if you are using npm as your package manager.</p>
<h2 id="heading-how-to-connect-to-the-azure-blob-through-sdk">How to Connect to the Azure Blob through SDK</h2>
<p>Using the <code>@azure/storage-blob</code> storage we can connect to Azure. We are still going to need the Multer package for managing file handling operations, and we'll use UUID to generate a unique name for each blob.</p>
<p>Let’s install them first.</p>
<ul>
<li><p><code>yarn add @azure/storage-blob uuidv4 @types/multer</code> or</p>
</li>
<li><p><code>npm install @azure/storage-blob uuidv4 @types/multer</code></p>
</li>
</ul>
<p>Now, let's add the connection string we saved earlier to our <strong>.env</strong> file</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-15-22-06-39-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-upload-the-image-through-the-api">How to Upload the Image through the API</h2>
<p>Since we’ve got the Azure connection set up, we can proceed with uploading our files. For starters, let’s create a file entity and service.</p>
<h3 id="heading-how-to-create-an-azure-service">How to Create an Azure Service</h3>
<p>The Azure service will help us in mapping out the logic to upload, download, and delete files from our Azure storage account we created.</p>
<p>To generate a service, we will use the NestJS CLI. Open the <code>terminal</code> and run <code>nest g service files modules/files --no-spec --flat</code>.</p>
<p>Add the following to the service files generated:</p>
<h6 id="heading-srcmodulesfilesfilesservicets">src/modules/files/files.service.ts</h6>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> { BlobServiceClient, BlockBlobClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@azure/storage-blob'</span>; 
<span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>; 
<span class="hljs-keyword">import</span> { ConfigService } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/config'</span>; 
<span class="hljs-keyword">import</span> { uuid } <span class="hljs-keyword">from</span> <span class="hljs-string">'uuidv4'</span>; 

@Injectable() <span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FilesAzureService</span> </span>{ 
    <span class="hljs-keyword">constructor</span>(private readonly configService: ConfigService) {} 
    private containerName: string; 

    private <span class="hljs-keyword">async</span> getBlobServiceInstance() { 
        <span class="hljs-keyword">const</span> connectionString = <span class="hljs-built_in">this</span>.configService.get(<span class="hljs-string">'CONNECTION_STRING'</span>); 
        <span class="hljs-keyword">const</span> blobClientService = <span class="hljs-keyword">await</span> BlobServiceClient.fromConnectionString( connectionString, ); 
        <span class="hljs-keyword">return</span> blobClientService; 
    } 

    private <span class="hljs-keyword">async</span> getBlobClient(imageName: string): <span class="hljs-built_in">Promise</span>&lt;BlockBlobClient&gt; {
        <span class="hljs-keyword">const</span> blobService = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.getBlobServiceInstance(); 
        <span class="hljs-keyword">const</span> containerName = <span class="hljs-built_in">this</span>.containerName; 
        <span class="hljs-keyword">const</span> containerClient = blobService.getContainerClient(containerName); 
        <span class="hljs-keyword">const</span> blockBlobClient = containerClient.getBlockBlobClient(imageName); 

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

    public <span class="hljs-keyword">async</span> uploadFile(file: Express.Multer.File, <span class="hljs-attr">containerName</span>: string) { 
        <span class="hljs-built_in">this</span>.containerName = containerName; 
        <span class="hljs-keyword">const</span> extension = file.originalname.split(<span class="hljs-string">'.'</span>).pop(); 
        <span class="hljs-keyword">const</span> file_name = uuid() + <span class="hljs-string">'.'</span> + extension; 
        <span class="hljs-keyword">const</span> blockBlobClient = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.getBlobClient(file_name);
        <span class="hljs-keyword">const</span> fileUrl = blockBlobClient.url; 
        <span class="hljs-keyword">await</span> blockBlobClient.uploadData(file.buffer); 

        <span class="hljs-keyword">return</span> fileUrl; 
    } 
}
</code></pre>
<p>The private functions create an instance of our Azure blob storage with the connection strings using the azure-sdk methods <code>BlobServiceClient.fromConnectionString()</code>. It also expects the <code>container name</code> we gave our blob container earlier during azure storage creation using <code>getContainerClient</code> and <code>getBlockBlobClient()</code>.</p>
<p>The <code>uploadFile()</code> is a public function that user service can call to make image upload to azure. This function uses the azure instance and the private functions to upload the file and returns the file url.</p>
<h2 id="heading-how-to-create-an-endpoint-for-uploading-images">How to Create an Endpoint for Uploading Images</h2>
<p>It's time to create our user resources that provide an endpoint for us to create (upload) images to Azure, view the image, and delete the image.</p>
<h3 id="heading-how-to-create-a-user-resource">How to Create a User Resource</h3>
<p>The NestJS CLI is a powerful tool that helps scaffold our resource by creating basic component as you know them. To easily create a resource, on the terminal type the following command and follow the prompt for REST APIs:</p>
<pre><code class="lang-js">nest generate resource users modules/users --no-spec --flat
</code></pre>
<p>The <code>--no-spec</code> ignores test files and the <code>--flat</code> creates the resource directly in a <code>modules/users</code> folder.</p>
<p>The above command added the folders <code>dto</code> and <code>entities</code> and files <code>user.controller.ts</code>, <code>user.module.ts</code> and <code>user.service.ts</code> inside the <code>src</code> file. It also performed all necessary updates to sync with <code>app.module.ts</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-17-22-46-33-2.png" alt="user resources created" width="600" height="400" loading="lazy"></p>
<p><em>User resources created</em></p>
<h3 id="heading-how-to-create-a-user-entity">How to Create a User Entity</h3>
<p>By saving the image URL directly in the database, we can access the public file very quickly.</p>
<h6 id="heading-srcmodulesusersusersentityts">src/modules/users/users.entity.ts</h6>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> { Column, Entity, PrimaryGeneratedColumn } <span class="hljs-keyword">from</span> <span class="hljs-string">'typeorm'</span>; 

@Entity() <span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{ 
    @PrimaryGeneratedColumn() 
    <span class="hljs-attr">id</span>: number; 

    @Column() 
    <span class="hljs-attr">image_url</span>: string; 
 }
</code></pre>
<h3 id="heading-how-to-create-a-user-service">How to Create a User Service</h3>
<p>The user service helps connect with the database and save the upload image_url, hence the <code>saveUrl()</code> function.</p>
<h6 id="heading-srcmodulesusersusersservicets">src/modules/users/users.service.ts</h6>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>; 
<span class="hljs-keyword">import</span> { InjectRepository } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/typeorm'</span>; 
<span class="hljs-keyword">import</span> { Repository } <span class="hljs-keyword">from</span> <span class="hljs-string">'typeorm'</span>; 
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'./entities/user.entity'</span>; 

@Injectable() <span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> </span>{ 
    <span class="hljs-keyword">constructor</span>( 
        @InjectRepository(User) private readonly userRepository: Repository&lt;User&gt;, 
    ) {} 

    <span class="hljs-keyword">async</span> saveUrl(file_url: string) { 
        <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userRepository.save({ <span class="hljs-attr">image_url</span>: file_url }); 
    } 
}
</code></pre>
<h3 id="heading-how-to-create-a-user-controller">How to Create a User Controller</h3>
<p>This defines the endpoint for a public user to make a file upload. To do that, we follow the <a target="_blank" href="https://docs.nestjs.com/techniques/file-upload">NestJS documentation</a> and use the <code>FileInterceptor</code> that utilizes <a target="_blank" href="https://www.npmjs.com/package/multer"><code>multer</code></a> under the hood.</p>
<h6 id="heading-srcmodulesusersuserscontrollersts">src/modules/users/users.controllers.ts</h6>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> { Controller, Post, UseInterceptors, UploadedFile, } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>; 
<span class="hljs-keyword">import</span> { UserService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./user.service'</span>; 
<span class="hljs-keyword">import</span> { FilesAzureService } <span class="hljs-keyword">from</span> <span class="hljs-string">'../files/file.azure.service'</span>; 
<span class="hljs-keyword">import</span> { FileInterceptor } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/platform-express'</span>;

@Controller(<span class="hljs-string">'user'</span>) <span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserController</span> </span>{ 
    <span class="hljs-keyword">constructor</span>( 
        private readonly userService: UserService, 
        private readonly fileService: FilesAzureService 
    ) {} 

    @Post(<span class="hljs-string">'upload'</span>) 
    @UseInterceptors(FileInterceptor(<span class="hljs-string">'image'</span>)) 
    <span class="hljs-keyword">async</span> create(@UploadedFile() file: Express.Multer.File) { 
        <span class="hljs-keyword">const</span> containerName = <span class="hljs-string">'fileupload'</span>; 
        <span class="hljs-keyword">const</span> upload = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.fileService.uploadFile(file, containerName) 
        <span class="hljs-built_in">this</span>.userService.saveUrl(upload); 
        <span class="hljs-keyword">return</span> { upload, <span class="hljs-attr">message</span>: <span class="hljs-string">'uploaded successfully'</span> } 
    } 
}
</code></pre>
<p>We can test with Postman:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-16-22-52-06-1.png" alt="Test upload endpoint from postman" width="600" height="400" loading="lazy"></p>
<p><em>Test upload endpoint from Postman</em></p>
<p>And confirm on Azure:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/Screenshot-from-2022-10-16-22-50-31-2.png" alt="Confirm image on the azure portal" width="600" height="400" loading="lazy"></p>
<p><em>Confirm image on the Azure portal</em></p>
<h2 id="heading-how-to-delete-files">How to Delete Files</h2>
<p>We also need a means to delete files after submitting them. We'll remove the files from both locations to maintain consistency between our database and Azure storage. Adding the method to the Files Service first:</p>
<p><strong>src/modules/files/files.service.ts</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> deleteFile(file_name: string, <span class="hljs-attr">containerName</span>: string) { 
    <span class="hljs-keyword">try</span> { 
        <span class="hljs-built_in">this</span>.containerName = containerName; 
        <span class="hljs-keyword">const</span> blockBlobClient = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.getBlobClient(file_name);
        <span class="hljs-keyword">await</span> blockBlobClient.deleteIfExists(); 
    } <span class="hljs-keyword">catch</span> (error) { 
        <span class="hljs-built_in">console</span>.log(error); 
    } 
}
</code></pre>
<p>We must now apply it to our Users Service. A crucial addition is that we remove the previous file when a user uploads one while already having one and making an upload endpoint by user id.</p>
<p><strong>src/modules/users/users.service.ts</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { InjectRepository } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/typeorm'</span>;
<span class="hljs-keyword">import</span> { Repository } <span class="hljs-keyword">from</span> <span class="hljs-string">'typeorm'</span>;
<span class="hljs-keyword">import</span> { FilesAzureService } <span class="hljs-keyword">from</span> <span class="hljs-string">'../files/file.azure.service'</span>;
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'./entities/user.entity'</span>;

@Injectable()
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> </span>{
  <span class="hljs-keyword">constructor</span>(
    @InjectRepository(User) private readonly userRepository: Repository&lt;User&gt;,
    private readonly fileService: FilesAzureService,
  ) {}

  <span class="hljs-keyword">async</span> saveUrl(id, <span class="hljs-attr">file_url</span>: string, <span class="hljs-attr">containerName</span>: string): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt; {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userRepository.findOne({
      <span class="hljs-attr">where</span>: { id },
    });
    <span class="hljs-keyword">const</span> file_image = user?.image_url;
    <span class="hljs-keyword">let</span> getfile = <span class="hljs-string">''</span>;

    <span class="hljs-keyword">if</span> (file_image) {
      getfile = file_image.split(<span class="hljs-string">'/'</span>).pop();
    }
    <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userRepository.save({
      ...user,
      <span class="hljs-attr">image_url</span>: file_url,
    });
    <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.fileService.deleteFile(getfile, containerName);
  }

  <span class="hljs-keyword">async</span> getimage(id: number): <span class="hljs-built_in">Promise</span>&lt;any&gt; {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userRepository.findOne({
      <span class="hljs-attr">where</span>: { id },
    });

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

  <span class="hljs-keyword">async</span> remove(id: number, <span class="hljs-attr">containerName</span>: string): <span class="hljs-built_in">Promise</span>&lt;User&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userRepository.findOne({
        <span class="hljs-attr">where</span>: { id },
      });
      <span class="hljs-keyword">const</span> file_url = user?.image_url;
      <span class="hljs-keyword">if</span> (file_url) {
        <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userRepository.update(id, {
          ...user,
          <span class="hljs-attr">image_url</span>: <span class="hljs-string">''</span>,
        });

        <span class="hljs-keyword">const</span> file_ = file_url.split(<span class="hljs-string">'/'</span>).pop();

        <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.fileService.deleteFile(file_, containerName);
      }

      <span class="hljs-keyword">return</span> user;
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.log(error);
      <span class="hljs-keyword">return</span> error;
    }
  }
}
</code></pre>
<p>Including an endpoint where users can send an image is the final component. To accomplish that, we use the <code>FileInterceptor</code>, which internally makes use of <code>multer</code>, in accordance with the NestJS documentation.</p>
<p><strong>src/modules/users/users.controller.ts</strong></p>
<pre><code class="lang-javascript">Controller(<span class="hljs-string">'user'</span>)
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserController</span> </span>{
  <span class="hljs-keyword">constructor</span>(
    private readonly userService: UserService,
    private readonly fileService: FilesAzureService
  ) {}

  @Post(<span class="hljs-string">'/:id/upload'</span>)
  @UseInterceptors(FileInterceptor(<span class="hljs-string">'image'</span>))
  <span class="hljs-keyword">async</span> create(@UploadedFile() file: Express.Multer.File, @Param(<span class="hljs-string">'id'</span>) id) {
    <span class="hljs-keyword">const</span> containerName = <span class="hljs-string">'fileupload'</span>;
    <span class="hljs-keyword">const</span> upload = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.fileService.uploadFile(file, containerName)
    <span class="hljs-built_in">this</span>.userService.saveUrl(id, upload, containerName);
    <span class="hljs-keyword">return</span> {
      upload,
      <span class="hljs-attr">message</span>: <span class="hljs-string">'uploaded successfully'</span>
    }
  }

  @Get(<span class="hljs-string">'/:id'</span>)
  <span class="hljs-keyword">async</span> getimage(@Param(<span class="hljs-string">'id'</span>) id) {
    <span class="hljs-keyword">const</span> image = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userService.getimage(id);
    <span class="hljs-keyword">return</span> {
      image,
      <span class="hljs-attr">message</span>: <span class="hljs-string">'get image successfully'</span>
    }
  }

  @Delete(<span class="hljs-string">'remove/:id'</span>)
  @UseInterceptors(FileInterceptor(<span class="hljs-string">'image'</span>))
  <span class="hljs-keyword">async</span> remove(@UploadedFile() file: Express.Multer.File , @Param(<span class="hljs-string">'id'</span>) id) {
    <span class="hljs-keyword">const</span> containerName = <span class="hljs-string">'fileupload'</span>;
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.userService.remove(id, containerName);
    <span class="hljs-keyword">return</span> {
      user,
      <span class="hljs-attr">message</span>: <span class="hljs-string">'deleted successfully'</span>
    }
  }
}
</code></pre>
<p>These sync with your Azure storage to delete unused file uploads.</p>
<p>You can find the <a target="_blank" href="https://github.com/Caesarsage/nest-file-upload-azure">full code repository Link on <strong>GitHub</strong></a><strong>.</strong></p>
<h2 id="heading-cleaning-up">Cleaning Up</h2>
<p>To avoid paying for the underlying Azure storage cost, you should clean up your resources if they're not being used.</p>
<p>To clean up your Azure resources, in the Azure portal go to or search for resources group, find the one you just created, and delete it. This will delete all resources in the resource group.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we've learnt the fundamentals of Azure Blob storage and how to use it in our NestJS API.</p>
<p>To accomplish that, we've given the required credentials to Azure SDK, and as a result, we've been able to upload and remove files to Azure.</p>
<p>To track our files, we've also kept our MySQL database synchronized with an Azure blob container. We used the FileInterceptor, which is powered by Multer, to upload files using the API.</p>
<p>As always, I hope you enjoyed the article and learned something new. If you want, you can also follow me on <a target="_blank" href="https://www.linkedin.com/in/destiny-erhabor">LinkedIn</a> or <a target="_blank" href="https://twitter.com/caesar_sage">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ SQL Aggregate Functions – How to GROUP BY in MySQL and PostgreSQL ]]>
                </title>
                <description>
                    <![CDATA[ In SQL, aggregate functions let you perform a calculation on multiple data and return a single value. That’s why they are called “aggregate” functions.  Those aggregate functions are AVG(), COUNT(), SUM(), MIN(), and MAX().  While making queries with... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/sql-aggregate-functions-how-to-group-by-in-mysql-and-postgresql/</link>
                <guid isPermaLink="false">66adf222db5636c0b30cba91</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ postgres ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kolade Chris ]]>
                </dc:creator>
                <pubDate>Thu, 01 Sep 2022 15:57:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/agg.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In SQL, aggregate functions let you perform a calculation on multiple data and return a single value. That’s why they are called “aggregate” functions. </p>
<p>Those aggregate functions are <code>AVG()</code>, <code>COUNT()</code>, <code>SUM()</code>, <code>MIN()</code>, and <code>MAX()</code>. </p>
<p>While making queries with the aggregate functions, you can also use them in combination with the <code>GROUP BY</code> clause and <code>HAVING</code> statement in any relational database – MySQL PostgreSQL, and others. </p>
<p>In this article, you will learn how to use aggregate functions on their own and with the <code>GROUP BY</code> clause and <code>HAVING</code> statement.</p>
<h2 id="heading-what-well-cover">What We'll Cover</h2>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-use-aggregate-functions">How to Use Aggregate Functions</a></li>
<li><a class="post-section-overview" href="#heading-syntax-of-aggregate-functions">Syntax of Aggregate Functions</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-avg-aggregate-function">How to Use the <code>AVG()</code> Aggregate Function</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-use-the-avg-function-with-group-by-and-having">How to use the <code>AVG()</code> Function with <code>GROUP BY</code> and <code>HAVING</code></a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-count-aggregate-function">How to Use the <code>COUNT()</code> Aggregate Function</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-use-count-with-group-by-and-having">How to Use <code>COUNT()</code> with <code>GROUP BY</code> and <code>HAVING</code></a>  </li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-max-aggregate-function">How to Use the <code>MAX()</code> Aggregate Function</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-use-max-with-group-by-and-having">How to Use <code>MAX()</code> with <code>GROUP BY</code> and <code>HAVING</code></a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-min-aggregate-function">How to Use the <code>MIN()</code> Aggregate Function</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-use-min-with-group-by-and-having">How to Use <code>MIN()</code> with <code>GROUP BY</code> and <code>HAVING</code> </a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-sum-aggregate-function">How to Use the <code>SUM()</code> Aggregate Function</a><ul>
<li><a class="post-section-overview" href="#heading-how-to-use-sum-with-group-by-and-having">How to Use <code>SUM()</code> with <code>GROUP BY</code> and <code>HAVING</code></a>  </li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-how-to-use-aggregate-functions">How to Use Aggregate Functions</h2>
<p>To show you how the aggregate functions work, I’ll be working with an <code>employees</code> table in an <code>employees_data</code> database.</p>
<p>Running <code>SELECT * FROM employees</code> got me the following:
<img src="https://www.freecodecamp.org/news/content/images/2022/09/ss1.png" alt="ss1" width="600" height="400" loading="lazy"></p>
<h2 id="heading-syntax-of-aggregate-functions">Syntax of Aggregate Functions</h2>
<p>The syntax for working with aggregate functions looks like this:</p>
<pre><code class="lang-sql">aggregate_function(MODIFIER | expression)
</code></pre>
<ul>
<li>the aggregate function could be <code>AVG</code>, <code>COUNT</code>, <code>MAX</code>, <code>MIN</code>, or <code>SUM</code></li>
<li>the modifier could be all the values or the values in a particular column</li>
</ul>
<p>This syntax would make more sense in practice, so let’s get to use it with the aggregate functions.</p>
<h2 id="heading-how-to-use-the-avg-aggregate-function">How to Use the <code>AVG()</code> Aggregate Function</h2>
<p>The <code>AVG()</code> aggregate function gets the total number of data and calculates their average.</p>
<p>I was able to get the average wage paid to the employees this way:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">AVG</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss2.png" alt="ss2" width="600" height="400" loading="lazy"> </p>
<p>The query below gets the average wage of junior developers:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">AVG</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Junior dev"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss3.png" alt="ss3" width="600" height="400" loading="lazy"> </p>
<h3 id="heading-how-to-use-the-avg-function-with-group-by-and-having">How to use the <code>AVG()</code> Function with <code>GROUP BY</code> and <code>HAVING</code></h3>
<p>You can get the average number of entries (rows) in a particular column with the <code>GROUP BY</code> clause and <code>HAVING</code> statement. This means you have to combine those two with <code>AVG()</code>.</p>
<p>For instance, I was able to get the average wage paid to employees in each row with this query:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">AVG</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss4.png" alt="ss4" width="600" height="400" loading="lazy"> </p>
<p>I was also able to get the average wage of senior developers by using the HAVING statement:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">AVG</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
<span class="hljs-keyword">HAVING</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Senior dev"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss5.png" alt="ss5" width="600" height="400" loading="lazy"> </p>
<h2 id="heading-how-to-use-the-count-aggregate-function">How to Use the <code>COUNT()</code> Aggregate Function</h2>
<p><code>COUNT()</code> returns the number of rows in a table based on the condition (or filter) you apply.</p>
<p>For example, to get the total number of rows, I ran the query below:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(*) 
<span class="hljs-keyword">FROM</span> employees
</code></pre>
<p>And I got 20:
<img src="https://www.freecodecamp.org/news/content/images/2022/09/ss6.png" alt="ss6" width="600" height="400" loading="lazy"> </p>
<p>To get the total number of employees from the USA, I ran the query below:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(*) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">WHERE</span> country = <span class="hljs-string">"USA"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss7.png" alt="ss7" width="600" height="400" loading="lazy"> </p>
<p>And to get the employees who are technical writers, I did this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(*) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Tech Writer"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss8.png" alt="ss8" width="600" height="400" loading="lazy"> </p>
<h3 id="heading-how-to-use-count-with-group-by-and-having">How to Use <code>COUNT()</code> with <code>GROUP BY</code> and <code>HAVING</code></h3>
<p>In a large database, you can use the <code>GROUP BY</code> clause and <code>HAVING</code> statement in combination with COUNT() to get the total number of entries (rows) in a particular column.</p>
<p>In the database I’m using in this article, I was able to get the total number of employees in each row with the GROUP BY clause:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">COUNT</span>(*) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss9.png" alt="ss9" width="600" height="400" loading="lazy"> </p>
<p>To get the number of only the employees that are senior developers, I attached <code>HAVING role = "Senior dev"</code> to the query:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">COUNT</span>(*) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
<span class="hljs-keyword">HAVING</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Senior dev"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss10.png" alt="ss10" width="600" height="400" loading="lazy"> </p>
<h2 id="heading-how-to-use-the-max-aggregate-function">How to Use the <code>MAX()</code> Aggregate Function</h2>
<p>The <code>MAX()</code> function returns the maximum value within non-NULL values. This means it would ignore fields that are empty and return the highest value within those that are not empty. </p>
<p>For example, to get the highest wage in the <code>employees</code> table, I used the <code>MAX()</code> function like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MAX</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss11.png" alt="ss11" width="600" height="400" loading="lazy"> </p>
<p>To get the maximum wage for mid-level developers, I used the <code>WHERE</code> statement:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MAX</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Mid level dev"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss12.png" alt="ss12" width="600" height="400" loading="lazy"> </p>
<h3 id="heading-how-to-use-max-with-group-by-and-having">How to Use <code>MAX()</code> with <code>GROUP BY</code> and <code>HAVING</code></h3>
<p>To get the maximum wage in each role, the <code>GROUP BY</code> clause comes in handy:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">MAX</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss13.png" alt="ss13" width="600" height="400" loading="lazy"> </p>
<p>And to get the maximum wage in a particular role, combining the HAVING statement with the <code>GROUP BY</code> clause gets it done: </p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">MAX</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
<span class="hljs-keyword">HAVING</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Tech writer"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss14.png" alt="ss14" width="600" height="400" loading="lazy"> </p>
<h2 id="heading-how-to-use-the-min-aggregate-function">How to Use the <code>MIN()</code> Aggregate Function</h2>
<p>The <code>MIN()</code> function is the opposite of the <code>MAX()</code> function – it returns the minimum value within non-NULL values. </p>
<p>For example, I got the lowest wage on the <code>employees</code> table this way:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MIN</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss15.png" alt="ss15" width="600" height="400" loading="lazy"> </p>
<h3 id="heading-how-to-use-min-with-group-by-and-having">How to Use <code>MIN()</code> with <code>GROUP BY</code> and <code>HAVING</code></h3>
<p>Again, to get the minimum wage in each role, the <code>GROUP BY</code> clause can get it done:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">MIN</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss16.png" alt="ss16" width="600" height="400" loading="lazy"> </p>
<p>And to get the minimum wage of a particular role, the <code>HAVING</code> statement and <code>GROUP BY</code> clause are what to use:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">MIN</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
<span class="hljs-keyword">HAVING</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Junior dev"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss17.png" alt="ss17" width="600" height="400" loading="lazy"> </p>
<h2 id="heading-how-to-use-the-sum-aggregate-function">How to Use the <code>SUM()</code> Aggregate Function</h2>
<p>The SUM() aggregate function adds the number of entries in a column based on the filter applied.</p>
<p>The query below gets the total number of wages paid to employees:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">SUM</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss18.png" alt="ss18" width="600" height="400" loading="lazy"> </p>
<h3 id="heading-how-to-use-sum-with-group-by-and-having">How to Use SUM() with <code>GROUP BY</code> and <code>HAVING</code></h3>
<p>To get the sum of the total wages paid for employees in each role, I selected the role, used <code>SUM()</code> on the wages, and grouped them by the role:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">SUM</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss19.png" alt="ss19" width="600" height="400" loading="lazy"> </p>
<p>To get the total wages paid to technical writers only, I used the <code>HAVING</code> statement:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">role</span>, <span class="hljs-keyword">SUM</span>(wage) 
<span class="hljs-keyword">FROM</span> employees
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">role</span>
<span class="hljs-keyword">HAVING</span> <span class="hljs-keyword">role</span> = <span class="hljs-string">"Tech Writer"</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ss20.png" alt="ss20" width="600" height="400" loading="lazy"> </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article took you through what aggregate functions are in SQL, their syntax, and how to use them. </p>
<p>In addition, you also learned how to use aggregate functions with the <code>GROUP BY</code> clause, <code>HAVING</code>, and <code>WHERE</code> statements.</p>
<p>If you want to learn how the <code>HAVING</code> statement works, you should read <a target="_blank" href="https://www.freecodecamp.org/news/sql-having-how-to-group-and-count-with-a-having-statement/">this article</a> I wrote on it.</p>
<p>Thank you for reading.</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 Join MySQL and Postgres in a Live Materialized View ]]>
                </title>
                <description>
                    <![CDATA[ By Bobby Iliev When you're working on a project that consists of a lot of microservices, it'll likely also include multiple databases.  For example, you might have a MySQL database and a PostgreSQL database, both running on separate servers. Usually,... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-join-mysql-and-postgres-in-a-live-materialized-view/</link>
                <guid isPermaLink="false">66d45dd5182810487e0ce0f1</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ postgres ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 03 May 2022 15:18:26 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/how-to-join-mysql-and-postgres-in-a-live-materialized-view2.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Bobby Iliev</p>
<p>When you're working on a project that consists of a lot of microservices, it'll likely also include multiple databases. </p>
<p>For example, you might have a <a target="_blank" href="https://www.mysql.com">MySQL database</a> and a <a target="_blank" href="https://www.postgresql.org">PostgreSQL database</a>, both running on separate servers.</p>
<p>Usually, to join the data from the two databases, you would have to introduce a new microservice that would join the data together. But this would increase the complexity of the system.</p>
<p>In this tutorial, we will be using Materialize to join MySQL and Postgres in a live materialized view. We'll then be able to query that directly and get results back from both databases in real-time using standard SQL.  </p>
<p><a target="_blank" href="https://github.com/MaterializeInc/materialize/">Materialize</a> is a source-available streaming database written in Rust that maintains the results of a SQL query (a materialized view) in memory as the data changes. </p>
<p>The tutorial includes a demo project which you can start using <code>docker-compose</code>.</p>
<p>The demo project that we are going to use will monitor the orders on our mock website. It'll generate events that could, later on, be used to send notifications when a cart has been abandoned for a long time.</p>
<p>The architecture of the demo project is as follows:</p>
<p><img src="https://user-images.githubusercontent.com/21223421/143267063-2dbb1ec2-d48d-4ba5-8da8-f0d9ac1404e4.png" alt="mz-abandoned-cart-demo" width="1382" height="706" loading="lazy"></p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>All of the services that we will be using in the demo will run inside Docker containers, that way you will not have to install any additional services on your laptop or server rather than Docker and Docker Compose.  </p>
<p>In case that you don't have Docker and Docker Compose already installed, you can follow the official instructions on how to do that here:</p>
<ul>
<li><a target="_blank" href="https://docs.docker.com/get-docker/">Install Docker</a></li>
<li><a target="_blank" href="https://docs.docker.com/compose/install/">Install Docker Compose</a></li>
</ul>
<h2 id="heading-overview">Overview</h2>
<p>As shown in the diagram above, we will have the following components:</p>
<ul>
<li>A mock service to continually generate orders.</li>
<li>The orders will be stored in a <strong>MySQL database</strong>.</li>
<li>As the database writes occur, <strong>Debezium</strong> streams the changes out of MySQL to a <strong>Redpanda</strong> topic.</li>
<li>We'll also have a <strong>Postgres</strong> database where we can get our users.</li>
<li>We'll then ingest this Redpanda topic into <strong>Materialize</strong> directly along with the users from the Postgres database.</li>
<li>In Materialize we'll join our orders and users together, do some filtering, and create a materialized view that shows the abandoned cart information.</li>
<li>We will then create a sink to send the abandoned cart data out to a new Redpanda topic.</li>
<li>At the end we will use <strong>Metabase</strong> to visualize the data.</li>
<li>You could, later on, use the information from that new topic to send out notifications to your users and remind them that they have an abandoned cart.</li>
</ul>
<p>As a side note here, you would be perfectly fine using Kafka instead of Redpanda. I just like the simplicity that Redpanda brings to the table, as you can run a single Redpanda instance instead of all of the Kafka components.</p>
<h2 id="heading-how-to-run-the-demo">How to Run the Demo</h2>
<p>First, start by cloning the repository:</p>
<pre><code>git clone https:<span class="hljs-comment">//github.com/bobbyiliev/materialize-tutorials.git</span>
</code></pre><p>After that you can access the directory:</p>
<pre><code>cd materialize-tutorials/mz-join-mysql-and-postgresql
</code></pre><p>Let's start by first running the Redpanda container:</p>
<pre><code>docker-compose up -d redpanda
</code></pre><p>Build the images:</p>
<pre><code>docker-compose build
</code></pre><p>Finally, start all of the services:</p>
<pre><code>docker-compose up -d
</code></pre><p>In order to Launch the Materialize CLI, you can run the following command:</p>
<pre><code>docker-compose run mzcli
</code></pre><p>This is just a shortcut to a Docker container with <code>postgres-client</code> pre-installed. If you already have <code>psql</code> you could run <code>psql -U materialize -h localhost -p 6875 materialize</code> instead.</p>
<h3 id="heading-how-to-create-a-materialize-kafka-source">How to Create a Materialize Kafka Source</h3>
<p>Now that you're in the Materialize CLI, let's define the <code>orders</code> tables in the <code>mysql.shop</code> database as Redpanda sources:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">SOURCE</span> orders
<span class="hljs-keyword">FROM</span> KAFKA BROKER <span class="hljs-string">'redpanda:9092'</span> TOPIC <span class="hljs-string">'mysql.shop.orders'</span>
<span class="hljs-keyword">FORMAT</span> AVRO <span class="hljs-keyword">USING</span> CONFLUENT <span class="hljs-keyword">SCHEMA</span> REGISTRY <span class="hljs-string">'http://redpanda:8081'</span>
ENVELOPE DEBEZIUM;
</code></pre>
<p>If you were to check the available columns from the <code>orders</code> source by running the following statement:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SHOW</span> <span class="hljs-keyword">COLUMNS</span> <span class="hljs-keyword">FROM</span> orders;
</code></pre>
<p>You would be able to see that, as Materialize is pulling the message schema data from the Redpanda registry, it knows the column types to use for each attribute:</p>
<pre><code class="lang-sql">    name      | nullable |   type
<span class="hljs-comment">--------------+----------+-----------</span>
 id           | f        | bigint
 user_id      | t        | bigint
 order_status | t        | integer
 price        | t        | numeric
 created_at   | f        | text
 updated_at   | t        | timestamp
</code></pre>
<h3 id="heading-how-to-create-materialized-views">How to Create Materialized Views</h3>
<p>Next, we will create our first Materialized View, to get all of the data from the <code>orders</code> Redpanda source:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> orders_view <span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> orders;
</code></pre>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> abandoned_orders <span class="hljs-keyword">AS</span>
    <span class="hljs-keyword">SELECT</span>
        user_id,
        order_status,
        <span class="hljs-keyword">SUM</span>(price) <span class="hljs-keyword">as</span> revenue,
        <span class="hljs-keyword">COUNT</span>(<span class="hljs-keyword">id</span>) <span class="hljs-keyword">AS</span> total
    <span class="hljs-keyword">FROM</span> orders_view
    <span class="hljs-keyword">WHERE</span> order_status=<span class="hljs-number">0</span>
    <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-number">1</span>,<span class="hljs-number">2</span>;
</code></pre>
<p>You can now use <code>SELECT * FROM abandoned_orders;</code> to see the results:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> abandoned_orders;
</code></pre>
<p>For more information on creating materialized views, check out the <a target="_blank" href="https://materialize.com/docs/sql/create-materialized-view/">Materialized Views</a> section of the Materialize documentation.</p>
<h3 id="heading-how-to-create-a-postgres-source">How to Create a Postgres Source</h3>
<p>There are two ways to create a Postgres source in Materialize:</p>
<ul>
<li>Using Debezium just like we did with the MySQL source.</li>
<li>Using the Postgres Materialize Source, which allows you to connect Materialize direct to Postgres so you don't have to use Debezium.</li>
</ul>
<p>For this demo, we will use the Postgres Materialize Source just as a demonstration on how to use it, but feel free to use Debezium instead.</p>
<p>To create a Postgres Materialize Source run the following statement:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">SOURCE</span> <span class="hljs-string">"mz_source"</span> <span class="hljs-keyword">FROM</span> POSTGRES
<span class="hljs-keyword">CONNECTION</span> <span class="hljs-string">'user=postgres port=5432 host=postgres dbname=postgres password=postgres'</span>
PUBLICATION <span class="hljs-string">'mz_source'</span>;
</code></pre>
<p>A quick rundown of the above statement:</p>
<ul>
<li><code>MATERIALIZED</code>: Materializes the PostgreSQL source’s data. All of the data is retained in memory and makes sources directly selectable.</li>
<li><code>mz_source</code>: The name for the PostgreSQL source.</li>
<li><code>CONNECTION</code>: The PostgreSQL connection parameters.</li>
<li><code>PUBLICATION</code>: The PostgreSQL publication, containing the tables to be streamed to Materialize.</li>
</ul>
<p>Once we've created the PostgreSQL source, in order to be able to query the PostgreSQL tables, we would need to create views that represent the upstream publication’s original tables. </p>
<p>In our case, we only have one table called <code>users</code> so the statement that we would need to run is:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> VIEWS <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">SOURCE</span> mz_source (<span class="hljs-keyword">users</span>);
</code></pre>
<p>To see the available views execute the following statement:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SHOW</span> <span class="hljs-keyword">FULL</span> VIEWS;
</code></pre>
<p>Once that is done, you can query the new views directly:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;
</code></pre>
<p>Next, let's go ahead and create a few more views.</p>
<h3 id="heading-how-to-create-a-kafka-sink">How to Create a Kafka Sink</h3>
<p><a target="_blank" href="https://materialize.com/docs/sql/create-sink/">Sinks</a> let you send data from Materialize to an external source.</p>
<p>For this Demo, we will be using <a target="_blank" href="https://materialize.com/docs/third-party/redpanda/">Redpanda</a>.</p>
<p>Redpanda is Kafka API-compatible and Materialize can process data from it just as it would process data from a Kafka source.</p>
<p>Let's create a materialized view, that will hold all of the high volume unpaid orders:</p>
<pre><code class="lang-sql"> <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">VIEW</span> high_value_orders <span class="hljs-keyword">AS</span>
      <span class="hljs-keyword">SELECT</span>
        users.id,
        users.email,
        abandoned_orders.revenue,
        abandoned_orders.total
      <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
      <span class="hljs-keyword">JOIN</span> abandoned_orders <span class="hljs-keyword">ON</span> abandoned_orders.user_id = users.id
      <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> <span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>
      <span class="hljs-keyword">HAVING</span> revenue &gt; <span class="hljs-number">2000</span>;
</code></pre>
<p>As you can see, here we are actually joining the <code>users</code> view which is ingesting the data directly from our Postgres source, and the <code>abandond_orders</code> view which is ingesting the data from the Redpanda topic, together.</p>
<p>Let's create a Sink where we will send the data of the above materialized view:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> SINK high_value_orders_sink
    <span class="hljs-keyword">FROM</span> high_value_orders
    <span class="hljs-keyword">INTO</span> KAFKA BROKER <span class="hljs-string">'redpanda:9092'</span> TOPIC <span class="hljs-string">'high-value-orders-sink'</span>
    <span class="hljs-keyword">FORMAT</span> AVRO <span class="hljs-keyword">USING</span>
    CONFLUENT <span class="hljs-keyword">SCHEMA</span> REGISTRY <span class="hljs-string">'http://redpanda:8081'</span>;
</code></pre>
<p>Now if you were to connect to the Redpanda container and use the <code>rpk topic consume</code> command, you will be able to read the records from the topic.</p>
<p>However, as of the time being, we won’t be able to preview the results with <code>rpk</code> because it’s AVRO formatted. Redpanda would most likely implement this in the future, but for the moment, we can actually stream the topic back into Materialize to confirm the format.</p>
<p>First, get the name of the topic that has been automatically generated:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> topic <span class="hljs-keyword">FROM</span> mz_kafka_sinks;
</code></pre>
<p>Output:</p>
<pre><code class="lang-sql">                              topic
<span class="hljs-comment">-----------------------------------------------------------------</span>
 high-volume-orders-sink-u12-1637586945-13670686352905873426
</code></pre>
<p>For more information on how the topic names are generated check out the documentation <a target="_blank" href="https://materialize.com/docs/sql/create-sink/#kafka-sinks">here</a>.</p>
<p>Then create a new Materialized Source from this Redpanda topic:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MATERIALIZED</span> <span class="hljs-keyword">SOURCE</span> high_volume_orders_test
<span class="hljs-keyword">FROM</span> KAFKA BROKER <span class="hljs-string">'redpanda:9092'</span> TOPIC <span class="hljs-string">' high-volume-orders-sink-u12-1637586945-13670686352905873426'</span>
<span class="hljs-keyword">FORMAT</span> AVRO <span class="hljs-keyword">USING</span> CONFLUENT <span class="hljs-keyword">SCHEMA</span> REGISTRY <span class="hljs-string">'http://redpanda:8081'</span>;
</code></pre>
<p>Make sure to change the topic name accordingly!</p>
<p>Finally, query this new materialized view:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> high_volume_orders_test <span class="hljs-keyword">LIMIT</span> <span class="hljs-number">2</span>;
</code></pre>
<p>Now that you have the data in the topic, you can have other services connect to it and consume it and then trigger emails or alerts for example.</p>
<h2 id="heading-how-to-connect-metabase">How to Connect Metabase</h2>
<p>In order to access the <a target="_blank" href="https://materialize.com/docs/third-party/metabase/">Metabase</a> instance visit <code>http://localhost:3030</code> if you are running the demo locally or <code>http://your_server_ip:3030</code> if you are running the demo on a server. Then follow the steps to complete the Metabase setup.</p>
<p>Make sure to select Materialize as the source of the data.</p>
<p>Once ready you will be able to visualize your data just as you would with a standard PostgreSQL database.</p>
<h2 id="heading-how-to-stop-the-demo">How to Stop the Demo</h2>
<p>To stop all of the services, run the following command:</p>
<pre><code>docker-compose down
</code></pre><h2 id="heading-conclusion">Conclusion</h2>
<p>As you can see, this is a very simple example of how to use Materialize. You can use Materialize to ingest data from a variety of sources and then stream it to a variety of destinations.</p>
<h2 id="heading-helpful-resources">Helpful resources:</h2>
<ul>
<li><a target="_blank" href="https://materialize.com/docs/sql/create-source/postgres/"><code>CREATE SOURCE: PostgreSQL</code></a></li>
<li><a target="_blank" href="https://materialize.com/docs/sql/create-source/"><code>CREATE SOURCE</code></a></li>
<li><a target="_blank" href="https://materialize.com/docs/sql/create-views"><code>CREATE VIEWS</code></a></li>
<li><a target="_blank" href="https://materialize.com/docs/sql/select"><code>SELECT</code></a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ SQL Inner Join – How to Join 3 Tables in SQL and MySQL ]]>
                </title>
                <description>
                    <![CDATA[ When you're working with your database, you might need to put together data from a few different tables. This article will show you how. I have already written about SQL joins here and here, but let's take a moment to review how a join works first, a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/sql-inner-join-how-to-join-3-tables-in-sql-and-mysql/</link>
                <guid isPermaLink="false">66b0c3b0cde8fd3100cae199</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ilenia Magoni ]]>
                </dc:creator>
                <pubDate>Fri, 01 Apr 2022 18:19:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/pexels-pixabay-269399--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're working with your database, you might need to put together data from a few different tables. This article will show you how.</p>
<p>I have already written about SQL joins <a target="_blank" href="https://www.freecodecamp.org/news/sql-join-types-inner-join-vs-outer-join-example/">here</a> and <a target="_blank" href="https://www.freecodecamp.org/news/sql-left-join-example-join-statement-syntax/">here</a>, but let's take a moment to review how a join works first, and particularly the syntax specific to MySQL.</p>
<h2 id="heading-sql-join-statement">SQL Join Statement</h2>
<p>Join is a statement that lets you put together two tables, matching rows that are related to each other, and keeping only the rows that can be matched, not keeping unpaired rows.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> table1 
  <span class="hljs-keyword">INNER</span> <span class="hljs-keyword">JOIN</span> table2
  <span class="hljs-keyword">ON</span> table1.id = table2.id;
</code></pre>
<p>The <code>SELECT ... FROM</code> statement indicates which is the first table, then the second table name is written just after the <code>INNER JOIN</code> keywords. </p>
<p>How the two tables should be joined is written in the <code>ON</code> statement. In this case the two tables are joined using the relationship <code>table1.id = table2.id</code>.</p>
<p>It is possible to use multiple join statements together to join more than one table at the same time.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> *
  <span class="hljs-keyword">FROM</span> table1
  <span class="hljs-keyword">INNER</span> <span class="hljs-keyword">JOIN</span> table2
  <span class="hljs-keyword">ON</span> table1.id = table2.id
  <span class="hljs-keyword">INNER</span> <span class="hljs-keyword">JOIN</span> table3
  <span class="hljs-keyword">ON</span> table2.id = table3.id;
</code></pre>
<p>To do that you add a second <code>INNER JOIN</code> statement and a second <code>ON</code> statement to indicate the third table and the second relationship.</p>
<p>Let's talk a moment about the relationships you can have between tables and why you might want to join three tables together.</p>
<h2 id="heading-relationships-between-tables-in-sql">Relationships Between Tables in SQL</h2>
<p>When you have tables that are related to each other, their relationships could be one of various types.</p>
<h3 id="heading-one-to-many">one-to-many</h3>
<p>In a one-to-many kind of relationship, one row of the first table can be related to multiple rows of the second table.</p>
<p>In a relational database this can be implemented with the second table having a <code>first_table_id</code> column that says to which row of the first table that row is related.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/image-11.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-many-to-one">many-to-one</h3>
<p>In a many-to-one kind of relationship, one row of the first table can be related to one single row of the second table, and one row of the second table can be related to multiple rows of the first table.</p>
<p>In a relational database this can be implemented with the first table having a <code>second_table_id</code> column that says to which row of the second table that row is related.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/image-10.png" alt="Image" width="600" height="400" loading="lazy">
<em>Many-to-one</em></p>
<h3 id="heading-many-to-many">many-to-many</h3>
<p>In this case multiple rows are related to multiple rows.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/image-9.png" alt="Image" width="600" height="400" loading="lazy">
<em>Many-to-many</em></p>
<p>This kind of relationship can't be represent as is with SQL tables – you need to add a coupling table between the two tables so that only many-to-one and one-to-many relationships are present between tables. </p>
<p>Each row of the table in the middle represents one relationship between the rows of the left table and and the rows of the right table.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/image-12.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In practice in MySQL, that middle table will have a column for <code>first_table_id</code> and a column for <code>second_table_id</code>, with each combination being unique.</p>
<h2 id="heading-joining-sql-tables-in-practice">Joining SQL Tables in Practice</h2>
<p>Let's imagine we have an organization's database, where we have a table with teams (their name, and other identifing info), and a table with projects (name, progress, and so on).</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>id</td><td>team_name</td><td>specialty</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Banana Throwers</td><td>Bananas</td></tr>
<tr>
<td>2</td><td>Wood gnawers</td><td>Gnawing on wood</td></tr>
<tr>
<td>3</td><td>The Pink Elephants</td><td>Stomping on the ground</td></tr>
<tr>
<td>4</td><td>Fluffy potatoes</td><td>Working and sleeping</td></tr>
</tbody>
</table>
</div><div class="hn-table">
<table>
<thead>
<tr>
<td>id</td><td>project_name</td><td>progress</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>Dam building</td><td>Some more wood gnawing and ground stomping needed</td></tr>
<tr>
<td>2</td><td>Banana Cake</td><td>Someone is eating all the bananas</td></tr>
<tr>
<td>3</td><td>Sleep research</td><td>To much sleeping not enough research</td></tr>
</tbody>
</table>
</div><p>As a team can work on multiple projects, and a project can be worked on by multiple teams, there is also a third table that keeps track of team-project matches.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>project_id</td><td>group_id</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>2</td></tr>
<tr>
<td>1</td><td>3</td></tr>
<tr>
<td>2</td><td>1</td></tr>
<tr>
<td>3</td><td>1</td></tr>
<tr>
<td>3</td><td>2</td></tr>
<tr>
<td>3</td><td>3</td></tr>
<tr>
<td>3</td><td>4</td></tr>
</tbody>
</table>
</div><p>We can use a <code>JOIN</code> statement to put everything together when we need to view the info from the tables in a human readable way, like this:</p>
<pre><code class="lang-mysql">SELECT
  teams.team_name AS team_name,
  projects.project_name AS project_name
FROM TABLE teams
INNER JOIN matches
  ON teams.id = matches.team_id
INNER JOIN matches
  ON matches.project_id = projects.id
ORDER BY teams.id;
</code></pre>
<p>We choose which columns to show from each table with a <code>SELECT</code> statement.</p>
<p>We specify how the rows of the tables are to be combined with an <code>ON</code> statement.</p>
<p>And we order the rows in the way we prefer with an <code>ORDER BY</code> statement.</p>
<p>The <code>ON</code> statements <code>teams.id = matches.team_id</code> and <code>matches.projects_id = projects.id</code> mean that the rows are combined using the rows of the <code>matches</code> table. Each row of the output table has the project name and the team name combined using the pairs of project id and team id in the <code>matches</code> table.</p>
<p>The output table will look like below. </p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Team_name</td><td>Project_name</td></tr>
</thead>
<tbody>
<tr>
<td>Banana Throwers</td><td>Banana Cake</td></tr>
<tr>
<td>Banana Throwers</td><td>Sleep Research</td></tr>
<tr>
<td>Wood gnawers</td><td>Dam Bulding</td></tr>
<tr>
<td>Wood gnawers</td><td>Sleep Research</td></tr>
<tr>
<td>The Pink Elephants</td><td>Dam Building</td></tr>
<tr>
<td>The Pink Elephants</td><td>Dam Building</td></tr>
<tr>
<td>Fluffy potatoes</td><td>Sleep Research</td></tr>
</tbody>
</table>
</div><p>There is no column directly from the <code>matches</code> table. The <code>matches</code> table is not shown in the output but it is used as instructions for how to combine the rows of the <code>teams</code> and <code>projects</code> tables.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The <code>JOIN</code> statement lets you join together one or more tables. It has to be used in conjunction with the <code>ON</code> statement to determine the relationship between the rows of a table and the rows of a different table. </p>
<p>In this article you have learned how to use the <code>JOIN</code> statement to join together three different tables.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
