<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Geni Jaho</title>
    <description>The latest articles on Forem by Geni Jaho (@genijaho).</description>
    <link>https://forem.com/genijaho</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%2F652105%2Fae1e68f9-f2f5-47ea-9b73-54b7be590e4c.jpeg</url>
      <title>Forem: Geni Jaho</title>
      <link>https://forem.com/genijaho</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/genijaho"/>
    <language>en</language>
    <item>
      <title>Simplifying work with custom stubs in Laravel</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Sat, 17 May 2025 16:33:25 +0000</pubDate>
      <link>https://forem.com/genijaho/simplifying-work-with-custom-stubs-in-laravel-4kf3</link>
      <guid>https://forem.com/genijaho/simplifying-work-with-custom-stubs-in-laravel-4kf3</guid>
      <description>&lt;p&gt;Testing is almost always difficult when developing applications that interact with external services, APIs, or complex features. One way to make testing easier is to use stub classes. Here's how I usually work with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick intro on benefits
&lt;/h2&gt;

&lt;p&gt;Stubs are fake implementations of interfaces or classes that simulate the behavior of real services. They allow you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test code without calling external services&lt;/li&gt;
&lt;li&gt;Work locally without API keys&lt;/li&gt;
&lt;li&gt;Speed up tests by avoiding expensive API calls&lt;/li&gt;
&lt;li&gt;Create predictable test scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  External Accounting Service Example
&lt;/h2&gt;

&lt;p&gt;Let's look at a simple interface for an external accounting service. In reality, you don't even need an interface to do this, but it makes it easier to swap implementations, and to keep them in sync.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ExternalAccountingInterface&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;createRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a real implementation that would call an external API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExternalAccounting&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ExternalAccountingInterface&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;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;HttpClient&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&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="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://api.accounting-service.com/v1/records"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s1"&gt;'json'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$responseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$responseData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'record_id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, here's a fake implementation for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeExternalAccounting&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ExternalAccountingInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$createdRecords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$hasEnoughCredits&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hasEnoughCredits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InsufficientCreditsException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Not enough credits to create a record"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$recordId&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;uuid&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;createdRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$recordId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$data&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;$recordId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Edge case simulation&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;withNotEnoughCredits&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;self&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;hasEnoughCredits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="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;span class="c1"&gt;// Helper methods for assertions&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;assertRecordsCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$eventData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$eventData&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;createdRecords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'Failed asserting that the record was created with the correct 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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;assertNothingCreated&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;assertEmpty&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;createdRecords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Records were created unexpectedly.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Before and After: Refactoring to Use Stubs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before: Using Mockery
&lt;/h3&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;testCreateAccountingRecord&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Create a mock using Mockery&lt;/span&gt;
    &lt;span class="nv"&gt;$accountingMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&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;// Set expectations&lt;/span&gt;
    &lt;span class="nv"&gt;$accountingMock&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;shouldReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'createRecord'&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;once&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mockery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$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="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'invoice'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                   &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'amount'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'amount'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mf"&gt;99.99&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;andReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'rec_123456'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Bind the mock&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;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$accountingMock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Execute the test&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/invoices'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'prod_123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;99.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert the response&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After: Using the Stub
&lt;/h3&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;testCreateAccountingRecord&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Create an instance of our custom stub&lt;/span&gt;
    &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FakeExternalAccounting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Bind the stub&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;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Execute the test&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/invoices'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'prod_123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;99.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert the response&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert that records were created with the expected data&lt;/span&gt;
    &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertRecordsCreated&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;'invoice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;99.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing Edge Cases
&lt;/h2&gt;

&lt;p&gt;Custom stubs make it easy to test edge cases and error scenarios:&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;testInvoiceFailsWhenNotEnoughCredits&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Create an instance of our custom stub&lt;/span&gt;
    &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FakeExternalAccounting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Configure the stub to simulate not enough credits&lt;/span&gt;
    &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withNotEnoughCredits&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Bind the stub&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;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Execute the test expecting a failure&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/invoices'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'prod_123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;99.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert the response handles the failure correctly&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Insufficient credits'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert that no records were created&lt;/span&gt;
    &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertNothingCreated&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Swapping Stubs in Your Base Test Case
&lt;/h2&gt;

&lt;p&gt;To avoid having to swap implementations in every test, you can set up your stubs in your base test case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseTestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Create and register the stub for all tests&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;swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FakeExternalAccounting&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 your tests, you can directly use the stub without having to register it, and you don't have to worry about accidentally using the real implementation, and forgetting to swap it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testCreateInvoice&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The accounting service is already swapped in the base test case&lt;/span&gt;
        &lt;span class="c1"&gt;// Just get it from the container&lt;/span&gt;
        &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&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;// Execute the test&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/invoices'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'product_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'prod_123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;99.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="c1"&gt;// Assert the response&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Use the stub's assertion methods&lt;/span&gt;
        &lt;span class="nv"&gt;$fakeAccounting&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertRecordsCreated&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;'invoice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;99.99&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;h2&gt;
  
  
  Using Stubs During Local Development
&lt;/h2&gt;

&lt;p&gt;Custom stubs aren't just useful for testing; they can also improve your local DX. Here's how to use them during development to avoid hitting API rate limits or needing API keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In a service provider&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;register&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Only use the mock in local environment&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'local'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&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="k"&gt;function&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;FakeExternalAccounting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Use the real implementation in other environments&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExternalAccountingInterface&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="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Application&lt;/span&gt; &lt;span class="nv"&gt;$app&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;ExternalAccounting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpClient&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="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'services.accounting.api_key'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup, your local development environment will use the fake implementation, allowing you to work without an API key and without worrying about hitting rate limits. When deployed to staging or production, the application will use the real implementation.&lt;/p&gt;

&lt;p&gt;The best part: you can have more than one fake implementation, so you can have a different one for local development and one for running tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  So try them out!
&lt;/h3&gt;

&lt;p&gt;I'm not going to list any more benefits of using stubs, but let me say, you'll definitely have fun with them 😁.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>testing</category>
      <category>mocking</category>
    </item>
    <item>
      <title>Upgrading to Laravel 10, PHPUnit 10, and Pest 2</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Sun, 17 Sep 2023 15:25:30 +0000</pubDate>
      <link>https://forem.com/genijaho/upgrading-to-laravel-10-phpunit-10-and-pest-2-4o4m</link>
      <guid>https://forem.com/genijaho/upgrading-to-laravel-10-phpunit-10-and-pest-2-4o4m</guid>
      <description>&lt;p&gt;We're upgrading from Laravel 9 to 10, and PHPUnit 9 to 10, and, if all goes well, we'll top it off by migrating to Pest, the latest version. To keep the positive vibes on I'll skip an upgrade from PHP 8.1 to 8.2, just because I don't want to restart Docker in the production server for now. Sunday is for safe stuff only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove or upgrade outdated packages
&lt;/h2&gt;

&lt;p&gt;Firstly, check the &lt;a href="https://laravel.com/docs/10.x/upgrade#updating-dependencies"&gt;dependencies guide here&lt;/a&gt;, and complete all the steps outlined in that section. You need to update the composer.json with the new versions, and then run &lt;code&gt;composer update&lt;/code&gt;. It will fail, most probably. The error logs will show you which packages are incompatible or need to be updated to support the latest version.&lt;/p&gt;

&lt;p&gt;I was using some packages that were deprecated or didn't have support for Laravel 10 as of now. Luckily, in my very specific case, I didn't really need or use them, so I just removed them. The two packages were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/fruitcake/laravel-cors"&gt;CORS Middleware for Laravel&lt;/a&gt; - all supported Laravel versions now include the CORS middleware in the core.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/gpressutto5/laravel-slack"&gt;Laravel Slack&lt;/a&gt; - helper package to make Slack notifications easier to send. I wasn't making good use of this package, and Laravel has built-in logging to Slack anyway.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After you've handled this step according to your specific case, you should now have a clean installation of Laravel 10. I'd make a commit for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Rector for Laravel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://getrector.com/"&gt;Rector&lt;/a&gt; will help us migrate to the new Laravel version with practically no effort. Follow the Composer installation guide &lt;a href="https://getrector.com/documentation"&gt;here&lt;/a&gt;, and then install the &lt;a href="https://github.com/driftingly/rector-laravel"&gt;Laravel extension&lt;/a&gt;. Among other configurations you might have in your &lt;code&gt;rector.php&lt;/code&gt; file, add the Laravel 10 set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Rector\Config\RectorConfig&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;RectorLaravel\Set\LaravelSetList&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;static&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;RectorConfig&lt;/span&gt; &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;LaravelSetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LARAVEL_100&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 just run &lt;code&gt;vendor/bin/rector process&lt;/code&gt; and see what happens. It will handle most of the changes for you, like model "dates" property, the &lt;code&gt;Bus&lt;/code&gt; facade's &lt;code&gt;dispatchNow&lt;/code&gt; to &lt;code&gt;dispatchSync&lt;/code&gt;, the &lt;code&gt;Relation&lt;/code&gt;'s &lt;code&gt;getBaseQuery&lt;/code&gt; to &lt;code&gt;getBaseQuery&lt;/code&gt;, etc. At this point, I make another commit to separate the automatic changes from my own changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the finishing touches to the upgrade
&lt;/h2&gt;

