<?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: Grant</title>
    <description>The latest articles on DEV Community by Grant (@grantholle).</description>
    <link>https://dev.to/grantholle</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%2F309415%2Fcda6de14-7f1f-40e3-9905-3c4a0eafead1.png</url>
      <title>DEV Community: Grant</title>
      <link>https://dev.to/grantholle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/grantholle"/>
    <language>en</language>
    <item>
      <title>Complex Filtering in Laravel Scout with Typesense</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Sun, 03 Aug 2025 23:49:26 +0000</pubDate>
      <link>https://dev.to/grantholle/complex-filtering-in-laravel-scout-with-typesense-74l</link>
      <guid>https://dev.to/grantholle/complex-filtering-in-laravel-scout-with-typesense-74l</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;tl;dr Passing an array as the second parameter in the query builder's &lt;code&gt;where&lt;/code&gt; function allows you to use different comparison operators for your Typesense filters as &lt;code&gt;[$operator, $value]&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://laravel.com/docs/scout" rel="noopener noreferrer"&gt;Laravel Scout&lt;/a&gt; is the first-party solution to adding a variety of full-text search tools, such as Algolia, Meilisearch or Typesense. Since it's a plug-and-play solution, they have to make the query builder API a one-size-fits-all solution for each driver.&lt;/p&gt;

&lt;p&gt;In this post I'll talk about how I added support for more complex filtering for &lt;a href="https://typesense.org/docs/29.0/api/search.html#filter-parameters" rel="noopener noreferrer"&gt;Typesense&lt;/a&gt; that was undocumented in Scout using the built-in &lt;code&gt;where&lt;/code&gt; query builder helper.&lt;/p&gt;

&lt;p&gt;I'll be talking in the context of Laravel Scout with Typesense only. These principles or ideas won't necessarily apply to the other search tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  You probably don't need Scout
&lt;/h2&gt;

&lt;p&gt;First, a bit of a warning.&lt;/p&gt;

&lt;p&gt;In my experience, you probably don't actually need Scout. Scout adds technical burden (read: debt) in a few ways, and unless you absolutely need full-text searching capabilities, with built-in typo support or other unique features, a simple query will do the trick most often than not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration takes time. Setting up the fields, applying the configuration to each model, and ensuring the right data types can be time consuming. There are some caveats, such as you cannot use &lt;code&gt;null&lt;/code&gt; for a &lt;code&gt;string&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;You have to maintain the schema for the collection in Typesense. This is a deployment burden if/when the schema for your model changes.

&lt;ul&gt;
&lt;li&gt;If it changes often enough, then you'll be doing a lot of &lt;code&gt;scout:flush&lt;/code&gt; and &lt;code&gt;scout:import&lt;/code&gt; calls. If you're not careful, exceptions will be thrown before you're able to update it in production.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Keeping the collection up-to-date is sometimes tricky. Scout does its best to keep the data in Typesense current, but getting out of sync is still easy.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  When you need it
&lt;/h2&gt;

&lt;p&gt;When you &lt;em&gt;do&lt;/em&gt; actually need the capabilities of Typesense, then it's absolutely powerful. If you can overcome the annoyances I've described above and are certain you need to leverage Typesense, you have a lot of power at your fingertips.&lt;/p&gt;

&lt;p&gt;Reading all the &lt;a href="https://typesense.org/docs/29.0/api/search.html#search-parameters" rel="noopener noreferrer"&gt;search options&lt;/a&gt; available in Typesense, it's easy to see they do all the heavy lifting for filtering and searching collections. It's fast and efficient, as advertised.&lt;/p&gt;

&lt;p&gt;There are things to keep in mind in a Laravel app with Scout, though.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have to really understand the Scout query builder. Again, it's a unified query builder API for each vendor. This means it's simplified compared to what each vendor &lt;em&gt;actually&lt;/em&gt; offers.&lt;/li&gt;
&lt;li&gt;Laravel is searching in Typesense, then converting that back to your model instances using your own database. You have to have a good mental model of the Typesense collection schema and the one that it actually references.&lt;/li&gt;
&lt;li&gt;Relationships aren't a thing in Typesense. You have to be creative in the ways that build your Typesense collection schema so that you can leverage filtering based on relationships.&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/grantholle" rel="noopener noreferrer"&gt;
        grantholle
      &lt;/a&gt; / &lt;a href="https://github.com/grantholle/scout-typesense-example" rel="noopener noreferrer"&gt;
        scout-typesense-example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An example of using Typesense with Scout using undocumented filtering
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Laravel Scout with Typesense&lt;/h1&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repo.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;composer install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set your Typesense server URL and API key in the &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;php artisan key:generate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;php artisan migrate --seed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;php artisan scout:import "App\Models\Post"&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/grantholle/scout-typesense-example" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;I've created a sample repo that shows how this works.&lt;/p&gt;

&lt;p&gt;It's a simple configuration with a &lt;code&gt;Post&lt;/code&gt; model that has many &lt;code&gt;Comment&lt;/code&gt; relationships. The Scout configuration for Typesense looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;\App\Models\Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;'collection-schema'&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;'fields'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string'&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="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'comments'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'string[]'&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="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'int64'&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="s1"&gt;'default_sorting_field'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'search-parameters'&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;'query_by'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'title,content,comments'&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;When we call &lt;code&gt;Post::search()&lt;/code&gt;, by default we want to search across the title, content and comment contents. We can adjust this if we wanted to, but this will allow us to leverage Typesense in a way that is harder than a typical Eloquent query. Again, the uses cases are less common than you might think for reaching for Scout. A blog search is a good example, though.&lt;/p&gt;

&lt;p&gt;Here's how we're indexing our actual data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;makeSearchableUsing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Collection&lt;/span&gt; &lt;span class="nv"&gt;$models&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Collection&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;$models&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'comments'&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="n"&gt;toSearchableArray&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&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="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'comments'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;timestamp&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;First, we're making the search query a bit more optimized by eager loading comments in &lt;code&gt;makeSearchableUsing()&lt;/code&gt;. Then we're populating the fields we configured in &lt;code&gt;scout.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We're wanting to search by comments as well, so we're creating an array of each comment &lt;code&gt;content&lt;/code&gt;, which is supported by the field's &lt;code&gt;string[]&lt;/code&gt; type. Now, Typesense knows and can search based on the comments. Note: since we're indexing the &lt;code&gt;Post&lt;/code&gt; model, it's not going to reindex when a comments change. We're going to have to re-trigger the index manually on the comment's &lt;code&gt;post&lt;/code&gt; when comments change.&lt;/p&gt;

