<?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>DEV Community: John Braun</title>
    <description>The latest articles on DEV Community by John Braun (@johnbraun).</description>
    <link>https://dev.to/johnbraun</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%2F324355%2F93c0496d-601f-4702-8811-65f8791aa6bb.jpg</url>
      <title>DEV Community: John Braun</title>
      <link>https://dev.to/johnbraun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnbraun"/>
    <language>en</language>
    <item>
      <title>Getting started with Mutation Testing</title>
      <dc:creator>John Braun</dc:creator>
      <pubDate>Thu, 11 Feb 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/johnbraun/mutation-testing-3ng9</link>
      <guid>https://dev.to/johnbraun/mutation-testing-3ng9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;How do you determine if the current test suite adds enough value? Do the tests cover all edge cases? If you solely rely on &lt;strong&gt;code coverage&lt;/strong&gt;, you might be missing out. &lt;/p&gt;

&lt;p&gt;In a recent talk at the &lt;a href="https://laracon.eu/online/discover/conference/?21"&gt;Laracon EU Online&lt;/a&gt; conference, my colleague &lt;a href="https://jeroeng.dev/"&gt;Jeroen Groenendijk&lt;/a&gt; highlighted the significance of  &lt;em&gt;Mutation Testing&lt;/em&gt; in achieving &lt;strong&gt;greater confidence&lt;/strong&gt; in the &lt;strong&gt;test suite&lt;/strong&gt; of your application.&lt;/p&gt;

&lt;p&gt;In this blog I'd like to highlight the concepts of &lt;a href="https://en.wikipedia.org/wiki/Mutation_testing"&gt;Mutation Testing&lt;/a&gt;, explain how to get started using &lt;a href="https://infection.github.io/"&gt;Infection PHP&lt;/a&gt; by &lt;a href="https://twitter.com/maks_rafalko"&gt;Maks Rafalko&lt;/a&gt;, show some practical examples and finally explain how to use Mutation Testing in a CI setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Mutation Testing
&lt;/h2&gt;

&lt;p&gt;A mutation testing tool will manipulate (&lt;em&gt;mutate&lt;/em&gt;) pieces of your source code and run the test suite against this piece of &lt;em&gt;mutant&lt;/em&gt; source code. The mutated code &lt;strong&gt;should&lt;/strong&gt; trigger a failing test, or the mutant &lt;strong&gt;escapes&lt;/strong&gt;. Escaped mutants are a sign of weakly tested code.&lt;/p&gt;

&lt;p&gt;As an illustration, take a look at the following example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b_2cLzw9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zt7ti4456xzdk0xbkbjb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b_2cLzw9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zt7ti4456xzdk0xbkbjb.png" alt="Mutation Testing diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;add()&lt;/code&gt; method is mutated in three different ways:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The method returns &lt;code&gt;null&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;+&lt;/code&gt; operator changed to the &lt;code&gt;-&lt;/code&gt; operator&lt;/li&gt;
&lt;li&gt;Method visibility changed from &lt;code&gt;public&lt;/code&gt; to &lt;code&gt;protected&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the diagram above, we see that the tests fail in case &lt;code&gt;null&lt;/code&gt; is returned and when the addition is replaced with a subtraction. The method's visibility however is likely too broad and &lt;em&gt;mutating&lt;/em&gt; it to &lt;code&gt;protected&lt;/code&gt; didn't yield a failing test. Therefore, it should be changed to &lt;code&gt;protected&lt;/code&gt; or even &lt;code&gt;private&lt;/code&gt;, adhering to the best practice of keeping the public API of a class to the minimum.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with Infection PHP
&lt;/h2&gt;

&lt;p&gt;If you just want to play around, check the &lt;a href="https://infection-php.dev/"&gt;Infection Playground&lt;/a&gt;, where you can write the code, tests, and run Infection right from within your browser. Read on to learn more about setting up Infection PHP in your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Infection PHP
&lt;/h3&gt;

&lt;p&gt;Infection requires PHP version 7.2 (or higher) and an enabled debugger of choice: &lt;a href="https://xdebug.org/"&gt;Xdebug&lt;/a&gt;, &lt;a href="https://infection.github.io/guide/usage.html#Running-with-phpdbg"&gt;phpdbg&lt;/a&gt; or &lt;a href="https://github.com/krakjoe/pcov/blob/develop/INSTALL.md"&gt;pcov&lt;/a&gt;. &lt;em&gt;Tip: make sure to check out &lt;a href="https://learnxdebug.com/"&gt;LearnXDebug.com&lt;/a&gt; when you're setting up Xdebug in a Laravel oriented development environment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the steps below, I assume having xDebug installed.&lt;/p&gt;

&lt;p&gt;While you have &lt;a href="https://infection.github.io/guide/installation.html"&gt;multiple options&lt;/a&gt; to install Infection, I would recommend installing it as a &lt;code&gt;dev&lt;/code&gt; dependency using composer within your project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1. Install Infection PHP
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; infection/infection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2. Run Infection
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vendor/bin/infection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first run, Infection will ask for input:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Directories to include:

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;Laravel projects&lt;/strong&gt; this means your &lt;code&gt;app&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;PHP packages&lt;/strong&gt; this means the &lt;code&gt;src&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Excluding directories from source directories:

&lt;ul&gt;
&lt;li&gt;Leave this blank, unless you have PHP code living in the source directory that shouldn't be mutated.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Where to store the text log file:

&lt;ul&gt;
&lt;li&gt;I would recommend saving the mutations to &lt;em&gt;e.g.&lt;/em&gt; &lt;code&gt;infection.log&lt;/code&gt;. All escaped mutants are saved here for later review.&lt;/li&gt;
&lt;li&gt;Alternatively, you may use the &lt;code&gt;--show-mutations&lt;/code&gt; option to log the mutations to the terminal output.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;You'll now find &lt;code&gt;infection.json.dist&lt;/code&gt; in the root of your project reflecting your input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"directories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"logs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"infection.log"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mutators"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"@default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can specify if you want to enable (or disable) a specific mutator under the "mutators" key. Make sure to check the &lt;a href="https://infection.github.io/guide/mutators.html"&gt;complete list of available mutators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tip: allow the mutation tests to run in parallel by providing the &lt;code&gt;--threads&lt;/code&gt; option and setting it greater than 1, for example by running &lt;code&gt;vendor/bin/infection --threads=4&lt;/code&gt;. This will greatly speed up running the mutation tests. An overview of all command-line options can be found &lt;a href="https://infection.github.io/guide/command-line-options.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Example 1: Calculating shipping cost
&lt;/h3&gt;

&lt;p&gt;To demonstrate Mutation Testing in practice, I'll borrow the example from Jeroen's talk.&lt;/p&gt;

&lt;p&gt;Imagine having a &lt;code&gt;ShippingCalculator&lt;/code&gt; service class to determine if a passed in &lt;code&gt;Product&lt;/code&gt; qualifies for free shipping. For simplicity’s sake, let's say the &lt;code&gt;Product&lt;/code&gt; class accepts a &lt;code&gt;$price&lt;/code&gt; integer through its constructor and provides public access to a &lt;code&gt;$shipsForFree&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ShippingCalculator&lt;/code&gt; class determines that a &lt;code&gt;Product&lt;/code&gt; receives free shipping when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The price is equal to or greater than the threshold (set to an arbitrary value)&lt;/li&gt;
&lt;li&gt;The product's &lt;code&gt;$shipsForFree&lt;/code&gt; property is &lt;code&gt;true&lt;/code&gt; (or truthy)
&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;FREE_SHIPPING_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;hasFreeShipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;        
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREE_SHIPPING_THRESHOLD&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="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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;shipsForFree&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="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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Testing the code with PHPUnit
&lt;/h4&gt;

&lt;p&gt;To make sure the &lt;code&gt;ShippingCalculator::hasFreeShipping()&lt;/code&gt; method works properly, we can think of adding the following unit tests to ensure proper behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a product's &lt;code&gt;$price&lt;/code&gt; exceeds the threshold, it should ship for free&lt;/li&gt;
&lt;li&gt;When a product's &lt;code&gt;$price&lt;/code&gt; does not exceed the threshold, it should not ship for free&lt;/li&gt;
&lt;li&gt;When a product's &lt;code&gt;$shipsforFree&lt;/code&gt; property set to &lt;code&gt;true&lt;/code&gt;, it should ship for free
&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShippingCalculatorTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/** @test */&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;product_ships_for_free_when_price_is_above_treshold&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$product&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;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREE_SHIPPING_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&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;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;hasFreeShipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/** @test */&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;product_does_not_ship_for_free_when_price_is_below_treshold&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$product&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;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREE_SHIPPING_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&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;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;hasFreeShipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/** @test */&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;product_ships_for_free_when_ships_for_free_property_is_true&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$product&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;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREE_SHIPPING_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;shipsForFree&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;hasFreeShipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&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;With these three tests, the code coverage report from PHPUnit (which you can generate using &lt;code&gt;vendor/bin/phpunit --coverage-text&lt;/code&gt;) reveals a code coverage of 100% for both classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Summary:
  Classes: 100.00% &lt;span class="o"&gt;(&lt;/span&gt;2/2&lt;span class="o"&gt;)&lt;/span&gt;
  Methods: 100.00% &lt;span class="o"&gt;(&lt;/span&gt;2/2&lt;span class="o"&gt;)&lt;/span&gt;
  Lines:   100.00% &lt;span class="o"&gt;(&lt;/span&gt;7/7&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Running Infection PHP
&lt;/h4&gt;

&lt;p&gt;Now, we'll run Infection PHP using &lt;code&gt;vendor/bin/infection&lt;/code&gt; and see if any mutants escape.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.M....                                               &lt;span class="o"&gt;(&lt;/span&gt;6 / 6&lt;span class="o"&gt;)&lt;/span&gt;

6 mutations were generated:
       5 mutants were killed
       0 mutants were not covered by tests
       1 covered mutants were not detected
       ...

Metrics:
         Mutation Score Indicator &lt;span class="o"&gt;(&lt;/span&gt;MSI&lt;span class="o"&gt;)&lt;/span&gt;: 83%
         Mutation Code Coverage: 100%
         Covered Code MSI: 83%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh no, a mutant has escaped!&lt;/p&gt;

&lt;p&gt;Furthermore, our &lt;strong&gt;MSI&lt;/strong&gt; is &lt;strong&gt;83%&lt;/strong&gt;, while the generated mutations covered 100% of the code. This means 5 out of 6 mutants were killed.&lt;/p&gt;

&lt;p&gt;When we check our log file, we see the &lt;code&gt;[M] GreaterThanOrEqualTo&lt;/code&gt; mutant escaped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Escaped mutants:
&lt;span class="o"&gt;================&lt;/span&gt;
1&lt;span class="o"&gt;)&lt;/span&gt; ../src/ShippingCalculator.php:15    &lt;span class="o"&gt;[&lt;/span&gt;M] GreaterThanOrEqualTo

&lt;span class="nt"&gt;--------&lt;/span&gt; Original
+++ New
@@ @@
-        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;-&amp;gt;price &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; self::FREE_SHIPPING_THRESHOLD&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
+        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;-&amp;gt;price &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; self::FREE_SHIPPING_THRESHOLD&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The missing test
&lt;/h4&gt;

&lt;p&gt;Now it becomes obvious that we are &lt;strong&gt;missing a crucial test&lt;/strong&gt;: we did not assert what happens when the &lt;code&gt;$price&lt;/code&gt; is &lt;strong&gt;equal&lt;/strong&gt; to the free shipping threshold.&lt;/p&gt;

&lt;p&gt;Infection PHP expected at least one of our tests to fail when mutating the conditional from a &lt;code&gt;great-than-or-equals&lt;/code&gt; to a &lt;code&gt;greater-than&lt;/code&gt; comparison. Since our test suite didn't fail, this mutation got away unnoticed.&lt;/p&gt;

&lt;p&gt;Let's fix that by adding in the "forgotten" boundary test:&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 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;product_ships_for_free_when_price_equals_threshold&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$product&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;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FREE_SHIPPING_THRESHOLD&lt;/span&gt;&lt;span class="p"&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;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ShippingCalculator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;hasFreeShipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$product&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;When we run Infection again, we get an MSI of 100%. No escaped mutants this time!&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Redirection
&lt;/h3&gt;

&lt;p&gt;After using mutation testing in my Laravel projects, I learned that mutation testing drives out tests I would otherwise not have written.&lt;/p&gt;

&lt;p&gt;When redirecting users to a specific page with a "date" parameter from the &lt;code&gt;store()&lt;/code&gt; controller action.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppointmentController&lt;/span&gt;
&lt;span class="p"&gt;{&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;store&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// controller logic&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="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'appointments.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$date&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;In my test for this controller action, I initially did not include a check that the user was redirected to a specific page. However, when running the Infection PHP mutation test suite I got the following &lt;strong&gt;escaped mutant&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;3&lt;span class="o"&gt;)&lt;/span&gt; .../app/Http/Controllers/AppointmentController.php:163    &lt;span class="o"&gt;[&lt;/span&gt;M] ArrayItemRemoval

&lt;span class="nt"&gt;--------&lt;/span&gt; Original
+++ New
@@ @@
-        &lt;span class="k"&gt;return &lt;/span&gt;redirect&lt;span class="o"&gt;(&lt;/span&gt;route&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'appointments.index'&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="o"&gt;]))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
+        &lt;span class="k"&gt;return &lt;/span&gt;redirect&lt;span class="o"&gt;(&lt;/span&gt;route&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'appointments.index'&lt;/span&gt;, &lt;span class="o"&gt;[]))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="o"&gt;}&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this &lt;code&gt;date&lt;/code&gt; parameter in the redirect is &lt;strong&gt;crucial&lt;/strong&gt; for users of the application to land on the appropriate page, &lt;strong&gt;I didn't have a test&lt;/strong&gt; for this behavior. Until now, not having a test for the specific redirect could go unnoticed. Thanks to mutation testing I've now discovered this gap and it has &lt;strong&gt;forced&lt;/strong&gt; me to add a test covering this scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Infection in CI
&lt;/h2&gt;

&lt;p&gt;It is possible to run mutation tests in Continuous Integration (CI) against newly added code or before builds. &lt;/p&gt;

&lt;p&gt;You can set a &lt;code&gt;--min-msi&lt;/code&gt; score option to force a certain percentage of mutants to be killed and gradually increase this number to improve the project's test suite. If the MSI score is lower than required, the build will fail. &lt;/p&gt;

&lt;h3&gt;
  
  
  Small projects
&lt;/h3&gt;

&lt;p&gt;For small projects, the simplest option is to first run the PHPUnit tests while saving the test coverage files in a &lt;code&gt;build&lt;/code&gt; directory and feeding those directly to Infection PHP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vendor/bin/phpunit &lt;span class="nt"&gt;--coverage-xml&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;build/coverage-xml &lt;span class="nt"&gt;--log-junit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;build/phpunit.junit.xml
vendor/bin/infection &lt;span class="nt"&gt;--threads&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;--coverage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;build &lt;span class="nt"&gt;--min-msi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;70
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Although Infection can be integrated with any CI setup, I like to highlight the newly added integration with GitHub Actions (in version 0.20), where it is possible to log escaped mutants in committed code directly within a PR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dEPVXKva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j798ogcgp6jaewaag157.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dEPVXKva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j798ogcgp6jaewaag157.png" alt="Infection GitHub Annotations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, you can add the following action to your GitHub Action workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Infection for added (A) and modified (M) files&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;git fetch --depth=1 origin $GITHUB_BASE_REF&lt;/span&gt;
    &lt;span class="s"&gt;php vendor/bin/infection --threads=2 --git-diff-base=origin/$GITHUB_BASE_REF --git-diff-filter=AM --logger-github&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out &lt;a href="https://infection.github.io/2020/11/01/whats-new-in-0.20.0/#Github-Annotations-logger-%F0%9F%9A%80"&gt;the release documentation&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Larger projects
