<?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: Mike Mollick</title>
    <description>The latest articles on Forem by Mike Mollick (@mmollick).</description>
    <link>https://forem.com/mmollick</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%2F14471%2F652f6176-1242-4281-bcd8-79e51005eda2.jpeg</url>
      <title>Forem: Mike Mollick</title>
      <link>https://forem.com/mmollick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mmollick"/>
    <language>en</language>
    <item>
      <title>Using Unique Columns with Soft Deletes in Laravel</title>
      <dc:creator>Mike Mollick</dc:creator>
      <pubDate>Wed, 29 Jul 2020 17:25:42 +0000</pubDate>
      <link>https://forem.com/mmollick/using-unique-columns-and-soft-deletes-in-laravel-470p</link>
      <guid>https://forem.com/mmollick/using-unique-columns-and-soft-deletes-in-laravel-470p</guid>
      <description>&lt;p&gt;When you mix Laravel's soft deletion feature with your database's unique column constraints you're may have seen error messages that look something like &lt;code&gt;SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This can be a common scenario if you use soft-deletes on your users table, where you have a unique constraint on the email field. With soft deletes this means if a user deletes their account they'll be unable to recreate the account at a later date because the email still exists in the table. Unless we intend to write user-hostile software, we probably want the user to be able to re-register with the same email. &lt;/p&gt;

&lt;p&gt;Let's dive into how we can mitigate this issue and allow users to be deleted without preventing the email from being used in the future.&lt;/p&gt;

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

&lt;p&gt;To start off let's cover some basic assumptions about our application. We have a base Laravel user table migration, with the soft deletes attribute added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&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="nx"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&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="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email_verified_at'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;rememberToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;softDeletes&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 also have the base Laravel &lt;code&gt;User&lt;/code&gt; model, with the &lt;code&gt;SoftDeletes&lt;/code&gt; attribute added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Authenticatable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Notifiable&lt;/span&gt;&lt;span class="err"&gt;, SoftDeletes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Demonstrating the Problem
&lt;/h3&gt;

&lt;p&gt;With this setup we can create uses and delete users, but creating a new user with the same email results in the "Integrity constraint violation" error.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Create User
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sam'&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="s1"&gt;'sam@example.com'&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="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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;Users Table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;email_verified_at&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;th&gt;remember_token&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;updated_at&lt;/th&gt;
&lt;th&gt;deleted_at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sam&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:sam@example.com"&gt;sam@example.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;$2y$10$OHcjoGZrF1zxSRLclThvbu5sNeiYdfzaxubzdJqZn64JtcAbauVai&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2020-07-29 16:39:38&lt;/td&gt;
&lt;td&gt;2020-07-29 16:39:38&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  2. Delete User
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Users Table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;email_verified_at&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;th&gt;remember_token&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;updated_at&lt;/th&gt;
&lt;th&gt;deleted_at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sam&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:sam@example.com"&gt;sam@example.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;$2y$10$OHcjoGZrF1zxSRLclThvbu5sNeiYdfzaxubzdJqZn64JtcAbauVai&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2020-07-29 16:39:38&lt;/td&gt;
&lt;td&gt;2020-07-29 16:40:56&lt;/td&gt;
&lt;td&gt;2020-07-29 16:40:56&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  3. Recreating user produces an error
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sam'&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="s1"&gt;'sam@example.com'&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="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Illuminate/Database/QueryException with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'sam@example.com' for key 'users.users_email_unique' (SQL: insert into `users` (`name`, `email`, `password`, `updated_at`, `created_at`) values (Sam, sam@example.com, $2y$10$DNrfq9wPEcSsOvFOX97tF.kiu2Zg.yRGqVvNPRRLi.BFtOK2fCbqC, 2020-07-29 16:41:30, 2020-07-29 16:41:30))'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



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

&lt;p&gt;The best way to get around this is to mutate the data contained in this unique column on delete. Now initially this might sound like a bad idea, since if you're using the soft deletion feature you probably want the data to remain in tact. But have no fear, we can mutate the data in a way that it remains readable.&lt;/p&gt;

&lt;p&gt;With Laravel we can use &lt;a href="https://laravel.com/docs/7.x/eloquent#observers"&gt;Model Observers&lt;/a&gt; to accomplish this with little effort.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Create a UserObserver class
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:observer UserObserver &lt;span class="nt"&gt;--model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;User
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Update the &lt;code&gt;delete&lt;/code&gt; method to mutate the email
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&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;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;update&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="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'::'&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="na"&gt;email&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;h4&gt;
  
  
  3. Register UserObserver within our &lt;code&gt;AppServiceProvider&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;boot&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="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserObserver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;class&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;
  
  
  Demonstrating the Solution
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Create User
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sam'&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="s1"&gt;'sam@example.com'&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="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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;Users Table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;email_verified_at&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;th&gt;remember_token&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;updated_at&lt;/th&gt;
&lt;th&gt;deleted_at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sam&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:sam@example.com"&gt;sam@example.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;$2y$10$eNHbAmyL4DzTkclDeUbLBu3a9d3dmCpgEa7Ayd0utmJK/klKc3BXG&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:15&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:15&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  2. Delete User
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the database we can see the magic happening, now our email field contains a timestamp that makes this value unique.&lt;/p&gt;