&lt;p&gt;One caveat I'll point out here: if &lt;code&gt;title&lt;/code&gt; was nullable in our database, we'd want to update &lt;code&gt;toSearchableArray()&lt;/code&gt; in the &lt;code&gt;title&lt;/code&gt; field to be &lt;code&gt;'title' =&amp;gt; $this-&amp;gt;title ?? '',&lt;/code&gt;. If we had &lt;code&gt;null&lt;/code&gt; as a &lt;code&gt;title&lt;/code&gt; value, indexing would throw an error: "Error importing document: Field &lt;code&gt;title&lt;/code&gt; must be a string."&lt;/p&gt;

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

&lt;p&gt;When we visit the endpoint, there's a simple controller to perform the searches for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&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="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;search&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;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&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="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;is_numeric&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;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created'&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="kt"&gt;Builder&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&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;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;subDays&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;integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;timestamp&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="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;empty&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;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'exclude'&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="kt"&gt;Builder&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&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;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereNotIn&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="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&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;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'exclude'&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="nf"&gt;when&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;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'not_title'&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="kt"&gt;Builder&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$title&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="nf"&gt;get&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;$posts&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'comments'&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;Here's the breakdown of the search options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we pass in a &lt;code&gt;q&lt;/code&gt; query string variable, this is the whole point of Scout. This performs the "search" in Typesense leveraging all of its magic. It's searching across the fields we've set up to &lt;code&gt;query_by&lt;/code&gt;. It's typo-tolerant, &lt;a href="https://typesense.org/docs/overview/features.html" rel="noopener noreferrer"&gt;etc.&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Sending a &lt;code&gt;created&lt;/code&gt; value will search for posts that have been created after X days ago. More on this later.&lt;/li&gt;
&lt;li&gt;We can exclude post ID's by comma separating them in &lt;code&gt;exclude&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Lastly, with &lt;code&gt;not_title&lt;/code&gt; we can exclude posts where titles contain the given values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, what's special?&lt;/p&gt;

&lt;p&gt;Behind the scenes, Scout is using the query builder to retrieve results from Typesense and taking those results to subsequently query the actual database to retrieve the model values.&lt;/p&gt;

&lt;p&gt;In this example, we could have just leveraged Scout for its searching capabilities and called it a day. However, that's not really a real-world example. Usually we're going to have multiple filtering options like I've implemented.&lt;/p&gt;

&lt;p&gt;Since Scout is a one-size-fits-all and each driver will implement filtering differently, it's basic by nature out of the box. As the &lt;a href="https://laravel.com/docs/12.x/scout#where-clauses" rel="noopener noreferrer"&gt;docs state&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since a search index is not a relational database, more advanced "where" clauses are not currently supported.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can hook into the query that's used to retrieve our models after performing the search in Typesense using the &lt;code&gt;query&lt;/code&gt; hook. There's also a catch there:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since this callback is invoked after the relevant models have already been retrieved from your application's search engine, the query method should not be used for "filtering" results. Instead, you should use Scout where clauses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's an example. Let's say we're just performing our search and paginating on those results with page sizes of 5. If we use the &lt;code&gt;query&lt;/code&gt; hook to filter further, we're only going to filter &lt;em&gt;on those 5 results that were returned&lt;/em&gt;. Each page would have inconsistent number of results, or even no results if the filter doesn't apply to any of those 5.&lt;/p&gt;

&lt;p&gt;Scout supports simple &lt;code&gt;where&lt;/code&gt;'s out of the box, but it's always strict comparison. When it's static, like give me posts with a given tag ID (tag = 1), then it's straightforward. But what about other types of comparisons?&lt;/p&gt;

&lt;p&gt;Spoiler: Typesense allows us to configure the comparison operators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the scenes
&lt;/h2&gt;

&lt;p&gt;In our example, we're retrieving posts that were created with in the last X days (created_at &amp;gt; 10 days ago). By default, Scout doesn't support this. The method signature for &lt;code&gt;where&lt;/code&gt; is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&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="n"&gt;wheres&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$value&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="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;It only accepts a field key and its value. Unlike Eloquent's query builder, which accepts an &lt;code&gt;$operator&lt;/code&gt; parameter, Scout's is simplified.&lt;/p&gt;

&lt;p&gt;The good news is, since each engine (Algolia, Typesense, Meilisearch) implements the filtering themselves, we can dive into Typesense's engine to see what's possible.&lt;/p&gt;

&lt;p&gt;The beginning of the hunt for the functionality comes from &lt;a href="https://github.com/laravel/scout/blob/10.x/src/Engines/TypesenseEngine.php#L331" rel="noopener noreferrer"&gt;&lt;code&gt;buildSearchParameters()&lt;/code&gt;&lt;/a&gt;. The key we're after for filtering is the &lt;code&gt;filter_by&lt;/code&gt; key, which is set by the &lt;code&gt;filters()&lt;/code&gt; function, which ultimately builds the filter clauses in &lt;code&gt;parseWhereFilter()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;parseWhereFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%s:%s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%s:=%s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&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 the &lt;code&gt;$value&lt;/code&gt; we pass in is an array, it's constructing the comparison using the two parameters. Otherwise, it's a strict "exactly equals" filter (&lt;code&gt;:=&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This is what explains our filter of &lt;code&gt;created_at&lt;/code&gt; in the controller:&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="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;subDays&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;integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can do numeric filtering now (greater than, less than, etc.) by passing an array as the value.&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="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$comparison&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can retrieve consistent results for pagination without having to rely on Eloquent to filter further for us which will be inconsistent behavior. The same goes for the &lt;code&gt;not_title&lt;/code&gt; query variable. We can use the &lt;code&gt;:!&lt;/code&gt; Typesense operator (not exact contains) to exclude posts with a title that contains a given value.&lt;/p&gt;

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

&lt;p&gt;Thankfully, the Scout Typesense engine was built to be flexible enough to support these filters out of the box. This allows us to leverage Typesense to its full potential. By passing a tuple of &lt;code&gt;[$operator, $value]&lt;/code&gt;, we can use Typesense's built-in filtering much simpler, providing an excellent all-in-one search/filter solution for our data.&lt;/p&gt;

&lt;p&gt;Extra credit: the Typesense &lt;a href="https://github.com/laravel/scout/blob/10.x/src/Engines/TypesenseEngine.php#L351" rel="noopener noreferrer"&gt;also allows&lt;/a&gt; us to define a &lt;code&gt;typesenseSearchParameters()&lt;/code&gt; function on the search model to include or overwrite other parameters that it supports when searching.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>typesense</category>
    </item>
    <item>
      <title>Exploring Middleware in Laravel 11</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Thu, 04 Jan 2024 05:00:19 +0000</pubDate>
      <link>https://dev.to/grantholle/exploring-middleware-in-laravel-11-2e10</link>
      <guid>https://dev.to/grantholle/exploring-middleware-in-laravel-11-2e10</guid>
      <description>&lt;p&gt;Laravel 11 is set to release in "Q1" of 2024, which may be as soon as next month.&lt;/p&gt;