&lt;/h3&gt;

&lt;p&gt;If you want to use Mutation Testing in CI for larger projects, make sure to check out &lt;a href="https://alejandrocelaya.blog/2018/02/17/mutation-testing-with-infection-in-big-php-projects/"&gt;this article&lt;/a&gt; by Alejandro Celaya. &lt;/p&gt;

&lt;p&gt;In his blog post, he advises using &lt;code&gt;phpdbg&lt;/code&gt; to run Infection which leads to a clear gain in performance.&lt;/p&gt;

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

&lt;p&gt;I hope this blog post provides some insight and direction to get started with Mutation Testing. In summary, Mutation Testing creates mutants from your source code which run against your existing test suite. If none of these tests fail, it is a sign that this piece of the source code (&lt;em&gt;the mutant&lt;/em&gt;) is weakly tested. This methodology helps to identify gaps in your current test suite as well as dead or unnecessary code (or &lt;em&gt;e.g.&lt;/em&gt; unnecessary broad method visibility).&lt;/p&gt;

&lt;p&gt;Although this blog post merely covers some examples, there is a lot more to explore in Mutation Testing: make sure to check out &lt;a href="https://infection.github.io/guide/mutators.html"&gt;all available mutators&lt;/a&gt; and the &lt;a href="https://infection.github.io/guide/how-to.html"&gt;how-to guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;p&gt;While Mutation Testing has a lot of advantages, think of the following possible drawbacks and things to be aware of.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Running mutation tests is quite slow. You can speed up the process by allowing testing in parallel (set &lt;code&gt;--threads&lt;/code&gt; &amp;gt; 1).&lt;/li&gt;
&lt;li&gt;Not all mutants have to be killed. Trying to kill all mutants might lead to &lt;strong&gt;tight coupling&lt;/strong&gt; between your code and your tests. Or lead to code that is &lt;em&gt;too strict&lt;/em&gt; and no longer &lt;strong&gt;flexible&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  More resources
&lt;/h3&gt;

&lt;p&gt;If you want to learn more about Mutation Testing, make sure to check out these great resources on Mutation Testing (using Infection):&lt;/p&gt;