&lt;p&gt;Users Table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;email_verified_at&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;th&gt;remember_token&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;updated_at&lt;/th&gt;
&lt;th&gt;deleted_at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sam&lt;/td&gt;
&lt;td&gt;1596041868::&lt;a href="mailto:sam@example.com"&gt;sam@example.com&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;$2y$10$eNHbAmyL4DzTkclDeUbLBu3a9d3dmCpgEa7Ayd0utmJK/klKc3BXG&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:15&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:48&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:48&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  3. Recreating user creates a new record
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sam'&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="s1"&gt;'sam@example.com'&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="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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;Users Table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;email_verified_at&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;th&gt;remember_token&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;updated_at&lt;/th&gt;
&lt;th&gt;deleted_at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sam&lt;/td&gt;
&lt;td&gt;1596041868::&lt;a href="mailto:sam@example.com"&gt;sam@example.com&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;$2y$10$eNHbAmyL4DzTkclDeUbLBu3a9d3dmCpgEa7Ayd0utmJK/klKc3BXG&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:15&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:48&lt;/td&gt;
&lt;td&gt;2020-07-29 16:57:48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Sam&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:sam@example.com"&gt;sam@example.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;$2y$10$NX62LTAQCbVn9uj3fjs4qeL55VnzCIflnGsG/xQ/8QlpXlbiONLIW&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2020-07-29 16:59:03&lt;/td&gt;
&lt;td&gt;2020-07-29 16:59:03&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;With the addition of a single Model Observer we're able to get around this issue and avoid a user-hostile experience. There are some draw backs to this, in particular the ability to restore soft-deleted records becomes tricky in the event that the user has created a new account. At that point however it's unlikely that you would need to restore the original record. &lt;/p&gt;

&lt;p&gt;If you intend on restoring the record for reporting you can instead use the &lt;code&gt;withTrashed&lt;/code&gt; attribute with a &lt;a href="https://laravel.com/docs/7.x/eloquent-mutators#defining-an-accessor"&gt;custom accessor&lt;/a&gt; for the email attribute to strip the prefixed data.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Encrypt your Development Environment Traffic</title>
      <dc:creator>Mike Mollick</dc:creator>
      <pubDate>Fri, 15 Feb 2019 04:41:20 +0000</pubDate>
      <link>https://forem.com/mmollick/encrypt-your-development-environment-traffic-5coi</link>
      <guid>https://forem.com/mmollick/encrypt-your-development-environment-traffic-5coi</guid>
      <description>&lt;p&gt;Keeping your development environment faithful to your production environment ensures your application works as expected. However, our development environments often come with caveats and drawbacks that lead to us negating specific settings, features or functionality. One of these often overlooked features is the ability to serve your application over HTTPS.&lt;/p&gt;

&lt;p&gt;While keeping the traffic from your local Docker container encrypted may not be a priority, ensuring your application is loading all of its assets and data over the correct protocol is. A hard-coded &lt;code&gt;HTTP://&lt;/code&gt; URL in a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag can break your application's styling or prevent scripts from loading at all. The best way to prevent this is to load your application over HTTPS during development as well.&lt;/p&gt;

&lt;p&gt;To load your application using HTTPS locally we'll have to generate SSL certificates. In a production environment, you're likely using a Certificate Authority (CA) to generate trusted certificates like &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt; or one of the dozens of paid CA's. &lt;/p&gt;

&lt;p&gt;While it is possible to reuse the same certificates in your production environment, passing around your production certificate and private keys for the certificate introduces significant security risks. It's best to not to use your production certificate and private key on your local development machine.&lt;/p&gt;

&lt;p&gt;The best solution to enable HTTPS locally is to use a self-signed certificate. Your browser won't trust this self-signed certificate natively and will most likely tell you "Your connection is not private." Encountering this message on a website you don't control is not something you should ignore. However, in this instance, you control the origin of the cert and the server itself so you can safely bypass this screen.&lt;/p&gt;

&lt;p&gt;While these principals work for any environment or stack; this article explains how to create a self-signed certificate with Docker and Nginx. If you're not interested in the Docker specific example, skip to the end of the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Example
&lt;/h2&gt;

&lt;p&gt;With Docker, we have two options for creating self-signed certificates. The first option is to generate the certificate on your local machine and copy them into the container during the build process. The second is to create a certificate during the build process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copying a certificate from your local machine.
&lt;/h3&gt;

&lt;p&gt;I won't go into very much detail on the process of generating a self-signed certificate, as it's been explained at great lengths many times over. You'll find additional links that explain the process in more detail at the end of the article.&lt;/p&gt;