&lt;p&gt;As a final step, take a good look at the &lt;a href="https://laravel.com/docs/10.x/upgrade#public-path-binding"&gt;upgrade guide&lt;/a&gt; for anything that needs your attention. There are certain things that Rector can not cover for you, like moving &lt;code&gt;lang&lt;/code&gt; files around. You're not entirely out of luck though, there's a paid service called &lt;a href="https://laravelshift.com/upgrade-laravel-9-to-laravel-10"&gt;Shift&lt;/a&gt;, that has that capability and is tailored for Laravel projects. I suggest you give it a chance to prove itself, it's worth every penny. But still, double-check with the upgrade guide and see if anything's missing.&lt;/p&gt;

&lt;p&gt;Make another commit for it if you've made any changes, and run your tests. Hopefully, everything's green.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade to PHPUnit 10
&lt;/h2&gt;

&lt;p&gt;Update your PHPUnit version to &lt;code&gt;"phpunit/phpunit": "^10"&lt;/code&gt; and run a &lt;code&gt;composer update&lt;/code&gt;. It will probably go smoothly. The first time you'll run your tests, it will ask you to update your &lt;code&gt;phpunit.xml&lt;/code&gt; schema to a newer version, you should run a command for that. Run it: &lt;code&gt;./vendor/bin/phpunit --migrate-configuration&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;rector.php&lt;/code&gt; file add the PHPUnit rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Rector\Config\RectorConfig&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;Rector\PHPUnit\Set\PHPUnitSetList&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;RectorLaravel\Set\LaravelSetList&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;static&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;RectorConfig&lt;/span&gt; &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&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;'/app'&lt;/span&gt;&lt;span class="p"&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;'/database'&lt;/span&gt;&lt;span class="p"&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;'/tests'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;LaravelSetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;LARAVEL_100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;PHPUnitSetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PHPUNIT_100&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;Don't forget to include your tests directory in the paths that Rector will look for. Run &lt;code&gt;./vendor/bin/rector&lt;/code&gt; again and see what happens. All your tests will be migrated to the new Attributes syntax, and your data providers will be made static. If you run your tests now they should all pass, unless you've written some suspicious test classes and used &lt;code&gt;$this&lt;/code&gt; inside your data providers, as I did 😀. And now's the time for another commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrate to Pest v2
&lt;/h2&gt;

&lt;p&gt;Pest has a migration tool of its own, it's called &lt;a href="https://pestphp.com/docs/migrating-from-phpunit-guide"&gt;Drift&lt;/a&gt;, and it's a Pest Plugin. You can install it with &lt;code&gt;composer require pestphp/pest-plugin-drift --dev&lt;/code&gt;. To convert all your tests you simply run &lt;code&gt;./vendor/bin/pest --drift&lt;/code&gt;. It will remove all classes of your tests, rename the tests to use spaces instead of snake case or camel case, and convert most of the assertions to the expectation APIs.&lt;/p&gt;