&lt;p&gt;Articles:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@maks_rafalko/infection-mutation-testing-framework-c9ccf02eefd1"&gt;Infection: Mutation Testing Framework - Maks Rafalko (Infection PHP creator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.moxio.com/blog/39/mutation-testing-in-php"&gt;Mutation testing in PHP - Moxio (    Arnout Boks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://matthewdaly.co.uk/blog/2018/09/13/mutation-testing-with-infection/"&gt;Mutation Testing With Infection - Matthew Daly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alejandrocelaya.blog/2018/02/17/mutation-testing-with-infection-in-big-php-projects/"&gt;Mutation testing with infection in big PHP projects - Alejandro Celaya&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Talks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://talksatconfs.com/talks/ddfa9479-2ceb-4634-90e7-ca53aa26c24c/kill-all-mutants-intro-to-mutation-testing"&gt;Kill All Mutants! - Dave Aronson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://skillsmatter.com/skillscasts/11237-better-code-by-making-bugs-with-mutation-testing"&gt;Better Code By Making Bugs with Mutation Testing - Theo Fidry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>testing</category>
      <category>php</category>
      <category>laravel</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to use fake dates in Cypress end-to-end testing</title>
      <dc:creator>John Braun</dc:creator>
      <pubDate>Mon, 07 Sep 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/johnbraun/how-to-use-fake-dates-in-cypress-end-to-end-testing-19h5</link>
      <guid>https://dev.to/johnbraun/how-to-use-fake-dates-in-cypress-end-to-end-testing-19h5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When writing an end-to-end test (or so called browser test), it might be tricky to force the application to use a certain (fake) date. In this post, I share my approach using a custom middleware to be able to write Cypress tests while your application is in a specific (fake) date.&lt;/p&gt;

&lt;p&gt;Recently, I wanted to write a &lt;a href="https://cypress.io"&gt;Cypress&lt;/a&gt; acceptance ("end-to-end") test for a Laravel project where I wanted &lt;code&gt;Carbon::now()&lt;/code&gt; to return a specific (fake) date, so I could act as if it were that specific date in my browser test.&lt;/p&gt;

&lt;p&gt;Initially, I thought I could just set &lt;code&gt;Carbon::setTestNow()&lt;/code&gt;, however this would only survive a single request and I wanted the custom date I specified to be applied globally in the application to make assertions about several endpoints within these tests.&lt;/p&gt;

&lt;p&gt;After a few online searches, I decided to ask for help &lt;a href="https://twitter.com/JhnBrn90/status/1302680650860855297"&gt;on Twitter&lt;/a&gt; and got a reply from &lt;a href="https://twitter.com/sasin91"&gt;@sasin91&lt;/a&gt; which sparked a new idea: perhaps I could use a middleware 💡.&lt;/p&gt;

&lt;p&gt;Eventually, I ended up using the following setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The middleware approach
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adding a new middleware
&lt;/h3&gt;

&lt;p&gt;First, I created a new middleware called &lt;code&gt;SetTestDate&lt;/code&gt;. We want to apply this middleware within the 'web' middleware group, but only when the environment is either "local" (the default dev environment) or "testing" (the environment in &lt;code&gt;.env.cypress&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Let's first conditionally push our middleware to the 'web' group within the &lt;code&gt;boot()&lt;/code&gt; method of the &lt;code&gt;AppServiceProvider&lt;/code&gt; as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Providers/AppServiceProvider.php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Http\Middleware\SetTestDate&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\Contracts\Http\Kernel&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppServiceProvider&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="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="k"&gt;if&lt;/span&gt; &lt;span class="p"&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;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'local'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'testing'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$kernel&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;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;appendMiddlewareToGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SetTestDate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Within the &lt;code&gt;SetTestDate&lt;/code&gt; middleware, we want to check if a certain cookie ("set_test_date" in this example) is set containing the specified (fake) date. For clarity and consistency let's store the name of this cookie in a class constant &lt;code&gt;TEST_DATE_COOKIE&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;// app/Http/Middleware/SetTestDate.php

&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\Http\Middleware&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;Carbon\Carbon&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;Closure&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;SetTestDate&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TEST_DATE_COOKIE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'set_test_date'&lt;/span&gt;&lt;span class="p"&gt;;&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;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&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;wantsToSetTestDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&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;setDateNow&lt;/span&gt;&lt;span class="p"&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;cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TEST_DATE_COOKIE&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;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setDateNow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Carbon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setTestNow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Carbon&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;wantsToSetTestDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&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;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TEST_DATE_COOKIE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&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;h3&gt;
  
  
  Setting a custom (fake) date in the Cypress test
&lt;/h3&gt;

&lt;p&gt;Now that we have our middleware in place, we can use the request variable we defined in our middleware to visit a route which uses the fake date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shows the current date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tuesday 1 september 2020&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;set_test_date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tuesday, September 1st 2020&lt;/span&gt;&lt;span class="dl"&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;h3&gt;
  
  
  Encrypted cookies
&lt;/h3&gt;

&lt;p&gt;If you'd run the test at this stage, no cookie will be resolved from the request and &lt;code&gt;null&lt;/code&gt; will be returned for &lt;code&gt;$request-&amp;gt;cookie('set_test_date')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since Laravel encrypts the cookies by default due to the &lt;code&gt;EncryptCookies&lt;/code&gt; middleware, we need to create an exception for the &lt;code&gt;set_test_date&lt;/code&gt; cookie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Middleware/EncryptCookies.php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EncryptCookies&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Middleware&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;$except&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'set_test_date'&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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It is possible to manipulate the current date in Laravel &lt;em&gt;end-to-end&lt;/em&gt; tests using a middleware that accepts a &lt;em&gt;fake date&lt;/em&gt; within a certain cookie and calling &lt;code&gt;Carbon::setTestNow()&lt;/code&gt; for each request that has this cookie.&lt;/p&gt;

&lt;p&gt;When using Cypress in a Laravel application, make sure to checkout out the &lt;a href="https://github.com/laracasts/cypress"&gt;laracasts/cypress&lt;/a&gt; helper package.&lt;/p&gt;

&lt;p&gt;To learn more about Cypress in the context of Laravel applications, I can highly recommend &lt;a href="https://laracasts.com/series/cypress-and-laravel-integration"&gt;this video series&lt;/a&gt; on Laracasts.&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>laravel</category>
      <category>testing</category>
    </item>
    <item>
      <title>
Refactoring toward reusable Vue components</title>
      <dc:creator>John Braun</dc:creator>
      <pubDate>Sun, 26 Jan 2020 14:07:14 +0000</pubDate>
      <link>https://dev.to/johnbraun/refactoring-toward-reusable-vue-components-2f69</link>
      <guid>https://dev.to/johnbraun/refactoring-toward-reusable-vue-components-2f69</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In this post, I want to highlight possible refactoring strategies toward &lt;strong&gt;resuable&lt;/strong&gt; Vue components: &lt;strong&gt;renderless components&lt;/strong&gt; &lt;em&gt;vs&lt;/em&gt; using &lt;strong&gt;Provide/Inject&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;To illustrate these strategies, I'll use a &lt;strong&gt;Dragonball Z&lt;/strong&gt; character selectioncomponent made using  &lt;a href="https://vuejs.org/"&gt;VueJS&lt;/a&gt;  and  &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; . &lt;/p&gt;

&lt;p&gt;For this project I've used Laravel as a backend and some things might be Laravel specific.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7E-ET01L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://johnbraun.blog/storage/wink/images/ITkqjfrUadt5c681BLjAWkKvTFvFZNydeQHyvsVj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7E-ET01L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://johnbraun.blog/storage/wink/images/ITkqjfrUadt5c681BLjAWkKvTFvFZNydeQHyvsVj.gif" alt="Choosing a Dragonball Z fighter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible approaches
&lt;/h2&gt;

&lt;p&gt;This post is divided in three sections. Each section is accompanied by a CodeSandbox demo, illustrating the approach.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The straightforward approach&lt;/strong&gt;: It makes sense to focus on the simplest implementation first, and just get it to work. Then, refactor later. In this section we'll build the selector from scratch and I'll remind you of some basic principles. &lt;a href="https://codesandbox.io/s/straightforward-approach-0f77m?fontsize=14"&gt;View the straightforward approach on CodeSandbox&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Renderless approach&lt;/strong&gt;: One way to make your components (more) reusable is taking advantage of Vue's render() function. In this section I'll show you how to take full control of the way your data is displayed (rendered) within the view.  &lt;a href="https://codesandbox.io/embed/renderless-approach-70cj2?fontsize=14"&gt;View the renderless approach on CodeSandbox&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Provide/Inject approach&lt;/strong&gt;: Although Renderless components give more flexibility than standard components, a major downside is that all code in your view gets more complicated/verbose. The &lt;strong&gt;Provide / Inject&lt;/strong&gt; strategy is somewhere in the middle between the other two strategies.  &lt;a href="https://codesandbox.io/embed/provide-inject-approach-d4cqd?fontsize=14"&gt;View the provide/inject approach on CodeSandbox&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What do we want to achieve?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Show 35 avatars of various Dragonball Z characters&lt;/li&gt;
&lt;li&gt;Make selection by clicking &lt;/li&gt;
&lt;li&gt;Undo the selection by clicking (again)&lt;/li&gt;
&lt;li&gt;Store the &lt;em&gt;id&lt;/em&gt; of the selected character in hidden  form field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;Character&lt;/strong&gt; model has an &lt;em&gt;id&lt;/em&gt;, &lt;em&gt;avatar&lt;/em&gt; and &lt;em&gt;name&lt;/em&gt; column in the database. The &lt;em&gt;avatar&lt;/em&gt; column holds the relative path to the image source.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; I will refer to &lt;a href="https://laravel.com/docs/5.8/blade"&gt;blade directives&lt;/a&gt; , which I will not go into detail to in this post.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. The straightforward approach
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Creating the view
&lt;/h2&gt;

&lt;p&gt;Given we have access to a &lt;code&gt;$characters&lt;/code&gt; variable, which holds an array of characters, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Goku"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;avatar:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"goku.jpeg"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;so&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on...&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We might structure our view file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/fighters"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- other fields --&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;character-selection&lt;/span&gt;
      &lt;span class="na"&gt;:characters=&lt;/span&gt;&lt;span class="s"&gt;"{{ $characters }}"&lt;/span&gt; 
       &lt;span class="na"&gt;previous-character=&lt;/span&gt;&lt;span class="s"&gt;"{{ old('character_id') }}"&lt;/span&gt; 
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="c"&gt;&amp;lt;!-- submit button --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;:characters&lt;/code&gt; and &lt;code&gt;previous-character&lt;/code&gt; props we're sending through will be available in our component. We leverage Laravel's &lt;code&gt;old()&lt;/code&gt; helper to pass the previous selection to the component (on failed submission of the form) to make sure we remember the selected character.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Vue component
&lt;/h2&gt;

&lt;p&gt;After you've created the &lt;code&gt;CharacterSelection.vue&lt;/code&gt; file, register the component globally within &lt;code&gt;resources/js/app.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;character-selection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/CharacterSelection.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;I won't mention this step whenever creating new components in the rest of this post, but please keep in mind that all Vue components mentioned are registered globally.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Accepting the props
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;CharacterSelection&lt;/code&gt; component, we'll accept the &lt;code&gt;previous-character&lt;/code&gt; and &lt;code&gt;characters&lt;/code&gt; props in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;characters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previous-character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Iterating over all characters
&lt;/h2&gt;

&lt;p&gt;Furthermore, we want to iterate over all characters and show an image in the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; section of our component. From the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; we can only return one root element and therefore have to wrap everything in a parent element, &lt;em&gt;e.g.&lt;/em&gt; a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;When iterating over items, due to its reactivity, Vue needs to be able to differentiate between DOM elements which is why we also pass a unique &lt;code&gt;:key&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;To display the avatars in a grid, we employ flexbox by adding the tailwind CSS classes 'flex' and 'flex-wrap' to wrap as necessary. &lt;/p&gt;

&lt;p&gt;The images are displayed at a predefined width and height (w-16 and h-12), also using Tailwind CSS classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &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;"flex flex-wrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
       &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"character in characters"&lt;/span&gt; 
       &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"character.id"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-16 h-12"&lt;/span&gt; 
         &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"`/images/fighters/${character.avatar}`"&lt;/span&gt; 
        &lt;span class="na"&gt;:title=&lt;/span&gt;&lt;span class="s"&gt;"character.name"&lt;/span&gt;
        &lt;span class="na"&gt;:alt=&lt;/span&gt;&lt;span class="s"&gt;"character.name"&lt;/span&gt; 
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding reactivity
&lt;/h2&gt;

&lt;p&gt;Although we can see the avatars now, there is no reactivity. To help us achieve that, we should employ a dedicated &lt;code&gt;&amp;lt;single-character&amp;gt;&lt;/code&gt; Vue component representing a single avatar rather than a &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;. This child component will receive the character as a prop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &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;"flex flex-wrap justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;single-character&lt;/span&gt;
        &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"character in characters"&lt;/span&gt;
        &lt;span class="na"&gt;:character=&lt;/span&gt;&lt;span class="s"&gt;"character"&lt;/span&gt;
        &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"character.id"&lt;/span&gt;
        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;SingleCharacter&lt;/code&gt; child component, we first need to make sure to render all avatars properly. We accept the current character and show the image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &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;"(omitted for clarity...)"&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;avatar&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="s2"&gt;`/images/fighters/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar&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="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that the avatars are showing up, let's add some reactivity by adding a click handler (&lt;code&gt;@click&lt;/code&gt;) and let the parent component know that we've made a choice by emitting an event (&lt;code&gt;this.$emit&lt;/code&gt;) sending along the character's id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &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;"(omitted for clarity...)"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"selectCharacter"&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;selectCharacter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;character-selected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To make the parent component capable of listening and acting on this event, we'll need to make some adjustments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listening to the 'character-selected' event
&lt;/h2&gt;

&lt;p&gt;First, we need to listen for an event called &lt;code&gt;character-selected&lt;/code&gt; by specifying an attribute on our child component: &lt;code&gt;@character-selected&lt;/code&gt; which will call a method that sets the internal property &lt;code&gt;selectedCharacter&lt;/code&gt; of the parent component to the selected character's id. &lt;/p&gt;

&lt;p&gt;This value is then bound using v-bind to the hidden input field using the &lt;code&gt;:value&lt;/code&gt; attribute. If the selected id was already selected, we set the &lt;code&gt;selectedCharacter&lt;/code&gt; property to &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &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;"flex flex-wrap justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;single-character&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;character-selected=&lt;/span&gt;&lt;span class="s"&gt;"selectCharacter"&lt;/span&gt;
        &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"character in characters"&lt;/span&gt;
        &lt;span class="na"&gt;:character=&lt;/span&gt;&lt;span class="s"&gt;"character"&lt;/span&gt;
        &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"character.id"&lt;/span&gt;
        &lt;span class="na"&gt;:selected=&lt;/span&gt;&lt;span class="s"&gt;"selectedCharacter === character.id"&lt;/span&gt;
        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; 
      &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"character_id"&lt;/span&gt; 
      &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"selectedCharacter"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;characters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previous-character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;selectedCharacter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousCharacter&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="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;selectCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedCharacter&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&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="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedCharacter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedCharacter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Tell the child which character is currently selected
&lt;/h2&gt;

&lt;p&gt;Lastly, our child component needs to know if it is currently selected. Therefore, we also pass the &lt;code&gt;:selected&lt;/code&gt; attribute. Within the child component we can accept the value as a prop and let it determine the classes which need to be applied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"selectCharacter"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt;
    &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"classes"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;selectCharacter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;character-selected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;avatar&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="s2"&gt;`/images/fighters/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar&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="p"&gt;},&lt;/span&gt;

    &lt;span class="nx"&gt;classes&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
        &lt;span class="s2"&gt;`border-2 border-black shadow-lg opacity-100`&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opacity-35&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opacity-85&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This concludes our first approach and we have a basic component which does exactly what we want. However, the current implementation is tightly bound to our specific grid of 7 x 5 and contains terminology like 'character' and 'fighters'. What if we want to use this component in a quiz about animals? &lt;/p&gt;

&lt;h1&gt;
  
  
  2. Refactoring to a renderless component
&lt;/h1&gt;

&lt;p&gt;Like we concluded from the previous section, the straightforward example works but is hard to reuse. All details regarding styles, layout and image paths of the avatars are hard coded in the components. If those change, we need to create a new component. That's not what we want.&lt;/p&gt;

&lt;p&gt;Adam Wathan has a  &lt;a href="https://adamwathan.me/renderless-components-in-vuejs/"&gt;great post&lt;/a&gt;  (and  &lt;a href="https://adamwathan.me/advanced-vue-component-design/"&gt;awesome videos&lt;/a&gt; !) that describe the goal of renderless components pretty well: &lt;em&gt;"Separating Presentation and Behavior".&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ideally, we want to be able to configure (some) behaviour from our view directly as indicated in the HTML below (which will not work, yet). This way, the Vue component allows itself to be "decorated" from within the layout file. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;create.blade.php&lt;/code&gt; view file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/fighters"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 

  &lt;span class="nt"&gt;&amp;lt;image-selector&lt;/span&gt; 
       &lt;span class="na"&gt;pre-selected=&lt;/span&gt;&lt;span class="s"&gt;"{{ old('character_id') }}"&lt;/span&gt;
      &lt;span class="na"&gt;:images=&lt;/span&gt;&lt;span class="s"&gt;"{{ $characters }}"&lt;/span&gt;
      &lt;span class="na"&gt;selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt;
       &lt;span class="na"&gt;once-selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 

      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"character in images"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"character.id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- render each image here --&amp;gt;&lt;/span&gt; 
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  

  &lt;span class="nt"&gt;&amp;lt;/image-selector&amp;gt;&lt;/span&gt;  

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit the form&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice that the component's name and the name of the props are more generic. Instead of &lt;code&gt;characters&lt;/code&gt;, we now have an &lt;code&gt;images&lt;/code&gt; prop. Although we changed the name of the prop, we still want to pass our original source of &lt;code&gt;$characters&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looping over the images
&lt;/h2&gt;

&lt;p&gt;From here on, I assume you already understand what &lt;a href="https://vuejs.org/v2/guide/components-slots.html"&gt;Vue's slots&lt;/a&gt;  are and how they work.&lt;/p&gt;

&lt;p&gt;Since we try to iterate over a &lt;code&gt;images&lt;/code&gt; variable which &lt;strong&gt;we don't have access to&lt;/strong&gt;, the listed code above will not work. Here is where &lt;strong&gt;scoped slots&lt;/strong&gt; come into play. &lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;scoped&lt;/strong&gt;slot allows us to pass data from a parent component to a child. The child might then use that data in rendering the &lt;strong&gt;slot&lt;/strong&gt; of the parent component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the renderless component
&lt;/h2&gt;

&lt;p&gt;Since the templating part will be managed by the layout file and passed back via a single scoped slot, we don't need to provide a template for our component. By definition, renderless components don't have a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; section, just a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. &lt;/p&gt;

&lt;p&gt;Here we can still accept any props and declare the &lt;code&gt;render()&lt;/code&gt; function, which will pass any variables ('slot props') back to the child component. &lt;/p&gt;

&lt;p&gt;For our &lt;code&gt;images&lt;/code&gt; prop, that would look as follows, in a more generically named &lt;code&gt;ImageSelector.vue&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="nx"&gt;render&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$scopedSlots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;images&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the slot props in our layout file
&lt;/h2&gt;

&lt;p&gt;To loop over the images in our current layout file, we will need to extract the images variable from the slot scope. Note that the syntax has been updated in Vue 2.6.0+ from using slot-scope to &lt;code&gt;v-slot&lt;/code&gt; (&lt;a href="https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots"&gt;more info&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create.blade.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/fighters"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 

  &lt;span class="nt"&gt;&amp;lt;image-selector&lt;/span&gt; 
       &lt;span class="na"&gt;pre-selected=&lt;/span&gt;&lt;span class="s"&gt;"{{ old('character_id') }}"&lt;/span&gt;
      &lt;span class="na"&gt;:images=&lt;/span&gt;&lt;span class="s"&gt;"{{ $characters }}"&lt;/span&gt;
      &lt;span class="na"&gt;selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt;
       &lt;span class="na"&gt;once-selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

       &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;v-slot=&lt;/span&gt;&lt;span class="s"&gt;"{ images }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- we need to return a single root node --&amp;gt;&lt;/span&gt;
         &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;"flex"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;      

          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"character in images"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"character.id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- render each image here --&amp;gt;&lt;/span&gt; 
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  

        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; 
      &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt; 

  &lt;span class="nt"&gt;&amp;lt;/image-selector&amp;gt;&lt;/span&gt;  

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit the form&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To bring the component to life, &lt;em&gt;i.e.&lt;/em&gt; adding reactivity, let's add the &lt;code&gt;props&lt;/code&gt;, &lt;code&gt;data()&lt;/code&gt; and &lt;code&gt;methods()&lt;/code&gt; from the &lt;code&gt;SingleCharacter.vue&lt;/code&gt; and &lt;code&gt;CharacterSelection.vue&lt;/code&gt; components (from the previous, 'straightforward' approach) and insert them above our &lt;code&gt;render()&lt;/code&gt; function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing data via the render() function
&lt;/h2&gt;

&lt;p&gt;To keep the component as generic as possible, we'll catch any events using Vue's &lt;code&gt;v-on&lt;/code&gt; directive and proxy them through to the &lt;code&gt;imageEvents()&lt;/code&gt; method, which registers handlers for specific actions. In our case, the &lt;code&gt;click&lt;/code&gt; event. The props are passed through using &lt;code&gt;imageProps()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we want to use &lt;code&gt;v-bind&lt;/code&gt; on the input field, we'll also need to offer the selected image as a &lt;code&gt;value&lt;/code&gt; through the &lt;code&gt;inputProps()&lt;/code&gt; method. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;ImageSelector.vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preSelected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selectedClasses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onceSelectedClasses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt; 

    &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selectedImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preSelected&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="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selectImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedClasses&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onceSelectedClasses&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&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="p"&gt;},&lt;/span&gt;

    &lt;span class="nx"&gt;render&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$scopedSlots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;inputProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="na"&gt;imageProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;

        &lt;span class="na"&gt;imageEvents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Use variables in the view
&lt;/h2&gt;

&lt;p&gt;Now, from within our &lt;code&gt;create.blade.php&lt;/code&gt; view file we can leverage destructuring to obtain each key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/fighters"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;image-selector&lt;/span&gt; 
    &lt;span class="na"&gt;:images=&lt;/span&gt;&lt;span class="s"&gt;"{{ $characters }}"&lt;/span&gt; 
    &lt;span class="na"&gt;pre-selected=&lt;/span&gt;&lt;span class="s"&gt;"{{ old('character_id') }}"&lt;/span&gt; 
    &lt;span class="na"&gt;selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"border-2 border-black shadow-lg opacity-100"&lt;/span&gt;
    &lt;span class="na"&gt;once-selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"opacity-35"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; 
      &lt;span class="na"&gt;v-slot=&lt;/span&gt;&lt;span class="s"&gt;"{ 
        images, 
        imageProps, 
        imageEvents, 
        inputProps 
      }"&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

       &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;"single-root-element"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
         &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;"flex flex-wrap justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

           &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
             &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"character in images"&lt;/span&gt; 
            &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"character.id"&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"(classes for an image...)"&lt;/span&gt; 
            &lt;span class="na"&gt;v-on=&lt;/span&gt;&lt;span class="s"&gt;"imageEvents(character)"&lt;/span&gt;
            &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"imageProps(character)"&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

             &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
               &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"`/images/fighters/${character.avatar}`"&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

         &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

         &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
           &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; 
           &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"character_id"&lt;/span&gt; 
           &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"inputProps"&lt;/span&gt;
         &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/image-selector&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit form&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We now have a customizable component which can be reused amongst other projects.&lt;/p&gt;

&lt;p&gt;A downside to this approach is that it generates a lot of extra code and it results in a somewhat more complex layout file. &lt;/p&gt;

&lt;p&gt;However, since we have a configurable, reusable component it is possible to generate pre-configured components which are composed by one or more renderless components.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Provide / Inject approach
&lt;/h1&gt;

&lt;p&gt;Vue offers another solution, which falls somewhere in the middle between the "straightforward" and renderless approach. It gives more flexibility to configure the component in the view template, while generating a less verbose layout file.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;create.blade.php&lt;/code&gt; view, we render a &lt;code&gt;&amp;lt;single-character&amp;gt;&lt;/code&gt; component within a &lt;code&gt;&amp;lt;character-selection&amp;gt;&lt;/code&gt; wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;character-selection&lt;/span&gt; 
  &lt;span class="na"&gt;previous-character=&lt;/span&gt;&lt;span class="s"&gt;"{{ old('character_id') }}"&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @foreach ($characters as $character)
        &lt;span class="c"&gt;&amp;lt;!-- Looping over Single Character component --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;single-character&lt;/span&gt;
          &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt; 
          &lt;span class="na"&gt;:character=&lt;/span&gt;&lt;span class="s"&gt;"{{ $character }}"&lt;/span&gt;
          &lt;span class="na"&gt;imageurl=&lt;/span&gt;&lt;span class="s"&gt;"/images/fighters/{{ $character-&amp;gt;avatar }}"&lt;/span&gt;
          &lt;span class="na"&gt;selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt;
         &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    @endforeach
&lt;span class="nt"&gt;&amp;lt;/character-selection&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At this stage, all looks very familiar to our first approach, except that we're taking care of looping over &lt;code&gt;&amp;lt;single-character&amp;gt;&lt;/code&gt; in the view instead of in the &lt;code&gt;&amp;lt;character-selection&amp;gt;&lt;/code&gt; Vue parent component. &lt;/p&gt;

&lt;p&gt;Now, instead of emitting an event from our child component to the parent, state will be shared in a &lt;code&gt;characterSelectionState&lt;/code&gt; property. This property will be provided by the parent component and injected into the child component. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The child component, however, can manipulate this shared (reactive) property&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, we &lt;strong&gt;provide&lt;/strong&gt; a shared variable &lt;code&gt;characterSelectionState&lt;/code&gt; using the &lt;code&gt;provide()&lt;/code&gt; method in &lt;code&gt;CharacterSelection.vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
       &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;"flex flex-wrap justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

       &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
          &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; 
          &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"character_id"&lt;/span&gt; 
          &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"sharedState.selectedCharacter"&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

   &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previous-character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

       &lt;span class="nx"&gt;provide&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="p"&gt;{&lt;/span&gt;
               &lt;span class="na"&gt;characterSelectionState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sharedState&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;

       &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
               &lt;span class="na"&gt;sharedState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="na"&gt;selectedCharacter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousCharacter&lt;/span&gt;
                     &lt;span class="p"&gt;),&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;SingleCharacter.vue&lt;/code&gt; we &lt;strong&gt;inject&lt;/strong&gt; the &lt;code&gt;characterSelectionState&lt;/code&gt; variable, making it available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"selectCharacter"&lt;/span&gt; &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"classes"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"imageurl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;imageUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selectedClasses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

       &lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;characterSelectionState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

       &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nx"&gt;selectCharacter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;characterSelectionState&lt;/span&gt;
                 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedCharacter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
                      &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nx"&gt;active&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;characterSelectionState&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedCharacter&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
           &lt;span class="p"&gt;},&lt;/span&gt;

           &lt;span class="nx"&gt;classes&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
                     &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedClasses&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
           &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In conclusion, Vue offers a couple of nice possibilities which allows us to write reusable components:&lt;br&gt;
