<?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: Adam Crampton</title>
    <description>The latest articles on Forem by Adam Crampton (@adam_crampton).</description>
    <link>https://forem.com/adam_crampton</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%2F62462%2F5bea03f2-2fa5-433b-b87f-2f234b34f84a.png</url>
      <title>Forem: Adam Crampton</title>
      <link>https://forem.com/adam_crampton</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/adam_crampton"/>
    <language>en</language>
    <item>
      <title>Quick Guide - Upgrading to Laravel 9 and PHP 8.1</title>
      <dc:creator>Adam Crampton</dc:creator>
      <pubDate>Thu, 17 Feb 2022 23:25:46 +0000</pubDate>
      <link>https://forem.com/adam_crampton/quick-guide-upgrading-to-laravel-9-and-php-81-2dbh</link>
      <guid>https://forem.com/adam_crampton/quick-guide-upgrading-to-laravel-9-and-php-81-2dbh</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;This article is a quick guide to the steps required for upgrading from PHP7.x to 8.1, and Laravel v8 to v9.&lt;/p&gt;

&lt;p&gt;Since &lt;a href="https://kinsta.com/blog/laravel-9/#minimum-php-requirement"&gt;Laravel 9 now requires PHP 8&lt;/a&gt;, I thought this would be a useful reference for tackling the task.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The environment used in this guide is Ubuntu 20.04 with Nginx. If using a different Linux distribution and/or Apache, the process and commands will be slightly different.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Approach
&lt;/h1&gt;

&lt;p&gt;It goes without saying, but ensure you run through this process on a test environment first, to ensure a smooth production upgrade once certain that everything works.&lt;/p&gt;

&lt;p&gt;PHP will need to be upgraded before Laravel, as we will run into Composer dependency issues if we try to do this the other way around.&lt;/p&gt;

&lt;h1&gt;
  
  
  Upgrading PHP
&lt;/h1&gt;

&lt;p&gt;Follow these steps to get PHP 8.1 up and running in your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade Steps
&lt;/h2&gt;

&lt;p&gt;1. Let's start by making sure we have the PPA repository added, so we're able to install PHP 8.1. Run these two commands:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. Now we update our packages.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update -y &amp;amp;&amp;amp; sudo apt upgrade -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;3. We can now install PHP8.1 along with the libraries required for Laravel.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install php8.1 php8.1-fpm php8.1-cli php8.1-curl php8.1-zip php8.1-mysql php8.1-mbstring php8.1-xml php8.1-bcmath
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may need to install additional modules for certain libraries such as php8.1-gd. Don't worry if you miss any though - Composer will pick up these incompatibilities later when you upgrade the Laravel core.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;4. Next, we'll need to update the FPM path in our Nginx hosts.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim /etc/nginx/sites-available/some-host.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this example we're upgrading from 7.4, so we'll change this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5. Finally, we save the file and restart Nginx and FPM.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart nginx &amp;amp;&amp;amp; sudo systemctl restart php8.1-fpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Upgrading Laravel
&lt;/h1&gt;

&lt;p&gt;This part can be quite tricky if you use a lot of libraries. My advice here is to check the status of the dependencies you use (i.e. in Packagist or GitHub), and ensure they are Laravel 9 compliant.&lt;/p&gt;

&lt;p&gt;The other thing to keep in mind is that it's very common to run into dependency errors when running the upgrade, as many libraries require a version upgrade to support Laravel 9.&lt;/p&gt;

&lt;p&gt;If this occurs, run &lt;code&gt;composer update -vvv&lt;/code&gt; for a verbose output, and check to see what is required. Quite often all the information is there, and fixing the issue is usually a case of updating the version reference in &lt;code&gt;composer.json&lt;/code&gt; to the latest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composer Edits
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://laravel.com/docs/9.x/upgrade"&gt;Per the documentation&lt;/a&gt;, you need to make some mandatory &lt;code&gt;composer.json&lt;/code&gt; updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Details here:&lt;/strong&gt; &lt;a href="https://laravel.com/docs/9.x/upgrade#updating-dependencies"&gt;https://laravel.com/docs/9.x/upgrade#updating-dependencies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as mentioned above, check off each dependency in your &lt;code&gt;composer.json&lt;/code&gt; to ensure Laravel 9 compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow The Upgrade Guide
&lt;/h2&gt;