&lt;p&gt;I am starting a new project, and because it's so close to the release date I figured I would take a look at what's going to be different with the new major release. I remember reading a &lt;a href="https://laravel-news.com/laravel-11" rel="noopener noreferrer"&gt;Laravel News article&lt;/a&gt; 6 months ago about how the Http Kernel was going away and didn't think much of it.&lt;/p&gt;

&lt;p&gt;When I created the project using &lt;code&gt;laravel new project --dev&lt;/code&gt;, I was quite surprised at how much smaller the project size was. It was very surprising to see an empty &lt;code&gt;config&lt;/code&gt; folder (you can publish the config files with &lt;code&gt;php artisan config:publish&lt;/code&gt;)!&lt;/p&gt;

&lt;p&gt;And sure enough, there is no Http Kernel. So… how do you add or change middleware? Prior to Laravel 11, the Http Kernel (found in &lt;code&gt;app/Http/Kernel.php&lt;/code&gt;) is where all the configuration for &lt;a href="https://laravel.com/docs/10.x/middleware" rel="noopener noreferrer"&gt;middleware&lt;/a&gt; was found. Also prior to Laravel 11, you would typically never touch &lt;code&gt;bootstrap/app.php&lt;/code&gt;. However, that is no longer the case in this new version.&lt;/p&gt;

&lt;p&gt;To be honest, as a Laravel user since v4.2, this is quite the paradigm shift. Instead of an easy-to-read &lt;code&gt;Kernel.php&lt;/code&gt; file, there is a lot more implicit knowledge you need before adding middleware.&lt;/p&gt;

&lt;p&gt;The new starting point for &lt;code&gt;bootstrap/app.php&lt;/code&gt; is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withProviders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withRouting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'/../routes/web.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// api: __DIR__.'/../routes/api.php',&lt;/span&gt;
        &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'/../routes/console.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// channels: __DIR__.'/../routes/channels.php',&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withMiddleware&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="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withExceptions&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="kt"&gt;Exceptions&lt;/span&gt; &lt;span class="nv"&gt;$exceptions&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am just exploring middleware in this post, but as you can see this is quite a different approach than we've seen historically. I sat there scratching my head, "How do I set up my own middleware? How do I change the defaults?" I had to explore the &lt;a href="https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Configuration/Middleware.php" rel="noopener noreferrer"&gt;&lt;code&gt;Illuminate\Foundation\Configuration\Middleware&lt;/code&gt;&lt;/a&gt; class to find out.&lt;/p&gt;

&lt;p&gt;Calling &lt;code&gt;Application::configure()&lt;/code&gt; returns an instance of &lt;code&gt;Illuminate\Foundation\Configuration\ApplicationBuilder&lt;/code&gt;, where it then calls the various functions, &lt;code&gt;withProviders()&lt;/code&gt;, &lt;code&gt;withRouting()&lt;/code&gt; and the &lt;code&gt;withMiddleware()&lt;/code&gt; functions. The &lt;code&gt;withMiddleware()&lt;/code&gt; function takes a &lt;code&gt;callable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using the boilerplate, we can add new &lt;a href="https://laravel.com/docs/10.x/middleware#assigning-middleware-to-routes" rel="noopener noreferrer"&gt;middleware aliases&lt;/a&gt; by calling &lt;code&gt;alias()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'some_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Http\Middleware\MyMiddleware&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this &lt;code&gt;some_key&lt;/code&gt; is added, we can assign it to individual routes and route groups. If we want to add middleware to every request, we can use the &lt;code&gt;append()&lt;/code&gt; or &lt;code&gt;prepend()&lt;/code&gt; functions to add global middleware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Using a string&lt;/span&gt;
    &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\App\Http\Middleware\MyMiddleware&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="c1"&gt;// Or adding multiple&lt;/span&gt;
    &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;\App\Http\Middleware\MyMiddleware&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="nc"&gt;\App\Http\Middleware\MyOtherMiddleware&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can remove middleware that's there by default by calling the &lt;code&gt;remove()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Using a string&lt;/span&gt;
    &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Illuminate\Http\Middleware\ValidatePostSize&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="c1"&gt;// Or removing multiple default middleware&lt;/span&gt;
    &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;\Illuminate\Http\Middleware\TrustProxies&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="nc"&gt;\Illuminate\Http\Middleware\HandleCors&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add or remove middleware to specific groups, i.e. the &lt;code&gt;web&lt;/code&gt; group using the &lt;code&gt;appendToGroup()&lt;/code&gt;/&lt;code&gt;prependToGroup()&lt;/code&gt; and &lt;code&gt;removeFromGroup()&lt;/code&gt; functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;appendToGroup&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;\App\Http\Middleware\MyMiddleware&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If/when this &lt;code&gt;bootstrap/app.php&lt;/code&gt; file gets a bit messy (which I imagine it will), we can clean it up by moving this stuff to an &lt;a href="https://www.amitmerchant.com/invokable-classes-php/" rel="noopener noreferrer"&gt;invokable class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've created a class in &lt;code&gt;app/Http&lt;/code&gt; called &lt;code&gt;AppMiddleware.php&lt;/code&gt;. As old habits die hard, you might call this &lt;code&gt;Kernel.php&lt;/code&gt;…&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http&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\Foundation\Configuration\Middleware&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;AppMiddleware&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;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;appendToGroup&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;\App\Http\Middleware\MyMiddleware&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in &lt;code&gt;bootstrap/app.php&lt;/code&gt;, replace the closure with a new instance of our invokable class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withProviders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withRouting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'/../routes/web.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// api: __DIR__.'/../routes/api.php',&lt;/span&gt;
        &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'/../routes/console.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// channels: __DIR__.'/../routes/channels.php',&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\App\Http\AppMiddleware&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withExceptions&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="kt"&gt;Exceptions&lt;/span&gt; &lt;span class="nv"&gt;$exceptions&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like all new things, change is hard. I have a lingering question with this change. We have all the same tooling and ability, but it's a bit more difficult, at least at first, to understand how to manage it all.&lt;/p&gt;

&lt;p&gt;I feel like this change will require a lot more implicit knowledge of the built-in middleware. Does it matter? Probably not. But in some cases, you might need to remove or replace a default middleware. This requires you actually &lt;em&gt;know&lt;/em&gt; what is there by default. Not just in the global middleware, but in groups and aliased middleware. Personally I feel like this change increases the learning curve. Even I forget what's there by default or what the aliases are and reference the &lt;code&gt;app/Http/Kernel.php&lt;/code&gt; file regularly.&lt;/p&gt;