&lt;strong&gt;Renderless components&lt;/strong&gt; give full control over how they are rendered since the behaviour is completely decoupled. &lt;/p&gt;

&lt;p&gt;However, in the end, you do create a more complex component &lt;strong&gt;and&lt;/strong&gt; you end up with more verbose code in your view template. &lt;/p&gt;

&lt;p&gt;Vue's &lt;strong&gt;Provide/Inject&lt;/strong&gt; methods can be a useful intermediate solution if you don't need a full blown completely configurable renderless component, but still want some configurational flexibility. &lt;/p&gt;

&lt;p&gt;Each of the three approaches mentioned in this post have their use. Each approach has their advantages and downsides. &lt;/p&gt;
&lt;h2&gt;
  
  
  The power of Renderless Components
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: I would advise to always go with the &lt;strong&gt;simplest&lt;/strong&gt;option for your specific needs. Don't create a renderless component when all you need is a simple component that you'll only use once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/embed/renderless-select-multiple-characters-j39lo?fontsize=14"&gt;View the multiple images approach on CodeSandbox&lt;/a&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Selecting Multiple Images, using our renderless component
&lt;/h3&gt;

&lt;p&gt;So far, we can use our renderless component to display any set of images in a particular way. &lt;br&gt;
However, what if we want to select &lt;strong&gt;multiple&lt;/strong&gt; images? &lt;/p&gt;

&lt;p&gt;With a little tweak to our renderless component, we can come up with the following solution in &lt;code&gt;create.blade.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;multiple-image-selector&lt;/span&gt; 
 &lt;span class="na"&gt;:images=&lt;/span&gt;&lt;span class="s"&gt;"{{ $characters }}"&lt;/span&gt;
 &lt;span class="na"&gt;selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"border-2 border-black shadow-lg opacity-100"&lt;/span&gt;
 &lt;span class="na"&gt;once-selected-classes=&lt;/span&gt;&lt;span class="s"&gt;"opacity-35"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;v-slot=&lt;/span&gt;&lt;span class="s"&gt;"{ 
     images, 
    imageProps, 
    imageEvents, 
    inputProps 
   }"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &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;"single-root-element"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

      &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;"flex flex-wrap justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
          &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"character in images"&lt;/span&gt; 
          &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"character.id"&lt;/span&gt;
          &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"(omitted for clarity...)"&lt;/span&gt; 
          &lt;span class="na"&gt;v-on=&lt;/span&gt;&lt;span class="s"&gt;"imageEvents(character)"&lt;/span&gt;
          &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"imageProps(character)"&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

           &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"`/images/fighters/${character.avatar}`"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

         &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; 
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"character_id"&lt;/span&gt; 
        &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"inputProps"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/multiple-image-selector&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, in our renderless &lt;code&gt;MultipleImageSelector.vue&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected-classes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;once-selected-classes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selectedImages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selectImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedClasses&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
                 &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onceSelectedClasses&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&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="p"&gt;},&lt;/span&gt;

    &lt;span class="nx"&gt;render&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$scopedSlots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="na"&gt;inputProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="na"&gt;imageProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;

        &lt;span class="na"&gt;imageEvents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>vue</category>
      <category>tutorial</category>
      <category>laravel</category>
      <category>renderless</category>
    </item>
    <item>
      <title>OAuth2 authentication across Laravel projects</title>
      <dc:creator>John Braun</dc:creator>
      <pubDate>Sun, 26 Jan 2020 13:24:21 +0000</pubDate>
      <link>https://dev.to/johnbraun/oauth2-authentication-across-laravel-projects-2mfn</link>
      <guid>https://dev.to/johnbraun/oauth2-authentication-across-laravel-projects-2mfn</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;A question popping up every now and then is how to let users log in to separate (child) applications using a single account they own on a central application. &lt;/p&gt;

&lt;p&gt;In this post I try to explain how to achieve this infrastructure, by creating a central application using  &lt;a href="https://laravel.com/docs/6.x/passport"&gt;Laravel Passport&lt;/a&gt;  (example.com) where users register once and then use  &lt;a href="https://oauth.net/2/"&gt;OAuth2&lt;/a&gt;  to grant access to their account to the other applications (app1.example.com, app2.example.com, etc.) using  &lt;a href="https://laravel.com/docs/6.x/socialite"&gt;Laravel Socialite&lt;/a&gt; . In this way, users will be able to login to the child applications without creating a new, separate account.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; the applications do not necessarily have to use the same domain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BJrLrBqB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/fmIHt5lcvv8wU7cEkePwfjV3QussBJ7fHbAYSE9Y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BJrLrBqB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/fmIHt5lcvv8wU7cEkePwfjV3QussBJ7fHbAYSE9Y.png" alt="Authorization request (Laravel Passport)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're not yet familiar with the OAuth2 protocol, I've included a section on the how and what below. &lt;/p&gt;

&lt;h1&gt;
  
  
  What is OAuth2?
&lt;/h1&gt;

&lt;p&gt;Before jumping into &lt;em&gt;Laravel Passport&lt;/em&gt;, it is important to understand the OAuth protocol it implements. &lt;/p&gt;

&lt;p&gt;OAuth is an open standard, designed to provide &lt;strong&gt;API access delegation&lt;/strong&gt;. Think of using a &lt;strong&gt;third party&lt;/strong&gt; Twitter app which can tweet &lt;strong&gt;on your behalf&lt;/strong&gt; to the Twitter platform. &lt;/p&gt;

&lt;p&gt;I explicitly mention &lt;a href="https://www.twitter.com/"&gt;Twitter&lt;/a&gt;  since development of this standard was (amongst others) driven by lead developer &lt;a href="https://en.wikipedia.org/wiki/Blaine_Cook_(programmer)"&gt;Blane Cook&lt;/a&gt;, in need of authorization of external parties.&lt;/p&gt;

&lt;p&gt;After initial release of version 1.0 in 2010, the protocol matured over the course of 2 years after which version 2.0 was released. This improved protocol offers support for &lt;strong&gt;Bearer tokens&lt;/strong&gt; and provides "specific &lt;strong&gt;authorization flows&lt;/strong&gt; for web applications, desktop applications, mobile phones, and smart devices." (via &lt;a href="https://en.wikipedia.org/wiki/OAuth"&gt;Wikipedia&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Let's have a look at what is meant with the terms &lt;em&gt;"Bearer token"&lt;/em&gt; and &lt;em&gt;"authorization flow"&lt;/em&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  Bearer tokens
&lt;/h2&gt;

&lt;p&gt;The word "Bearer" means you're in possession of a certain (access) token. The bearer being the third party application, which possesses an &lt;strong&gt;access token&lt;/strong&gt; issued by the &lt;strong&gt;identity provider&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This token provides &lt;strong&gt;immediate access&lt;/strong&gt; to a resource, without requiring a username and password. &lt;/p&gt;

&lt;p&gt;All necessary information is linked to this token, including user details and the &lt;strong&gt;scope&lt;/strong&gt; of possible actions this third party may perform on behalf of this user. &lt;/p&gt;

&lt;p&gt;The bearer token is usually included in the Headers of a GET or POST request to an API endpoint. &lt;/p&gt;

&lt;p&gt;A concrete example of using the Bearer token is shown below, making a GET request to &lt;code&gt;/api/user&lt;/code&gt; using the  &lt;a href="http://docs.guzzlephp.org/en/stable/"&gt;Guzzle HTTP library&lt;/a&gt;  to a corresponding API endpoint sending along a Bearer Authorization header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;request&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="s1"&gt;'/api/user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'headers'&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;'Accept'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$accessToken&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;
  
  
  Authorization flow
&lt;/h2&gt;

&lt;p&gt;Now that we know what a Bearer access token is ... how do we obtain one? &lt;br&gt;
First, let's look at the formal roles in OAuth2:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource Owner&lt;/strong&gt;: the user who wants to login and delegate access to their account details to a third party application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Server&lt;/strong&gt;: the server (API) where the user has an account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt;: the third party application that wants to access the account information on the resource server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The OAuth 2.0 protocol performs a standard communication flow between &lt;strong&gt;Client&lt;/strong&gt; and &lt;strong&gt;Resource Server&lt;/strong&gt;, where each step and given/required parameters are defined in advance. &lt;/p&gt;

&lt;p&gt;Ultimately, the &lt;strong&gt;Client&lt;/strong&gt; receives the Bearer &lt;strong&gt;access token&lt;/strong&gt; from the &lt;strong&gt;Resource server&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This process is illustrated in the figure below (created using  &lt;a href="https://www.draw.io/?lightbox=1&amp;amp;highlight=0000ff&amp;amp;edit=_blank&amp;amp;layers=1&amp;amp;nav=1&amp;amp;title=OAuth%20dance.drawio#R7Vzdl5o4FP9rPKd9GA8QQH2cL7u70912a3e3fZqDEDUdJJTEUfev3yQk8hFUtMA4ezoPCpckJvfzd2%2FC9MDtcvMu8eLF7ziAYc8ygk0P3PUsa2S77JMTtinBNSRhnqAgJZkZYYL%2BhZJoSOoKBZAUGlKMQ4riItHHUQR9WqB5SYLXxWYzHBZ%2FNfbmUCNMfC%2FUqf%2BggC5S6tAaZPRfIJov1C%2Bb7ih9svRUY7kSsvACvM6RwH0P3CYY0%2FRqubmFIeed4kvab7zn6W5iCYxonQ7bz%2FHDHw9%2Ffnl4Al%2Bub9Bnigi4kqM8e%2BFKLvgTJHiV%2BJBRJzB5homcPd0qliR4FQWQj2r0wM16gSicxJ7Pn66ZDjDagi5DdmeySzYARYyd1yGaR4xGMW8wwxGdyBHVFFhDuNm7NnPHMaZpEC8hTbasiezg2pLJUsuA23dSwjoTmunKRou8wBTRk4oy3w2e8ZJdSHaewFprqHEOBky35C1O6ALPceSF9xn1psjbrM17zPkmOPoNUrqVhuKtKC7yG24Q%2FZK7%2FsqHYsxI7%2B42cmRxs1U3EVsv73Rl9A1zpChpV5utQxKy3uKu0P0jTBDjG1OXlJiunS%2F4sEgZf1J1O8BIafZeMof0mC7rKpLA0KPouTiP5qWtGdJtiPhSX4H1lIxHWcQx0xm1ZTnK9%2BeY%2BR7PUXSAl5xZnmSSz5bN9fBmhsLwFoc4Ea3B2BxbY5vRCU3wE1RPIhzBItc5f3P9nJE7GBqK76nhmVYDfHct5aW2KugZVX7LdHTmA9AW8x2N%2BWaf3efiwod1xMOCsfYiSkRAZh8hnvOhI3Wv1N%2Fw4jhkOkwR1gXIeEeLvK%2BUTV6QkqQJu2wqSxQEwqVWmVjRCPP2BHT5j8dDw8h82g%2BJ3CqamlkVp0CVsVmtGZurybtnuSEXS4Ce2eWcX767%2FyxcaaoDfSKRwRizCLRg3%2FwLJ8I2xqo7I%2BdGqBhUkaZJmVLdVW931mAXqYNN6JYLCso1cnXVMipUC7SmWqM6quULR%2FEoR6qpOgkMUMLw%2FuMqQazfglKeD1zz%2BVpjX7qeMRNFOPX8pxOGJTGOCHyk25jP2GfpTO3OxMcxPGURhHpUoLipb1rgbHVtQHVACQKYhlnlmECX2mPpuUkFD%2B94rNnilQxHKvrECX5GXHaG5%2FuQ7KIUa5mkRGZ9u9Y736FGVcFrJ5Hdkzb9WxPe7CR4edSBpXizCaij6gBSwQamDnOcqvTMbE29dMR%2BvQtih4FmCVcaxtAT%2BEBjZzGQiKbOQDTdjzZn4q8NtOmYRRu3dRm4FUhTlTqal8BAk8CtF%2Fm8fnQK%2B6Fj2jXZf2MZRhX7MzbvF0cDEhhYFyYBoCdarzZr3SHkF0tbVUpWB0kfAyz7o8z%2FFr0CuyhPG9SDr3Zr8rRrwVcOEdlD50blQCLVvRJ051WCQTAs1SWsQUUq0SkYtC1t7R0WU43%2BQFVQv%2BYeHammmnox1bWc3hnF1FJMMozRyGi4yApkrna0ygqMauXppswKaqWU1p6CVQOAnIuix7eblDTc7yucNlByyZHSvrviCMn9XDqQGtygCz5Rv5x3HEkJGg8GSWrLr6Sa5lilmGHqGMBy7ApE1ZqfqhUzREH1b2bks21V9pmGgqrks662SgP4voKEVv1CLil%2BgtGhn3qBcloIZw2oYMFfGs2pXAl1OpaOOodd1m9tPYvSZOKRON0bn6ENZ9JNjBEHfffPkFfwpZjU%2FjTnVOCRxY6dx2QZelMYfsQEiRp%2FBaJ8X2owxZTi5d5M4dhGjmGYw%2BFdOmVRcVtu5vzwQX9JfA%2F2Ra84QQT2n%2BD2MYbJEhHCfpkcUI0EU0%2FOznYbUhWzDKOGOqQ1zS51ZX%2F0lHEtUxoVxPiDKyIQEstWjGG80QOc5l%2FqREv%2BNx7rg8l6MIF%2BAvOhsBgwDzrHYtOLzJhadFGDkosa6uDd7tJDObpD6hS714fuh4XSCM5W8eMoznZe9DiDo1eGKmAG4DBjt9FLEwR3hXa48RdeNIc6jlCGbXhRwD%2FzWbPwFSdsucywqObzfnshTUuF9kvBOR1B7fIBK%2FulkbaqZr%2F%2Bo2vFojCoW8c022OtfgTk44fJ4QMAqdVlZcuf2%2FytFUpLKQgYVOzUdrrP79Q6QnLWPn8RDGYdz6jgtHRkgGUebILyvEAhmD2eFMrOrCO%2FTEEYDHUn1W1B2KlKe08ttGQqaY21fKNGKaQq9zizWLN%2FkocAUqUZnD4DgeN%2BJWQFfxaLmszEXNs%2BajZWldm0FtoVkPiZi%2BUg5IXnYupY3%2BXL7NDWlzM46z2CDtTArasGrvWialDrPJxd2vpSiZBBYBSQijxZpuFvcMzjC0M%2F27fsdkVEpwBSD4W8dYqJ0ty%2Bzi7VCx%2BDu5TA0lF2Xn7%2Fya4INOawy1Kza2na%2BmqPFtmDuil5a6cV3Vr7ip8gXSXcpn%2BbfPiDfeHpN77vI96OZIYcoWi%2Bt1JWwzq9JWd2NCX8603qNzK3wW7%2BSr1GdrpW%2Bo%2B3LdjnpWTjpWq7PaqZjbd2bMnVqzca%2B7UNwZwgxN4gSVNRnnhWsBwtxVuz2tlJSb9DS6Zn4xBN%2BQL4nhz7foDbPnme9wpbbcBpRgqmU3z1wXEqXqMCXbq%2FWiURkZ5OvGeYRdzMdnTDfCOq3bkXrRj4IjQ1at797YFM9fTYK9LSa0Kwj8RxBD0xVIRDu3yj0Z4zMfuSytK5mB9LN7VFrRGvVhp0gUjG81ePMro56%2BBYFUbV0FkHdpu9CS%2Be5f6dALj%2FDw%3D%3D"&gt;draw.io&lt;/a&gt; ). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OZl_xj3B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/epj9UpbUuBI0bIkbsuM9f6C6d2Vffn8cgyW7OzGo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OZl_xj3B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/epj9UpbUuBI0bIkbsuM9f6C6d2Vffn8cgyW7OzGo.png" alt="The OAuth authorization flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; it is assumed that the &lt;strong&gt;Client&lt;/strong&gt; (third party application) is registered with the &lt;strong&gt;Resource Server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After this little "dance", the &lt;strong&gt;Client&lt;/strong&gt; now possesses an &lt;strong&gt;access token&lt;/strong&gt; which can either be long-lived or short-lived (more secure). &lt;/p&gt;

&lt;p&gt;If the access token is &lt;strong&gt;short-lived&lt;/strong&gt;, the &lt;strong&gt;Resource Server&lt;/strong&gt; will also provide a &lt;strong&gt;refresh token&lt;/strong&gt; which can be used - in combination with the &lt;strong&gt;client secret&lt;/strong&gt; - to obtain a new access token in a way resembling &lt;em&gt;Step 3&lt;/em&gt; in the diagram above. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: the "state" parameter is used to prevent CSRF related attacks, by verifying the request is not forged.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, let's see how Laravel Passport implements this protocol.&lt;/p&gt;
&lt;h1&gt;
  
  
  Laravel Passport
&lt;/h1&gt;

&lt;p&gt;In case of our example, we want the &lt;strong&gt;identity provider&lt;/strong&gt; (example.com) to make use of Laravel Passport.&lt;/p&gt;

&lt;p&gt;Laravel Passport is an OAuth2 server, built upon the &lt;a href="https://github.com/thephpleague/oauth2-server"&gt;League OAuth2 server&lt;/a&gt; . It provides an easy implementation for existing Laravel applications by requiring the composer package. &lt;/p&gt;
&lt;h2&gt;
  
  
  Resource server
&lt;/h2&gt;

&lt;p&gt;To set up a resource server, follow the clear  &lt;a href="https://laravel.com/docs/6.x/passport#installation"&gt;installation instructions&lt;/a&gt;  and/or watch &lt;a href="https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/13"&gt;this explanation on Laracasts&lt;/a&gt;  by Taylor Otwell. Don't forget to add the &lt;code&gt;hasApiTokens&lt;/code&gt; trait to your &lt;code&gt;User&lt;/code&gt; model. &lt;/p&gt;

&lt;p&gt;After installation, you now have the possibility to add new &lt;strong&gt;Clients&lt;/strong&gt;having a &lt;strong&gt;callback url&lt;/strong&gt; and an automatically generated &lt;strong&gt;secret&lt;/strong&gt;. For each of the "child" applications (&lt;code&gt;app1.example.com&lt;/code&gt;, &lt;code&gt;app2.example.com&lt;/code&gt;, etc.) you need to create a new &lt;strong&gt;Client&lt;/strong&gt;with their own callback, for now you might choose &lt;code&gt;https://app1.example.com/login/callback&lt;/code&gt; for example (we will come back to this).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WpLlriri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/kUoCbgpigADcf0FxPSx3NdYHNM9E6VIIY0l0jFzn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WpLlriri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/kUoCbgpigADcf0FxPSx3NdYHNM9E6VIIY0l0jFzn.png" alt="Creating a new Client in Laravel Passport"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Laravel Passport will take care of the authorization dialog, providing an authorization code, verifying the client secret in combination with the authorization code and lastly provide a User object and (by default) a &lt;strong&gt;long-lived&lt;/strong&gt; access token. The lifespan of the access and refresh tokens are  &lt;a href="https://laravel.com/docs/6.x/passport#token-lifetimes"&gt;configurable&lt;/a&gt; . &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b1zoyUcX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/GOtP1K8X367Gfy0jWaD5uo3ZNcPSQeUgJpqxkswO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b1zoyUcX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/GOtP1K8X367Gfy0jWaD5uo3ZNcPSQeUgJpqxkswO.png" alt="Example of a Client having an ID and a secret (in Laravel Passport)"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Laravel Socialite
&lt;/h1&gt;

&lt;p&gt;Now that we have a &lt;strong&gt;Resource Server&lt;/strong&gt; (identity provider), we need to take care of the &lt;strong&gt;Client&lt;/strong&gt;side of things using Laravel's package called  &lt;a href="https://laravel.com/docs/6.x/socialite"&gt;Laravel Socialite&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Out of the box, it allows authentication with the services of &lt;em&gt;Facebook&lt;/em&gt;, &lt;em&gt;Twitter&lt;/em&gt;, &lt;em&gt;LinkedIn&lt;/em&gt;, &lt;em&gt;Google&lt;/em&gt;, &lt;em&gt;GitHub&lt;/em&gt;, &lt;em&gt;GitLab&lt;/em&gt; and &lt;em&gt;Bitbucket&lt;/em&gt;. However, there is a  &lt;a href="https://socialiteproviders.netlify.com/"&gt;collection of additional providers&lt;/a&gt; , amongst which an adapter supporting &lt;strong&gt;Laravel Passport&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To achieve a shared login system across multiple Laravel applications, my proposed solution involves making use of the  &lt;a href="https://socialiteproviders.netlify.com/providers/laravel-passport.html"&gt;Socialite provider for Laravel Passport.&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Looking at the installation instructions of the Socialite provider, there are quite a number of steps, to make sure a client application is able to identify users using the Laravel Passport identity provider. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Laravel Socialite&lt;/li&gt;
&lt;li&gt;Install Laravel Passport &lt;strong&gt;provider&lt;/strong&gt; for Socialite&lt;/li&gt;
&lt;li&gt;Add methods to &lt;code&gt;LoginController&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the 'laravelpassport' &lt;strong&gt;config&lt;/strong&gt; to config/services.php&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;SocialiteWasCalled&lt;/code&gt; &lt;strong&gt;event&lt;/strong&gt; and listener to &lt;code&gt;EventServiceProvider&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, this seems like a lot of steps to repeat for every &lt;strong&gt;Client&lt;/strong&gt; application we might already have and want to couple to our &lt;strong&gt;Resource Server&lt;/strong&gt;. That's why I created a support package, as described in the next section.&lt;/p&gt;
&lt;h1&gt;
  
  
  Socialite-Passport support package
&lt;/h1&gt;

&lt;p&gt;To simplify the process, I made &lt;a href="https://github.com/jhnbrn90/socialite-passport"&gt;a support package&lt;/a&gt; ) that combines Laravel Socialite with the Passport driver and can be configured in a more efficient way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TnAC2L-7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/0L8EJfCNPKi6zeTkWB4bnqTpYucLijYjUS0rteKL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TnAC2L-7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/0L8EJfCNPKi6zeTkWB4bnqTpYucLijYjUS0rteKL.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;In a &lt;strong&gt;Client&lt;/strong&gt; Laravel application that you want to couple to the Passport*&lt;em&gt;Resource Server&lt;/em&gt;*, first install the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require jhnbrn90/socialite-passport
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, publish the configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan vendor:publish --provider="JhnBrn90\SocialitePassport\SocialitePassportServiceProvider" --tag="config" 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will publish a file &lt;code&gt;socialite-passport.php&lt;/code&gt; in the &lt;code&gt;config&lt;/code&gt; directory, in which you can define which Controller (defaults to &lt;code&gt;LoginController&lt;/code&gt; that ships with Laravel) and which method should be called when the login route (also configurable) is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'controller'&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;'class'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;\App\Http\Controllers\Auth\LoginController&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="s1"&gt;'method'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'loginWithPassport'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="s1"&gt;'route'&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;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'uri'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/login'&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;Assuming the defaults in the above configuration, after Authorization is granted by the &lt;strong&gt;Resource Owner&lt;/strong&gt; at the &lt;strong&gt;Resource Server&lt;/strong&gt; (Laravel Passport), the method &lt;code&gt;loginWithPassport()&lt;/code&gt; will be called and a &lt;code&gt;User&lt;/code&gt; object will be injected. &lt;/p&gt;

