<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Carlos Santos</title>
    <description>The latest articles on Forem by Carlos Santos (@sanzocarlos).</description>
    <link>https://forem.com/sanzocarlos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F774629%2Fe427dc6c-c6bb-4c2c-ae74-095d4967a102.png</url>
      <title>Forem: Carlos Santos</title>
      <link>https://forem.com/sanzocarlos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sanzocarlos"/>
    <language>en</language>
    <item>
      <title>⛵ Laravel Sail - Windows 10 Installation</title>
      <dc:creator>Carlos Santos</dc:creator>
      <pubDate>Sun, 04 Sep 2022 23:31:22 +0000</pubDate>
      <link>https://forem.com/sanzocarlos/laravel-sail-windows-10-installation-1687</link>
      <guid>https://forem.com/sanzocarlos/laravel-sail-windows-10-installation-1687</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/laravel/sail" rel="noopener noreferrer"&gt;Laravel Sail&lt;/a&gt; is a light-weight command-line interface for interacting with Laravel's default Docker development environment. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience.&lt;/p&gt;

&lt;p&gt;At its heart, Sail is the &lt;code&gt;docker-compose.yml&lt;/code&gt; file and the &lt;code&gt;sail&lt;/code&gt; script that is stored at the root of your project. The &lt;code&gt;sail&lt;/code&gt; script provides a CLI with convenient methods for interacting with the Docker containers defined by the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Laravel Sail is supported on macOS, Linux, and Windows (via &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/about" rel="noopener noreferrer"&gt;WSL2&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Docker Desktop
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/desktop/install/windows-install/" rel="noopener noreferrer"&gt;https://docs.docker.com/desktop/install/windows-install/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the installation is complete go to your windows user directory (C:\Users\YOURUSER) and create a file named &lt;code&gt;.wslconfig&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;wsl2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="no"&gt;GB&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Limits&lt;/span&gt; &lt;span class="no"&gt;VM&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="no"&gt;WSL&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; 
&lt;span class="n"&gt;processors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Makes&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="no"&gt;WSL&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="no"&gt;VM&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;processors&lt;/span&gt;
&lt;span class="n"&gt;localhostForwarding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;specifying&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ports&lt;/span&gt; &lt;span class="n"&gt;bound&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;wildcard&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="no"&gt;WSL&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="no"&gt;VM&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;connectable&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="n"&gt;via&lt;/span&gt; &lt;span class="nl"&gt;localhost:&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="no"&gt;GB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need to restart your computer after this step.&lt;/p&gt;

&lt;h2&gt;
  
  
  WSL2 Configuration
&lt;/h2&gt;

&lt;p&gt;Install Ubuntu distribution and set it as default using Windows PowerShell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check list of available distributions&lt;/span&gt;
wsl &lt;span class="nt"&gt;--list&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;

wsl &lt;span class="nt"&gt;--set-default-version&lt;/span&gt; 2

&lt;span class="c"&gt;# Install Ubuntu-20.04 distribution&lt;/span&gt;
&lt;span class="c"&gt;# Don't forget to add a user with password on this step&lt;/span&gt;
wsl &lt;span class="nt"&gt;--install&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Ubuntu-20.04"&lt;/span&gt;

&lt;span class="c"&gt;# Set distribution as default&lt;/span&gt;
wsl &lt;span class="nt"&gt;-s&lt;/span&gt; Ubuntu-20.04

&lt;span class="c"&gt;# Start distribution&lt;/span&gt;
wsl &lt;span class="nt"&gt;-d&lt;/span&gt; Ubuntu-20.04
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;wsl --list --verbose&lt;/code&gt; at the end and the Ubuntu distribution should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83hl9z7wk0qy762faxou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83hl9z7wk0qy762faxou.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now make sure Docker Desktop settings look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2krdnvnifsmhvfvtghj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2krdnvnifsmhvfvtghj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipavq8elb7t9iay6pjte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipavq8elb7t9iay6pjte.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install PHP in Ubuntu 20.04 distribution.
&lt;/h2&gt;

&lt;p&gt;In Windows Power Shell run the command &lt;code&gt;wsl&lt;/code&gt; to start a new Ubuntu session. Once inside the Ubuntu terminal run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;apt&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="nl"&gt;ppa:&lt;/span&gt;&lt;span class="n"&gt;ondrej&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;
&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;apt&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;common&lt;/span&gt; &lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cli&lt;/span&gt;
&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;apt&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt; &lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gd&lt;/span&gt; &lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mbstring&lt;/span&gt; &lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;php8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;
&lt;span class="n"&gt;php&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Composer 2 should also be installed. Check version by running &lt;code&gt;composer -V&lt;/code&gt; . If it is not installed follow this installation guide: &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-composer-on-ubuntu-20-04-quickstart" rel="noopener noreferrer"&gt;https://www.digitalocean.com/community/tutorials/how-to-install-composer-on-ubuntu-20-04-quickstart&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now you should be able to run a laravel project with sail.&lt;/p&gt;

&lt;p&gt;Adding Sail to a Project&lt;/p&gt;

&lt;p&gt;Move to the project root directory and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;composer&lt;/span&gt; &lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="n"&gt;laravel&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sail&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;
&lt;span class="n"&gt;php&lt;/span&gt; &lt;span class="n"&gt;artisan&lt;/span&gt; &lt;span class="nl"&gt;sail:&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;
&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sail&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the container in a project with Sail already initialized (Existing docker-compose.yml):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;
&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sail&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes container start might fail and its related to permissions issues so be mindful of where to place your project files and give them the proper permissions. (or run it with sudo prefix)&lt;/p&gt;

&lt;h2&gt;
  
  
  📋 Related articles
&lt;/h2&gt;

&lt;p&gt;Laravel Sail installation guide&lt;/p&gt;

&lt;p&gt;&lt;a href="https://laravel.com/docs/9.x/installation#getting-started-on-windows" rel="noopener noreferrer"&gt;https://laravel.com/docs/9.x/installation#getting-started-on-windows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Laravel Sail usage guide&lt;/p&gt;

&lt;p&gt;&lt;a href="https://laravel.com/docs/9.x/sail#introduction" rel="noopener noreferrer"&gt;https://laravel.com/docs/9.x/sail#introduction&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>windows10</category>
      <category>sail</category>
      <category>php</category>
    </item>
    <item>
      <title>🔐 Laravel File Permissions Setup</title>
      <dc:creator>Carlos Santos</dc:creator>
      <pubDate>Sat, 21 May 2022 23:35:17 +0000</pubDate>
      <link>https://forem.com/sanzocarlos/laravel-file-permissions-setup-443j</link>
      <guid>https://forem.com/sanzocarlos/laravel-file-permissions-setup-443j</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Hi, I wanted to offer a brief and straight to the point way to setup your Laravel project permissions. This examples are like a work for all case. You will have to taken into consideration your server users, configurations, etc.&lt;br&gt;
‎‎&lt;br&gt;
There are basically two ways to setup your ownership and permissions. Either you give yourself ownership or you make the webserver the owner of all files.&lt;/p&gt;
&lt;h2&gt;
  
  
  ‏‏‎
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Web Server as owner  (Recommended for PROD)&lt;/strong&gt;&lt;br&gt;
Webserver as owner (the way most people do it, and the Laravel doc's way):  &lt;/p&gt;

&lt;p&gt;Assuming www-data (it could be something else) is your webserver user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data /path/to/your/laravel/root/directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do that, the webserver owns all the files, and is also the group, and you will have some problems uploading files or working with files via FTP, because your FTP client will be logged in as you, not your webserver, so add your user to the webserver user group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; www-data ubuntu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, this assumes your webserver is running as www-data (the Homestead default), and your user is ubuntu (it's vagrant if you are using Homestead).&lt;/p&gt;

&lt;p&gt;Then you set all your directories to 755 and your files to 644... SET file permissions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;find /path/to/your/laravel/root/directory &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set directory permissions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;find /path/to/your/laravel/root/directory &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;755 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you need to give read and write permissions to the webserver for storage, cache and any other directories the webserver needs to upload or write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chgrp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data storage bootstrap/cache  
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; ug+rwx storage bootstrap/cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ‏‏‎
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Your user as owner (Convenient for STAGING and DEV)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you prefer to own all the directories and files (it makes working with everything much easier), go to your Laravel root directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/html/laravel &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; assuming this is your current root directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‏‏‎ ‎&lt;br&gt;
If you do that, the webserver owns all the files, and is also the group, and you will have some problems uploading files or working with files via FTP, because your FTP client will be logged in as you, not your webserver, so add your user to the webserver user group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; www-data ubuntu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;:www-data &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you give your user and the webserver permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;664 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;  
&lt;span class="nb"&gt;sudo &lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;775 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‏‏‎&lt;br&gt;
Then you need to give read and write permissions to the webserver for storage, cache and any other directories the webserver needs to upload or write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chgrp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data storage bootstrap/cache  
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; ug+rwx storage bootstrap/cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ‏
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I hope you found this article helpful.&lt;/p&gt;

&lt;p&gt;If you have any feedback or found mistakes, please don’t hesitate to reach out to me.&lt;/p&gt;

&lt;p&gt;Credit to: Andres Felipe and bgies &lt;a href="https://stackoverflow.com/questions/30639174/how-to-set-up-file-permissions-for-laravel"&gt;answers&lt;/a&gt;.  &lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>NestJS ⚡ Multiple DB Setup with TypeORM</title>
      <dc:creator>Carlos Santos</dc:creator>
      <pubDate>Sat, 07 May 2022 03:37:50 +0000</pubDate>
      <link>https://forem.com/sanzocarlos/nestjs-multiple-db-setup-with-typeorm-31k9</link>
      <guid>https://forem.com/sanzocarlos/nestjs-multiple-db-setup-with-typeorm-31k9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hi, in this post I will describe how to setup and use multiple database connections with a simple example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.nestjs.com/techniques/database#multiple-databases"&gt;NestJS Documentation&lt;/a&gt; is great for the most part but it has some important omissions on the multiple databases section.&lt;/p&gt;

&lt;p&gt;I will proceed under the assumption that you already have created a NestJS application and have your 2 or more databases setup and ready to be connected to. Let's go straight to the point.&lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Let's get to it
&lt;/h2&gt;
&lt;h4&gt;
  
  
  Define DB connection options
&lt;/h4&gt;

&lt;p&gt;Following NestJS docs we will find the connection options example credentials directly set on code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;TypeOrmModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mysql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;synchronize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Instead of having credentials exposed on the code like that we should use an environment configuration file or a similar strategy.&lt;/p&gt;

&lt;p&gt;For this example we will make use of the &lt;code&gt;@nestjs/config&lt;/code&gt; package to get the database credentials from an environment file. You can find more information about how to install and use the config package at &lt;a href="https://docs.nestjs.com/techniques/configuration"&gt;NestJS Configuration Docs&lt;/a&gt;. It is a simple process.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;ConfigModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;isGlobal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;TypeOrmModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forRootAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ConfigModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mssql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIN_DB_HOST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIN_DB_PORT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
          &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIN_DB_DATABASE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIN_DB_USERNAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIN_DB_PASSWORD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIN_DB_SCHEMA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;synchronize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We don't need to define a connection name four our first connection. It will be named 'default' by default 😬. For any other defined connections we will need to provide a name.&lt;/p&gt;

&lt;p&gt;NestJS docs tells us that the &lt;code&gt;name&lt;/code&gt; property should be added inside the options object, at the same level as host, port, etc.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;TypeOrmModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_db_host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;TypeOrmModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;albumsConnection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;album_db_host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Album&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, because we are getting our credentials from the &lt;code&gt;.env&lt;/code&gt; file and using &lt;code&gt;forRootAsync&lt;/code&gt; &lt;strong&gt;we need to add the connection&lt;/strong&gt; &lt;code&gt;name&lt;/code&gt; &lt;strong&gt;property  outside of the &lt;code&gt;useFactory&lt;/code&gt; function&lt;/strong&gt; 👀. This is a really important detail that is not mentioned anywhere in the docs.&lt;/p&gt;

&lt;p&gt;The final &lt;code&gt;app.module.ts&lt;/code&gt; file should look like this.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h4&gt;
  
  
  Entities
&lt;/h4&gt;

&lt;p&gt;We have created two entities for the sake of this excercise. Customer entity will use the main db connection and AccessLog entity will use the secondary db connection. For more information about TypeOrm Entity and Repository pattern check out &lt;a href="https://docs.nestjs.com/techniques/database"&gt;NestJS TypeOrm Docs&lt;/a&gt; and &lt;a href="https://typeorm.io/entities"&gt;TypeOrm Docs&lt;/a&gt;.&lt;br&gt;
 &lt;/p&gt;
&lt;h4&gt;
  
  
  Use Main DB Connection
&lt;/h4&gt;

&lt;p&gt;In order to have access to the Customer entity TypeOrm functionality we need to import TypeOrm into the Customer module. &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And this is how we use it in the Customer service.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Use Secondary DB Connection
&lt;/h4&gt;

&lt;p&gt;As we did for the main connection and the Customer entity we need to import TypeOrm into the AccessLog module, but this time we will also need to pass a connection name to the &lt;code&gt;TypeOrmModule.forFeature([Entities, ...], connectionName)&lt;/code&gt; method.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And this is how we use it in the AccessLog service.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
 

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;✅ We managed to setup a multiple db configuration with credentials values coming from an environment file.&lt;br&gt;
✅ Defined what connection should each entity use.&lt;br&gt;
✅ With the knowledge of how to add a secondary connection we should be able to add many more as needed.&lt;br&gt;
✅ Learned about the little gotchas when initializing TypeOrm asynchronously.&lt;/p&gt;

&lt;p&gt;I really hope this article was helpful.&lt;/p&gt;

&lt;p&gt;If you have any feedback or found mistakes, please don’t hesitate to reach out to me.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>javascript</category>
      <category>database</category>
      <category>typeorm</category>
    </item>
    <item>
      <title>Laravel Mailtrap Setup</title>
      <dc:creator>Carlos Santos</dc:creator>
      <pubDate>Sat, 18 Dec 2021 21:57:19 +0000</pubDate>
      <link>https://forem.com/sanzocarlos/laravel-mailtrap-setup-160p</link>
      <guid>https://forem.com/sanzocarlos/laravel-mailtrap-setup-160p</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2207hov30vj3u417628.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2207hov30vj3u417628.png" alt="Laravel Mailtrap Setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;How will this email look? Will it look nice on mobile? Is the email data correct? You can test all that and more with Mailtrap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mailtrap.io/" rel="noopener noreferrer"&gt;Mailtrap&lt;/a&gt; is a testing tool designed to trap any emails coming out of your application. Setting up Mailtrap is a must for your development environment, this way you can test your application functionalities without worrying about spamming real users.&lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Mailtrap Account
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a href="https://mailtrap.io/" rel="noopener noreferrer"&gt;https://mailtrap.io/&lt;/a&gt; and sign up.&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once you are logged in you will see a menu with 3 options; Inboxes, API, and Billing. Go to Inboxes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z2dyms6uj5ixc9x98fq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z2dyms6uj5ixc9x98fq.png" alt="Mailtrap Inboxes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;On the Inboxes page you will see a list of your inboxes and you will also have the option of creating a new project or creating a new inbox. We will use the default inbox for this setup.&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the inbox and on SMTP Settings select Integrations -&amp;gt; Laravel.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetv476mwsy7rx3c9x25z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetv476mwsy7rx3c9x25z.png" alt="Mailtrap Inbox Settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here you will see the .env variables you need to copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5h0vacnpoyd34ykitli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5h0vacnpoyd34ykitli.png" alt="Mailtrap Laravel Env"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Laravel Configuration
&lt;/h2&gt;

&lt;p&gt;Add (or replace) mail environment variables from Mailtrap to your application .env file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=YOUR_INBOX_USERNAME
MAIL_PASSWORD=YOUR_INBOX_PASSWORD
MAIL_ENCRYPTION=tls

# Also
MAIL_FROM_ADDRESS=no-reply@test.local
MAIL_FROM_NAME="Mailtrap Test App"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Test
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Create simple test mailable with markdown.&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;php artisan make:mail MailtrapTest --markdown=emails.test&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;App\Mail\MailtrapTest.php&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class MailtrapTest extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this-&amp;gt;markdown('emails.test');
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;resources\views\emails\test.blade.php (Markdown file)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@component('mail::message')
# TEST EMAIL

The cake is a lie

Thanks,&amp;lt;br&amp;gt;
{{ config('app.name') }}
@endcomponent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add test route&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...

Route::get('/mailtrap/test', function () {
    // Using a temp email. 
    // This way we can test that emails are really trapped.
    $testEmail = 'rognodugni@vusra.com';

    return Mail::to($testEmail)-&amp;gt;send(new MailtrapTest());
});

...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now hit test URL. You should have a new email on your Mailtrap inbox and you should not have any email from your application on the test email inbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5xq8bjk5qr48v4alfal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5xq8bjk5qr48v4alfal.png" alt="Mailtrap Inbox"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Mailtrap is a very useful tool for development and it's super easy to setup with Laravel. You can find more information at &lt;a href="https://mailtrap.io/blog/send-email-in-laravel/" rel="noopener noreferrer"&gt;https://mailtrap.io/blog/send-email-in-laravel/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this article was helpful.&lt;/p&gt;

&lt;p&gt;If you have any feedback or found mistakes, please don’t hesitate to reach out to me.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>mailtrap</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>✉ Laravel Twilio SMS Implementation</title>
      <dc:creator>Carlos Santos</dc:creator>
      <pubDate>Wed, 15 Dec 2021 01:54:24 +0000</pubDate>
      <link>https://forem.com/sanzocarlos/laravel-twilio-sms-implementation-2j19</link>
      <guid>https://forem.com/sanzocarlos/laravel-twilio-sms-implementation-2j19</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96ieis37fjz3de2iys7b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96ieis37fjz3de2iys7b.png" alt="Laravel Twilio SMS Implementation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Twilio integrations have been a lot more common than I thought during the last few years. So I wanted to write the article I wish I have found the first time a had to do it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twilio.com/docs/sms/api" rel="noopener noreferrer"&gt;Twilio's SMS API&lt;/a&gt; is a flexible building block that can take you from sending your first text message all the way to sending and receiving millions.&lt;br&gt;
In this article, I will talk about how to set up a Laravel application Twilio configuration and start sending SMS.&lt;/p&gt;

&lt;p&gt;After that, we will dig deeper into how to keep track of the status of sent messages and keep a local updated representation of Twilio messages.&lt;/p&gt;

&lt;p&gt;Finally, we will prepare the application to receive notifications every time your defined Twilio phone number gets a message and then store the received message locally.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Configuration and sending the first SMS
&lt;/h2&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;1. Get Twilio Credentials and Phone Number&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftv7ngvhp14v6q83ztzap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftv7ngvhp14v6q83ztzap.png" alt="Twilio Credentials"&gt;&lt;/a&gt;&lt;br&gt;
Keep in mind that a trial phone number can only send SMS to verified phones. This means you will have to add the phone number you intend to send messages to a verified numbers list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi40dl4s2iguthtlpand2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi40dl4s2iguthtlpand2.png" alt="Twilio Verified Numbers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;2. Add Twilio Credentials to Laravel configuration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;.env file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TWILIO_SID=YOUR_ACCOUNT_SID
TWILIO_AUTH_TOKEN=YOUR_ACCOUNT_AUTH_TOKEN
TWILIO_FROM_NUMBER=YOUR_ACCOUNT_FROM_NUMBER(e.g. +1XXXXXXXXXX)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;/config/app.php&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'twilio' =&amp;gt; [
    'sid' =&amp;gt; env('TWILIO_SID'),
    'auth_token' =&amp;gt; env('TWILIO_AUTH_TOKEN'),
    'from_number' =&amp;gt; env('TWILIO_FROM_NUMBER')
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt; &lt;br&gt;
&lt;strong&gt;3. Install Twilio SDK for PHP&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;composer require twilio/sdk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;4. Create Twilio Service Class&lt;/strong&gt;&lt;br&gt;
It is convenient to handle all Twilio SMS operations from a single class that is globally available. So every time you send a message you will do it through this service.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;Remember to bind the service so it is globally accessible. In this case, we will create a provider CustomServiceProvider and bind TwilioService in the boot function.&lt;br&gt;&lt;br&gt;
&lt;code&gt;php artisan make:provider CustomServiceProvider&lt;/code&gt;&lt;br&gt;&lt;/p&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;And then add &lt;strong&gt;App\Providers\CustomServiceProvider::class&lt;/strong&gt; into the $providers array on config/app.php.&lt;br&gt;&lt;br&gt;
Now the TwilioService should be available and we can test it from anywhere.&lt;br&gt;&lt;br&gt;
Also not required, but it will be convenient to add a new channel at &lt;strong&gt;/config/logging.php&lt;/strong&gt; to log Twilio operations, at least for debugging.&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
'twilio' =&amp;gt; [
    'driver' =&amp;gt; 'single',
    'path' =&amp;gt; storage_path('logs/twilio.log'),
    'level' =&amp;gt; 'debug',
],
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt; &lt;br&gt;
&lt;strong&gt;5. Create Twilio SMS Controller.&lt;/strong&gt;&lt;br&gt;
If you only care about sending messages then you don't really need to create a controller. You can test and use the TwilioService sendMessage function from anywhere.&lt;/p&gt;

&lt;p&gt;For this implementation, we will use this controller to provide a sendTest function. Later it will also be used to handle &lt;strong&gt;Twilio SMS Status Callback&lt;/strong&gt; requests and &lt;strong&gt;Twilio SMS Received&lt;/strong&gt; requests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php artisan make:controller TwilioSmsController&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Add testing endpoint to your API routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route::any(
'/twilio/send-test',
[TwilioSmsController::class, 'sendTest'])
-&amp;gt;name('twilio.send-test');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You are ready to make your first test. Hopefully, everything goes right on the first try. If not, you will get a debug error message.&lt;/p&gt;

&lt;p&gt;Common errors you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invalid number. The provided To number must have &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fwww.twilio.com%2Fdocs%2Fglossary%2Fwhat-e164" rel="noopener noreferrer"&gt;E.164&lt;/a&gt; formatting.&lt;/li&gt;
&lt;li&gt;To phone number not verified. If you are using a trial phone number then you can only send SMS to &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fsupport.twilio.com%2Fhc%2Fen-us%2Farticles%2F223180048-Adding-a-Verified-Phone-Number-or-Caller-ID-with-Twilio" rel="noopener noreferrer"&gt;verified numbers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/r/?url=https%3A%2F%2Fwww.twilio.com%2Fdocs%2Fapi%2Ferrors%2F21408%3Fdisplay%3Dembedded" rel="noopener noreferrer"&gt;Permission to send an SMS has not been enabled for the region indicated by the 'To' number&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it! 🎉 If you only need to send messages then you are ready to go.&lt;/p&gt;

&lt;p&gt;If you want to keep track of the messages you sent and also be able to receive messages in your application then keep reading 👀.&lt;/p&gt;
&lt;h2&gt;
  
  
   
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Track Twilio SMS status changes
&lt;/h2&gt;

&lt;p&gt;When we make a send SMS request to Twilio the message has to go through a &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fsupport.twilio.com%2Fhc%2Fen-us%2Farticles%2F223134347-What-are-the-Possible-SMS-and-MMS-Message-Statuses-and-What-do-They-Mean-" rel="noopener noreferrer"&gt;sequence of statuses&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accepted&lt;/li&gt;
&lt;li&gt;queued&lt;/li&gt;
&lt;li&gt;sending&lt;/li&gt;
&lt;li&gt;sent&lt;/li&gt;
&lt;li&gt;delivery_unknown&lt;/li&gt;
&lt;li&gt;delivered&lt;/li&gt;
&lt;li&gt;undelivered&lt;/li&gt;
&lt;li&gt;failed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The response we get from making a successful send SMS request will tell us that the message status is &lt;strong&gt;queued&lt;/strong&gt;. This just means that Twilio accepted the request and it was added to the queue of messages to send, but we don't know if it was actually sent.&lt;/p&gt;

&lt;p&gt;If we want to keep track of an SMS status we have to provide a &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fsupport.twilio.com%2Fhc%2Fen-us%2Farticles%2F360008989454-Tracking-the-Delivery-Status-of-an-Outbound-Twilio-SMS-or-MMS-Message" rel="noopener noreferrer"&gt;statusCallback&lt;/a&gt; parameter on each request. The statusCallback will be the URL to a webhook in our application that will be prepared to receive requests from Twilio.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;1. Create TwilioSms Model and TwilioSmsLog Model.&lt;/strong&gt;&lt;br&gt;
TwilioSms will represent a Twilio SMS in our application and TwilioSmsLog will represent each event related to an SMS (status change, error, etc).&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:model TwilioSms -m
php artisan make:model TwilioSmsLog -m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Models&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Migrations&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt; &lt;br&gt;
&lt;strong&gt;2. Create status callback webhook route and implement Twilio Request Validation middleware.&lt;/strong&gt;&lt;br&gt;
Add new webhook url to api routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Twilio statusCallBack webhook
 * is-twilio-request middleware makes sure only twilio has access to this route
 */
Route::any('/twilio/webhook/status-changed', [TwilioController::class, 'statusChanged'])-&amp;gt;middleware(['is-twilio-request'])-&amp;gt;name('api.twilio.status-changed');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notice that we are using the middleware &lt;strong&gt;is-twilio-request&lt;/strong&gt;. This route will be open to the public so we need to make sure that it only serves valid requests from Twilio. We can accomplish that by using the RequestValidator method provided by the Twilio PHP SDK.&lt;/p&gt;

&lt;p&gt;Create Middleware&lt;br&gt;
&lt;code&gt;php artisan make:middleware TwilioRequestIsValid&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Add the new middleware to app/Http/Kernel.php $routeMiddleware array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    ...
    'is-twilio-request' =&amp;gt; \App\Http\Middleware\TwilioRequestIsValid::class,
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt; &lt;br&gt;
&lt;strong&gt;3. Update Twilio Service. Add status callback, create TwilioSmsModel on send and log.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TwilioService final version:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now every time we make a sendMessage request the following will happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add callback URL to sendMessage request.&lt;/li&gt;
&lt;li&gt;If the request was successful create TwilioSms record on DB.&lt;/li&gt;
&lt;li&gt;Create TwilioSmsLog.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Created message.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foufbeit656idc0ifjzy1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foufbeit656idc0ifjzy1.png" alt="Created message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Created log.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5uuxsqcvp091spwjmkz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5uuxsqcvp091spwjmkz.png" alt="Created log"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;4. Handle Twilio Status Changed Request.&lt;/strong&gt;&lt;br&gt;
Add statusChanged Method to TwilioSmsController. The logic is just an example of implementation. You can make any adjustment as long as you &lt;strong&gt;return response(['success' =&amp;gt; true], 200)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In summary, we log every request and if we can match the request with an SMS in our application and the request status is different from the current TwilioSms status we update it.&lt;/p&gt;

&lt;p&gt;TwilioSmsController with status changed method:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;With the webhook in place, we will start to see our messages go through different states. It usually just takes a few seconds to go from queued to sent.&lt;/p&gt;

&lt;p&gt;It will look like this in the database.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pm4v44trvmemsoazo42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pm4v44trvmemsoazo42.png" alt="logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it for it tracking SMS status changes and keeping an updated representation of each Twilio SMS sent by the application. The next step is to receive and store messages.&lt;/p&gt;
&lt;h2&gt;
  
  
   
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Receive SMS
&lt;/h2&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;1. Create messageReceived method on TwilioSmsController.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TwilioSmsController final version:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt; &lt;br&gt;
&lt;strong&gt;2. Add webhook route.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...
/**
* Twilio message received webhook
* is-twilio-request middleware makes sure only twilio has access to this route
*/
Route::any('/twilio/webhook/message-received', [TwilioSmsController::class, 'messageReceived'])
-&amp;gt;middleware(['is-twilio-request'])
-&amp;gt;name('api.twilio.message-received');
...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;br&gt;
&lt;strong&gt;3. Add webhook URL to a phone number in Twilio console.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Find your phone number details page.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3jf4lii8wnyi04vpztz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3jf4lii8wnyi04vpztz.png" alt="console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the phone number you want to manage and then find the Messaging section on the details page. Here you can define a “&lt;strong&gt;message comes in&lt;/strong&gt;” action. Select webhook and input your webhook URL.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfwgoqxy856esd6q7337.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfwgoqxy856esd6q7337.png" alt="message webhook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save and you are all set to start receiving SMS in your application.&lt;/p&gt;

&lt;p&gt;Whenever a message comes in we are going to create a TwilioSms record and a TwilioSmsLog record.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxn3ypv9tjfzz4ueosee7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxn3ypv9tjfzz4ueosee7.png" alt="sms"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg12u0dt0i8i5qmow0zrh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg12u0dt0i8i5qmow0zrh.png" alt="log"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
   
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We accomplished:&lt;/strong&gt;&lt;br&gt;
✅ Send SMS.&lt;br&gt;
✅ Track Sent SMS Status.&lt;br&gt;
✅ Keep local updated representation of a Twilio SMS.&lt;br&gt;
✅ Every time a Twilio phone receives a message we get it and store it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possible gotchas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test with phone numbers in E.164 formatting.&lt;/li&gt;
&lt;li&gt;Trial phone numbers can only send messages to verified numbers.&lt;/li&gt;
&lt;li&gt;Sometimes sending a message to a region is disabled, so you have to enable the region on the Twilio console.&lt;/li&gt;
&lt;li&gt;Webhook URL must be publicly accessible. No auth. Not behind a server configuration restriction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this article was helpful.&lt;/p&gt;

&lt;p&gt;If you have any feedback or found mistakes, please don’t hesitate to reach out to me.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>twilio</category>
      <category>php</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