&lt;p&gt;I question whether the lack of verbosity and smaller file footprint is worth it?&lt;/p&gt;

&lt;p&gt;What are your thoughts with this new change?&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Does anyone else experience FOMO?</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Wed, 03 Nov 2021 00:40:42 +0000</pubDate>
      <link>https://dev.to/grantholle/does-anyone-else-experience-fomo-2ek4</link>
      <guid>https://dev.to/grantholle/does-anyone-else-experience-fomo-2ek4</guid>
      <description>&lt;p&gt;Cover Photo by &lt;a href="https://unsplash.com/@heatherz?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Heather Zabriskie&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/broken?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FO·MO | ˈfōmō |&lt;br&gt;
&lt;em&gt;noun&lt;/em&gt; informal&lt;br&gt;
anxiety that an exciting or interesting event may currently be happening elsewhere, often aroused by posts seen on social media.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;FOMO, or &lt;em&gt;fear of missing out&lt;/em&gt;, can be paralyzing.&lt;/p&gt;

&lt;p&gt;I'm starting to feel paralyzed.&lt;/p&gt;

&lt;p&gt;Languages I don't know but want to learn, JavaScript frameworks, blockchain and Web3.0, new CSS features, Jamstack and serverless tooling, conferences, books, career development, "hustling"...&lt;/p&gt;

&lt;p&gt;How do you manage it all?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>discuss</category>
      <category>programming</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Laravel 🔥 tip: Generating migrations easier</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Fri, 15 Oct 2021 01:31:15 +0000</pubDate>
      <link>https://dev.to/grantholle/laravel-tip-generating-migrations-easier-2jd8</link>
      <guid>https://dev.to/grantholle/laravel-tip-generating-migrations-easier-2jd8</guid>
      <description>&lt;p&gt;Generating database migrations in Laravel is already really easy to do with the &lt;code&gt;php artisan make:migration&lt;/code&gt;. There are some nice ergonomics that aren't documented that will make it even easier.&lt;/p&gt;

&lt;p&gt;I find myself struggling to write the name in snake case (replacing spaces with the &lt;code&gt;_&lt;/code&gt; character). I was thinking, wouldn't it be nice if I could just write it normally and it do that for me? It already does!&lt;/p&gt;

&lt;p&gt;If we wrap the name of our migration in quotes, we're able to write it much quicker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:migration "my migration name"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate the correct name of the migration for us by automatically running the name through the &lt;code&gt;Str::snake()&lt;/code&gt; helper. This means we could also have weird casing, and it will all be ok.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto table detections
&lt;/h2&gt;

&lt;p&gt;If you want to generate the migration with the &lt;code&gt;Schema::create()&lt;/code&gt; or &lt;code&gt;Schema::table()&lt;/code&gt; scaffolding already there for you, you can pass a &lt;code&gt;--create=&lt;/code&gt; or &lt;code&gt;--table=&lt;/code&gt; option respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:migration "my migration name" --table=users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, the command also tries to guess your table name based on the name of your migration if you don't explicitly tell it a name.&lt;/p&gt;

&lt;p&gt;The below example will automatically create a migration with the &lt;code&gt;Schema::table('users'...)&lt;/code&gt; scaffolding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:migration "add column to users"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We didn't specify the table option, but it detected the table based on how we named our migration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&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="c1"&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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;down&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&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="c1"&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;up()&lt;/code&gt; and &lt;code&gt;down()&lt;/code&gt; functions automatically included our table. Let's check out how.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;MigrateMakeCommand&lt;/code&gt; command class, there's a call to &lt;code&gt;TableGuesser::guess($name)&lt;/code&gt;. In that guesser class, it's checking if our name contains a certain pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;CREATE_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'/^create_(\w+)_table$/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'/^create_(\w+)$/'&lt;/span&gt;&lt;span class="p"&gt;,&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;CHANGE_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'/_(to|from|in)_(\w+)_table$/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'/_(to|from|in)_(\w+)$/'&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;What this means is that if our migration name is simply &lt;code&gt;create_[table name]_table&lt;/code&gt; or just &lt;code&gt;create_[table name]&lt;/code&gt; it will assume you're creating a table name whatever value [table name] is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:migration "create planets table"

# or

php artisan make:migration "create planets"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our migration will include the same scaffolding had we done the command this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:migration --create=planets "create planets"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Likewise for the &lt;code&gt;table&lt;/code&gt; migrations, we can just include a few keywords along with the table name at the end of the migration name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:migration "add favorite color to users table"

# or 

php artisan make:migration "add favorite color to users"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The migration will include the table details for us. You just need to include any one of the prepositions "to/from/in" followed by your table name at the end of your migration name and it will detect your table name for you automatically.&lt;/p&gt;

&lt;p&gt;Here are some examples of good migration names that will automatically detect the &lt;code&gt;users&lt;/code&gt; table for us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"make name nullable in users"&lt;/li&gt;
&lt;li&gt;"add favorite color to users table"&lt;/li&gt;
&lt;li&gt;"remove ssn from users"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This behavior "rewards" us when we write meaningful migration names in the form of less typing... win!&lt;/p&gt;

</description>
      <category>laravel</category>
    </item>
    <item>
      <title>Better CSRF refreshing in Laravel and axios</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Sun, 10 Oct 2021 09:17:02 +0000</pubDate>
      <link>https://dev.to/grantholle/better-csrf-refreshing-in-laravel-and-axios-177c</link>
      <guid>https://dev.to/grantholle/better-csrf-refreshing-in-laravel-and-axios-177c</guid>
      <description>&lt;p&gt;This article is written in the context of using &lt;a href="https://inertiajs.com/" rel="noopener noreferrer"&gt;Inertia.js&lt;/a&gt;, but the concept and code snippets work anytime you're using the &lt;code&gt;axios&lt;/code&gt; package in your Laravel project.&lt;/p&gt;

&lt;p&gt;I'm a huge fan of &lt;a href="https://inertiajs.com/" rel="noopener noreferrer"&gt;Inertia.js&lt;/a&gt; when building applications with Laravel. If you're unfamiliar with it, go to their site and read up on it.&lt;/p&gt;