&lt;p&gt;To create a new self-signed certificate you'll first need to ensure OpenSSL is available on your machine. If you're running a Unix machine (Linux or MacOS), it's likely already installed. To ensure it's available, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This command should output something like "LibreSSL 2.6.5" or "OpenSSL 1.1.1a  20 Nov 2018" depending on your platform. If you receive a message saying "command not found," you'll need to install OpenSSL before proceeding.  Once you've verified that OpenSSL is available, you can run the following command in the terminal and follow the interactive prompts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout PROJECT_DIRECTORY/nginx.key -out PROJECT_DIRECTORY/nginx.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace "PROJECT_DIRECTORY" with the directory where your Dockerfiles are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once completed you'll be left with a &lt;code&gt;nginx.key&lt;/code&gt; and &lt;code&gt;nginx.crt&lt;/code&gt; file. Your Dockerfile can be modified to copy these two files into your container. This step is shown below with the two lines that begin with &lt;code&gt;ADD PROJECT_DIRECTORY/...&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:latest&lt;/span&gt;

&lt;span class="c"&gt;# Adds certificates from local machine to docker container&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; PROJECT_DIRECTORY/nginx.key /etc/nginx/ssl/nginx.key&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; PROJECT_DIRECTORY/nginx.crt /etc/nginx/ssl/nginx.crt&lt;/span&gt;

&lt;span class="c"&gt;# Assumes servers/vhosts are defined in /etc/nginx/conf.d/&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; PROJECT_DIRECTORY/vhost.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With the certificates now in our Docker container, the last step is to update our Nginx server configuration to use the certificates. This step is demonstrated below in our &lt;code&gt;vhost.conf&lt;/code&gt; file using the &lt;code&gt;ssl_certificate&lt;/code&gt; and &lt;code&gt;ssl_certificate_key&lt;/code&gt; options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt; &lt;span class="s"&gt;default_server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt; &lt;span class="s"&gt;default_server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/public&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Self-signed certificate setup&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/nginx.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/nginx.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Example PHP application&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="n"&gt;/index.php?&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Example PHP application upstream&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.php$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_split_path_info&lt;/span&gt; &lt;span class="s"&gt;^(.+&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;.php)(/.+)&lt;/span&gt;$&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_index&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;fastcgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_param&lt;/span&gt; &lt;span class="s"&gt;SCRIPT_FILENAME&lt;/span&gt; &lt;span class="nv"&gt;$document_root$fastcgi_script_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_param&lt;/span&gt; &lt;span class="s"&gt;PATH_INFO&lt;/span&gt; &lt;span class="nv"&gt;$fastcgi_path_info&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;This option works well if you're developing a project on your own. However, if you're working with several other developers, they'll also need the certificates. Each developer could generate a local certificate to use, but requires introduces additional steps to the setup process. A better method is to check the certificate into source control although there is another option which makes the process easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate the Certificate with Docker Build
&lt;/h3&gt;

&lt;p&gt;The best solution I've found is to generate the certificate during the build process with the Dockerfile. This method allows every developer to have their certificates that only their machine knows about as well as removing the burden of managing certificate expirations in source control. &lt;/p&gt;

&lt;p&gt;If a developer ever encounters an expired certificate they can rebuild the Docker container with a single command to get a fresh certificate. The previous approach would require someone of the team to generate a new cert locally, commit it to version control and then rebuild the docker container.&lt;/p&gt;

&lt;p&gt;To do this, we'll modify the previous Dockerfile to look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:latest&lt;/span&gt;

&lt;span class="c"&gt;# Ensure OpenSSL is available and generate the certificate chain&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; openssl&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; /etc/nginx/ssl&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="nt"&gt;-keyout&lt;/span&gt; /etc/nginx/ssl/nginx.key &lt;span class="nt"&gt;-out&lt;/span&gt; /etc/nginx/ssl/nginx.crt &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=US/ST=Ohio/L=Cleveland/O=X/CN=www.example.com"&lt;/span&gt;

&lt;span class="c"&gt;# Assumes servers/vhosts are defined in /etc/nginx/conf.d/&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; PROJECT_DIRECTORY/vhost.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Instead of copying the static certificates with the &lt;code&gt;ADD&lt;/code&gt; command we're now installing OpenSSL (if not already available) and generating the certificate on the container. OpenSSL normally requires us to answer several questions before issuing the certificate chain. However, the &lt;code&gt;-subj&lt;/code&gt; flag allows us to specify the answers to these questions when we run the command. With this, we can now create "unattended" self-signed certificates. In this scenario, we would use the same Nginx config from the previous example.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Use self-signed certificates for local development and auto-generate them during your development environment setup. Use the following command to generate unattended certificates (no prompts).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt -subj "/C=US/ST=Ohio/L=Cleveland/O=X/CN=www.example.com"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>ssl</category>
      <category>https</category>
      <category>dx</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