&lt;p&gt;This method needs to be implemented in the defined controller (in our example, the &lt;code&gt;LoginController&lt;/code&gt;):&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;LoginController&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loginWithPassport&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="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// example:&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;firstOrCreate&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="nv"&gt;$user&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="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To be able to provide the &lt;strong&gt;Resource Server&lt;/strong&gt; with a &lt;code&gt;client_id&lt;/code&gt;, &lt;code&gt;callback_uri&lt;/code&gt;, etc. you must add these variables to your &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LARAVELPASSPORT_CLIENT_ID=
LARAVELPASSPORT_CLIENT_SECRET=
LARAVELPASSPORT_REDIRECT_URI=/login/callback
LARAVELPASSPORT_HOST=https://example.com 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remember, &lt;code&gt;LARAVELPASSPORT_CLIENT_ID&lt;/code&gt; and &lt;code&gt;LARAVELPASSPORT_CLIENT_SECRET&lt;/code&gt; come from the Laravel Passport &lt;strong&gt;Resource Server&lt;/strong&gt;, where the &lt;strong&gt;Client&lt;/strong&gt; needs to be created first. &lt;/p&gt;

&lt;p&gt;The package will make sure to match the route you provide as the &lt;code&gt;LARAVELPASSPORT_REDIRECT_URI&lt;/code&gt; environment variable and proxies the request through the corresponding method and controller you've configured in the config file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final remarks
&lt;/h1&gt;

&lt;p&gt;That is all it takes to implement a basic functional shared login system. &lt;/p&gt;

&lt;p&gt;I hope this post could shed some light on the OAuth2 protocol and how it can be used with the mentioned tools to achieve a shared authentication system amongst different Laravel projects.&lt;/p&gt;