&lt;p&gt;This part is self expanatory - run through the entire upgrade guide to see if any of your codebase is affected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Details here:&lt;/strong&gt; &lt;a href="https://laravel.com/docs/9.x/upgrade"&gt;https://laravel.com/docs/9.x/upgrade&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most people have found the v8 - v9 migration fairly simple, with minimal breaking changes. In reality, this process only takes 15-20 minutes for non-complex codebases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run The Update
&lt;/h2&gt;

&lt;p&gt;To kick off the update, run &lt;code&gt;composer update&lt;/code&gt;. If you run into compatibility issues while attempting the update, check your dependencies and make the appropriate updates in &lt;code&gt;composer.json&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Some tips if you are running into issues with the Composer update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Double check PHP 8 has upgraded and FPM is configured with the correct path in the Nginx host configuration (it may still be using 7.x).&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;composer update -vvv&lt;/code&gt; for full verbose output - this should give you a clue as to which dependencies are having a problem.&lt;/li&gt;
&lt;li&gt;Remove the dependencies and run an install from scratch - &lt;code&gt;sudo rm -rf vendor &amp;amp;&amp;amp; composer install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If all else fails, remove all dependencies from your &lt;code&gt;composer.json&lt;/code&gt; except for the ones that ship with the Laravel core. You should then be able to add them back one-by-one until you find the problematic library.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Wrapping Up
&lt;/h1&gt;

&lt;p&gt;Thanks for reading the article, and best of luck if you're about to dive into the upgrade!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>nginx</category>
      <category>upgrade</category>
    </item>
    <item>
      <title>10 Step Guide: Varnish + Nginx + PHP On Ubuntu With SSL</title>
      <dc:creator>Adam Crampton</dc:creator>
      <pubDate>Mon, 15 Feb 2021 21:03:12 +0000</pubDate>
      <link>https://forem.com/adam_crampton/10-step-guide-varnish-nginx-php-on-ubuntu-with-ssl-28do</link>
      <guid>https://forem.com/adam_crampton/10-step-guide-varnish-nginx-php-on-ubuntu-with-ssl-28do</guid>
      <description>&lt;p&gt;Setting up Varnish with SSL can be tricky, largely because there isn't any one place to get all the information you need in a straightforward manner.&lt;/p&gt;

&lt;p&gt;Having gone through the frustration of piecing it all together myself, I decided to write a quick guide that details the process from start to finish.&lt;/p&gt;

&lt;p&gt;Initially, I attempted the SSL portion of the job by using Hitch - which ended up being a complete waste of time (mostly because of really poor documentation). HAProxy on the other hand ended up being a great solution, as well as having other really great features like load balancing included.&lt;/p&gt;