&lt;p&gt;A common issue when doing an SPA-like application, like when using Inertia, is that you'll run in to CSRF mismatch exceptions (read more about the what and why of CSRF &lt;a href="https://laravel.com/docs/csrf" rel="noopener noreferrer"&gt;here&lt;/a&gt;). Inertia &lt;a href="https://inertiajs.com/csrf-protection#handling-mismatches" rel="noopener noreferrer"&gt;mentions a way&lt;/a&gt; to make the user aware in a friendly way, but I still find that lacking as far as a good user experience.&lt;/p&gt;

&lt;p&gt;The user has no concept of what a CSRF token is or that it's needed to make some requests in the application. If they come back to their session after it being idle, the token will have expired. If they're in the middle of doing something and attempt to save, telling them that the "page has expired" will be frustrating, and it's not very intuitive that they will need to refresh the page to solve the problem.&lt;/p&gt;

&lt;p&gt;I've found that by adding a simple axios interceptor and an endpoint to create a fresh token, it can be seamless for the user and still serve its security purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the endpoint
&lt;/h2&gt;

&lt;p&gt;First, let's add the endpoint for getting a fresh token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:controller RefreshCsrfTokenController --invokable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will generate an &lt;a href="https://laravel.com/docs/controllers#single-action-controllers" rel="noopener noreferrer"&gt;invokable (single-action) controller&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Controllers&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\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;RefreshCsrfTokenController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */&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;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&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;//&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 functionality we need to generate a fresh token is only one line!&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;regenerateToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That creates a new token and stores it in our session for us. After regenerating a new token, we can return a simple json response. The completed controller function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;regenerateToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;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="nf"&gt;json&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;Finally, we need to add the endpoint to &lt;code&gt;routes/web.php&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/csrf-token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;\App\Http\Controllers\RefreshCsrfTokenController&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the backend is ready, let's handle the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding an axios interceptor
&lt;/h2&gt;

&lt;p&gt;Axios, which is the popular library that Inertia uses to make requests, has a feature called &lt;a href="https://github.com/axios/axios#interceptors" rel="noopener noreferrer"&gt;interceptors&lt;/a&gt; which allows us to "intercept" the response immediately after it is made and do some custom handling before Inertia takes over the response.&lt;/p&gt;

&lt;p&gt;I like to organize this behavior in a &lt;code&gt;plugins&lt;/code&gt; directory in my Vue application and include it in the main &lt;code&gt;app.js&lt;/code&gt; file. Let's create a file in &lt;code&gt;resources/js/plugins&lt;/code&gt; called &lt;code&gt;https.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We need to import the &lt;code&gt;axios&lt;/code&gt; library and set up the interceptor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// resources/js/plugins/http.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Add this line in resources/js/app.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./plugins/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;axios.interceptors.response.use&lt;/code&gt; function accepts two arguments, a successful (&lt;code&gt;2xx&lt;/code&gt; status code) response handler and an error (non-&lt;code&gt;2xx&lt;/code&gt; status code) handler. For the successful response, we don't need to do anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just returns the response normally without any processing.&lt;/p&gt;

&lt;p&gt;For our error handler, we need to check for a status code of &lt;code&gt;419&lt;/code&gt;, which is what Laravel sends when it throws an &lt;code&gt;Illuminate\Session\TokenMismatchException&lt;/code&gt;. I'm going to be using &lt;code&gt;lodash/get&lt;/code&gt; (&lt;code&gt;lodash&lt;/code&gt; comes with the default Laravel project) for a clean way to get a variable from an object that may not have all the properties we're wanting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;get&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;lodash/get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;response.status&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;419&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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 the status isn't 419, we're going to reject the promise as usual.&lt;/p&gt;

&lt;p&gt;In the error response, axios gives us the configuration that was used to make the initial request. Basically, we just need to do a couple things at this point:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set a new token using the &lt;code&gt;/csrf-token&lt;/code&gt; endpoint that we made at the beginning.&lt;/li&gt;
&lt;li&gt;Retry the original request having the regenerated token.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm going to make the callback &lt;code&gt;async&lt;/code&gt; so we can call our &lt;code&gt;/csrf-token&lt;/code&gt; endpoint using &lt;code&gt;await&lt;/code&gt;. Here's the full implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;get&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;lodash/get&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&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;err&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;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;response.status&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;419&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Refresh our session token&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;/csrf-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Return a new request using the original request's configuration&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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="nx"&gt;config&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;At this point, we actually only needed to add a couple lines of code to make a seamless experience for the end user.&lt;/p&gt;

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