&lt;p&gt;It might be a good idea to also store the access token (and the resource token) on the user model to be able to update information whenever a user changes its profile on the &lt;strong&gt;Resource Server&lt;/strong&gt;. Or to collect other data of that user from the Resource Server of course.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://laravel.com/docs/6.x/passport"&gt;Laravel Passport&lt;/a&gt;  implements a fully functional OAuth2 server (Resource Server)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://laravel.com/docs/6.x/socialite"&gt;Laravel Socialite&lt;/a&gt;  implements a way to authenticate with an OAuth2 server (Client)&lt;/li&gt;
&lt;li&gt;The  &lt;a href="https://socialiteproviders.netlify.com/providers/laravel-passport.html"&gt;Socialite provider&lt;/a&gt;  for Laravel Passport allows authentication with Laravel Passport&lt;/li&gt;
&lt;li&gt;Clients can be prepared using  &lt;a href="https://github.com/jhnbrn90/socialite-passport"&gt;this package&lt;/a&gt;  combining both Socialite with the Passport adapter&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>oauth</category>
      <category>laravel</category>
      <category>authentication</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Show waiting users what's happening in the background</title>
      <dc:creator>John Braun</dc:creator>
      <pubDate>Sun, 26 Jan 2020 13:03:11 +0000</pubDate>
      <link>https://dev.to/johnbraun/show-waiting-users-what-s-happening-in-the-background-18fd</link>
      <guid>https://dev.to/johnbraun/show-waiting-users-what-s-happening-in-the-background-18fd</guid>
      <description>&lt;p&gt;Recently, Miguel Piedrafita (&lt;a href="https://dev.to/m1guelpf"&gt;https://dev.to/m1guelpf&lt;/a&gt;) &lt;a href="https://twitter.com/m1guelpf/status/1170010256480645122"&gt;tweeted&lt;/a&gt; about the importance of including some form of progress indicators whenever your application needs to perform one or more slow (background) tasks/processes. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DZVfoxNo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/icL0gjNaKkZNLiXaXq6hAJzCMW20wCQ0TxxGilX5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DZVfoxNo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/icL0gjNaKkZNLiXaXq6hAJzCMW20wCQ0TxxGilX5.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This inspired me to write this blog post. In his example, the users' website needs to be saved to a database, added to a deployment platform and dispatched for first deploy. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z3Q8zyVT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/keWqVzFtQ2lh9rQWhOE6qdpBWM3OieaGj2KxOCan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z3Q8zyVT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/keWqVzFtQ2lh9rQWhOE6qdpBWM3OieaGj2KxOCan.png" alt="Showing the user what's going on"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like Miguel mentions, by adding small indicators to each of these running processes your users are reassured something is happening and they just need to be patient.&lt;/p&gt;

&lt;p&gt;After I reading this tip, I wondered how to achieve these progress indicators. In this post I want to share my approach, using &lt;strong&gt;VueJS&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;I do not claim this to be the best available option and I'm open to alternative solutions and improvements. &lt;/p&gt;

&lt;p&gt;The code is available on  &lt;a href="https://codesandbox.io/embed/progress-indicator-ixdl9"&gt;CodeSandbox. &lt;/a&gt; &lt;/p&gt;

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

&lt;p&gt;Since we'll need to update the progress in realtime, I like to defer to &lt;strong&gt;VueJS&lt;/strong&gt;, my javascript framework of choice. &lt;/p&gt;

&lt;p&gt;Ultimately we want to display a list of tasks, which are processed &lt;em&gt;sequentially&lt;/em&gt;. To this extent we'll leverage javascript's &lt;code&gt;async/await&lt;/code&gt; functionality. &lt;/p&gt;

&lt;p&gt;Additionally, the tasks should indicate whenever they're finished and show an  &lt;a href="https://epic-spinners.epicmax.co/#/"&gt;epic spinner&lt;/a&gt;  (by  &lt;a href="https://epicmax.co/"&gt;Epicmax&lt;/a&gt; ) in the meantime. An example of our desired end result is shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1vZD_hI9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://johnbraun.blog/storage/wink/images/alfVleWL8h7TBwNIN09ubqjsRz54ApEvLDy00ViR.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1vZD_hI9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://johnbraun.blog/storage/wink/images/alfVleWL8h7TBwNIN09ubqjsRz54ApEvLDy00ViR.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Object representation of a single process
&lt;/h2&gt;

&lt;p&gt;To achieve this, I was thinking of the following object representation of a single process: we specify a &lt;code&gt;name&lt;/code&gt;, the &lt;code&gt;work&lt;/code&gt; that needs to be done with a &lt;strong&gt;callback&lt;/strong&gt; (returning a &lt;code&gt;Promise&lt;/code&gt;) and lastly keep track of its state through a &lt;code&gt;finished&lt;/code&gt; boolean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Collecting credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// perform the task&lt;/span&gt;

        &lt;span class="c1"&gt;// resolve the promise&lt;/span&gt;
        &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; we're only passing a &lt;code&gt;resolve argument&lt;/code&gt; to the &lt;code&gt;Promise&lt;/code&gt; object for now, ignoring potential failures. Make sure to check out the &lt;em&gt;"not-so-happy-path"&lt;/em&gt; section in the &lt;strong&gt;Conclusion&lt;/strong&gt; on managing (potential) errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Vue component
&lt;/h2&gt;

&lt;p&gt;With this approach and end goal in mind, we can shape our basic Vue component, in which we'll register three processes: 'Collecting credentials', 'Saving to database' and 'Finishing up registration'. For this demo, let's &lt;em&gt;simulate&lt;/em&gt; performing &lt;strong&gt;work&lt;/strong&gt; by a &lt;code&gt;setTimeout&lt;/code&gt; function, waiting for 2 seconds (2000 ms):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// if we want to use the epic spinner, let's import it here&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LoopingRhombusesSpinner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;epic-spinners&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// and declare the epic spinner component here&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;LoopingRhombusesSpinner&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; 

  &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;processes&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Collecting credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
          &lt;span class="na"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Collecting credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
          &lt;span class="na"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Collecting credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
          &lt;span class="na"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we have access to our loading spinner and the &lt;code&gt;processes&lt;/code&gt; property, we can generate a list of processes in the &lt;strong&gt;template:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"process.name"&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"process in processes"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        {{ process.name }}

        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"process.finished"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;check;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;looping-rhombuses-spinner&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;
          &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display: inline-block;"&lt;/span&gt;
          &lt;span class="na"&gt;:animation-duration=&lt;/span&gt;&lt;span class="s"&gt;"2500"&lt;/span&gt;
          &lt;span class="na"&gt;:rhombus-size=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;
          &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"#ff1d5e"&lt;/span&gt;
        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With the template in place, we need to make sure that our processes start whenever the page is loaded. We can do so by hooking in to Vue's &lt;code&gt;mounted()&lt;/code&gt; lifecycle hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="nx"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finished&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="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;initialize()&lt;/code&gt; method (which is called when the component was created) we want to loop over the processes and perform the work of each process in sequential order by handling the promises one by one using await before continuing to the next task. This requires that we declare the method as &lt;code&gt;async initialize()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Since we are working with Promises, we can not simply use a &lt;code&gt;forEach&lt;/code&gt; loop to iterate over the processes. Instead, we use a &lt;code&gt;for/of&lt;/code&gt; loop ( &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of"&gt;MDN reference&lt;/a&gt; ) which allows us to iterate over interable objects (in our case the asynchronous processes). &lt;/p&gt;

&lt;p&gt;After the work has been performed, we'll mark a process as finished which dynamically updates the loading spinner to a checkmark.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We've made a basic process indicator Vue component, in which we can define multiple tasks by declaring a &lt;code&gt;name&lt;/code&gt;, (initial) &lt;code&gt;state&lt;/code&gt; and a &lt;strong&gt;callback&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The tasks are then executed sequentially and their "finished" state is updated in realtime.&lt;/p&gt;

&lt;p&gt;For our demo purposes we've simulated the workload with a &lt;code&gt;setTimeout()&lt;/code&gt;, however in real life this would probably be an AJAX call which could look as follows (using axios):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.johnbraun.blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// (optional) do something with the response ...&lt;/span&gt;
    &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;💡 You might want to add additional tasks at runtime, which you can easily do by adding the following method to your component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;addProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling queued processes in the backend
&lt;/h2&gt;

&lt;p&gt;Now, there might be situations where the processes are &lt;strong&gt;queued&lt;/strong&gt; on your backend. In that scenario, the above discussed frontend solution doesn't suffice and I would advise to defer to a WebSocket implementation. &lt;/p&gt;

&lt;p&gt;WebSockets allow realtime communication from the backend to the frontend. You might have a look at my post on using &lt;a href="https://johnbraun.blog/posts/websockets-in-laravel"&gt;WebSockets in Laravel&lt;/a&gt; , which explains how to communicate changes in a queued job back to the frontend. &lt;/p&gt;

&lt;h2&gt;
  
  
  The not-so-happy path
&lt;/h2&gt;

&lt;p&gt;So far, we only discussed the happy path, but let's be realistic: what if a process fails? &lt;/p&gt;

&lt;p&gt;Javascript's &lt;code&gt;Promise()&lt;/code&gt; object accepts - in addition to 'resolve' - another argument 'reject' to indicate failure. &lt;/p&gt;

&lt;p&gt;In this regard, we should dissect the code for a single "process" in our component into a part that resolves the promise when succesful, and rejects the promise upon failure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saving to database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.johnbraun.blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Additionally, you might want to halt all other sequential processes since we're dealing with a failure. One way to go about it is to delete all remaining processes from the array, making sure the &lt;code&gt;.work()&lt;/code&gt; method is not called. We also should store the process which failed to show a big red cross.&lt;/p&gt;

&lt;p&gt;To accommodate for potential failure, one way to go about it is to let the user know which process failed (with a big red cross) and delete all remaining, unexecuted processes from the array preventing the remaining &lt;code&gt;.work()&lt;/code&gt; methods being called. Additionally, we store the process in an 'errors' property so we can show the user which process failed. &lt;/p&gt;

&lt;p&gt;These changes are summarized in the code block below, and also available on the &lt;a href="https://codesandbox.io/embed/progress-indicator-ixdl9"&gt;Codesandbox page&lt;/a&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"process.name"&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"process in processes"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{ process.name }}

      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"process.finished"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color: green;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;check;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"errors.includes(process)"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color: red;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;cross;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;looping-rhombuses-spinner&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!process.finished &amp;amp;&amp;amp; !errors.includes(process)"&lt;/span&gt;
        &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display: inline-block;"&lt;/span&gt;
        &lt;span class="na"&gt;:animation-duration=&lt;/span&gt;&lt;span class="s"&gt;"2500"&lt;/span&gt;
        &lt;span class="na"&gt;:rhombus-size=&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;
        &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"#ff1d5e"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"errors.length &amp;gt; 0"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color:red;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Something went wrong, so we bailed...
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// keep track which process(es) failed&lt;/span&gt;
      &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markFinished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;haltIteration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&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="nx"&gt;haltIteration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// get the current item's index&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// determine how many processes are left&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// remove other processes from being executed.&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>indicator</category>
      <category>vue</category>
      <category>progress</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Handle incoming mail in Laravel</title>
      <dc:creator>John Braun</dc:creator>
      <pubDate>Sun, 26 Jan 2020 12:41:11 +0000</pubDate>
      <link>https://dev.to/johnbraun/handle-incoming-mail-in-laravel-4l6k</link>
      <guid>https://dev.to/johnbraun/handle-incoming-mail-in-laravel-4l6k</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Sending&lt;/strong&gt; e-mails with laravel couldn't be simpler using the Mail facade, but what if you want to act on &lt;strong&gt;incoming&lt;/strong&gt; e-mails instead? &lt;/p&gt;

&lt;p&gt;For example, if you want to build a &lt;strong&gt;helpdesk&lt;/strong&gt; system, where users can respond to their tickets directly via e-mail. Or maybe you want to publish posts to your blog via e-mail.&lt;/p&gt;

&lt;p&gt;That's where the 📬 &lt;a href="https://docs.beyondco.de/laravel-mailbox/"&gt;Laravel Mailbox &lt;/a&gt; package by &lt;a href="https://marcelpociot.de/"&gt;Marcel Pociot&lt;/a&gt; comes in!&lt;/p&gt;

&lt;p&gt;In this post, I want to guide you through the steps to create an application which listens to &lt;strong&gt;incoming&lt;/strong&gt; e-mails and perform an action based on its &lt;strong&gt;sender&lt;/strong&gt; (from), &lt;strong&gt;recipient address&lt;/strong&gt; (to) or &lt;strong&gt;subject&lt;/strong&gt;. Alternatively, you can also catch &lt;strong&gt;all&lt;/strong&gt; incoming mail.&lt;/p&gt;

&lt;h1&gt;
  
  
  Requirements
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain name and server&lt;/strong&gt;: You'll need to own a &lt;strong&gt;domain name&lt;/strong&gt; with access to its DNS management, a &lt;strong&gt;server&lt;/strong&gt; to host the application and a &lt;strong&gt;mail service&lt;/strong&gt; that &lt;em&gt;listens&lt;/em&gt; to incoming e-mail and &lt;em&gt;forwards&lt;/em&gt; them to the application. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mailgun account&lt;/strong&gt;: Laravel Mailbox offers support for the &lt;strong&gt;mail services&lt;/strong&gt;  &lt;a href="https://www.mailgun.com/"&gt;Mailgun&lt;/a&gt; ,  &lt;a href="https://sendgrid.com/"&gt;Sendgrid&lt;/a&gt;  and  &lt;a href="https://postmarkapp.com/"&gt;Postmark&lt;/a&gt;  out of the box. For this article, I chose to use &lt;strong&gt;Mailgun&lt;/strong&gt; since I am most familiar with their service and I think they offer a reasonably generous free plan. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHPUnit&lt;/strong&gt;: Since I'll demonstrate driving out the desired functionality using TDD, you will need to have &lt;strong&gt;PHPUnit&lt;/strong&gt; working on your system. If you are new to this, I'd recommend to check out  &lt;a href="https://laracasts.com/series/setup-a-mac-dev-machine-from-scratch/episodes/7"&gt;this free video&lt;/a&gt;  on Laracasts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; references to "example.com" or "johnbraun.blog" should be replaced by &lt;strong&gt;your own&lt;/strong&gt; domain.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up Mailgun
&lt;/h1&gt;

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

&lt;p&gt;Mailgun offers a &lt;strong&gt;Free plan&lt;/strong&gt; which allows you to send 10 000 e-mails / month, limited to a sandbox domain. If you provide your payment details (credit card) you'll be upgraded to the &lt;strong&gt;Concept plan&lt;/strong&gt;, which is still free but unlocks the ability to &lt;strong&gt;add custom domains&lt;/strong&gt;. Be aware that you'll be charged if you send more than the 10 000 free e-mails. You're allowed to &lt;strong&gt;receive&lt;/strong&gt; an &lt;strong&gt;unlimited&lt;/strong&gt; number of e-mails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a domain
&lt;/h2&gt;

&lt;p&gt;Visit the &lt;em&gt;domains&lt;/em&gt; page in the &lt;em&gt;Sending&lt;/em&gt; menu section and choose &lt;em&gt;Add your domain&lt;/em&gt;. Mailgun will suggest you to use a &lt;strong&gt;subdomain&lt;/strong&gt; like &lt;em&gt;mg.example.com&lt;/em&gt;. However, if we want to be able to receive e-mails on &lt;a href="mailto:info@example.com"&gt;info@example.com&lt;/a&gt; (instead of &lt;a href="mailto:info@mg.example.com"&gt;info@mg.example.com&lt;/a&gt;) we'll need to &lt;strong&gt;ignore the red warning&lt;/strong&gt; and register &lt;em&gt;example.com&lt;/em&gt;. Choose your region (EU / US) and confirm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j-aDBSYc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/keKbeAgcpT414norqkpPuVTFw3ZR88yiIZ1JQMIb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j-aDBSYc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/keKbeAgcpT414norqkpPuVTFw3ZR88yiIZ1JQMIb.png" alt="Mailgun's warning that we're not using a subdomain"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up DNS records
&lt;/h2&gt;

&lt;p&gt;After adding a new domain, you'll be asked to add some DNS records: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TXT records (2x)&lt;/li&gt;
&lt;li&gt;MX records (2x)&lt;/li&gt;
&lt;li&gt;CNAME record (1x)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are (most probably) managed by the company you've registered your domain name with. &lt;br&gt;
As long as your registrar supports manual configuration of the DNS records it doesn't matter which company you choose. Personally, I use  &lt;a href="https://www.hover.com/"&gt;Hover&lt;/a&gt;  for registration of my &lt;strong&gt;domains&lt;/strong&gt; and  &lt;a href="https://www.digitalocean.com/"&gt;DigitalOcean&lt;/a&gt;  to mange the &lt;strong&gt;DNS settings&lt;/strong&gt; (by directing the nameservers to DigitalOcean) since my &lt;strong&gt;server&lt;/strong&gt; lives there as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fQtCHP-A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/vy8vj8z3MslKRLMZvFFhn4hGNVeNuLLG2Zm4eSw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fQtCHP-A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/vy8vj8z3MslKRLMZvFFhn4hGNVeNuLLG2Zm4eSw4.png" alt="DNS records at DigitalOcean"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out these guides for specific configuring of &lt;strong&gt;DNS records&lt;/strong&gt; at  &lt;a href="https://www.godaddy.com/help/manage-dns-zone-files-680"&gt;GoDaddy&lt;/a&gt; ,  &lt;a href="https://www.namecheap.com/support/knowledgebase/article.aspx/579/2237/which-record-type-option-should-i-choose-for-the-information-im-about-to-enter"&gt;NameCheap&lt;/a&gt; ,  &lt;a href="http://www.networksolutions.com/support/how-to-manage-advanced-dns-records/"&gt;Network Solutions&lt;/a&gt; ,  &lt;a href="https://support.rackspace.com/how-to/set-up-dns-records-for-cloud-office-email/"&gt;Rackspace Email &amp;amp; Apps&lt;/a&gt; ,  &lt;a href="https://support.rackspace.com/how-to/creating-dns-records-with-cloud-dns/"&gt;Rackspace Cloud DNS&lt;/a&gt; ,  &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-creating.html"&gt;Amazon Route 53&lt;/a&gt;  and  &lt;a href="https://www.digitalocean.com/docs/networking/dns/how-to/manage-records/"&gt;Digital Ocean&lt;/a&gt; . &lt;/p&gt;
&lt;h2&gt;
  
  
  TXT records
&lt;/h2&gt;

&lt;p&gt;To be able to send e-mail with Mailgun, you'll need to configure two TXT records.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--flKWVf7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/5IXcZiQhOdzRfHl8iNXUYPpZe5z7kK6Mp9FGka6A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--flKWVf7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/5IXcZiQhOdzRfHl8iNXUYPpZe5z7kK6Mp9FGka6A.png" alt="Expected TXT records"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, using the root hostname (using an "@" in DigitalOcean) with the value &lt;code&gt;"v=spf1 include:eu.mailgun.org ~all"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i9cgNQmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/DOQoOBv5fuWWbwMKmuCPWX1zdT7VwPV3L6JV4bAx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i9cgNQmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/DOQoOBv5fuWWbwMKmuCPWX1zdT7VwPV3L6JV4bAx.png" alt="Adding root TXT record"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The second TXT record indicates your domain key using the provided subdomain prefix, "&lt;strong&gt;mailo._domainkey&lt;/strong&gt;" in the example below. &lt;em&gt;Note:&lt;/em&gt; The specific subdomain you'll need to create will vary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5SKayS8h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/RUfdcB4NqGsPjjUamcnY0SNFcdtrNxRZtTOJ54vx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5SKayS8h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/RUfdcB4NqGsPjjUamcnY0SNFcdtrNxRZtTOJ54vx.png" alt="Adding domain key TXT record"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  MX records
&lt;/h2&gt;

&lt;p&gt;Next, to be able to receive e-mails, we'll need to add two MX records. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--onHf2lAB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/ZE7zlzfksUIxbGgBJr64ZyRfC4ztiwcBpBpmcBhf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--onHf2lAB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/ZE7zlzfksUIxbGgBJr64ZyRfC4ztiwcBpBpmcBhf.png" alt="Expected MX records"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we're not using a subdomain, we'll add those MX records on our root hostname, which is shown below (for DigitalOcean). &lt;em&gt;Important:&lt;/em&gt; if you &lt;strong&gt;do&lt;/strong&gt; use a subdomain, you'll need to configure the MX records for the subdomain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c70SbhOo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/VUWPpcnnN9rYDeCSVR8PqU3QCAx9HGa52aIuOU1H.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c70SbhOo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/VUWPpcnnN9rYDeCSVR8PqU3QCAx9HGa52aIuOU1H.png" alt="Adding MX records"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat this step for the other (&lt;strong&gt;mxb&lt;/strong&gt;.eu.mailgun.org) MX record. &lt;/p&gt;
&lt;h2&gt;
  
  
  CNAME record