&lt;p&gt;Let's get straight to it. Here's the stack I'm working through in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 18.04&lt;/li&gt;
&lt;li&gt;PHP 7.4&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;Varnish 5.2.1&lt;/li&gt;
&lt;li&gt;Letsencrypt SSL&lt;/li&gt;
&lt;li&gt;HAProxy&lt;/li&gt;
&lt;li&gt;Laravel 8&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can ignore the PHP and Laravel bits if you're using a different stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  0. Getting Started
&lt;/h2&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 18.04 installation + sudo SSH access&lt;/li&gt;
&lt;li&gt;DNS configured for the server block you are going to add to Nginx (it'll be example.com in this article)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Set Up Nginx
&lt;/h2&gt;

&lt;p&gt;Install nginx using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo apt update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt install nginx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Set the default ports: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sudo vim /etc/nginx/sites-available/default&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
  listen 8080 default_server;
  listen [::]:8080 default_server;
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Don't create the server block for your site just yet, we'll get to that shortly.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Install PHP 7.4 + Required Extensions
&lt;/h2&gt;

&lt;p&gt;If PHP isn't already installed, run the following commands to get PHP 7.4 along with extensions required for a Laravel (and most modern PHP apps) project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo apt install software-properties-common&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo add-apt-repository ppa:ondrej/php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt install php7.4 php7.4-cli php7.4-fpm php7.4-json php7.4-common php7.4-mysql php7.4-zip php7.4-gd php7.4-mbstring php7.4-curl php7.4-xml php7.4-pear php7.4-bcmath&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Install FPM + Composer
&lt;/h2&gt;

&lt;p&gt;Install FPM and Composer if you require it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo apt install php7.4-fpm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt install composer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Create Nginx server block
&lt;/h2&gt;

&lt;p&gt;Now we can create the Nginx server block for our site:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo vim /etc/nginx/sites-available/example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Paste this in (or use your preferred boilerplate):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 8080;
    server_name example.com www.example.com;
    root /var/www/example.com/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Save the file, then create the symlink by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;sudo ls -n /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restart Nginx: &lt;code&gt;sudo service nginx restart&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Install Cerbot
&lt;/h2&gt;

&lt;p&gt;Certbot needs to be installed to request a Let's Encrypt certificate for our site. Ensure your DNS records are pointing to this server and run the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo add-apt-repository ppa:certbot/certbot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt install python-certbot-nginx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo certbot --nginx -d example.com -d www.example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;No Redirect&lt;/strong&gt; option&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;/etc/nginx/sites-available/example.com&lt;/code&gt; and remove SSL lines added by Certbot at the bottom of the file - it will look something like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Prepare PEM File
&lt;/h2&gt;

&lt;p&gt;Next we will need to create a PEM file for our SSL proxy to use further down the track. To do so, we'll append the private key and fullchain key values to a single file:&lt;/p&gt;

&lt;p&gt;Switch to root (&lt;code&gt;sudo su&lt;/code&gt;), then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cat /etc/letsencrypt/live/example.com/privkey.pem /etc/letsencrypt/live/example.com/fullchain.pem &amp;gt; /etc/ssl/private/exampledotcom.pem
exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Install + Configure Varnish
&lt;/h2&gt;

&lt;p&gt;Varnish can now be installed and configured:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo apt -y install varnish&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;/lib/systemd/system/varnish.service&lt;/code&gt; and change the default port from 6081 to 80:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ExecStart=/usr/sbin/varnishd -j unix,user=vcache -F -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;/etc/varnish/default.vcl&lt;/code&gt; and ensure the &lt;code&gt;backend default&lt;/code&gt; port is set to &lt;code&gt;8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sudo systemctl daemon-reload &amp;amp;&amp;amp; sudo service varnish restart&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Varnish should now be working for your site for HTTP.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Install + Configure HAProxy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install HAProxy: &lt;code&gt;sudo apt-get install haproxy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open for editing: &lt;code&gt;sudo vim /etc/haproxy/haproxy.cfg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a front end binding, pointing towards PEM file created earlier:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frontend haproxynode
        bind *:443 ssl crt /etc/ssl/private/exampledotcom.pem
        mode http
        default_backend backendnodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add back end config + node:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend backendnodes
        balance roundrobin
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request add-header X-Forwarded-Proto https if { ssl_fc }
        server example.com 172.1.35.35:80 check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; IP address above must be that of the server this host sits on. use &lt;code&gt;ifconfig&lt;/code&gt; to determine that address.&lt;/p&gt;

&lt;p&gt;To test the config for errors, run &lt;code&gt;sudo haproxy -c -f /etc/haproxy/haproxy.cfg&lt;/code&gt;: &lt;/p&gt;

&lt;h2&gt;
  
  
  9. Restart Everything
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo service nginx restart &amp;amp;&amp;amp; sudo service varnish restart &amp;amp;&amp;amp; sudo service haproxy restart&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. Trust Proxy Config (Laravel only)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add the server IP from step 8 to the &lt;code&gt;$proxies&lt;/code&gt; property in &lt;code&gt;App\Http\Middleware\TrustProxies&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'172.1.35.35'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are behind a load balancer where you cannot reliably determine the IP, you can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 18.04 + Nginx install guide: &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04"&gt;https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Let's Encrypt Nginx Tutorial: &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04"&gt;https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VCL Reference: &lt;a href="https://varnish-cache.org/docs/5.2/users-guide/vcl.html"&gt;https://varnish-cache.org/docs/5.2/users-guide/vcl.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;An excellent VCL configuration template can be found here: &lt;a href="https://github.com/mattiasgeniar/varnish-5.0-configuration-templates"&gt;https://github.com/mattiasgeniar/varnish-5.0-configuration-templates&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks!
&lt;/h2&gt;

&lt;p&gt;Hopefully this guide was helpful for you, thanks for taking a look. &lt;/p&gt;

&lt;p&gt;If there are any corrections to the above, please leave a comment and I will update the article.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>php</category>
      <category>ubuntu</category>
      <category>nginx</category>
    </item>
    <item>
      <title>Capistrano And Laravel: Setting The App Version</title>
      <dc:creator>Adam Crampton</dc:creator>
      <pubDate>Wed, 03 Feb 2021 05:01:21 +0000</pubDate>
      <link>https://forem.com/adam_crampton/capistrano-and-laravel-setting-the-app-version-328p</link>
      <guid>https://forem.com/adam_crampton/capistrano-and-laravel-setting-the-app-version-328p</guid>
      <description>&lt;p&gt;If you're of the few folks out there that uses Capistrano to deploy their Laravel projects (we DO exist!), this article will hopefully be useful for you.&lt;/p&gt;

&lt;p&gt;Versioning your app is great for a number of reasons, and there are some use cases for needing access to this value in a production environment.&lt;/p&gt;

&lt;p&gt;In my particular case, I maintain a large dashboard which receives frequent updates, and having the version number displayed in the footer helps as a quick visual reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;p&gt;Fortunately, this is not a particularly complicated task. Here's what we need to do:&lt;/p&gt;

&lt;p&gt;Ensure an &lt;code&gt;APP_VERSION&lt;/code&gt; parameter exists in the &lt;code&gt;.env&lt;/code&gt; file stored in the &lt;code&gt;secrets&lt;/code&gt; directory within the production environment.&lt;/p&gt;

&lt;p&gt;Add a task to the &lt;code&gt;deploy.rb&lt;/code&gt; file which will prompt the user for a version string, then rewrite the line in the &lt;code&gt;.env&lt;/code&gt; file within the release directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding The Task
&lt;/h2&gt;

&lt;p&gt;Here's the task code that I use for this purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:set_version&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:laravel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:sed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"-i 's/^APP_VERSION.*/APP_VERSION=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/' &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;release_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.env"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what's going on in the above snippet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We are defining our task as &lt;code&gt;set_version&lt;/code&gt;, which when executed, prompts the user for an app version string.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the value is received, it is stored in the &lt;code&gt;app_version&lt;/code&gt; configuration variable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This task sits within a &lt;code&gt;laravel&lt;/code&gt; namespace in the file, which explains the &lt;code&gt;on roles(:laravel)&lt;/code&gt; line.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;execute&lt;/code&gt; directive is for a 1-line bash command, using &lt;code&gt;sed&lt;/code&gt;, to perform an inline edit of the &lt;code&gt;.env&lt;/code&gt; file located in the root of the release path.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;sed&lt;/code&gt; command which perform a regex match for &lt;code&gt;APP_VERSION&lt;/code&gt; and replaces the entire line with the new value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Still within the &lt;code&gt;sed&lt;/code&gt; command, we have &lt;code&gt;fetch(:app_version)&lt;/code&gt;, which is the configuration value we stored earlier (mentioned in point 1).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we can set the task to run like in the example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:deploy&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="ss"&gt;:updated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"laravel:set_version"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Clearing Config Cache
&lt;/h2&gt;

&lt;p&gt;You will of course need to clear the config cache within the release directory to pick up the change to the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;It's likely you are already doing this in some way; my preferred method is to use a task like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:clear_config&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:laravel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
         &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;release_path&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
             &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:php&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artisan config:cache"&lt;/span&gt;
             &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:php&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artisan view:cache"&lt;/span&gt;
             &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:php&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artisan route:cache"&lt;/span&gt;
             &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="ss"&gt;:php&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"artisan cache:clear"&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just be sure to clear the config cache after the app version task has been run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks!
&lt;/h2&gt;

&lt;p&gt;I appreciate you taking the time to read this; hopefully it helps someone along the way.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>capistrano</category>
      <category>php</category>
      <category>deployment</category>
    </item>
    <item>
      <title>Testing Laravel API endpoints with jwt-auth</title>
      <dc:creator>Adam Crampton</dc:creator>
      <pubDate>Fri, 20 Mar 2020 04:02:30 +0000</pubDate>
      <link>https://forem.com/adam_crampton/testing-laravel-api-endpoints-with-jwt-auth-4cki</link>
      <guid>https://forem.com/adam_crampton/testing-laravel-api-endpoints-with-jwt-auth-4cki</guid>
      <description>&lt;p&gt;JWT authentication with Laravel is made relatively headache-free using the fantastic jwt-auth library, which can be found here:&lt;br&gt;
&lt;a href="https://github.com/tymondesigns/jwt-auth"&gt;https://github.com/tymondesigns/jwt-auth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have JWT up and running in your project, you will want to start thinking about building tests as you add functionality to your codebase.&lt;/p&gt;

&lt;p&gt;The three-step approach I've taken in this article is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a default API user and setting the details in config&lt;/li&gt;
&lt;li&gt;Add tests for token handling&lt;/li&gt;
&lt;li&gt;Add tests for project endpoints, using token auth&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When these tests pass, you can be sure that tokens can be issued, refreshed, and used on your protected endpoints.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Setting a default API user
&lt;/h2&gt;

&lt;p&gt;While you can certainly use a factory to automate this, sometimes it makes sense to use pre-saved credentials to test auth.&lt;/p&gt;

&lt;p&gt;To that end, we can create a default &lt;code&gt;User&lt;/code&gt; model for this purpose (manually, or using a seeder), and store the email and password in the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The best way to make these .env variables available in tests is to create a config file (e.g. &lt;code&gt;app\config\api.php&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
|--------------------------------------------------------------------------
| API specific config
|--------------------------------------------------------------------------
*/&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Default API user - mostly used for tests.&lt;/span&gt;
    &lt;span class="s1"&gt;'apiEmail'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'API_USER_EMAIL'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'apiPassword'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'API_USER_PASSWORD'&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;h2&gt;
  
  
  2. Testing the auth routes
&lt;/h2&gt;

&lt;p&gt;The three endpoints we can test here are login, logout, and refresh. This will ensure we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate a token&lt;/li&gt;
&lt;li&gt;Pass the token into a logout request&lt;/li&gt;
&lt;li&gt;Regenerate a token on the fly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Logging in
&lt;/h3&gt;

&lt;p&gt;Here we'll hit the auth login route with the default user's credentials (email and password) and get a token back.&lt;/p&gt;

&lt;p&gt;To ensure that we're getting the proper response, we'll assert a 200 HTTP status check and the 3 necessary fields in the JSON response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
* Login as default API user and get token back.
*
* @return void
*/&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testLogin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.url'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/api/auth/login'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api.apiEmail'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api.apiPassword'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$baseUrl&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJsonStructure&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'token_type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'expires_in'&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;h3&gt;
  
  
  Logging Out
&lt;/h3&gt;

&lt;p&gt;We can now use our token from the login test to request a logout action for the default user.&lt;/p&gt;

&lt;p&gt;Since I have set up the logout method in the Auth Controller to return an exact message, we can assert an exact match on the return message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Add a use statement for &lt;code&gt;Tymon\JWTAuth\Facades\JWTAuth&lt;/code&gt; to easily access previously generated tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
* Test logout.
*
* @return void
*/&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testLogout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api.apiEmail'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JWTAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.url'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/api/auth/logout?token='&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertExactJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Successfully logged out'&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;h3&gt;
  
  
  Refreshing the token
&lt;/h3&gt;

&lt;p&gt;Finally, we'll refresh the token for the default user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
* Test token refresh.
*
* @return void
*/&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testRefresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api.apiEmail'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JWTAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.url'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/api/auth/refresh?token='&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJsonStructure&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'access_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'token_type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'expires_in'&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;h2&gt;
  
  
  3. Testing the endpoints
&lt;/h2&gt;

&lt;p&gt;Now that we're confident the token handling is working as expected, we can now use our default API user to authenticate to JWT protected endpoints. &lt;/p&gt;

&lt;p&gt;Here's an example where we grab all data from the &lt;code&gt;User&lt;/code&gt; model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
* Get all users.
*
* @return void
*/&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testGetUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api.apiEmail'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JWTAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.url'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/api/users?token='&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$baseUrl&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Now that you have a basic framework for automated endpoint authentication, you can easily add your tests as you build out your project. &lt;/p&gt;

&lt;p&gt;Hope you enjoyed this post!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>authentication</category>
      <category>security</category>
    </item>
    <item>
      <title>Large file uploads to an S3 bucket done neatly in Laravel</title>
      <dc:creator>Adam Crampton</dc:creator>
      <pubDate>Mon, 24 Feb 2020 02:35:42 +0000</pubDate>
      <link>https://forem.com/adam_crampton/large-file-uploads-to-an-s3-bucket-done-neatly-in-laravel-344i</link>
      <guid>https://forem.com/adam_crampton/large-file-uploads-to-an-s3-bucket-done-neatly-in-laravel-344i</guid>
      <description>&lt;p&gt;Lately I've been looking at ways to reduce the amount of clutter in my Laravel controllers, and found macros can be super useful for wrapping reusable snippets of code behind an easily accessible facades.&lt;/p&gt;

&lt;p&gt;If you'd like to learn more about Laravel macros, here's a great primer, with a list of "Macroable" classes:&lt;br&gt;
&lt;a href="https://tighten.co/blog/the-magic-of-laravel-macros"&gt;https://tighten.co/blog/the-magic-of-laravel-macros&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Dealing with large files
&lt;/h1&gt;

&lt;p&gt;The best way to reliably upload large files to an S3 bucket in Laravel is with the Flysystem S3 adapter, specifically using the &lt;code&gt;writeStream&lt;/code&gt; and &lt;code&gt;putStream&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Special note:&lt;/strong&gt; You are almost certainly going to need to tweak the host's php.ini configuration, specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;post_max_size&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;upload_max_filesize&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Setting up
&lt;/h1&gt;

&lt;p&gt;The first thing I would recommend, if you don't do this already, is to set up a Service Provider specifically for your macros. To do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;php artisan make:provider MacroServiceProvider&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;App\Providers\MacroServiceProvider::class&lt;/code&gt; to your project's &lt;code&gt;app.php&lt;/code&gt; config file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, install the Flysystem AWS S3 Adapter: &lt;br&gt;
&lt;code&gt;composer require league/flysystem-aws-s3-v3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, you'll want to ensure these four values are set in your project's &lt;code&gt;.env&lt;/code&gt; file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS_ACCESS_KEY=YOURACCESSKEY&lt;/li&gt;
&lt;li&gt;AWS_SECRET_ACCESS_KEY=YOURSECRETACCESSKEY&lt;/li&gt;
&lt;li&gt;AWS_REGION=aws-bucket-region&lt;/li&gt;
&lt;li&gt;AWS_S3_BUCKET=name.of.s3.bucket&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Create the macro
&lt;/h1&gt;

&lt;p&gt;For this macro, we are going to extend the built in &lt;code&gt;File&lt;/code&gt; component so we can use its facade. &lt;/p&gt;

&lt;p&gt;Within &lt;code&gt;App\Providers\MacroServiceProvider&lt;/code&gt;, let's add a closure to the &lt;code&gt;boot&lt;/code&gt; method with four parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt;: Path within the bucket to save the file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;filename&lt;/code&gt;: Filename you want to save as&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;file&lt;/code&gt;: The file object from &lt;code&gt;$request-&amp;gt;file('input_name')&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;overWrite&lt;/code&gt;: Whether or not you want to overwrite the file if a file with the same name already exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the code:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Providers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aws\S3\S3Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\ServiceProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\File&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;League\Flysystem\AwsS3v3\AwsS3Adapter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;League\Flysystem\Filesystem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MacroServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ServiceProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Bootstrap services.
     *
     * @return void
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;macro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'streamUpload'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$overWrite&lt;/span&gt; &lt;span class="o"&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="c1"&gt;// Set up S3 connection.&lt;/span&gt;
            &lt;span class="nv"&gt;$resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRealPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'r+'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'filesystems.disks.s3'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'credentials'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'key'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="s1"&gt;'secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'secret'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'region'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'region'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="s1"&gt;'version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'latest'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;

            &lt;span class="nv"&gt;$adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AwsS3Adapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'AWS_S3_BUCKET'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$filesystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Filesystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$adapter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$overWrite&lt;/span&gt; 
                    &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$filesystem&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;putStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                    &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$filesystem&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;writeStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$resource&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;h1&gt;
  
  
  Update your controller
&lt;/h1&gt;

&lt;p&gt;Now all you need to do is grab the file from the request, set your options, and call the macro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;            &lt;span class="c1"&gt;// Set file attributes.&lt;/span&gt;
            &lt;span class="nv"&gt;$filepath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'upload/destination/files'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'uploaded-file'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Hidden input with a generated value&lt;/span&gt;

            &lt;span class="c1"&gt;// Upload to S3, overwriting if filename exists.&lt;/span&gt;
            &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;streamUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$file&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aaand that's it! Happy uploading!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>uploads</category>
      <category>macro</category>
    </item>
    <item>
      <title>Catching Laravel Eloquent Exceptions: Easy Peasy Error Messages</title>
      <dc:creator>Adam Crampton</dc:creator>
      <pubDate>Mon, 09 Sep 2019 09:08:39 +0000</pubDate>
      <link>https://forem.com/adam_crampton/catching-laravel-eloquent-exceptions-easy-peasy-error-messages-1mpn</link>
      <guid>https://forem.com/adam_crampton/catching-laravel-eloquent-exceptions-easy-peasy-error-messages-1mpn</guid>
      <description>&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;I recently found myself in a situation where I had a codebase that would throw exceptions from certain Eloquent queries - but in a &lt;em&gt;sort of&lt;/em&gt; legitimate way. &lt;/p&gt;

&lt;p&gt;Essentially, the scenario was that certain user accounts did not have relationships with certain models, but were critical for the controller to complete its work.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Solution
&lt;/h1&gt;

&lt;p&gt;This sort of thing should normally be dealt with by fixing the issue at the entry point (validation on user creation), however in this particular scenario, it was a much better idea to return an error to the user to let them know that human intervention was required.&lt;/p&gt;

&lt;p&gt;So, to solve this, I needed to catch the exception, generate a human-readable message, and send it back to the front end. This functionality should not clutter the controllers, and should be easy to maintain.&lt;/p&gt;

&lt;p&gt;Here's my step-by-step approach:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Set up a trait
&lt;/h2&gt;

&lt;p&gt;Since we probably want this functionality to be available globally through the app, let's set up a trait - which are super useful for this kind of thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Traits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;Trait&lt;/span&gt; &lt;span class="nc"&gt;GlobalUtility&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// Code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Define the errors
&lt;/h2&gt;

&lt;p&gt;Within the trait, we set up an array of error messages, with a meaningful key name for each. A default message is added to ensure something is always returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Traits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;Trait&lt;/span&gt; &lt;span class="nc"&gt;GlobalUtility&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$customExceptionErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'generic'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sorry, there was a problem with this page. Please contact your Sales Account Manager for help.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'noDealerRelationship'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sorry, your user account does not appear to be connected to a Dealer. Please contact your Sales Account Manager for help.'&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;h2&gt;
  
  
  3. Return logic
&lt;/h2&gt;

&lt;p&gt;The return method is a simple check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts a named exception parameter, which is aligned with the keys in the &lt;code&gt;$customExceptionErrors&lt;/code&gt; array&lt;/li&gt;
&lt;li&gt;Fetches the error message&lt;/li&gt;
&lt;li&gt;Returns the error string or the generic version if none is found (or no parameter is passed)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Traits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;Trait&lt;/span&gt; &lt;span class="nc"&gt;GlobalUtility&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$customExceptionErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'generic'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sorry, there was a problem with this page. Please contact your Sales Account Manager for help'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'noDealerRelationship'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sorry, your user account does not appear to be connected to a Dealer. Please contact your Sales Account Manager for help.'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Returns a custom error message for the exception.
     *
     * @param string $exceptionName
     * @return string
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getExceptionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$exceptionName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'generic'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;customExceptionErrors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$exceptionName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;customExceptionErrors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'generic'&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;h2&gt;
  
  
  4. Add Trait and Exception Class to Controller
&lt;/h2&gt;

&lt;p&gt;Next, the trait and exception class are added to the controller where the Eloquent query is being made.&lt;/p&gt;

&lt;p&gt;After the namespace declaration, we add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Traits\GlobalUtility&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\ModelNotFoundException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And within the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;GlobalUtility&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Update the Query
&lt;/h2&gt;

&lt;p&gt;Now we must ensure the query utilises a "orFail" method, like &lt;code&gt;firstOrFail&lt;/code&gt; or &lt;code&gt;findOrFail&lt;/code&gt;, then pop it into a try &amp;amp; catch blocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$dealer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;worksFor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;firstOrFail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ModelNotFoundException&lt;/span&gt; &lt;span class="nv"&gt;$e&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;h2&gt;
  
  
  6. Configure the redirect
&lt;/h2&gt;

&lt;p&gt;Within the catch statement, we add the redirect with a call to the trait method to grab the appropriate error. We'll set this as &lt;code&gt;customError&lt;/code&gt;, which will be a value available via the Session later on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$dealer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;worksFor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;firstOrFail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ModelNotFoundException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'orders.history'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'customError'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getExceptionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'noDealerRelationship'&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;h2&gt;
  
  
  7. Add check and fetch to the Blade file
&lt;/h2&gt;

&lt;p&gt;Finally, we need to ask the app if the &lt;code&gt;customError&lt;/code&gt; value exists in the session, and if so, display.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{-- Show custom defined errors (caught from exceptions) if they exist --}}
@if (Session::has('customError'))
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-danger"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;{{ Session::get('customError') }}&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
@endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Hope someone finds this useful :)&lt;/p&gt;

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