&lt;p&gt;Let's create a simple &lt;code&gt;post&lt;/code&gt; endpoint to try the functionality using a closure. In &lt;code&gt;routes/web.php&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ok'&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to add a little disruption to our &lt;code&gt;app/Http/Middleware/VerifyCsrfToken.php&lt;/code&gt; middleware to simulate an expired token. By default, this middleware extends Laravel's &lt;code&gt;Illuminate\Foundation\Http\Middleware\VerifyCsrfToken&lt;/code&gt; class that already includes the functionality for the &lt;code&gt;handle&lt;/code&gt; function. We can cause a little mayhem by regenerating our token before it's checked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;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="nb"&gt;random_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;regenerateToken&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;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&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="nv"&gt;$next&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;random_int(0, 1)&lt;/code&gt; generates a random number, either &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;, and if it's &lt;code&gt;1&lt;/code&gt; then it will generate a new token in our session. Here's how it breaks down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When the middleware gets handled, it will check if the token that was passed with the request matches the one we sent.&lt;/li&gt;
&lt;li&gt;The snippet we added will randomly regenerate the token. If it gets regenerated, the tokens won't match.&lt;/li&gt;
&lt;li&gt;When they don't match, it will throw the &lt;code&gt;TokenMismatchException&lt;/code&gt;, aka a &lt;code&gt;419&lt;/code&gt; status code.&lt;/li&gt;
&lt;li&gt;Our interceptor will then call &lt;code&gt;/csrf-token&lt;/code&gt;, which then once again regenerates a token.&lt;/li&gt;
&lt;li&gt;Eventually, the middleware will not preemptively change the token and the request will resolve correctly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since I'm using Vue 3 with Inertia, I'm going to create a component to make this request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&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;"button"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.prevent=&lt;/span&gt;&lt;span class="s"&gt;"sendTest"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Test&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&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;defineComponent&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&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;axios&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;setup &lt;/span&gt;&lt;span class="p"&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;sendTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="nx"&gt;sendTest&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;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've just got a simple button that when clicked sends a &lt;code&gt;post&lt;/code&gt; request to our &lt;code&gt;/test&lt;/code&gt; endpoint and logs the results, which in this case should be &lt;code&gt;{ "status": "ok" }&lt;/code&gt; as returned from our &lt;code&gt;/test&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;Clicking on my button shows this in the console:&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;The first time it posts to &lt;code&gt;/test&lt;/code&gt;, it fails with a &lt;code&gt;419&lt;/code&gt;, which means that it threw the &lt;code&gt;TokenMismatchException&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It then sends a request to &lt;code&gt;/csrf-token&lt;/code&gt;, which means that it has been caught by our interceptor.&lt;/li&gt;
&lt;li&gt;The original &lt;code&gt;/test&lt;/code&gt; request is retried, this time successful (the middleware didn't regenerate the token before handling the request).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we click the button several times, sometimes it doesn't fail, while other times it will fail multiple times before finally being successful. This is because we're simulating a random token mismatch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleaning up
&lt;/h3&gt;

&lt;p&gt;Once we're done testing, we can delete the middleware's &lt;code&gt;handle&lt;/code&gt; function entirely since we want Laravel manage this particular middleware. We'll also delete the &lt;code&gt;/test&lt;/code&gt; route from &lt;code&gt;routes/web.php&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Overall, the impact on our codebase is quite minimal, yet the user experience is greatly improved. The user can leave their session and allow their CSRF token to expire. Coming back and doing a request won't interrupt their desired workflow, which in my opinion is more desired than telling them that the "page has expired" and making them refresh the page.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>inertia</category>
    </item>
    <item>
      <title>Speed up migrations when running Laravel tests</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Wed, 25 Aug 2021 07:15:46 +0000</pubDate>
      <link>https://dev.to/grantholle/speed-up-migrations-when-running-laravel-tests-51ak</link>
      <guid>https://dev.to/grantholle/speed-up-migrations-when-running-laravel-tests-51ak</guid>
      <description>&lt;p&gt;I'm currently doing maintenance on a two year old application. Not that old, right? Eh, sort of.&lt;/p&gt;

&lt;p&gt;It was created using Laravel 7. I was able to upgrade to Laravel 8 &lt;em&gt;and&lt;/em&gt; PHP 8.0, so that's great! The thing is, I'm now going test-by-test and optimizing and house cleaning. I'm also mad at Past Grant for being lazy.&lt;/p&gt;

&lt;p&gt;The application is... big. One of my biggest I've ever built. Like 126-migration-files big. When I went to my first test, I noticed that it took &lt;strong&gt;forever&lt;/strong&gt; for it to actually run the test (it failed if you were wondering). When I mean forever, I mean over 11 seconds, which might has well been 11 minutes when I'm staring at the console waiting for it to start.&lt;/p&gt;

&lt;p&gt;I remembered when watching the keynote on Laravel 8, Taylor introduced the new feature of &lt;a href="https://laravel.com/docs/8.x/migrations#squashing-migrations" rel="noopener noreferrer"&gt;squashing migrations&lt;/a&gt;. Let's see what impact it had on my tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan schema:dump &lt;span class="nt"&gt;--prune&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates a file in &lt;code&gt;database/schema&lt;/code&gt; and deletes all my migration files. Before squashing the migrations, it was taking 11.031 seconds according to well placed &lt;a href="https://myray.app/" rel="noopener noreferrer"&gt;Ray&lt;/a&gt; call in the &lt;code&gt;RefreshDatabase&lt;/code&gt; trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;refreshTestDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'migrating'&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="o"&gt;!&lt;/span&gt; &lt;span class="nc"&gt;RefreshDatabaseState&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$migrated&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;artisan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'migrate:fresh'&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;migrateFreshUsing&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="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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setArtisan&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="nc"&gt;RefreshDatabaseState&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$migrated&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="nf"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'migrating'&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;beginDatabaseTransaction&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;After squashing, it took only 1.396 seconds. That's almost a 90% decrease in time! Now I can spend the next several days fixing all my terrible tests, but they'll at least run faster.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>testing</category>
    </item>
    <item>
      <title>Implementing Laravel's built-in token authentication</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Thu, 04 Mar 2021 02:07:55 +0000</pubDate>
      <link>https://dev.to/grantholle/implementing-laravel-s-built-in-token-authentication-48cf</link>
      <guid>https://dev.to/grantholle/implementing-laravel-s-built-in-token-authentication-48cf</guid>
      <description>&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/grantholle" rel="noopener noreferrer"&gt;
        grantholle
      &lt;/a&gt; / &lt;a href="https://github.com/grantholle/laravel-token-auth-example" rel="noopener noreferrer"&gt;
        laravel-token-auth-example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is a basic implementation of Laravel's default "token" auth driver.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;One of the great things about Laravel is its mission to provide developers with the tools they need out of the box, as easily as possible.&lt;/p&gt;

&lt;p&gt;More often than not when developing an application you're going to need some mechanism of authentication. Up until recently, Laravel shipped with a complete authentication toolbox: controllers, routes and views. Recently Laravel migrated a lot of its backend authentication functionality into &lt;a href="https://laravel.com/docs/fortify" rel="noopener noreferrer"&gt;Laravel Fortify&lt;/a&gt; and provided a frontend simple implementation using &lt;a href="https://laravel.com/docs/starter-kits" rel="noopener noreferrer"&gt;Breeze&lt;/a&gt;. There's also a more opinionated auth setup using &lt;a href="https://jetstream.laravel.com/" rel="noopener noreferrer"&gt;JetStream&lt;/a&gt; which combines Fortify and other currently-popular frontend tools &lt;a href="https://laravel-livewire.com/" rel="noopener noreferrer"&gt;Livewire&lt;/a&gt; and (my personal favorite) &lt;a href="https://inertiajs.com/" rel="noopener noreferrer"&gt;Inertiajs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what about API's? Laravel provides two solutions, &lt;a href="https://laravel.com/docs/sanctum" rel="noopener noreferrer"&gt;Sanctum&lt;/a&gt; and &lt;a href="https://laravel.com/docs/passport" rel="noopener noreferrer"&gt;Passport&lt;/a&gt;. Both of these are quite fully featured; they come with all the tools for users to generate tokens for themselves to interact with your application.&lt;/p&gt;

&lt;p&gt;But recently I needed to implement very simple API authentication for interacting with an application &lt;em&gt;from a different&lt;/em&gt; application. Think "machine to machine" interaction. Passport ships with an entire OAuth implementation, which includes &lt;a href="https://laravel.com/docs/passport#client-credentials-grant-tokens" rel="noopener noreferrer"&gt;client credentials grant tokens&lt;/a&gt;. The gist is that you provide a token to authenticate without any of the traditional OAuth flow.&lt;/p&gt;

&lt;p&gt;While Passport is a great tool, it includes way more than I needed. Lucky for you and me, Laravel already ships with a token-based authentication mechanism. Unfortunately it's actually quite undocumented.&lt;/p&gt;

&lt;h1&gt;
  
  
  Begin!
&lt;/h1&gt;

&lt;p&gt;I'm starting with a clean project using PHP 8 (version isn't important, just being thorough) for this tutorial, but I added it to my existing project using the same logic. What's really great is that there are &lt;strong&gt;no packages&lt;/strong&gt; we need to install. Laravel ships with all the tools we need.&lt;/p&gt;