&lt;/h2&gt;

&lt;p&gt;Lastly, add a CNAME record for the "&lt;strong&gt;email&lt;/strong&gt;" subdomain: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jaVDR7GR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/WWZOIsjdCjxa7xfYK00ke0xFgSDyVX3IL5Yu2ei5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jaVDR7GR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/WWZOIsjdCjxa7xfYK00ke0xFgSDyVX3IL5Yu2ei5.png" alt="Expected CNAME record"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sc_wy-14--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/8dOSHO6nG6JZyXrZmNDP62N8TrupLElhRzBIFMqO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sc_wy-14--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/8dOSHO6nG6JZyXrZmNDP62N8TrupLElhRzBIFMqO.png" alt="Adding a CNAME record"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Verify DNS Settings
&lt;/h2&gt;

&lt;p&gt;After you've configured the DNS records, hit 'verify DNS settings' on the Mailgun page after which a green tick should appear, indicating that everything was properly configured.&lt;br&gt;
You're now ready to send and receive incoming mail on this domain.&lt;/p&gt;
&lt;h1&gt;
  
  
  Installing Laravel Mailbox
&lt;/h1&gt;

&lt;p&gt;Pull in the  &lt;a href="https://github.com/beyondcode/laravel-mailbox"&gt;Laravel Mailbox&lt;/a&gt;  package in your Laravel app using composer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require beyondcode/laravel-mailbox
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Further more, following the installation instructions, at the  &lt;a href="https://docs.beyondco.de/laravel-mailbox/1.0/getting-started/installation.html"&gt;documentation&lt;/a&gt;  page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Publish the migrations&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan vendor:publish --provider="BeyondCode\Mailbox\MailboxServiceProvider" --tag="migrations"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run the migrations&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Publish the config file&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan vendor:publish --provider="BeyondCode\Mailbox\MailboxServiceProvider" --tag="config"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that the package is ready, let's dive into driving out some functionality using TDD (Test Driven Development). &lt;/p&gt;

&lt;h1&gt;
  
  
  Testing
&lt;/h1&gt;

&lt;p&gt;For this demo we will simply record a received e-mail in a &lt;code&gt;received_mails&lt;/code&gt; table containing a 'sender', 'subject' and 'body' of the e-mail. We'll drive out this behaviour using a feature test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:test IncomingMailTest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up our test
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment file
&lt;/h3&gt;

&lt;p&gt;To be able to test this package locally, instead of receiving a call to the Mailgun webhook, the Laravel Mailbox package can listen to e-mails sent to the &lt;strong&gt;log&lt;/strong&gt;. Therefore, we can specify &lt;code&gt;MAIL_DRIVER=log&lt;/code&gt; and &lt;code&gt;MAILBOX_DRIVER=log&lt;/code&gt; in the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the log driver in our tests
&lt;/h3&gt;

&lt;p&gt;In the setup method of our test, we have to explicitly tell PHPunit to use the log driver, otherwise it will default to the "array" driver.&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;IncomingMailTest&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;TestCase&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;RefreshDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

       &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'mail.driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'log'&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;
  
  
  Writing a test by wishful thinking
&lt;/h2&gt;

&lt;p&gt;Let's write a test that will verify incoming e-mails are saved to the database, so we can later act on them. &lt;em&gt;Note:&lt;/em&gt; not all of the code described exists yet, we'll let our test tell us to implement / write the missing pieces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;Tests\Feature&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;App\ReceivedMail&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;App\Mail\TestMail&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;Illuminate\Support\Facades\Mail&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;IncomingEmailTest&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; 

  &lt;span class="cd"&gt;/** @test **/&lt;/span&gt;  
  &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;incoming_mail_is_saved_to_the_mails_table&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Given: we have an e-mail &lt;/span&gt;
    &lt;span class="nv"&gt;$email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TestMail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'sender@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Test E-mail'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Some example text in the body'&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// When: we receive that e-mail&lt;/span&gt;
    &lt;span class="nb"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'incoming@johnbraun.blog'&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Then: we assert the e-mails (meta)data was stored&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="na"&gt;assertCount&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="nx"&gt;ReceivedMail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="nx"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ReceivedMail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;first&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;$mail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&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="na"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;sender&lt;/span&gt;&lt;span class="p"&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="na"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&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="na"&gt;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Want to know more about Laravel's &lt;code&gt;tap()&lt;/code&gt; function? Check out &lt;a href="https://medium.com/@taylorotwell/tap-tap-tap-1fc6fc1f93a6"&gt;Taylor Otwell's post&lt;/a&gt; on one of his favorite helpers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a mailable
&lt;/h2&gt;

&lt;p&gt;The first error we'll encounter when running the tests is that there is no &lt;code&gt;IncomingTestMail&lt;/code&gt; mailable class, so let's create it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:mail TestMail --markdown="emails.tests.testmail" 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open up the mailable, and accept the sender's address, subject and body in the constructor and store them in the public properties.&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;TestMail&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Mailable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$subject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&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="na"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sender&lt;/span&gt;&lt;span class="p"&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="na"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$subject&lt;/span&gt;&lt;span class="p"&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="na"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;build&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="na"&gt;from&lt;/span&gt;&lt;span class="p"&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="na"&gt;sender&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;subject&lt;/span&gt;&lt;span class="p"&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="na"&gt;subject&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;markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tests.emails.testmail'&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;In the markdown e-mail, echo out the body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;@component('mail::message')

{{ $body }}

Thanks,&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
{{ config('app.name') }}
@endcomponent
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a ReceivedMail model
&lt;/h2&gt;

&lt;p&gt;If we run the test again at this stage, we're missing a &lt;code&gt;ReceivedMail&lt;/code&gt; model, so let's create it, together with a migration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:model ReceivedMail -m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;First, make all properties on our model fillable.&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;ReceivedMail&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Model&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;$guarded&lt;/span&gt; &lt;span class="o"&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;Next, add the 'sender', 'subject' and 'body' columns to our received_mails_table migration file.&lt;br&gt;
&lt;/p&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;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&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;'received_mails'&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;bigIncrements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&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;'sender'&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;'subject'&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;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body'&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="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;Run the migration with &lt;code&gt;php artisan migrate&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining a Mailbox
&lt;/h2&gt;

&lt;p&gt;Running the test again, shows the following error, as we don't act on incoming mail yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed asserting that actual size 0 matches expected size 1. 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can easily set up a listener in the &lt;code&gt;AppServiceProvider&lt;/code&gt;, referring incoming e-mails addressed &lt;strong&gt;to&lt;/strong&gt; a certain address (&lt;code&gt;Mailbox::to()&lt;/code&gt;) or &lt;strong&gt;coming from&lt;/strong&gt; a specific address (&lt;code&gt;Mailbox::from()&lt;/code&gt;) to a &lt;strong&gt;callback&lt;/strong&gt; or - as we'll use in this case - an &lt;strong&gt;invokable&lt;/strong&gt; &lt;code&gt;MailHandler&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\MailHandler&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;BeyondCode\Mailbox\Facades\Mailbox&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;AppServiceProvider&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;ServiceProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&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;Mailbox&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'incoming@johnbraun.blog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MailHandler&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To learn all of the available options, check out  &lt;a href="https://docs.beyondco.de/laravel-mailbox/1.0/basic-usage/mailboxes.html#matching-sender-emails"&gt;this page&lt;/a&gt;  in the documentation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; you are able to use parameters (&lt;a href="https://docs.beyondco.de/laravel-mailbox/1.0/basic-usage/mailboxes.html#using-parameters"&gt;see docs&lt;/a&gt;) in the matching rules, which will be passed down to the handling class as additional arguments to the  &lt;code&gt;InboundEmail $email&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an invokable MailHandler class
&lt;/h2&gt;

&lt;p&gt;In the app folder, let's create &lt;code&gt;MailHandler.php&lt;/code&gt; and set up a magic &lt;code&gt;__invoke(&lt;/code&gt; method, which gets passed in an &lt;code&gt;InboundEmail&lt;/code&gt; object where we can call a &lt;code&gt;from()&lt;/code&gt;, &lt;code&gt;subject()&lt;/code&gt; and &lt;code&gt;text()&lt;/code&gt; methods to obtain the corresponding data. All &lt;a href="https://docs.beyondco.de/laravel-mailbox/1.0/basic-usage/handling.html#available-methods"&gt;available methods&lt;/a&gt;  are listed in the documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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&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;App\ReceivedMail&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;BeyondCode\Mailbox\InboundEmail&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;MailHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;InboundEmail&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;ReceivedMail&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;'sender'&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s1"&gt;'subject'&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s1"&gt;'body'&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we run our test now, it passes which means we can now listen for e-mails to '&lt;a href="mailto:incoming@our-domain.com"&gt;incoming@our-domain.com&lt;/a&gt;', however we still first need to setup an endpoint for Mailgun, which we'll do in the next section. &lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up for production
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Installing Guzzle
&lt;/h2&gt;

&lt;p&gt;Since Laravel requires GuzzleHTTP for working with the Mailgun driver, let's first install this dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require guzzlehttp/guzzle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring a webhook in Mailgun
&lt;/h2&gt;

&lt;p&gt;We're almost ready to receive inbound e-mails on our domain, we just need to configure Mailgun to call our application whenever it receives mail. To do this, first push your application to the domain and make sure it is accessible (externally).&lt;/p&gt;

&lt;p&gt;In Mailgun, hit up the "receiving" link in the menu and choose "create route". Here we can specify to either catch all e-mails, or a selection. Let's only catch e-mails sent to "&lt;a href="mailto:incoming@johnbraun.blog"&gt;incoming@johnbraun.blog&lt;/a&gt;". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MWKMxWUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/44Q3vjBmNP9yizzwT0x1Bfi4egWNOTauPSymKorv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MWKMxWUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/44Q3vjBmNP9yizzwT0x1Bfi4egWNOTauPSymKorv.png" alt="Adding a new route for incoming mail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check "store and notify" and fill in the endpoint for the Mailgun driver (as  &lt;a href="https://docs.beyondco.de/laravel-mailbox/1.0/drivers/drivers.html#mailgun"&gt;described here&lt;/a&gt; ): &lt;code&gt;https://[your-domain.com]/laravel-mailbox/mailgun/mime&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Give your route a description and hit the create button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update production environment variables
&lt;/h2&gt;

&lt;p&gt;Lastly, we'll need to specify the Mailgun driver in the environment file. Add/update the following properties in your .env file. The Mailgun secret key is obtained from the "API security" page under the "settings" tab. You'll need to provide the private key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAILBOX_DRIVER=mailgun
MAILBOX_MAILGUN_KEY=......

MAIL_DRIVER=mailgun
MAILGUN_DOMAIN=johnbraun.blog
MAILGUN_SECRET=........
MAILGUN_ENDPOINT="api.eu.mailgun.net"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NWr1XGA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/6NayC0W9srBGTPZiN0HxCfZwHhI8bHXHtFmiQ0Mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NWr1XGA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/6NayC0W9srBGTPZiN0HxCfZwHhI8bHXHtFmiQ0Mg.png" alt="API settings page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Ready for action
&lt;/h1&gt;

&lt;p&gt;At this stage, you're able to send mails to your defined address (in our demo case '&lt;a href="mailto:incoming@johnbraun.blog"&gt;incoming@johnbraun.blog&lt;/a&gt;') after which a new &lt;code&gt;ReceivedMail&lt;/code&gt; model wil be saved to the database. &lt;/p&gt;

&lt;p&gt;Don't forget, that there are a lot of other things you could do. To name a few: it can handle attachments, send an auto-reply, capture the HTML contents of the e-mail, etc. &lt;/p&gt;

&lt;p&gt;Check the available methods in the Laravel Mailbox  &lt;a href="https://docs.beyondco.de/laravel-mailbox/1.0/basic-usage/handling.html#handling-inbound-emails"&gt;documentation&lt;/a&gt; . &lt;/p&gt;

&lt;p&gt;I hope this post helped you to make a start if you're interested to start receiving and handling inbound e-mail.&lt;/p&gt;

</description>
      <category>mail</category>
      <category>laravel</category>
      <category>mailbox</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>WebSockets in Laravel</title>
      <dc:creator>John Braun</dc:creator>
      <pubDate>Sun, 26 Jan 2020 12:08:25 +0000</pubDate>
      <link>https://dev.to/johnbraun/websockets-in-laravel-2n62</link>
      <guid>https://dev.to/johnbraun/websockets-in-laravel-2n62</guid>
      <description>&lt;p&gt;&lt;a href="https://freek.dev/"&gt;Freek Van der Herten&lt;/a&gt;  (from  &lt;a href="https://spatie.be/"&gt;Spatie&lt;/a&gt; ) and  &lt;a href="https://marcelpociot.de/"&gt;Marcel Pociot&lt;/a&gt;  (from  &lt;a href="https://beyondco.de/"&gt;BeyondCode&lt;/a&gt; ) have published a package called  &lt;a href="https://docs.beyondco.de/laravel-websockets/"&gt;Laravel WebSockets&lt;/a&gt; which provides an alternative to services like  &lt;a href="https://www.pusher.com/"&gt;Pusher&lt;/a&gt; . &lt;/p&gt;

&lt;p&gt;In this post I want to explain how to get started with &lt;strong&gt;realtime broadcasting&lt;/strong&gt; in Laravel using WebSockets. The stepwise guide will explain how to setup basic broadcasting. The first part covers using &lt;strong&gt;Pusher&lt;/strong&gt;, the second part dives into switching to the &lt;strong&gt;Laravel WebSockets&lt;/strong&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vevPi5c_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/RqdEm2fPujfwzi34RsORWRBHHxRepPaHTio6RIJX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vevPi5c_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/RqdEm2fPujfwzi34RsORWRBHHxRepPaHTio6RIJX.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  When to use WebSockets?
&lt;/h1&gt;

&lt;p&gt;WebSockets is a relatively young TCP protocol, finalized in 2011, which enables two-way communication between the server and a user's browser. They are nowadays commonly used in applications that want to communicate changes in realtime. Chatrooms are probably the most well known example along with online multiplayer games (quizzes), social media streams and sports tickers. &lt;/p&gt;

&lt;h1&gt;
  
  
  Our demo application
&lt;/h1&gt;

&lt;p&gt;Let's assume our application needs to do some heavy lifting (which we will mimic using PHP's &lt;code&gt;sleep()&lt;/code&gt; function). For example, imagine that a user can upload a video, which then needs to undergo a time intensive post-procesing task. Since we don't want to keep our user waiting, we'll put a &lt;strong&gt;job&lt;/strong&gt; on the &lt;strong&gt;queue&lt;/strong&gt; to execute that work behind the scenes. In the meantime, our user will see a &lt;em&gt;"waiting to be processed"&lt;/em&gt; status until the job is processed. Within the job, we'll emit an &lt;strong&gt;event&lt;/strong&gt; upon completion that should be broadcasted to the &lt;strong&gt;specific&lt;/strong&gt; user only. That should in turn update the status for that user without requiring a "hard refresh" of the webpage.&lt;/p&gt;