&lt;p&gt;Depending on your test structure, there might be some more work involved. For example, you might have tests in directories other than Feature or Unit, like Billing, etc. You can either specify the directories one by one in your &lt;code&gt;Pest.php&lt;/code&gt; file, or include the whole directory to use the correct &lt;code&gt;TestCase&lt;/code&gt; with a single line:&lt;br&gt;&lt;br&gt;
&lt;code&gt;uses(\Tests\TestCase::class)-&amp;gt;in('./');.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The migration will remove all classes in your tests, so if you had &lt;code&gt;private&lt;/code&gt; helper methods, they will be converted to functions. Inside those functions, you lose access to the &lt;code&gt;$this&lt;/code&gt; variable, so you must check each case and fix it manually, at least at the time of this post. For example, instead of &lt;code&gt;$this-&amp;gt;mock(...)&lt;/code&gt; you have to use just &lt;code&gt;mock(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And now you should be good to go. Probably all or most of your tests are green.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>phpunit</category>
      <category>pest</category>
    </item>
    <item>
      <title>Speed up GitHub Actions by caching Composer, Rector, &amp; Pint</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Tue, 20 Jun 2023 18:51:47 +0000</pubDate>
      <link>https://forem.com/genijaho/speed-up-github-actions-by-caching-composer-rector-pint-4id</link>
      <guid>https://forem.com/genijaho/speed-up-github-actions-by-caching-composer-rector-pint-4id</guid>
      <description>&lt;p&gt;GitHub's 2000 minutes per month of free usage of GitHub Actions is usually enough to handle light workflows. However, as more tech is run automatically, we are forced to upgrade to a paid plan or optimize our workflows.&lt;/p&gt;

&lt;p&gt;Currently, I am running Rector, Pint, and PHPUnit tests on every PR. If Rector or Pint discover something that can be improved on the PR they're configured to make another commit. That triggers another run of the pipeline.&lt;/p&gt;

&lt;p&gt;In a modest codebase, the three of them together added up to an average of 15 minutes per execution, which is painfully a lot. By using the GitHub Actions caching mechanism we managed to bring it down to an average of 4.5 minutes. The full code and some useful links are at the bottom, here are the steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching Composer vendor
&lt;/h2&gt;

&lt;p&gt;This is part of the job definition where we cache the vendor folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;code-quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shivammathur/setup-php@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;php-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8.1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Vendor&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-vendor&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-vendor-${{ hashFiles('**/composer.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Dependencies"&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-vendor.outputs.cache-hit != 'true'&lt;/span&gt; &lt;span class="c1"&gt;# Skip if cache hit&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not much of an improvement, as Composer installs are usually fast, but you shelve off about 6-8 seconds out of every run. Caching the other stuff is much more impactful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching Rector
&lt;/h2&gt;

&lt;p&gt;Rector is run in parallel by default, and it's usually really fast locally. However, GitHub-hosted runners have only 2 cores, so Rector is a bit slow. On average, Rector took about 5 minutes to run, before caching.&lt;/p&gt;

&lt;p&gt;The first thing we do is enable file-based caching in our &lt;code&gt;rector.php&lt;/code&gt; configuration.&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="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Rector\Caching\ValueObject\Storage\FileCacheStorage&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;Rector\Config\RectorConfig&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;static&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;RectorConfig&lt;/span&gt; &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&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;'/app'&lt;/span&gt;&lt;span class="p"&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;'/tests'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;importNames&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Ensure file system caching is used instead of in-memory.&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FileCacheStorage&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;// Specify a path that works locally as well as on CI job runners.&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheDirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./storage/rector/cache'&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 also speed up your subsequent local Rector runs, as it will only check the modified files instead of the whole codebase.&lt;/p&gt;

&lt;p&gt;Next up, in GitHub Actions we do the same caching as before, but instead of caching the &lt;code&gt;vendor&lt;/code&gt; directory, we cache the &lt;code&gt;storage/rector/cache&lt;/code&gt; directory, or wherever you stored your Rector cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;code-quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Rector&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./storage/rector/cache&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-rector-${{ hashFiles('**/composer.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-rector-&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Rector&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bin/rector --ansi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time the pipeline is triggered it will take the full amount of time to run. However, it will store the Rector cache and use it in subsequent runs. Depending on the number of files affected in your PRs, the Rector step now only takes a few seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching Pint
&lt;/h2&gt;

&lt;p&gt;I struggled with this a bit as Pint is built on top of PHP CS Fixer and there was no clear way on how to configure caching. I found out that PHP CS Fixer is using a cache by default, and you can specify the cache file location by providing an option when running the fixer command. However, Pint did not pass the &lt;code&gt;--cache-file&lt;/code&gt; option to the PHP CS Fixer.&lt;/p&gt;

&lt;p&gt;Instead, you can configure the Pint cache location in your &lt;code&gt;pint.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laravel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cache-file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"storage/pint.cache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can choose whatever location fits you best. Now that we have a fixed cache location, we do the same code for GitHub Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;code-quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Pint&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./storage/pint.cache&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-pint-${{ hashFiles('**/composer.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-pint-&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Pint&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./vendor/bin/pint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This brings Pint execution down to just a few seconds as well, depending on the size of your PRs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete pipeline
&lt;/h2&gt;

&lt;p&gt;Here's the full code of a Laravel project pipeline that runs Rector and Pint, and does a commit to your PR with refactoring or styling changes, if needed. When the Rector and Pint job finishes, the PHPUnit tests start to run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rector, Pint &amp;amp; Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;code-quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shivammathur/setup-php@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;php-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8.1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Must be used to trigger workflow after push&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ACCESS_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Vendor&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-vendor&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-vendor-${{ hashFiles('**/composer.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Dependencies"&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-vendor.outputs.cache-hit != 'true'&lt;/span&gt; &lt;span class="c1"&gt;# Skip if cache hit&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Rector&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./storage/rector/cache&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-rector-${{ hashFiles('**/composer.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-rector-&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Rector"&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bin/rector --ansi&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Pint&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./storage/pint.cache&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-pint-${{ hashFiles('**/composer.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-pint-&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Pint"&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./vendor/bin/pint&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stefanzweifel/git-auto-commit-action@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;commit_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[ci-review]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Rector&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Pint'&lt;/span&gt;
          &lt;span class="na"&gt;commit_author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;actions@github.com&amp;gt;'&lt;/span&gt;
          &lt;span class="na"&gt;commit_user_email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action@github.com'&lt;/span&gt;

  &lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-quality&lt;/span&gt;
    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0.25&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_app_test&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3306/tcp&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shivammathur/setup-php@2.23.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;php-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8.1'&lt;/span&gt;
          &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mbstring, dom, fileinfo, mysql&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Must be used to trigger workflow after push&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ACCESS_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Copy .env&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php -r "file_exists('.env') || copy('.env.example', '.env');"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Vendor&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-vendor&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-vendor-${{ hashFiles('**/composer.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Dependencies&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-vendor.outputs.cache-hit != 'true'&lt;/span&gt; &lt;span class="c1"&gt;# Skip if cache hit&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate key&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php artisan key:generate&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PHPUnit&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php artisan test --parallel&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DB_TEST_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.services.mysql.ports['3306'] }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out these resources for more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://getrector.com/documentation/cache-in-ci"&gt;https://getrector.com/documentation/cache-in-ci&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://getrector.com/blog/new-setup-ci-command-to-let-rector-work-for-you"&gt;https://getrector.com/blog/new-setup-ci-command-to-let-rector-work-for-you&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows"&gt;https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>testing</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>Doing stricter checks in PHP</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Sun, 09 Apr 2023 10:41:36 +0000</pubDate>
      <link>https://forem.com/genijaho/doing-stricter-checks-in-php-14l0</link>
      <guid>https://forem.com/genijaho/doing-stricter-checks-in-php-14l0</guid>
      <description>&lt;p&gt;A nice refactoring I've learned recently is to utilize types in conditionals.&lt;/p&gt;

&lt;p&gt;For years I’ve been doing &lt;code&gt;if (!empty($posts))&lt;/code&gt;, but &lt;a href="https://getrector.com/"&gt;Rector&lt;/a&gt;, a tool to automatically refactor code, changed that to &lt;code&gt;if ($posts === [])&lt;/code&gt;. The main advantage of using &lt;code&gt;$posts === []&lt;/code&gt; is that it is more explicit and less prone to unexpected behavior.&lt;/p&gt;

&lt;p&gt;It might not seem like much, but it's quite telling of the codebase you're working with. Using &lt;code&gt;$posts === []&lt;/code&gt; demonstrates a higher level of attention to detail and a commitment to writing more reliable and predictable code. It shows that you are thinking about the specific requirements of your code and taking steps to ensure that it behaves as expected.&lt;/p&gt;

&lt;p&gt;On the other hand, using &lt;code&gt;!empty($posts)&lt;/code&gt; can suggest a more casual approach to programming, where you are relying on PHP's default behavior rather than explicitly defining the expected behavior of your code. While this may be acceptable for smaller projects, it can lead to unexpected bugs and difficulties as your codebase grows and becomes more complex.&lt;/p&gt;

&lt;p&gt;Here are some other refactorings I've learned that make me feel like I'm in charge of the code, and not the way around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using strict comparison to check for null or empty strings:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two conditions check if &lt;code&gt;$user&lt;/code&gt; is not truthy, which includes checking if it's &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, 0, or an empty string. With the after example, we can use the strict comparison operator &lt;code&gt;===&lt;/code&gt; to specifically check if &lt;code&gt;$username&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Type hints in function parameters:
&lt;/h2&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="n"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$y&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;int&lt;/code&gt; type hint ensures that &lt;code&gt;$x&lt;/code&gt; and &lt;code&gt;$y&lt;/code&gt; are integers. If a non-integer value is passed to the function, PHP will throw a &lt;code&gt;TypeError&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;array_key_exists()&lt;/code&gt; to check for array keys:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&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="c1"&gt;// After&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;array_key_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the before example, we're using the &lt;code&gt;isset()&lt;/code&gt; function to check if &lt;code&gt;$userData['username']&lt;/code&gt; exists. With the after example, we can use the &lt;code&gt;array_key_exists()&lt;/code&gt; function to specifically check if the &lt;code&gt;'username'&lt;/code&gt; key exists in &lt;code&gt;$userData&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Null coalescing operator and null coalescing assignment operator:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Guest'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'Guest'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the before example, we're using an &lt;code&gt;if&lt;/code&gt; statement to check if &lt;code&gt;$userData['username']&lt;/code&gt; exists and setting &lt;code&gt;$username&lt;/code&gt; accordingly. With the after example, we can use the null coalescing operator &lt;code&gt;??&lt;/code&gt; to set &lt;code&gt;$username&lt;/code&gt; to &lt;code&gt;$userData['username']&lt;/code&gt; if it exists, and &lt;code&gt;'Guest'&lt;/code&gt; if it doesn't. Additionally, we can use the null coalescing assignment operator &lt;code&gt;??=&lt;/code&gt; to assign the default value to &lt;code&gt;$username&lt;/code&gt; only if it's not already set.&lt;/p&gt;

&lt;p&gt;These stricter checks help us write more reliable and predictable code, catch errors early, and save ourselves from potential headaches in the future. While some of these refactorings may take some getting used to, they are worth the effort to improve the quality of our code. Or, just install Rector and let it handle them for you. Happy coding!&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>refactoring</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Upgrading to Eloquent accessors &amp; mutators from Laravel 9</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Sun, 26 Feb 2023 13:16:54 +0000</pubDate>
      <link>https://forem.com/genijaho/upgrading-to-eloquent-accessors-mutators-from-laravel-9-35kl</link>
      <guid>https://forem.com/genijaho/upgrading-to-eloquent-accessors-mutators-from-laravel-9-35kl</guid>
      <description>&lt;p&gt;Laravel 10 is making the headlines these days. However, many apps are stuck in older versions, or at least still use the syntax and methods of older releases. One of the most significant changes introduced in Laravel 9 is the syntax for accessors and mutators.&lt;/p&gt;

&lt;p&gt;The old syntax used a simple convention, just wrapping the attribute between 'get' or 'set' and the word 'Attribute', such as &lt;code&gt;getFullNameAttribute&lt;/code&gt; and &lt;code&gt;setFullNameAttribute&lt;/code&gt;, that did the trick. In the new syntax, you may define an accessor and mutator using a single, non-prefixed method by type-hinting a return type of &lt;code&gt;Illuminate\Database\Eloquent\Casts\Attribute&lt;/code&gt; (&lt;a href="https://laravel.com/docs/9.x/releases#eloquent-accessors-and-mutators" rel="noopener noreferrer"&gt;docs&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Casts\Attribute&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;name&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Attribute&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;Attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;fn&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;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;strtoupper&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="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;fn&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;=&amp;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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the old syntax is still usable, it might get removed in the future, or might confuse Laravel newcomers, so it's best to update it sooner rather than later. You can modify those methods by hand, or you can use an automated tool, like &lt;a href="https://getrector.com/" rel="noopener noreferrer"&gt;Rector&lt;/a&gt;. It's an open-source tool to upgrade PHP projects and automate the boring stuff for you.&lt;/p&gt;

&lt;p&gt;You can install Rector with &lt;code&gt;composer require rector/rector --dev&lt;/code&gt;. Apart from the core package, we'll require another &lt;a href="https://github.com/driftingly/rector-laravel" rel="noopener noreferrer"&gt;package&lt;/a&gt; that is specifically tailored to Laravel apps. You can install it with &lt;code&gt;composer require driftingly/rector-laravel:dev-main --dev&lt;/code&gt;, and initiate a config file with &lt;code&gt;vendor/bin/rector init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You only need to apply one Rector rule to migrate to the new syntax automatically. Just write this in your &lt;code&gt;rector.php&lt;/code&gt; file:&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="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Rector\Config\RectorConfig&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;RectorLaravel\Rector\ClassMethod\MigrateToSimplifiedAttributeRector&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;static&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;RectorConfig&lt;/span&gt; &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&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;'/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MigrateToSimplifiedAttributeRector&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;It will take care of migrating all your Eloquent models to use the new syntax. If you found a bug or something went wrong, let me know. This Rector rule was contributed to the library by yours truly 😊.&lt;/p&gt;

&lt;h2&gt;
  
  
  There is one gotcha
&lt;/h2&gt;

&lt;p&gt;The old syntax allowed you to use the accessors and mutators however you wanted. So, if you had a &lt;code&gt;getFullNameAttribute()&lt;/code&gt;, you could use it either like &lt;code&gt;$user-&amp;gt;full_name&lt;/code&gt; or &lt;code&gt;$user-&amp;gt;fullName&lt;/code&gt;. Both would work. The new attributes in Laravel 9 still allow you to use both cases, however, if you append the attributes to your models using camel case, you're a bit out of luck. Suppose you have &lt;code&gt;protected $appends = ['firstName'];&lt;/code&gt;, you will see this error: &lt;code&gt;Call to undefined method App\Models\User::getFirstNameAttribute()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem is that Laravel is trying to determine your attribute using the snake case name, but it can't find it (we're doing &lt;code&gt;protected function firstName(): Attribute&lt;/code&gt;), so it defaults to searching for attributes in the old syntax and it fails.&lt;/p&gt;

&lt;p&gt;To overcome that, you need to double-check your Eloquent &lt;code&gt;$appends&lt;/code&gt; property or any other manual appends that you apply to your models that use &lt;code&gt;camelCase&lt;/code&gt; names for your accessors or mutators. Change them to snake case. Also, if you're using your models in the front end, check to see if the front-end logic is still working. Better yet, make these changes before you migrate to the new syntax and check them beforehand. I know, it does not look that fun now, but what were you going to do on a weekend anyway?&lt;/p&gt;

&lt;p&gt;The other hacky solution I found was to disable attribute snake casing by using this in your models: &lt;code&gt;public static $snakeAttributes = false;&lt;/code&gt;. This will make all your errors go away and will properly find the attributes regardless of their casing. However, I think this is also used to &lt;a href="https://github.com/laravel/framework/blob/a47e7a21fdcdce84b17d39f49ea69523e9aef887/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L349" rel="noopener noreferrer"&gt;determine the names of model relationships&lt;/a&gt;, and it might have implications for them as well. I think you shouldn't take this approach, and your safest bet is to just not upgrade right away the mutators/accessors that cause this issue.&lt;/p&gt;

&lt;p&gt;Overall, the new syntax for accessors and mutators in Laravel 9 provides a more intuitive and readable way of working with Eloquent models. By upgrading your existing code to the new syntax, you can take advantage of the latest features and enhancements, and ensure that your code is more maintainable and easy to read.&lt;/p&gt;

</description>
      <category>gratitude</category>
      <category>career</category>
    </item>
    <item>
      <title>Development Driven Testing</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Wed, 01 Feb 2023 15:18:56 +0000</pubDate>
      <link>https://forem.com/genijaho/development-driven-testing-3jcb</link>
      <guid>https://forem.com/genijaho/development-driven-testing-3jcb</guid>
      <description>&lt;p&gt;One of the first roles that I've been working on since I went full-time remote was that of the unit tester. I wasn't used to being the QA guy before and although this was new to me, I've gotten the hang of it. However, as part of my role, I had to write the tests as well (PHPUnit). The other devs in my teams were not initially comfortable writing tests, or they were just more focused on releasing stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing after the fact is pretty common
&lt;/h2&gt;

&lt;p&gt;I am sure this doesn't happen only to me. I'm sure there are countless other projects out there that follow the same process. Devs do the feature, someone does some QA, and then the task card is moved to the Unit Testing column. For anyone appreciating a TDD workflow, that's just straight-out weird.&lt;/p&gt;

&lt;p&gt;What is this Development Driven Testing thing weird? It's weird because you lose so many of the benefits of writing tests alongside the features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing after the fact is often unproductive
&lt;/h2&gt;

&lt;p&gt;For starters, let's say you found the feature to test to be very badly architected because of... deadlines. So bad that writing tests for it is either too difficult and non-productive, or the only way to do it is to refactor the whole thing. But the other dev has already delivered a refactored code, should you do it again? Or just skip the whole testing thing and release it, because, of deadlines?&lt;/p&gt;

&lt;p&gt;Let's say the code to be tested is well-written and can be worked with. By what metric? Most of the untested code that I've seen is controllers, services, and methods with hundreds of lines of code and very high complexity. Sure, it may look simple and well written for a programmer's eye, but what does Phpmetrics have to say? Even if I, the tester, wrote a large feature without tests, I'd still produce the same suboptimal code that another programmer would call mehh 😂.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not testing at all is the path to the dark side
&lt;/h2&gt;

&lt;p&gt;The other way around with untested code, over-engineering, is also a problem. It's easy to get distracted from a feature you're building and start accounting for details and sub-features that weren't requested and will probably never be used. It's hard sometimes to just stay focused and build only what you need to build. It's hard to know when to stop refactoring, but then again, it happens.&lt;/p&gt;

&lt;p&gt;Ever worked on a legacy project? Why do all legacy projects have the same problems? How did they come to be like that? My biggest bet is that they grew with time, started making real money, and everyone was too afraid to mess around and "do things properly". Why upgrade and groom a project if it's working? If only there was a way to improve the project safely...&lt;/p&gt;

&lt;h2&gt;
  
  
  Hey, but doesn't TDD take up a lot of time?
&lt;/h2&gt;

&lt;p&gt;That's another big misconception when it comes to writing tests alongside features. I've written tested code and I've written untested code and I can say with full confidence that tests haven't cost me as much time as I anticipated they would. There are many cases when a testing environment helps me to write a feature faster. For example, when implementing a form with various inputs. I'm writing the inputs one by one and without tests, I have to check constantly with the browser to make sure that everything works. Otherwise, I don't even have to leave the editor.&lt;/p&gt;

&lt;p&gt;Oh, and it's this other critical win when using tests. They change your way of thinking about problems, or rather, they help you uncover a sane path to dealing with them. The reason for this is that you are forced to think about use cases and implement them in sequential steps, which is very similar to the way machines process instructions. Eventually, you write code that follows this sequential thought process, steadily built with pieces that form a whole feature, and guess what, it will be very readable and easy to grasp by an outside observer, or future you.&lt;/p&gt;

&lt;h2&gt;
  
  
  TDD is your decision to make
&lt;/h2&gt;

&lt;p&gt;I still think writing tests after the fact is here to stay. People who make decisions are not easily convinced by some software "theories", right? They want hard facts and results. However, the decision on this is in your hands, fellow developer. You're not going to change anything by asking for permission.&lt;/p&gt;

&lt;p&gt;Funny enough, to prove this point even to myself, I got introduced to a modern Laravel project that had no tests, and I insisted that I would only accept the work if I was allowed to write tests. A few months in, we needed to do a major refactoring and upgrade to PHP 8.2. We did that, the tests were all green, and we were good to go. We all felt safe and protected and decided that every developer write their own tests, the benefits were obvious.&lt;/p&gt;

&lt;p&gt;So give it a try at your projects. YOLO, don't tell anybody. Just write some tests for your own tasks, let them hang in there, and grow them in size for some months, you'll see the benefit in time. Your whole team will see.&lt;/p&gt;

&lt;h2&gt;
  
  
  TDD in the era of Copilot and ChatGPT
&lt;/h2&gt;

&lt;p&gt;With the recent rise of ChatGPT and GitHub Copilot, the experience of writing tests has gone to another level. Once you get a bit comfortable with writing stuff by hand, an AI assistant will just blow your mind. Copilot is already generating so many of my tests these days, it barely takes minutes to write complex logic and assertions. I feel like these tools were made for TDD.&lt;/p&gt;

&lt;p&gt;I thank you for reading this far and I hope you got some value for your time. I write about refactoring and testing from time to time. See you in the next post.&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>web3</category>
      <category>offers</category>
    </item>
    <item>
      <title>Allowing users to send emails with their own SMTP settings in Laravel 9</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Sun, 22 Jan 2023 22:09:57 +0000</pubDate>
      <link>https://forem.com/genijaho/allowing-users-to-send-emails-with-their-own-smtp-settings-in-laravel-9-304p</link>
      <guid>https://forem.com/genijaho/allowing-users-to-send-emails-with-their-own-smtp-settings-in-laravel-9-304p</guid>
      <description>&lt;p&gt;Various guides exist on how to do this in Laravel, but all the ones I found (&lt;a href="https://laravel-news.com/allowing-users-to-send-email-with-their-own-smtp-settings-in-laravel" rel="noopener noreferrer"&gt;this&lt;/a&gt; and &lt;a href="https://blog.creekorful.org/2021/11/laravel-dynamic-smtp-mail-configuration/" rel="noopener noreferrer"&gt;this&lt;/a&gt;) worked only up to Laravel 8. The reason is that in v9 the Swift Mailer was no longer supported in favor of &lt;a href="https://laravel.com/docs/9.x/releases#symfony-mailer" rel="noopener noreferrer"&gt;Symfony Mailer&lt;/a&gt;. The Laravel docs show how to write your own custom transports, but that's not what we need here. There might be a package that sends emails with custom SMTP params when reading this post, so you might want to take a look first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Laravel does behind the scenes
&lt;/h2&gt;

&lt;p&gt;The first thing we want to handle is related to a class called &lt;code&gt;MailManager&lt;/code&gt;. It is responsible for configuring and resolving the various transport drivers. We already know that we want to use the SMTP transport, we just need to know how to override the default configuration coming from the &lt;code&gt;.env&lt;/code&gt; file. This &lt;code&gt;MailManager&lt;/code&gt; has a method for that:&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;getConfig&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;$name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'mail.driver'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'mail'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"mail.mailers.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the method that we want to override. Before we do that, we need to know how the &lt;code&gt;MailManager&lt;/code&gt; is resolved and used by the framework. In the framework's &lt;code&gt;MailServiceProvider&lt;/code&gt; we find this piece of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;registerIlluminateMailer&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;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mail.manager'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&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;MailManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&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;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mailer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mail.manager'&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;mailer&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 tells us that to work with another mailer instead of the default one, we have to use another instance of a &lt;code&gt;MailManager&lt;/code&gt; and get the mailer from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the custom Mailer
&lt;/h2&gt;

&lt;p&gt;So we make this new class called &lt;code&gt;App\Extensions\UserMailManager&lt;/code&gt;, place it or call it anything you want, that accepts a configuration when created and overrides the &lt;code&gt;getConfig()&lt;/code&gt; method we saw earlier to use that configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Extensions&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\Mail\MailManager&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;UserMailManager&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MailManager&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;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$customConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getConfig&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;$name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;customConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your &lt;code&gt;AppServiceProvider&lt;/code&gt;, or any other provider, we bind an instance of this new &lt;code&gt;MailManager&lt;/code&gt; to the container, which will allow us to pass custom SMTP parameters when needed. It could've been a singleton, not sure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ServiceProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user.mailer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$parameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserMailManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$parameters&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;mailer&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="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You would utilize our setup so far 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="cd"&gt;/** @var Mailer $mailer */&lt;/span&gt;
&lt;span class="nv"&gt;$mailer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user.mailer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$smtpParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$mailer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mailable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does not look all that handy though. Usually, you have your SMTP params in a column in your &lt;code&gt;users&lt;/code&gt; table (or another table, doesn't matter). We're going to use that to our advantage, by creating a helper method in the &lt;code&gt;User&lt;/code&gt; model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;sendMail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Mailable&lt;/span&gt; &lt;span class="nv"&gt;$mailable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;smtp_enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$smtpParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'transport'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'smtp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;smtp_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'smtp_host'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'port'&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;smtp_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'smtp_port'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'username'&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;smtp_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'smtp_user'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&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;smtp_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'smtp_password'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'verify_peer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="cd"&gt;/** @var Mailer $mailer */&lt;/span&gt;
        &lt;span class="nv"&gt;$mailer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'creator.mailer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$smtpParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$mailer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mailable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mailable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the mailer with custom SMTP params
&lt;/h2&gt;

&lt;p&gt;Now, in the other parts of the code where you want to use the custom params, you can swap &lt;code&gt;Mail::send(new TestMail($testVariable))&lt;/code&gt; with &lt;code&gt;$user-&amp;gt;sendMail(new TestMail($testVariable))&lt;/code&gt;. Handy, right?&lt;/p&gt;

&lt;p&gt;If you want to queue the mail instead, I'd suggest you add a new method called &lt;code&gt;queueMail($mailable, ...)&lt;/code&gt;, and tailor it to your needs. The only difference would probably be using &lt;code&gt;$mailer-&amp;gt;queue($mailable);&lt;/code&gt; inside the method.&lt;/p&gt;

&lt;p&gt;That's it. Leave a thumbs up if this helped you, and thank you for making it this far.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Safely upgrade from PHP 7.4 to 8.1 using Rector</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Sun, 25 Sep 2022 20:53:17 +0000</pubDate>
      <link>https://forem.com/genijaho/safely-upgrade-from-php-74-to-81-using-rector-4ghg</link>
      <guid>https://forem.com/genijaho/safely-upgrade-from-php-74-to-81-using-rector-4ghg</guid>
      <description>&lt;p&gt;I have to say it from the start, the only thing that comes close to safely upgrading a codebase is having a solid set of automated tests. Nothing gives you more comfort than a bunch of green checkmarks dancing on the screen.&lt;/p&gt;

&lt;p&gt;With that said, we're going to upgrade a Laravel project running on the soon-to-be-dead &lt;a href="https://www.php.net/supported-versions.php"&gt;PHP 7.4 (Nov 28)&lt;/a&gt;. Currently, the last stable PHP version is 8.1 and we're going to upgrade to it using a tool called &lt;a href="https://getrector.org/"&gt;Rector&lt;/a&gt;. Rector will help us move on to the new syntax in a very comfortable way.&lt;/p&gt;

&lt;p&gt;Without further ado, here are the steps I followed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change &lt;code&gt;composer.json&lt;/code&gt; to the new PHP 8.1
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"php"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alongside manually changing the version in &lt;code&gt;composer.json&lt;/code&gt;, I also changed my environment to use PHP 8.1. In Homestead, that's done simply by running &lt;code&gt;php81&lt;/code&gt;. In Valet that command is &lt;code&gt;valet use php@8.1&lt;/code&gt;. If you're on Windows and using Wamp or Xampp, well, good luck 😁.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do a &lt;code&gt;composer update&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;After changing the environment, I ran a nice and risky &lt;code&gt;composer update&lt;/code&gt;. Not entirely sure about this, as there might be a cleaner way, but I guess composer needs to check and update all packages to the specified PHP version.&lt;/p&gt;

&lt;p&gt;This is the point where you'll probably get stuck and face composer errors because not all packages you use support the new PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check for packages that might break
&lt;/h2&gt;

&lt;p&gt;In this particular project, the only package that did not have support was &lt;code&gt;fzaninotto/faker&lt;/code&gt;, and that's because that project has said &lt;a href="https://marmelab.com/blog/2020/10/21/sunsetting-faker.html"&gt;goodnight since 2020&lt;/a&gt;. Good news though, there's an alternative to it called &lt;a href="https://fakerphp.github.io/"&gt;fakerphp/faker&lt;/a&gt;, which you can just use directly in composer.json (&lt;code&gt;"fzaninotto/faker" =&amp;gt; "fakerphp/faker"&lt;/code&gt;). And you don't need to change a thing in your factories, it just works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the tests
&lt;/h2&gt;

&lt;p&gt;At this point, I run the tests, and I notice nothing breaks, they're all passing. Phew.&lt;br&gt;
However, if it doesn't go as smoothly for you, or you don't have tests, check out the deprecations &lt;a href="https://www.php.net/releases/8.1/en.php#deprecations_and_bc_breaks"&gt;here&lt;/a&gt;. Just make sure that you've handled those and ignore the new syntax, because we're not going to upgrade to it the old fashion way. Just commit your changes so you have a clean slate for the syntax changes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install, configure, and run Rector
&lt;/h2&gt;


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

&lt;/div&gt;


&lt;p&gt;This command will install Rector for you and create a &lt;code&gt;rector.php&lt;/code&gt; file at the root of your project. You can modify it to tell Rector to upgrade all the codebase up to PHP 8.1, with this configuration:&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="k"&gt;static&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;RectorConfig&lt;/span&gt; &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&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;'/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;importNames&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;LevelSetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UP_TO_PHP_81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;SpatieEnumClassToEnumRector&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'll discuss that last rule shortly, but now you should be able to run &lt;code&gt;vendor/bin/rector --dry-run&lt;/code&gt;. If your codebase is huge, you'll have to wait a minute or two. When it's done you'll see all the changes that are to be applied, like a git diff. If you omit the &lt;code&gt;--dry-run&lt;/code&gt; it will instead go ahead and do all those changes to your code, so you can pick what you want to keep in your diff browser of choice.&lt;/p&gt;

&lt;p&gt;That last rule &lt;code&gt;SpatieEnumClassToEnumRector&lt;/code&gt; is skipped because I'm using the &lt;code&gt;spatie/enum&lt;/code&gt; &lt;a href="https://github.com/spatie/enum"&gt;package&lt;/a&gt;, and I don't want it to be converted to PHP native enums automatically. The reason for that is that the Spatie package has some nice little helpers, and it's safer in my opinion to migrate some of the enums manually.&lt;/p&gt;

&lt;p&gt;Another little gotcha I want to point out is with the &lt;code&gt;match&lt;/code&gt; expression. It does strict comparisons, unlike &lt;code&gt;switch&lt;/code&gt;, and that might do some damage if left unchecked. As a remedy, you can use the &lt;code&gt;match (true) {...}&lt;/code&gt; version, which is not as beautiful but will do a loose comparison like the &lt;code&gt;switch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'm not going to go over what's new in 8.1 and what Rector &lt;a href="https://blog.genijaho.dev/improve-code-quality-in-laravel-using-rector"&gt;will do for you&lt;/a&gt;, but damn it feels good to see all the code clean itself. PHPStorm supports it out of the box. You just have to try it, heck, let the tests break, just run it.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://blog.genijaho.dev/safely-upgrade-from-php-74-to-81-using-rector"&gt;https://blog.genijaho.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>refactoring</category>
      <category>rector</category>
    </item>
    <item>
      <title>Refactoring #8: What is dead may never run</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Sun, 04 Sep 2022 20:08:10 +0000</pubDate>
      <link>https://forem.com/genijaho/refactoring-8-what-is-dead-may-never-run-ca4</link>
      <guid>https://forem.com/genijaho/refactoring-8-what-is-dead-may-never-run-ca4</guid>
      <description>&lt;p&gt;It's important to take a good look at your code and clean up when you're done with it. Once in a while, we tend to forget this little detail and just move on, leaving behind trails of unreachable statements.&lt;/p&gt;

&lt;p&gt;Here's a very blatant example of a dead piece of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auth&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;user&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// This line will never run&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="n"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;publish&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 last line above will never execute, and while this type of code looks like a very rare occurrence, it still shows up a lot. There is this automated tool called &lt;a href="https://getrector.org/"&gt;Rector&lt;/a&gt; that is fantastic at finding dead code like this and removing it, or letting you know of its existence.&lt;/p&gt;

&lt;p&gt;Rector also unearths other dead code that is not completely obvious at first sight. This one below is very common, actually.&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;/**
     * @param GeocoderInterface $geocoder
     */&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;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;GeocoderInterface&lt;/span&gt; &lt;span class="nv"&gt;$geocoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&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 PHPDoc is expressing the same information that is clearly stated in the method declaration. A dev can easily see that the method needs a &lt;code&gt;GeocoderInterface&lt;/code&gt; object, the extra comment does not tell more than that. Same for an IDE, you will get the autocomplete you need without the comment.&lt;/p&gt;

&lt;h2&gt;
  
  
  PHPDocs can be liars in disguise
&lt;/h2&gt;

&lt;p&gt;One more thing I don't like about these redundant docs is that they often 'lie'. Take this for example:&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;/**
     * @return User
     */&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;user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps me up at night. This Eloquent relation has a return type of &lt;code&gt;BelongsTo&lt;/code&gt;, but if you use it as a method &lt;code&gt;$post-&amp;gt;user()&lt;/code&gt; somewhere in code, the IDE will wrongly suggest you autocomplete options that belong to an Eloquent Model. It will be fun trying to find these bugs when they happen in runtime, so just remove the comment entirely.&lt;/p&gt;

&lt;p&gt;If you find yourself doing this often because you want some more autocomplete options when accessing the actual model &lt;code&gt;$post-&amp;gt;user&lt;/code&gt;, consider annotating it, 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="cd"&gt;/**
 * @property User $user
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Laravel's default stubs
&lt;/h2&gt;

&lt;p&gt;Another piece of dead code is the Laravel framework's default stubs. They're there so we can easily input our dependencies. What do we do if we don't have any? Nothing, we just leave it there, because we're lazy.&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;/**
     * Create the event listener.
     *
     * @return void
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="cd"&gt;/**
     * Create a new command instance.
     *
     * @return void
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&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;No harm in keeping them, but if you're not injecting anything, frankly, wipe these suckers from the face of the earth, they're just taking valuable space and pushing the real code down below. You can also change the default stubs by running &lt;code&gt;php artisan stub:publish&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditions that are always true
&lt;/h2&gt;

&lt;p&gt;Dead code candidate #4 is tricky. Usually, it's safe to remove the redundant check like in this simple case:&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nf"&gt;auth&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;check&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;'not logged in'&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 second condition is always true&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;auth&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;check&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'logged in'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// You can just execute the code without the condition&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'logged in'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there may be cases where a check like that is needed if there's stuff happening behind the scenes, like in this uninspired example:&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;canPost&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mf"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="mf"&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;updateUserPermissions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// You might want to keep this check&lt;/span&gt;
    &lt;span class="c1"&gt;// even if you've checked at the top&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;canPost&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tool like Rector won't magically understand the underlying sorcery that is happening and will simply suggest removing the last condition. To silence it, simply add this comment:&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;/** @noRector \Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector */&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;canPost&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it for the dead code. You killed it. I've also posted about other spells Rector can cast for you, like using updated syntax from PHP 8.1 and above, &lt;a href="https://blog.genijaho.dev/improve-code-quality-in-laravel-using-rector"&gt;auto-importing class names&lt;/a&gt; in the whole codebase, etc. Check out &lt;a href="https://getrector.org/"&gt;their site&lt;/a&gt;, they add refactorings weekly 🏠🔥.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>refactoring</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Refactoring #7: Upping the coding style game in PHP using Rector</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Wed, 31 Aug 2022 14:06:41 +0000</pubDate>
      <link>https://forem.com/genijaho/refactoring-7-upping-the-coding-style-game-in-php-using-rector-45pd</link>
      <guid>https://forem.com/genijaho/refactoring-7-upping-the-coding-style-game-in-php-using-rector-45pd</guid>
      <description>&lt;p&gt;Continuing from &lt;a href="https://dev.to/genijaho/improve-code-quality-in-laravel-using-rector-2fa0"&gt;the last post&lt;/a&gt;, we're now exploring other Rector configurations that help us improve our code.&lt;/p&gt;

&lt;p&gt;It's funny actually, &lt;a href="https://getrector.org/"&gt;Rector is a refactoring tool&lt;/a&gt;, but I'd be lying if I didn't use it to learn a good practice or two. Really, they have a catalog of refactorings ready for us to consume and expand upon.&lt;/p&gt;

&lt;p&gt;Anyway, proceeding with the &lt;code&gt;SetList::CODING_STYLE&lt;/code&gt; configuration sets.&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;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;LevelSetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UP_TO_PHP_74&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;SetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CODE_QUALITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;SetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CODING_STYLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upgrades I absolutely love
&lt;/h2&gt;

&lt;p&gt;We'll start with some top-notch improvements that I think we should apply to every PHP project.&lt;/p&gt;

&lt;h3&gt;
  
  
  New line after statement
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$appends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'order_subtotal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'total'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$guarded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$appends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'order_subtotal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'total'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$guarded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might seem a bit intrusive, in the sense that there might be cases where you want to pack lines together, but I don't think you'll have problems with that. This refactoring only touches the code that you really want to have some breathing room for, like class methods and properties, conditionals, loops, etc. Actually here's &lt;a href="https://github.com/rectorphp/rector/blob/0.13.10/rules/CodingStyle/Rector/Stmt/NewlineAfterStatementRector.php#L41"&gt;the full list&lt;/a&gt; as of Aug 2022.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separate multiple &lt;code&gt;use&lt;/code&gt; statements
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Delivered&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Dispatchable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InteractsWithSockets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SerializesModels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Dispatchable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;InteractsWithSockets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;SerializesModels&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 this one. Putting the &lt;code&gt;use&lt;/code&gt; statements one under the other lets me find them more easily. Also, hidden gems, if you add/remove one of them they'll appear as additions/deletions when viewing the git diff, instead of a one-line change that will take you a second to figure out.&lt;/p&gt;

&lt;p&gt;Notice that the rule with the new lines above does not affect the &lt;code&gt;use&lt;/code&gt; statements, they're still packed together nicely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplify quote escaping
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&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;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'You\'re already verified.'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="o"&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;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"You're already verified."&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh, I am guilty of this. It's pretty common actually to start a string with single quotes when it fits in pleasantly with neighboring strings, only to find out you need to use 'you\'re' with an awkward backslash. Same with using double quotes. What a lovely refactor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap variables in curly braces
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Service &lt;/span&gt;&lt;span class="nv"&gt;$service&lt;/span&gt;&lt;span class="s2"&gt; does not exist!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Service &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$service&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; does not exist!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do I really need this refactoring? Quite some time ago PHPStorm suggested that you use the curly braces on all occasions. Lately, I'm seeing that it advocates removing unnecessary braces. My mind is split right now, but I guess I liked having them in my strings in the first place. So I'm keeping the braces, and telling PHPStorm to stop complaining for now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert &lt;code&gt;switch&lt;/code&gt; to &lt;code&gt;if-else&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'payment_intent.succeeded'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handlePaymentIntentSucceeded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;            &lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'payment_intent.succeeded'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handlePaymentIntentSucceeded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&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 is a perfect example of a developer thinking of far-future use cases when writing code, making things harder to read. If your &lt;code&gt;switch&lt;/code&gt; only contains one &lt;code&gt;case&lt;/code&gt;, you're better off with the good old &lt;code&gt;if&lt;/code&gt;. I'd never think of doing this refactoring manually, by the way, this tool rocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Match &lt;code&gt;catch&lt;/code&gt; exceptions with their types
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getJsonBody&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="nv"&gt;$card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$card&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getJsonBody&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, I had some doubts about this rule. I liked the short &lt;code&gt;$e&lt;/code&gt; version, it was nice and clean, but I guess a proper exception name provides more context after all. Alright, alright, I'm keeping this one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inherited method same visibility as parent
&lt;/h3&gt;



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

&lt;span class="o"&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;setUp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't notice this before but I had a lot of test classes where I'd set the &lt;code&gt;setUp&lt;/code&gt; function as &lt;code&gt;public&lt;/code&gt;. I remember doing it once in one test, and then I've copied that method ever since 😂.&lt;/p&gt;

&lt;p&gt;Don't do that mistake, generate your tests with &lt;code&gt;php artisan make:test ExampleTest&lt;/code&gt;, and modify the stubs however you like with &lt;code&gt;php artisan stub:publish&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrades I have mixed feelings for
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Static arrow functions and closures
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'some:dummy-command'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&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="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="nc"&gt;Carbon&lt;/span&gt;&lt;span class="o"&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;endOfWeek&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;isToday&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="o"&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="k"&gt;static&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="nc"&gt;Carbon&lt;/span&gt;&lt;span class="o"&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;endOfWeek&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;isToday&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are these two rules, which actually occurred quite frequently, that prefix the arrow functions and closures with &lt;code&gt;static&lt;/code&gt; whenever possible. That is, if a closure does not need the &lt;code&gt;$this&lt;/code&gt; reference, it can be made static. I'm dropping these rules, however, since the &lt;a href="https://wiki.php.net/rfc/arrow_functions_v2"&gt;RFC&lt;/a&gt; notes that I probably don't need them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Static closures are rarely used: They're mainly used to prevent &lt;code&gt;$this&lt;/code&gt; cycles, which make GC behavior less predictable. Most code need not concern itself with this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can skip rules by simply adding them to your rector configuration, 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="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="nc"&gt;StaticArrowFunctionRector&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;StaticClosureRector&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;h3&gt;
  
  
  Convert post increments to pre increments
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one really made me hmmm big time. Why do I need this? I check in with the &lt;a href="https://github.com/rectorphp/rector/issues/4859"&gt;issue and PR&lt;/a&gt; that added the rule and I find out that this is a mirror rule from PHPStan. I know there are some edge cases when developers will &lt;code&gt;++&lt;/code&gt; everywhere they can, in &lt;code&gt;if&lt;/code&gt; statements and array keys, but damn this rule made a lot of code a bit suspicious. Or I'm just not used to this and it's actually pretty common. I'm skipping it right now, maybe we cross paths again in the future (&lt;code&gt;PostIncDecToPreIncDecRector&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert inline variables to &lt;code&gt;sprintf&lt;/code&gt;s
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;    &lt;span class="s2"&gt;"Order canceled with reason &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;'Order canceled with reason %s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not sure I understand how the &lt;code&gt;sprintf&lt;/code&gt; version is more readable than the encapsed one. Maybe if I wanted to do some formatting, but in an existing project, the formatting has already been done in some other way. This feels a lot like C, ignoring this one as well (&lt;code&gt;EncapsedStringsToSprintfRector&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We're pausing here for now. This list is of course, incomplete, it's impossible to cover all the &lt;a href="https://github.com/rectorphp/rector/blob/785f5e3b060a02fa689dd253a74ccca68cfe1f14/config/set/coding-style.php"&gt;Code Style refactorings&lt;/a&gt; it can do. We'll continue exploring other refactorings like dead code and early returns in another post. See ya 🏠.&lt;/p&gt;

</description>
      <category>php</category>
      <category>refactoring</category>
      <category>rector</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Refactoring #6: Improve Code Quality in Laravel using Rector</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Thu, 11 Aug 2022 13:41:00 +0000</pubDate>
      <link>https://forem.com/genijaho/improve-code-quality-in-laravel-using-rector-2fa0</link>
      <guid>https://forem.com/genijaho/improve-code-quality-in-laravel-using-rector-2fa0</guid>
      <description>&lt;p&gt;I recently discovered Rector and was completely blown away by its power and effectiveness. The promise is simple: you install and run the package, you get instant automated upgrades and refactorings.&lt;/p&gt;

&lt;p&gt;Damn, that's bold, I thought as I dry ran it into one of my projects. While still reading their first page instructions at &lt;a href="https://getrector.org/#first-run"&gt;getrector.org&lt;/a&gt;, I decided to dive in and refactor a whole project overnight, to whatever extent possible.&lt;/p&gt;

&lt;p&gt;I'm running a Laravel 8 project in a PHP 7.4 environment, and I started out with this simple configuration that handles simple code quality improvements:&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="k"&gt;static&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;RectorConfig&lt;/span&gt; &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&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;'/app'&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;importNames&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$rectorConfig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;LevelSetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UP_TO_PHP_74&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;SetList&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CODE_QUALITY&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;Don't worry if you don't understand the code above, I didn't either half an hour ago 😂.&lt;/p&gt;

&lt;p&gt;After running &lt;code&gt;vendor/bin/rector --dry-run&lt;/code&gt;, I immediately saw these little upgrades that I was never going to do myself anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto import class names
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;     &lt;span class="cd"&gt;/**
-     *
-     * @return \Illuminate\Broadcasting\Channel|array
-     */&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Channel&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="o"&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;broadcastOn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change is coming from the &lt;code&gt;$rectorConfig-&amp;gt;importNames();&lt;/code&gt; configuration, and it's a lifesaver. Most coding standards prefer short class names, so auto-importing the classes in the whole project is a big big win.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing the string class name to a class constant
&lt;/h3&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;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'App\User'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fantastic for old Laravel projects since most of them still use the string version to this day. It's a great upgrade because IDEs have much better support for the &lt;code&gt;User::class&lt;/code&gt; version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arrow functions from PHP 7.4
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;       &lt;span class="nv"&gt;$schedule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dummy-command'&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;daily&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="o"&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="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Carbon\Carbon&lt;/span&gt;&lt;span class="o"&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;endOfWeek&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;isToday&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="o"&gt;-&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="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="nc"&gt;Carbon&lt;/span&gt;&lt;span class="o"&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;endOfWeek&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;isToday&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't even know that arrow functions were introduced in PHP 7.4, and I could have used them without any language upgrade. Now I get to utilize their power with a single rector command. &lt;/p&gt;

&lt;h3&gt;
  
  
  Inline useless variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="nv"&gt;$phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&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="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str_replace&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="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is another low-hanging fruit. The &lt;code&gt;$phone&lt;/code&gt; variable here adds nothing to the quality of the code, so it can be safely removed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combine the assignment operator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;order_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;order_count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;order_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an easy one as well. The short version is always better in my opinion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplify the &lt;code&gt;if&lt;/code&gt; return statements
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;company_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;company_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think you almost always want to do this, it's a beautiful one-liner that's easy to the eye and helps you grasp the logic quicker.&lt;br&gt;
If, however, it ruins your formatting in one of your files, where the existing way makes more sense, you can ignore this rule by adding a comment like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/** @noRector \Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector */&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Throw error on JSON operations
&lt;/h3&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;setImageUrlsAttribute&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="o"&gt;-&lt;/span&gt;        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'image_urls'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'image_urls'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&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="no"&gt;JSON_THROW_ON_ERROR&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 extra parameter makes it so that when encoding or decoding JSON goes wrong, it won't return &lt;code&gt;null&lt;/code&gt; but it will throw an exception instead. I like this one, but I'm not entirely sure I'm ready to have this optimization in my code yet. Since this throws an exception, I'm a bit weary and will store this for later.&lt;/p&gt;

&lt;p&gt;We can ignore certain rules by using &lt;a href="https://github.com/rectorphp/rector/blob/main/docs/how_to_ignore_rule_or_paths.md"&gt;this configuration&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert &lt;code&gt;compact&lt;/code&gt; usages into arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;         &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auth&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;user&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
         &lt;span class="nv"&gt;$credits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;credits&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;latest&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="o"&gt;-&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user.credits'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'credits'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;span class="o"&gt;+&lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user.credits'&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="s1"&gt;'credits'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$credits&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today I learned that there's an opinion about using the &lt;code&gt;compact()&lt;/code&gt; function that states &lt;a href="https://stackoverflow.com/questions/16319729/why-does-php-compact-use-strings-instead-of-actual-variables"&gt;it's not ideal&lt;/a&gt;, and maybe an antipattern. Not sure how I feel about this, but I'm keeping this refactoring and going with the flow. Nothing wrong with plain old arrays, right?&lt;/p&gt;

&lt;p&gt;And many other small upgrades actually. Not to forget that this is coming from just the &lt;code&gt;CODE_QUALITY&lt;/code&gt; rules that I imported in a single line above. There are &lt;a href="https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md"&gt;30+ other categories&lt;/a&gt; that I can't wait to use and see what they offer.&lt;/p&gt;

&lt;p&gt;At this point I just run &lt;code&gt;vendor/bin/rector&lt;/code&gt;, all tests are still passing, commit, push, and deploy. All is well in production now 😁.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>webdev</category>
      <category>refactoring</category>
      <category>php</category>
    </item>
    <item>
      <title>Adding user-specific settings in Laravel</title>
      <dc:creator>Geni Jaho</dc:creator>
      <pubDate>Thu, 28 Apr 2022 19:58:39 +0000</pubDate>
      <link>https://forem.com/genijaho/adding-user-specific-settings-in-laravel-m8k</link>
      <guid>https://forem.com/genijaho/adding-user-specific-settings-in-laravel-m8k</guid>
      <description>&lt;p&gt;The latest feature we added to &lt;a href="https://openlittermap.com/community"&gt;OLM&lt;/a&gt; is the ability to make yourself known to the world using your social media accounts. The links to your accounts appear on the photos you upload, in the Global Map. See the end result in this &lt;a href="https://openlittermap.com/global?lat=41.3254775&amp;amp;lon=19.8288426&amp;amp;zoom=17&amp;amp;photo=278127"&gt;photo on the map&lt;/a&gt;. This is the approach we're following in our back-end.&lt;/p&gt;

&lt;p&gt;There are many ways to handle this, but after some googling, we came to the conclusion that the best solution in our case was to use a &lt;code&gt;settings&lt;/code&gt; column in our &lt;code&gt;User&lt;/code&gt; model. Database JSON columns come to the rescue. We copied a good chunk of code from &lt;a href="https://itnext.io/adding-user-settings-to-a-laravel-app-7f97315f7ba7"&gt;this awesome post&lt;/a&gt; 🤣.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing the settings in the database
&lt;/h2&gt;

&lt;p&gt;Starting off with adding the new column to the &lt;code&gt;users&lt;/code&gt; table, this simple migration does the trick:&lt;/p&gt;


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


&lt;p&gt;We want it to be nullable since existing users of the platform won't have any settings whatsoever, and it's easier than updating everyone with a default value. We could also provide a default value to this JSON column, like &lt;code&gt;{}&lt;/code&gt;, but it's only supported from &lt;a href="https://stackoverflow.com/a/61169233/5828796"&gt;MySQL version 8.0.13&lt;/a&gt; onwards.&lt;/p&gt;

&lt;p&gt;To save the user's settings, we have a &lt;code&gt;SettingsController&lt;/code&gt; and a method we called &lt;code&gt;update()&lt;/code&gt;:&lt;/p&gt;


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


&lt;p&gt;Notice that we're using the &lt;code&gt;$request-&amp;gt;validated()&lt;/code&gt; method instead of &lt;code&gt;$request-&amp;gt;all()&lt;/code&gt;. That's because we don't want any setting that we don't have validation for to be stored in our database. The &lt;code&gt;settings()&lt;/code&gt; method on the &lt;code&gt;User&lt;/code&gt; model is a nice helper that does the actual heavy lifting.&lt;/p&gt;


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


&lt;p&gt;It simply merges the new settings we add with the existing ones, overriding the old values where the keys match. Again, inspired by &lt;a href="https://itnext.io/adding-user-settings-to-a-laravel-app-7f97315f7ba7"&gt;this awesome post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Data validation is handled in a &lt;a href="https://laravel.com/docs/9.x/validation#form-request-validation"&gt;FormRequest&lt;/a&gt;:&lt;/p&gt;


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


&lt;p&gt;The &lt;code&gt;UpdateSettingsRequest&lt;/code&gt; does just some simple validation, it requires that the links provided be valid URLs. Through the &lt;code&gt;messages()&lt;/code&gt; method we can &lt;a href="https://laravel.com/docs/9.x/validation#customizing-the-error-messages"&gt;override the default error messages&lt;/a&gt; to be more user-friendly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sprinkle some getters on the User
&lt;/h2&gt;

&lt;p&gt;Now that we have a simple way to set a user's settings, we need a simple way to get them as well.&lt;/p&gt;


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


&lt;p&gt;From top to bottom:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It feels very comfortable when using the &lt;code&gt;@property&lt;/code&gt; doc blocks. Through them, we notify our IDE that, on every &lt;code&gt;$user&lt;/code&gt; instance, we can access properties like &lt;code&gt;$user-&amp;gt;settings&lt;/code&gt; and &lt;code&gt;$user-&amp;gt;social_links&lt;/code&gt;, and they will return an &lt;code&gt;array&lt;/code&gt;. The more you have of these doc blocks, the merrier.&lt;/li&gt;
&lt;li&gt;We add the &lt;code&gt;'settings' =&amp;gt; 'array'&lt;/code&gt; pair to the &lt;code&gt;$casts&lt;/code&gt; array so that we don't have to &lt;code&gt;json_encode()&lt;/code&gt; and &lt;code&gt;json_decode()&lt;/code&gt; all the time to access the settings. You can also use &lt;code&gt;'json'&lt;/code&gt; instead of &lt;code&gt;'array'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;setting()&lt;/code&gt; method is straightforward, it simply checks if we have a setting with a given name and returns it, otherwise, it returns the default value provided. One gotcha here is that if the setting exists, but has a null-ish value, this method will return that instead of the default fallback. We want that by the way, at least for now. Also, notice &lt;code&gt;$this-&amp;gt;settings ?? []&lt;/code&gt;, we need this check since the column could be null.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;getSocialLinksAttribute()&lt;/code&gt; method will allow us to access it like &lt;code&gt;$user-&amp;gt;social_links&lt;/code&gt;, and it will return only the settings that are not null-ish. This is for easier access in the front-end or other places in code. From Laravel 9 and above, there's a better way to &lt;a href="https://laravel.com/docs/9.x/eloquent-mutators#defining-an-accessor"&gt;define Accessors and Mutators&lt;/a&gt; on your models, you should check it out, it's the new kid in the block.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Does this work with nested settings?
&lt;/h2&gt;

&lt;p&gt;In closing, we didn't go with a nested settings approach, something like this:&lt;/p&gt;


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


&lt;p&gt;The reason is the complexity of dealing with it in the front end. Saving and updating nested settings would require some deep watching of properties and suspicious object merging operations. This solution does allow us to do that as it is implemented here, but we're keeping it simple for now.&lt;/p&gt;

&lt;p&gt;See you in the next post, and thank you for making it here.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The code used for illustration is taken from the &lt;a href="https://github.com/OpenLitterMap/openlittermap-web"&gt;OpenLitterMap&lt;/a&gt; project. They're doing a great job at creating the world's most advanced open database on litter, brands &amp;amp; plastic pollution. The project is open-sourced and would love your contributions, both as users and developers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Originally published at &lt;a href="https://genijaho.dev/blog/adding-user-specific-settings-in-laravel"&gt;https://genijaho.dev&lt;/a&gt;.&lt;/p&gt;

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