&lt;p&gt;The important thing to note here is that for this example, I needed to perform some actions at the admin level, not anything based on a particular &lt;em&gt;user&lt;/em&gt; account. I actually don't care &lt;em&gt;who&lt;/em&gt; is doing this API action (i.e. what the value of &lt;code&gt;$request-&amp;gt;user()&lt;/code&gt; is), just that I needed to do something remotely that is triggered by a different system, sort of like a microservice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the sample project
&lt;/h2&gt;

&lt;p&gt;Create the new &lt;a href="https://github.com/grantholle/laravel-token-auth-example" rel="noopener noreferrer"&gt;project&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;laravel new token-auth-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to set up your own database and know how to configure it to get it going. I'm not going to cover that here.&lt;/p&gt;

&lt;p&gt;Since we don't care about users right now, we can ignore that entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the token table
&lt;/h2&gt;

&lt;p&gt;Next, let's create a new migration for a table called &lt;code&gt;api_clients&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; php artisan make:migration --create=api_clients create_api_clients_table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll modify it to have just a column, &lt;code&gt;api_token&lt;/code&gt;. This is the column that Laravel will use to look up the token:&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="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_clients'&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="kt"&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="nf"&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="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_token'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;unique&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 we'll run the migrations.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure the api guard and provider
&lt;/h2&gt;

&lt;p&gt;We need to make the necessary changes to &lt;code&gt;config/auth.php&lt;/code&gt; to configure our token authentication. This was confusing for me for a while to understand what the different keys mean, but I'll try myself to explain it.&lt;/p&gt;

&lt;p&gt;The guards are what protect parts of your application. Laravel allows you to create separate guards so theoretically you could have different user models that use different parts of your application. I've done this and it can get a little messy, but it's nice that Laravel is flexible enough to allow this. By default it has two generic guards, "web" and "api". The web guard is for authenticating into your web app via the browser and the api guard is for, you guessed it, api access. There are separate guards because a browser and api are two different mediums of interacting with your application. &lt;/p&gt;

&lt;p&gt;The default configuration is pretty close to what we need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'guards' =&amp;gt; [
    'web' =&amp;gt; [
        'driver' =&amp;gt; 'session',
        'provider' =&amp;gt; 'users',
    ],

    'api' =&amp;gt; [
        'driver' =&amp;gt; 'token',
        'provider' =&amp;gt; 'api_clients', // was users
        'hash' =&amp;gt; true, // was false
    ],
],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've modified the &lt;code&gt;provider&lt;/code&gt; key to be &lt;code&gt;api_clients&lt;/code&gt; (more on that next) and then set &lt;code&gt;hash&lt;/code&gt; to be true. The &lt;code&gt;hash&lt;/code&gt; option means that we're going to store the token hashed (i.e. not in plain text)&lt;/p&gt;

&lt;p&gt;Next we'll need to configure its provider. The provider "provides" who the user is that is being authenticated. Your users (in our case clients) are stored in the database, and we need to tell Laravel &lt;em&gt;how&lt;/em&gt; to retrieve the user to verify they're valid.&lt;/p&gt;

&lt;p&gt;The default &lt;code&gt;users&lt;/code&gt; provider is that it's your &lt;code&gt;App\Models\User&lt;/code&gt; Eloquent model. For our api client, we could use Eloquent too, but again, we &lt;em&gt;don't care&lt;/em&gt; who it is. We don't need Eloquent's bells and whistles. We just need a table, which we've already created.&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="s1"&gt;'providers'&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;'users'&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;'eloquent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'model'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;App\Models\User&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="s1"&gt;'api_clients'&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;// &amp;lt;- Add the api_clients that was configured in our `api` guard&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;'database'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// We don't need eloquent&lt;/span&gt;
        &lt;span class="s1"&gt;'table'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'api_clients'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Change to be our table name, which happens to be the same as our provider name&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;Believe it or not, that's all we need for Laravel to automatically do token authentication in our api routes. But... how do we create clients?&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating API clients
&lt;/h2&gt;

&lt;p&gt;For our simple use-case, we don't need routes to manage tokens and all of that jazz. We need &lt;em&gt;one&lt;/em&gt; token to exist at a time. In times like this, I reach for an &lt;a href="https://laravel.com/docs/artisan" rel="noopener noreferrer"&gt;Artisan command&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:command GenerateAuthToken
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us all the functionality we need: remove old tokens and create a new one, providing the new token to me to use. In &lt;code&gt;app/Console/Commands/GenerateAuthToken.php&lt;/code&gt; we can add this in a few lines.&lt;/p&gt;

&lt;p&gt;Change the signature and description to something more meaningful:&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;/**
 * The name and signature of the console command.
 *
 * @var string
 */&lt;/span&gt;
&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'make:token'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * The console command description.
 *
 * @var string
 */&lt;/span&gt;
&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Generates a new api client auth token to consume our api'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the code for the constructor because we don't need it. In &lt;code&gt;handle()&lt;/code&gt; we need to create a token, remove old clients and create the new client with the new token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Illuminate\Support\Facades\DB&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\Str&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="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Generate a new long token&lt;/span&gt;
    &lt;span class="c1"&gt;// Be sure to import Illuminate\Support\Str at the top&lt;/span&gt;
    &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Delete existing tokens, maybe we lost the token&lt;/span&gt;
    &lt;span class="c1"&gt;// so we don't want existing ones floating around somewhere&lt;/span&gt;
    &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_clients'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereNotnull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_token'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a new entry with the hashed token value&lt;/span&gt;
    &lt;span class="c1"&gt;// so we don't store the token in plain text&lt;/span&gt;
    &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_clients'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'api_token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sha256'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Spit out the token so we can use it&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;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can try it out and generate a token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ php artisan make:token
YHEt7CNf1SBmQs6JbTPf7qMK8FgnynI5SiPmyJELrbAO61heKy0eKuiXrxBJ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we check out the database, the value for &lt;code&gt;api_token&lt;/code&gt; is hashed as &lt;code&gt;277b302457eeccc1607b024b16f6c50bfaf37d7db028f9bf1daf4add58282a57&lt;/code&gt;. When we make a request, Laravel will hash our token to match it against what's in the database, rather than comparing the raw token. This also means that you better put that token somewhere safe, as that's the only time you'll be able to see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implement a route
&lt;/h2&gt;