&lt;p&gt;Both implementations, &lt;strong&gt;Pusher&lt;/strong&gt; as well as &lt;strong&gt;Laravel WebSockets&lt;/strong&gt;, require the following prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Laravel Echo&lt;/strong&gt;: a javascript library that makes it a breeze to listen to broadcasted events from our front end. You can install Laravel Echo via npm: &lt;code&gt;npm install --save laravel-echo pusher-js&lt;/code&gt;. After installation, uncomment the Laravel Echo specific code in your &lt;code&gt;resources/js/bootstrap.js&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Echo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;laravel-echo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Pusher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Echo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;broadcaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIX_PUSHER_APP_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIX_PUSHER_APP_CLUSTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;encrypted&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt;: In this example we'll use Redis as our &lt;strong&gt;queue&lt;/strong&gt; driver, which can be installed using composer: &lt;code&gt;composer require predis/predis&lt;/code&gt;. Now, in the &lt;code&gt;.env&lt;/code&gt; file set &lt;code&gt;QUEUE_CONNECTION=redis&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Tips and tricks before starting
&lt;/h1&gt;

&lt;p&gt;If you are new to broadcasting, from my experience some things might be a bit tricky at first. Therefore I wanted to dedicate a small section to hopefully clear things up. &lt;/p&gt;

&lt;h2&gt;
  
  
  Uncomment the BroadcastingServiceProvider
&lt;/h2&gt;

&lt;p&gt;I've been tricked more than once, in debugging where things go wrong only to find out I forgot to uncomment this line in the providers array of config/app.php. Make this the first thing you do! (if you're like me).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/app.php:&lt;/span&gt;

  &lt;span class="cm"&gt;/*
  * Application Service Providers...
  */&lt;/span&gt; 
  &lt;span class="nx"&gt;App\Providers\AppServiceProvider&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="nx"&gt;App\Providers\AuthServiceProvider&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="nx"&gt;App\Providers\BroadcastServiceProvider&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="nx"&gt;App\Providers\EventServiceProvider&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="nx"&gt;App\Providers\TelescopeServiceProvider&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="nx"&gt;App\Providers\RouteServiceProvider&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;h2&gt;
  
  
  Always recompile your assets
&lt;/h2&gt;

&lt;p&gt;If you ever change the &lt;code&gt;PUSHER_*&lt;/code&gt; credentials in the &lt;code&gt;.env&lt;/code&gt; file, always recompile your assets using &lt;code&gt;npm run dev&lt;/code&gt; (or watch / prod). This is necessary as Laravel Echo needs them and compiles them down from your environment variables, as you can see in the instantiation of Echo in &lt;code&gt;resources/js/bootstrap.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIX_PUSHER_APP_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIX_PUSHER_APP_CLUSTER&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Pusher
&lt;/h1&gt;

&lt;p&gt;The easiest way to get started is using the Pusher implementation. We'll first require the pusher specific package using composer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require pusher/pusher-php-server "~3.0"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, we need to get our credentials from  &lt;a href="https://www.pusher.com/"&gt;Pusher.com&lt;/a&gt; . If you don't have an account yet, you first need to register. They offer a free account, which should suffice for small to medium applications. On their website, create a new app and select the cluster that's closest to you. Copy the credentials on the "App Keys" page to the &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUSHER_APP_ID=(app_id)
PUSHER_APP_KEY=(app_key)
PUSHER_APP_SECRET=(app_secret)
PUSHER_APP_CLUSTER=(cluster)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Building a demo application
&lt;/h1&gt;

&lt;p&gt;In this section, let's walk through a minimum setup to get our basic demo application working.&lt;br&gt;
First, we'll scaffold the default Laravel authentication and a Video model with a migration and a controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:auth

php artisan make:model Video -mc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add a user to the &lt;code&gt;Videos&lt;/code&gt; table migration:&lt;br&gt;
&lt;/p&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;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&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;'videos'&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;bigIncrements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&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;bigInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_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;timestamps&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, perform the migrations: &lt;code&gt;php artisan migrate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We have a &lt;em&gt;post&lt;/em&gt; route to store a new video, and a show route which will let the user know the current status of his/her uploaded video. In &lt;code&gt;routes/web.php&lt;/code&gt;:&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;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/videos/{video}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'VideoController@show'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/videos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'VideoController@store'&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 &lt;code&gt;VideoController&lt;/code&gt;, we'll add the corresponding methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/VideoController.php:&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\Video&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;App\Jobs\ProcessVideo&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;Illuminate\Http\Request&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;VideoController&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// here we would the uploaded video from $request&lt;/span&gt;
       &lt;span class="c1"&gt;// and store it along with its path  &lt;/span&gt;
       &lt;span class="nv"&gt;$video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Video&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="c1"&gt;// Then, pass the heavy lifting to our job&lt;/span&gt;
       &lt;span class="nx"&gt;ProcessVideo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/videos/'&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$video&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="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Video&lt;/span&gt; &lt;span class="nv"&gt;$video&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="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'videos.show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'video'&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;To reach the &lt;strong&gt;post&lt;/strong&gt; route, we add a form to our welcome.blade.php:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;welcome.blade.php:

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/videos"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  @csrf
  &lt;span class="c"&gt;&amp;lt;!-- some upload and other form fields --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Upload some video&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After posting this form, the user will hit the &lt;code&gt;store()&lt;/code&gt; method of our controller and a &lt;code&gt;ProcessVideo&lt;/code&gt; job will be dispatched before the user gets redirected to the video's show page. We haven't created any of these yet, so let's start by scaffolding the &lt;code&gt;ProcessVideo&lt;/code&gt; job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:job ProcessVideo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since we want to have a reference to the video that needs processing, accept the &lt;code&gt;$video&lt;/code&gt; in the constructor. In the &lt;code&gt;handle()&lt;/code&gt; method, the application wil sleep for 10 seconds and then emit an event called &lt;code&gt;VideoWasProcessed&lt;/code&gt;. Don't forget to import the &lt;code&gt;Video&lt;/code&gt; and &lt;code&gt;VideoWasProcessed&lt;/code&gt; classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Jobs/ProcessVideo.php:&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\Video&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;App\Events\VideoWasProcessed&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;ProcessVideo&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ShouldQueue&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;Dispatchable&lt;/span&gt;&lt;span class="err"&gt;, InteractsWithQueue, Queueable, SerializesModels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Video&lt;/span&gt; &lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&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="na"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;VideoWasProcessed&lt;/span&gt;&lt;span class="p"&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="na"&gt;video&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;Since the job implements the &lt;code&gt;ShouldQueue&lt;/code&gt; interface, Laravel will always try to queue this job. Then, our Laravel queue workers (using the redis connection) in the background will make sure the job is executed. When completed (in our case after 10 seconds), the &lt;code&gt;VideoWasProcessed&lt;/code&gt; event will be fired.&lt;/p&gt;

&lt;p&gt;We haven't created this event yet, so run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:event VideoWasProcessed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In addition to accepting the reference to the video in the constructor, we also want to implement the &lt;code&gt;ShouldBroadcast&lt;/code&gt; protocol. The event &lt;em&gt;will not&lt;/em&gt; be broadcasted unless you implement the &lt;code&gt;ShouldBroadcast&lt;/code&gt; interface.  Also, don't forget to import the &lt;code&gt;Video&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Events/VideoWasProcessed.php:&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\Video&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;VideoWasProcessed&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ShouldBroadcast&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;Dispatchable&lt;/span&gt;&lt;span class="err"&gt;, InteractsWithSockets, SerializesModels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Video&lt;/span&gt; &lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&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="na"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;broadcastOn&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="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PrivateChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;"videos.&lt;/span&gt;&lt;span class="si"&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="na"&gt;video&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="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;broadcastOn()&lt;/code&gt; method specifies which channel to broadcast on. Since we don't want everyone to get an update on the processing status of this video we broadcast on a &lt;code&gt;PrivateChannel&lt;/code&gt;. Who can access to this channel is configured in the &lt;code&gt;routes/channels.php&lt;/code&gt; file. If the closure as defined below returns true, the user has access. If the closure returns false, the user fails authorization. We add a constraint that only allows the owner of the video to see updates in the &lt;code&gt;routes/channels.php&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// routes/channels.php:&lt;/span&gt;

&lt;span class="nx"&gt;Broadcast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'videos.{id}'&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;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&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;$user&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="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Video&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="nv"&gt;$id&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;user_id&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;
  
  
  Reflecting updates on the front end
&lt;/h2&gt;

&lt;p&gt;The magic revolves around the show page of our video. For the javascript side of things, we'll levarage &lt;a href="https://vuejs.org/"&gt;Vue JS&lt;/a&gt;. In &lt;code&gt;resources/views/videos/show.blade.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Video's page&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- always include your CSRF token --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"csrf-token"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"{{ csrf_token() }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- our Vue component --&amp;gt;&lt;/span&gt; 
      &lt;span class="nt"&gt;&amp;lt;video-progress&lt;/span&gt; &lt;span class="na"&gt;:video=&lt;/span&gt;&lt;span class="s"&gt;"{{ $video }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- include our compiled javascript --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ asset('js/app.js') }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;VideoProgress.vue&lt;/code&gt; component consists of a data variable &lt;code&gt;processing&lt;/code&gt; which holds information on the processing status of the video. In the &lt;code&gt;created()&lt;/code&gt; lifecycle hook of Vue, we defined a private listener which passes through the video's ID and listens for a &lt;code&gt;VideoWasProcessed&lt;/code&gt; event. When it gets that event, the closure is executed and in this case the &lt;code&gt;processing&lt;/code&gt; data variable will be set to false, reactively updating the &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; in our template.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;resources/js/components/VideoProgress.vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        Your video with ID {{ video.id }} is currently:
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color: red;"&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"processing"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Processing...
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color:green"&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           Finished!
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

        &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;processing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;

        &lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`videos.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VideoWasProcessed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lastly, we need to register our Vue component in &lt;code&gt;resources/js/app.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video-progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/VideoProgress.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, after recompilation of all assets using &lt;code&gt;npm run dev&lt;/code&gt;(or watch / prod), start a queue worker (&lt;code&gt;php artisan queue:work&lt;/code&gt;) and visit the demo app in the website. &lt;/p&gt;

&lt;p&gt;In the welcome view, click the submit button and (if all went well) you should see the status updating from 'running' to 'finished' automatically, after the job was processed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Switching to Laravel WebSockets
&lt;/h1&gt;

&lt;p&gt;The Laravel WebSockets package makes it easy to smoothly transition from using Pusher to using your own WebSockets server. &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;We first need to require the package via composer
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require beyondcode/laravel-websockets
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then, publish the migrations:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;* Publish the configuration file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



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

&lt;p&gt;The package will use the &lt;code&gt;pusher&lt;/code&gt; driver, but we don't &lt;em&gt;actually&lt;/em&gt; want to use Pusher. Therefore we add our own &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;port&lt;/code&gt; configuration to 'pusher' section in &lt;code&gt;config/broadcasting.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'pusher'&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;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'pusher'&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="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PUSHER_APP_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="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PUSHER_APP_SECRET'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'app_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PUSHER_APP_ID'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'options'&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;'cluster'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PUSHER_APP_CLUSTER'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'encrypted'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'port'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;6001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'scheme'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'http'&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;In our &lt;code&gt;bootstrap.js&lt;/code&gt; file we need to tell Laravel Echo to use the alternative host and port (&lt;code&gt;wsHost&lt;/code&gt;, &lt;code&gt;wsPort&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Echo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;laravel-echo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Pusher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Echo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Echo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;broadcaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIX_PUSHER_APP_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;wsHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;wsPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;disableStats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Although we are not using Pusher, we still need to supply &lt;strong&gt;some&lt;/strong&gt; Pusher configuration in our &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUSHER_APP_ID=testapp
PUSHER_APP_KEY=websocketkey
PUSHER_APP_SECRET=somethingsecret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Compiling assets
&lt;/h2&gt;

&lt;p&gt;We need to recompile &lt;code&gt;bootstrap.js&lt;/code&gt;, as discussed in the &lt;em&gt;Tips and Tricks&lt;/em&gt; section, by running &lt;code&gt;npm run dev&lt;/code&gt; (or watch / prod).&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting the WebSockets server
&lt;/h2&gt;

&lt;p&gt;Finally, boot up the WebSockets server and start a queue worker (in a separate terminal window):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan queue:work

php artisan websockets:serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Follow the  &lt;a href="https://docs.beyondco.de/laravel-websockets/1.0/basic-usage/starting.html#keeping-the-socket-server-running-with-supervisord"&gt;documentation&lt;/a&gt;  to learn which other options are available to you.&lt;br&gt;
Now, you should see that our demo application still works but now handling the WebSockets locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the server in the background
&lt;/h2&gt;

&lt;p&gt;Just like queue workers, we don't want to run the WebSockets server ourself on our real server. The solution is the same as for our queue workers: let &lt;strong&gt;supervisord&lt;/strong&gt; take care of it. To keep this post focused, please review the docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://laravel.com/docs/5.8/queues#supervisor-configuration"&gt;Configure supervisord for Laravel's queues&lt;/a&gt; &lt;/li&gt;
&lt;li&gt; &lt;a href="https://docs.beyondco.de/laravel-websockets/1.0/basic-usage/starting.html#keeping-the-socket-server-running-with-supervisord"&gt;Configure supervisord for the WebSockets server&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The Laravel WebSockets package even comes with a dashboard, comparable to Pusher's &lt;em&gt;Debug Console&lt;/em&gt;. The default location of the dashboard is &lt;code&gt;/laravel-websockets&lt;/code&gt; from your root path of the demo application and is automagically available. Follow the  &lt;a href="https://docs.beyondco.de/laravel-websockets/1.0/debugging/dashboard.html#accessing-the-dashboard"&gt;documentation&lt;/a&gt;  to learn more about the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t83WrOLW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/kD8HPxX407YBSlrVigGgKUc0lovm6ciBU6GORO6p.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t83WrOLW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://johnbraun.blog/storage/wink/images/kD8HPxX407YBSlrVigGgKUc0lovm6ciBU6GORO6p.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I hope that I've convinced you that it's easier than ever to get started with realtime broadcasting. Two of the many possible implementations were discussed in this post. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pusher&lt;/strong&gt; is a great option to get up and running fast and easy. They offer a free account which offers a maximum of 100 simultaneous connections, 200 000 messages a day and unlimited channels. For small applications, it's a no brainer and I would always choose for the convenience of Pusher.&lt;/p&gt;

&lt;p&gt;However, if your application needs a bit more room to breathe the &lt;strong&gt;Laravel WebSockets&lt;/strong&gt; package offers a great alternative which let you handle all WebSocket connections yourself and even comes with a dashboard! On top of that, switching over from &lt;strong&gt;Pusher&lt;/strong&gt; to &lt;strong&gt;Laravel WebSockets&lt;/strong&gt; is a breeze.&lt;/p&gt;

&lt;p&gt;Happy broadcasting!&lt;/p&gt;

</description>
      <category>websockets</category>
      <category>laravel</category>
      <category>pusher</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