&lt;p&gt;Head on over to &lt;code&gt;routes/api.php&lt;/code&gt; and change the route to something else:&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="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'auth:api'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/test'&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="kt"&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="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'Authenticated!'&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;For this simple example, we are using a Closure. You would probably use a real controller, such as a &lt;a href="https://laravel.com/docs/controllers#single-action-controllers" rel="noopener noreferrer"&gt;single action controller&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a few ways we can use our token to access this route. The main ways are either passing a variable in the query string of the request or using an &lt;code&gt;Authentication&lt;/code&gt; header &lt;code&gt;Bearer&lt;/code&gt; token. I'll just use the browser with a query string. I'm going to use Laravel's &lt;code&gt;php artisan serve&lt;/code&gt; to test it out quickly.&lt;/p&gt;

&lt;p&gt;I can now go to &lt;code&gt;http://localhost:8000/api/test?api_token=YHEt7CNf1SBmQs6JbTPf7qMK8FgnynI5SiPmyJELrbAO61heKy0eKuiXrxBJ&lt;/code&gt; to see if it worked.&lt;/p&gt;

&lt;p&gt;I do in fact see "Authenticated!". Awesome! I'm going to modify the token, or even remove it entirely, to see what happens.&lt;/p&gt;

&lt;p&gt;You're going to see a "Route [login] not defined" error. This just means that we &lt;em&gt;weren't&lt;/em&gt; authenticated and it tried to redirect us to a login page, which doesn't exist.&lt;/p&gt;

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

&lt;p&gt;For my use case of performing an admin task in my application from a &lt;em&gt;different&lt;/em&gt; application (server to server/machine-to-machine), this token authentication is just what I needed. The best part was that I didn't need any additional package as Laravel already had the token driver I needed. An Artisan command gave me all the functionality I needed to manage clients and tokens. We're all set!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>api</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Laravel Inertia and API Resources</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Wed, 29 Apr 2020 02:27:08 +0000</pubDate>
      <link>https://dev.to/grantholle/laravel-inertia-and-api-resources-168j</link>
      <guid>https://dev.to/grantholle/laravel-inertia-and-api-resources-168j</guid>
      <description>&lt;p&gt;If you haven't checked out &lt;a href="https://inertiajs.com/" rel="noopener noreferrer"&gt;Inertia&lt;/a&gt;, it's worth a look. It's sort of like a hybrid SPA. I've built a few apps with it and don't see myself not using it for the next while.&lt;/p&gt;

&lt;p&gt;Let's take a look at a nice server side tip that I find really useful.&lt;/p&gt;

&lt;p&gt;Say we're working on our &lt;code&gt;/users/{user}&lt;/code&gt; route. Assume we have a &lt;code&gt;show&lt;/code&gt; method in our &lt;code&gt;UserController&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This is just a helper for Inertia\Inertia::render()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;inertia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'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;'user'&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="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will be totally valid and fine. When your &lt;code&gt;user&lt;/code&gt; variable gets sent to the front end, it will be converted to an array with the &lt;code&gt;$user-&amp;gt;toArray()&lt;/code&gt; method. You can define that on &lt;code&gt;App\User&lt;/code&gt; and all will be good.&lt;/p&gt;

&lt;p&gt;But what happens when you have relationships that you'd like included or you'd like your app to be more flexible? Then you would reach for &lt;a href="https://laravel.com/docs/eloquent-resources" rel="noopener noreferrer"&gt;API Resources&lt;/a&gt;. Resources allow you to define the shape of your objects when they get sent to API's. Since Inertia is sort of like using an API, we can leverage API Resources to make our objects be sent to our views more reliably and consistently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:resource UserResource
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates &lt;code&gt;app/Http/Resources/UserResource.php&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Resources&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\Http\Resources\Json\JsonResource&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;UserResource&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JsonResource&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */&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;toArray&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="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&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;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can refactor our controller to return our resource instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;inertia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'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;'user'&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="nc"&gt;\App\Http\Resources\UserResource&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, Inertia will convert this to its "response" version as if you were returning it directly from your controller, but allows you to return a resource for each variable you want to return to your views.&lt;/p&gt;

&lt;p&gt;We can go one step further to make things "cleaner" and easier to use when you have multiple controllers returning the same resource. I'm lazy so I like this.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;App\User&lt;/code&gt; model, let's add a method called &lt;code&gt;toResource&lt;/code&gt; and return that resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;toResource&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="nc"&gt;\App\Http\Resources\UserResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&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 final state of our controller looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;inertia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'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;'user'&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toResource&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;I love API Resources. Coupled with Inertia, it's a really great developer experience on top of an already amazing experience with Laravel. Make sure to check out &lt;a href="https://inertiajs.com/" rel="noopener noreferrer"&gt;Inertia&lt;/a&gt; and &lt;a href="https://laravel.com/docs/eloquent-resources" rel="noopener noreferrer"&gt;API Resources&lt;/a&gt;. The real power with API Resources comes with &lt;a href="https://laravel.com/docs/eloquent-resources#conditional-relationships" rel="noopener noreferrer"&gt;relationships&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>laravel</category>
    </item>
    <item>
      <title>Multi or single-tenancy?</title>
      <dc:creator>Grant</dc:creator>
      <pubDate>Wed, 18 Mar 2020 03:20:13 +0000</pubDate>
      <link>https://dev.to/grantholle/multi-or-single-tenancy-14kb</link>
      <guid>https://dev.to/grantholle/multi-or-single-tenancy-14kb</guid>
      <description>&lt;p&gt;I have an idea I'm excited about for a Human Resources (HRIS/HRM) SaaS project. I'm doing some light planning and prepping.&lt;/p&gt;

&lt;p&gt;The question I can't quite answer is how should I manage the tenants. I'm leaning single-tenancy. In my mind here's how it would go...&lt;/p&gt;

&lt;p&gt;When someone signs up, it would automate creating a server instance and install/configure/deploy the application. They are isolated from other tenants in every sense, given a server geographically beneficial to them, [insert other benefits of being single tenant]. They get their own subdomain (also possible with multi-tenancy) or could point their own domain to that server (branding to an extent is also possible with multi-tenancy).&lt;/p&gt;

&lt;p&gt;There would be a central server to manage the subscriptions/accounts, but it could also be managed from their own server talking to the said "central" server.&lt;/p&gt;

&lt;p&gt;Does anyone have anecdotes of doing single vs multi-tenancy? Is this too much work? A silly idea? Too ambitious?&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>software</category>
      <category>scale</category>
      <category>question</category>
    </item>
  </channel>
</rss>
