<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Geni Jaho on Medium]]></title>
        <description><![CDATA[Stories by Geni Jaho on Medium]]></description>
        <link>https://medium.com/@genijaho?source=rss-dde02408d820------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*8yHXVtyIbx0U3KhhuHOJzw.jpeg</url>
            <title>Stories by Geni Jaho on Medium</title>
            <link>https://medium.com/@genijaho?source=rss-dde02408d820------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 18 Jul 2026 19:05:02 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@genijaho/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Simplifying work with custom stubs in Laravel]]></title>
            <link>https://genijaho.medium.com/simplifying-work-with-custom-stubs-in-laravel-23ccfd876d03?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/23ccfd876d03</guid>
            <category><![CDATA[phpunit]]></category>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[mocking]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[testing]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Sat, 17 May 2025 17:39:37 GMT</pubDate>
            <atom:updated>2025-05-17T17:39:37.121Z</atom:updated>
            <content:encoded><![CDATA[<p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*okrgel9kvHwV20kjyUC27A.png" /></figure><h3>Quick intro on benefits</h3><p>Stubs are fake implementations of interfaces or classes that simulate the behavior of real services. They allow you to:</p><ul><li>Test code without calling external services</li><li>Work locally without API keys</li><li>Speed up tests by avoiding expensive API calls</li><li>Create predictable test scenarios</li></ul><h3>External Accounting Service Example</h3><p>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.</p><pre>interface ExternalAccountingInterface<br>{<br>    public function createRecord(array $data): string;<br>}</pre><p>Here’s a real implementation that would call an external API:</p><pre>class ExternalAccounting implements ExternalAccountingInterface<br>{<br>    public function __construct(<br>        private readonly HttpClient $client,<br>        private readonly string $apiKey,<br>    ) {}<br><br>    public function createRecord(array $data): string<br>    {<br>        $response = $this-&gt;client-&gt;post(&quot;https://api.accounting-service.com/v1/records&quot;, [<br>            &#39;headers&#39; =&gt; [<br>                &#39;Authorization&#39; =&gt; &quot;Bearer {$this-&gt;apiKey}&quot;,<br>                &#39;Content-Type&#39; =&gt; &#39;application/json&#39;,<br>            ],<br>            &#39;json&#39; =&gt; $data,<br>        ]);<br><br>        $responseData = json_decode($response-&gt;getBody(), true);<br>        <br>        return $responseData[&#39;record_id&#39;];<br>    }<br>}</pre><p>Now, here’s a fake implementation for testing:</p><pre>class FakeExternalAccounting implements ExternalAccountingInterface<br>{<br>    private array $createdRecords = [];<br>    private bool $hasEnoughCredits = true;<br><br>    public function createRecord(array $data): string<br>    {<br>        if (! $this-&gt;hasEnoughCredits) {<br>            throw new InsufficientCreditsException(&quot;Not enough credits to create a record&quot;);<br>        }<br>        <br>        $recordId = Str::uuid();<br>        $this-&gt;createdRecords[$recordId] = $data;<br>        return $recordId;<br>    }<br>    <br>    // Edge case simulation<br>    public function withNotEnoughCredits(): self<br>    {<br>        $this-&gt;hasEnoughCredits = false;<br>        return $this;<br>    }<br>    <br>    // Helper methods for assertions<br>    public function assertRecordsCreated(array $eventData): void<br>    {<br>        Assert::assertContains(<br>            $eventData,<br>            $this-&gt;createdRecords,<br>            &#39;Failed asserting that the record was created with the correct data.&#39;<br>        );<br>    }<br>    <br>    public function assertNothingCreated(): void<br>    {<br>        Assert::assertEmpty($this-&gt;createdRecords, &#39;Records were created unexpectedly.&#39;);<br>    }<br>}</pre><h3>Before and After: Refactoring to Use Stubs</h3><h3>Before: Using Mockery</h3><pre>public function testCreateAccountingRecord(): void<br>{<br>    // Create a mock using Mockery<br>    $accountingMock = $this-&gt;mock(ExternalAccountingInterface::class);<br><br>    // Set expectations<br>    $accountingMock-&gt;shouldReceive(&#39;createRecord&#39;)<br>        -&gt;once()<br>        -&gt;with(Mockery::on(function ($data) {<br>            return isset($data[&#39;type&#39;]) &amp;&amp; $data[&#39;type&#39;] === &#39;invoice&#39; &amp;&amp;<br>                   isset($data[&#39;amount&#39;]) &amp;&amp; $data[&#39;amount&#39;] === 99.99;<br>        }))<br>        -&gt;andReturn(&#39;rec_123456&#39;);<br><br>    // Bind the mock<br>    $this-&gt;swap(ExternalAccountingInterface::class, $accountingMock);<br><br>    // Execute the test<br>    $response = $this-&gt;post(&#39;/api/invoices&#39;, [<br>        &#39;product_id&#39; =&gt; &#39;prod_123&#39;,<br>        &#39;amount&#39; =&gt; 99.99,<br>    ]);<br><br>    // Assert the response<br>    $response-&gt;assertStatus(200);<br>    $response-&gt;assertJson([&#39;success&#39; =&gt; true]);<br>}</pre><h3>After: Using the Stub</h3><pre>public function testCreateAccountingRecord(): void<br>{<br>    // Create an instance of our custom stub<br>    $fakeAccounting = new FakeExternalAccounting;<br><br>    // Bind the stub<br>    $this-&gt;swap(ExternalAccountingInterface::class, $fakeAccounting);<br><br>    // Execute the test<br>    $response = $this-&gt;post(&#39;/api/invoices&#39;, [<br>        &#39;product_id&#39; =&gt; &#39;prod_123&#39;,<br>        &#39;amount&#39; =&gt; 99.99,<br>    ]);<br><br>    // Assert the response<br>    $response-&gt;assertStatus(200);<br>    $response-&gt;assertJson([&#39;success&#39; =&gt; true]);<br><br>    // Assert that records were created with the expected data<br>    $fakeAccounting-&gt;assertRecordsCreated([<br>        &#39;type&#39; =&gt; &#39;invoice&#39;,<br>        &#39;amount&#39; =&gt; 99.99,<br>    ]);<br>}</pre><h3>Testing Edge Cases</h3><p>Custom stubs make it easy to test edge cases and error scenarios:</p><pre>public function testInvoiceFailsWhenNotEnoughCredits(): void<br>{<br>    // Create an instance of our custom stub<br>    $fakeAccounting = new FakeExternalAccounting;<br><br>    // Configure the stub to simulate not enough credits<br>    $fakeAccounting-&gt;withNotEnoughCredits();<br><br>    // Bind the stub<br>    $this-&gt;swap(ExternalAccountingInterface::class, $fakeAccounting);<br><br>    // Execute the test expecting a failure<br>    $response = $this-&gt;post(&#39;/api/invoices&#39;, [<br>        &#39;product_id&#39; =&gt; &#39;prod_123&#39;,<br>        &#39;amount&#39; =&gt; 99.99,<br>    ]);<br><br>    // Assert the response handles the failure correctly<br>    $response-&gt;assertStatus(422);<br>    $response-&gt;assertJson([&#39;error&#39; =&gt; &#39;Insufficient credits&#39;]);<br><br>    // Assert that no records were created<br>    $fakeAccounting-&gt;assertNothingCreated();<br>}</pre><h3>Swapping Stubs in Your Base Test Case</h3><p>To avoid having to swap implementations in every test, you can set up your stubs in your base test case:</p><pre>class TestCase extends BaseTestCase<br>{<br>    protected function setUp(): void<br>    {<br>        parent::setUp();<br><br>        // Create and register the stub for all tests<br>        $this-&gt;swap(ExternalAccountingInterface::class, new FakeExternalAccounting);<br>    }<br>}</pre><p>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.</p><pre>class InvoiceTest extends TestCase<br>{<br>    public function testCreateInvoice(): void<br>    {<br>        // The accounting service is already swapped in the base test case<br>        // Just get it from the container<br>        $fakeAccounting = app(ExternalAccountingInterface::class);<br><br>        // Execute the test<br>        $response = $this-&gt;post(&#39;/api/invoices&#39;, [<br>            &#39;product_id&#39; =&gt; &#39;prod_123&#39;,<br>            &#39;amount&#39; =&gt; 99.99,<br>        ]);<br><br>        // Assert the response<br>        $response-&gt;assertStatus(200);<br><br>        // Use the stub&#39;s assertion methods<br>        $fakeAccounting-&gt;assertRecordsCreated([<br>            &#39;type&#39; =&gt; &#39;invoice&#39;,<br>            &#39;amount&#39; =&gt; 99.99,<br>        ]);<br>    }<br>}</pre><h3>Using Stubs During Local Development</h3><p>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:</p><pre>// In a service provider<br>public function register(): void<br>{<br>    // Only use the mock in local environment<br>    if ($this-&gt;app-&gt;environment(&#39;local&#39;)) {<br>        $this-&gt;app-&gt;bind(ExternalAccountingInterface::class, function () {<br>            return new FakeExternalAccounting;<br>        });<br>    } else {<br>        // Use the real implementation in other environments<br>        $this-&gt;app-&gt;bind(ExternalAccountingInterface::class, function (Application $app) {<br>            return new ExternalAccounting(<br>                $app-&gt;make(HttpClient::class),<br>                config(&#39;services.accounting.api_key&#39;)<br>            );<br>        });<br>    }<br>}</pre><p>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.</p><p>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.</p><h3>So try them out!</h3><p>I’m not going to list any more benefits of using stubs, but let me say, you’ll definitely have fun with them 😁.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=23ccfd876d03" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Upgrading to Laravel 10, PHPUnit 10, and Pest 2]]></title>
            <link>https://genijaho.medium.com/upgrading-to-laravel-10-phpunit-10-and-pest-2-e42e520e2427?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/e42e520e2427</guid>
            <category><![CDATA[pest]]></category>
            <category><![CDATA[rector]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[phpunit]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Sun, 17 Sep 2023 15:21:49 GMT</pubDate>
            <atom:updated>2023-09-18T08:50:48.145Z</atom:updated>
            <content:encoded><![CDATA[<p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9lROiypXHGWT-haqnoTRlg.png" /></figure><h3>Remove or upgrade outdated packages</h3><p>Firstly, check the <a href="https://laravel.com/docs/10.x/upgrade#updating-dependencies">dependencies guide here</a>, and complete all the steps outlined in that section. You need to update the composer.json with the new versions, and then run composer update. 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.</p><p>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:</p><ul><li><a href="https://github.com/fruitcake/laravel-cors">CORS Middleware for Laravel</a> — all supported Laravel versions now include the CORS middleware in the core.</li><li><a href="https://github.com/gpressutto5/laravel-slack">Laravel Slack</a> — 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.</li></ul><p>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.</p><h3>Install Rector for Laravel</h3><p><a href="https://getrector.com/">Rector</a> will help us migrate to the new Laravel version with practically no effort. Follow the Composer installation guide <a href="https://getrector.com/documentation">here</a>, and then install the <a href="https://github.com/driftingly/rector-laravel">Laravel extension</a>. Among other configurations you might have in your rector.php file, add the Laravel 10 set:</p><pre>use Rector\Config\RectorConfig;<br>use RectorLaravel\Set\LaravelSetList;<br><br>return static function (RectorConfig $rectorConfig): void {<br>    $rectorConfig-&gt;sets([<br>        LaravelSetList::LARAVEL_100,<br>    ]);<br>};</pre><p>Now just run vendor/bin/rector process and see what happens. It will handle most of the changes for you, like model &quot;dates&quot; property, the Bus facade&#39;s dispatchNow to dispatchSync, etc. At this point, I make another commit to separate the automatic changes from my own changes.</p><h3>Add the finishing touches to the upgrade</h3><p>As a final step, take a good look at the <a href="https://laravel.com/docs/10.x/upgrade#public-path-binding">upgrade guide</a> for anything that needs your attention. There are certain things that Rector can not cover for you, like moving lang files around. You&#39;re not entirely out of luck though, there&#39;s a paid service called <a href="https://laravelshift.com/upgrade-laravel-9-to-laravel-10">Shift</a>, that has that capability and is tailored for Laravel projects. I suggest you give it a chance to prove itself, it&#39;s worth every penny. But still, double-check with the upgrade guide and see if anything&#39;s missing.</p><p>Make another commit for it if you’ve made any changes, and run your tests. Hopefully, everything’s green.</p><h3>Upgrade to PHPUnit 10</h3><p>Update your PHPUnit version to &quot;phpunit/phpunit&quot;: &quot;^10&quot; and run a composer update. It will probably go smoothly. The first time you&#39;ll run your tests, it will ask you to update your phpunit.xml schema to a newer version, you should run a command for that. Run it: ./vendor/bin/phpunit --migrate-configuration.</p><p>In your rector.php file add the PHPUnit rule:</p><pre>use Rector\Config\RectorConfig;<br>use Rector\PHPUnit\Set\PHPUnitSetList;<br>use RectorLaravel\Set\LaravelSetList;<br><br>return static function (RectorConfig $rectorConfig): void {<br>    $rectorConfig-&gt;paths([<br>        __DIR__ . &#39;/app&#39;,<br>        __DIR__ . &#39;/database&#39;,<br>        __DIR__ . &#39;/tests&#39;,<br>    ]);<br><br>    $rectorConfig-&gt;sets([<br>        LaravelSetList::LARAVEL_100,<br>        PHPUnitSetList::PHPUNIT_100,<br>    ]);<br>};</pre><p>Don’t forget to include your tests directory in the paths that Rector will look for. Run ./vendor/bin/rector 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&#39;ve written some suspicious test classes and used $this inside your data providers, as I did 😀. And now&#39;s the time for another commit.</p><h3>Migrate to Pest v2</h3><p>Pest has a migration tool of its own, it’s called <a href="https://pestphp.com/docs/migrating-from-phpunit-guide">Drift</a>, and it’s a Pest Plugin. You can install it with composer require pestphp/pest-plugin-drift --dev. To convert all your tests you simply run ./vendor/bin/pest --drift. 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.</p><p>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 Pest.php file, or include the whole directory to use the correct TestCase with a single line:<br>uses(\Tests\TestCase::class)-&gt;in(&#39;./&#39;);.</p><p>The migration will remove all classes in your tests, so if you had private helper methods, they will be converted to functions. Inside those functions, you lose access to the $this variable, so you must check each case and fix it manually, at least at the time of this post. For example, instead of $this-&gt;mock(...) you have to use just mock(...).</p><p>And now you should be good to go. Probably all or most of your tests are green.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e42e520e2427" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Speed up GitHub Actions by caching Composer, Rector, & Pint]]></title>
            <link>https://genijaho.medium.com/speed-up-github-actions-by-caching-composer-rector-pint-b719e3fa035a?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/b719e3fa035a</guid>
            <category><![CDATA[php]]></category>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[refactoring]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Tue, 20 Jun 2023 15:24:24 GMT</pubDate>
            <atom:updated>2023-06-28T14:34:54.693Z</atom:updated>
            <content:encoded><![CDATA[<p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kbWa5LTyDOnngekjjlFlhA.png" /></figure><p>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.</p><p>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.</p><h3>Caching Composer vendor</h3><p>This is part of the job definition where we cache the vendor folder:</p><pre>jobs:<br>  code-quality:<br>    runs-on: ubuntu-latest<br>    steps:<br>      - uses: shivammathur/setup-php@v2<br>        with:<br>          php-version: 8.1<br>      - uses: actions/checkout@v3<br>      - name: Cache Vendor<br>        id: cache-vendor<br>        uses: actions/cache@v3<br>        with:<br>          path: vendor<br>          key: ${{ runner.os }}-vendor-${{ hashFiles(&#39;**/composer.lock&#39;) }}<br>      - name: &quot;Install Dependencies&quot;<br>        if: steps.cache-vendor.outputs.cache-hit != &#39;true&#39; # Skip if cache hit<br>        run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist</pre><p>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.</p><h3>Caching Rector</h3><p>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.</p><p>The first thing we do is enable file-based caching in our rector.php configuration.</p><pre>&lt;?php<br><br>declare(strict_types=1);<br><br>use Rector\Caching\ValueObject\Storage\FileCacheStorage;<br>use Rector\Config\RectorConfig;<br><br>return static function (RectorConfig $rectorConfig): void {<br>    $rectorConfig-&gt;paths([<br>        __DIR__.&#39;/app&#39;,<br>        __DIR__.&#39;/tests&#39;,<br>    ]);<br><br>    $rectorConfig-&gt;importNames();<br><br>    $rectorConfig-&gt;sets([...]);<br><br>    // Ensure file system caching is used instead of in-memory.<br>    $rectorConfig-&gt;cacheClass(FileCacheStorage::class);<br><br>    // Specify a path that works locally as well as on CI job runners.<br>    $rectorConfig-&gt;cacheDirectory(&#39;./storage/rector/cache&#39;);<br>};</pre><p>This will also speed up your subsequent local Rector runs, as it will only check the modified files instead of the whole codebase.</p><p>Next up, in GitHub Actions we do the same caching as before, but instead of caching the vendor directory, we cache the storage/rector/cache directory, or wherever you stored your Rector cache.</p><pre>jobs:<br>  code-quality:<br>    runs-on: ubuntu-latest<br>    steps:<br>      - ...<br>      - name: Cache Rector<br>        uses: actions/cache@v3<br>        with:<br>          path: ./storage/rector/cache<br>          key: ${{ runner.os }}-rector-${{ hashFiles(&#39;**/composer.lock&#39;) }}<br>          restore-keys: ${{ runner.os }}-rector-<br>      - name: Run Rector<br>        run: vendor/bin/rector --ansi</pre><p>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.</p><h3>Caching Pint</h3><p>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 --cache-file option to the PHP CS Fixer.</p><p>Instead, you can configure the Pint cache location in your pint.json:</p><pre>{<br>    &quot;preset&quot;: &quot;laravel&quot;,<br>    &quot;exclude&quot;:[...],<br>    &quot;cache-file&quot;: &quot;storage/pint.cache&quot;,<br>    &quot;rules&quot;: {...}<br>}</pre><p>You can choose whatever location fits you best. Now that we have a fixed cache location, we do the same code for GitHub Actions:</p><pre>jobs:<br>  code-quality:<br>    runs-on: ubuntu-latest<br>    steps:<br>      - ...<br>      - name: Cache Pint<br>        uses: actions/cache@v3<br>        with:<br>          path: ./storage/pint.cache<br>          key: ${{ runner.os }}-pint-${{ hashFiles(&#39;**/composer.lock&#39;) }}<br>          restore-keys: ${{ runner.os }}-pint-<br>      - name: Run Pint<br>        run: ./vendor/bin/pint</pre><p>This brings Pint execution down to just a few seconds as well, depending on the size of your PRs.</p><h3>The complete pipeline</h3><p>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.</p><pre>name: Rector, Pint &amp; Tests<br><br>on:<br>  pull_request:<br>    branches: [ master ]<br><br>jobs:<br>  code-quality:<br>    runs-on: ubuntu-latest<br>    steps:<br>      - uses: shivammathur/setup-php@v2<br>        with:<br>          php-version: 8.1<br>      - uses: actions/checkout@v3<br>        with:<br>          # Must be used to trigger workflow after push<br>          token: ${{ secrets.ACCESS_TOKEN }}<br>      - name: Cache Vendor<br>        id: cache-vendor<br>        uses: actions/cache@v3<br>        with:<br>          path: vendor<br>          key: ${{ runner.os }}-vendor-${{ hashFiles(&#39;**/composer.lock&#39;) }}<br>      - name: &quot;Install Dependencies&quot;<br>        if: steps.cache-vendor.outputs.cache-hit != &#39;true&#39; # Skip if cache hit<br>        run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist<br>      - name: Cache Rector<br>        uses: actions/cache@v3<br>        with:<br>          path: ./storage/rector/cache<br>          key: ${{ runner.os }}-rector-${{ hashFiles(&#39;**/composer.lock&#39;) }}<br>          restore-keys: ${{ runner.os }}-rector-<br>      - name: &quot;Run Rector&quot;<br>        run: vendor/bin/rector --ansi<br>      - name: Cache Pint<br>        uses: actions/cache@v3<br>        with:<br>          path: ./storage/pint.cache<br>          key: ${{ runner.os }}-pint-${{ hashFiles(&#39;**/composer.lock&#39;) }}<br>          restore-keys: ${{ runner.os }}-pint-<br>      - name: &quot;Run Pint&quot;<br>        run: ./vendor/bin/pint<br>      - uses: stefanzweifel/git-auto-commit-action@v4<br>        with:<br>          commit_message: &#39;[ci-review] Rector &amp; Pint&#39;<br>          commit_author: &#39;GitHub Action &lt;actions@github.com&gt;&#39;<br>          commit_user_email: &#39;action@github.com&#39;<br><br>  tests:<br>    runs-on: ubuntu-latest<br>    needs: code-quality<br>    services:<br>      mysql:<br>        image: mysql:8.0.25<br>        env:<br>          MYSQL_ALLOW_EMPTY_PASSWORD: false<br>          MYSQL_ROOT_PASSWORD: password<br>          MYSQL_DATABASE: my_app_test<br>        ports:<br>          - 3306/tcp<br>        options: --health-cmd=&quot;mysqladmin ping&quot; --health-interval=10s --health-timeout=5s --health-retries=3<br>    steps:<br>      - uses: shivammathur/setup-php@2.23.0<br>        with:<br>          php-version: &#39;8.1&#39;<br>          extensions: mbstring, dom, fileinfo, mysql<br>      - uses: actions/checkout@v3<br>        with:<br>          # Must be used to trigger workflow after push<br>          token: ${{ secrets.ACCESS_TOKEN }}<br>      - name: Copy .env<br>        run: php -r &quot;file_exists(&#39;.env&#39;) || copy(&#39;.env.example&#39;, &#39;.env&#39;);&quot;<br>      - name: Cache Vendor<br>        id: cache-vendor<br>        uses: actions/cache@v3<br>        with:<br>          path: vendor<br>          key: ${{ runner.os }}-vendor-${{ hashFiles(&#39;**/composer.lock&#39;) }}<br>      - name: Install Dependencies<br>        if: steps.cache-vendor.outputs.cache-hit != &#39;true&#39; # Skip if cache hit<br>        run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist<br>      - name: Generate key<br>        run: php artisan key:generate<br>      - name: PHPUnit<br>        run: php artisan test --parallel<br>        env:<br>          DB_TEST_PORT: ${{ job.services.mysql.ports[&#39;3306&#39;] }}</pre><p>Check out these resources for more:</p><ul><li><a href="https://getrector.com/documentation/cache-in-ci">https://getrector.com/documentation/cache-in-ci</a></li><li><a href="https://getrector.com/blog/new-setup-ci-command-to-let-rector-work-for-you">https://getrector.com/blog/new-setup-ci-command-to-let-rector-work-for-you</a><br><a href="https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows">https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b719e3fa035a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Doing stricter checks in PHP]]></title>
            <link>https://genijaho.medium.com/doing-stricter-checks-in-php-2686eecbefc8?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/2686eecbefc8</guid>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[rector]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Sun, 09 Apr 2023 10:37:58 GMT</pubDate>
            <atom:updated>2023-04-09T10:37:58.408Z</atom:updated>
            <content:encoded><![CDATA[<p>A nice refactoring I’ve learned recently is to utilize types in conditionals.</p><p>For years I’ve been doing if (!empty($posts)), but <a href="https://getrector.com/">Rector</a>, a tool to automatically refactor code, changed that to if ($posts === []). The main advantage of using $posts === [] is that it is more explicit and less prone to unexpected behavior.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wZ9CPa6fF3TnC9RmesqAaA.png" /></figure><p>It might not seem like much, but it’s quite telling of the codebase you’re working with. Using $posts === [] 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.</p><p>On the other hand, using !empty($posts) can suggest a more casual approach to programming, where you are relying on PHP&#39;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.</p><p>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.</p><h3>Using strict comparison to check for null or empty strings:</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bI8Mr0svKSqvoS8FicBsDg.png" /></figure><p>The first two conditions check if $user is not truthy, which includes checking if it&#39;s null, false, 0, or an empty string. With the after example, we can use the strict comparison operator === to specifically check if $username is null.</p><h3>Type hints in function parameters:</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uDdXGlRGJefitDTtURD53A.png" /></figure><p>In this example, the int type hint ensures that $x and $y are integers. If a non-integer value is passed to the function, PHP will throw a TypeError.</p><h3>Using array_key_exists() to check for array keys:</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lt8cwIZrrKLWbYeFkEB4vg.png" /></figure><p>In the before example, we’re using the isset() function to check if $userData[&#39;username&#39;] exists. With the after example, we can use the array_key_exists() function to specifically check if the &#39;username&#39; key exists in $userData.</p><h3>Null coalescing operator and null coalescing assignment operator:</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BzNuj8JI0PXg-cRSG_itAQ.png" /></figure><p>In the before example, we’re using an if statement to check if $userData[&#39;username&#39;] exists and setting $username accordingly. With the after example, we can use the null coalescing operator ?? to set $username to $userData[&#39;username&#39;] if it exists, and &#39;Guest&#39; if it doesn&#39;t. Additionally, we can use the null coalescing assignment operator ??= to assign the default value to $username only if it&#39;s not already set.</p><p>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!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2686eecbefc8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Upgrading to Eloquent accessors & mutators from Laravel 9]]></title>
            <link>https://genijaho.medium.com/upgrading-to-eloquent-accessors-mutators-from-laravel-9-10b02e59c4fc?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/10b02e59c4fc</guid>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[rector]]></category>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Sun, 26 Feb 2023 13:11:38 GMT</pubDate>
            <atom:updated>2023-02-26T13:12:23.095Z</atom:updated>
            <content:encoded><![CDATA[<p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AZQd0L6oW-STkZjzE-o4IQ.png" /></figure><p>The old syntax used a simple convention, just wrapping the attribute between ‘get’ or ‘set’ and the word ‘Attribute’, such as getFullNameAttribute and setFullNameAttribute, 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 Illuminate\Database\Eloquent\Casts\Attribute (<a href="https://laravel.com/docs/9.x/releases#eloquent-accessors-and-mutators">docs</a>):</p><pre>use Illuminate\Database\Eloquent\Casts\Attribute;<br><br>public function name(): Attribute<br>{<br>    return new Attribute(<br>        get: fn ($value) =&gt; strtoupper($value),<br>        set: fn ($value) =&gt; $value,<br>    );<br>}</pre><p>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 <a href="https://getrector.com/">Rector</a>. It’s an open-source tool to upgrade PHP projects and automate the boring stuff for you.</p><p>You can install Rector with composer require rector/rector --dev. Apart from the core package, we&#39;ll require another <a href="https://github.com/driftingly/rector-laravel">package</a> that is specifically tailored to Laravel apps. You can install it with composer require driftingly/rector-laravel:dev-main --dev, and initiate a config file with vendor/bin/rector init.</p><p>You only need to apply one Rector rule to migrate to the new syntax automatically. Just write this in your rector.php file:</p><pre>&lt;?php<br><br>declare(strict_types=1);<br><br>use Rector\Config\RectorConfig;<br>use RectorLaravel\Rector\ClassMethod\MigrateToSimplifiedAttributeRector;<br><br>return static function (RectorConfig $rectorConfig): void {<br>    $rectorConfig-&gt;paths([<br>        __DIR__ . &#39;/app&#39;,<br>    ]);<br><br>    $rectorConfig-&gt;rule(MigrateToSimplifiedAttributeRector::class);<br>};</pre><p>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 😊.</p><h3>There is one gotcha</h3><p>The old syntax allowed you to use the accessors and mutators however you wanted. So, if you had a getFullNameAttribute(), you could use it either like $user-&gt;full_name or $user-&gt;fullName. 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&#39;re a bit out of luck. Suppose you have protected $appends = [&#39;firstName&#39;];, you will see this error: Call to undefined method App\Models\User::getFirstNameAttribute().</p><p>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 protected function firstName(): Attribute), so it defaults to searching for attributes in the old syntax and it fails.</p><p>To overcome that, you need to double-check your Eloquent $appends property or any other manual appends that you apply to your models that use camelCase names for your accessors or mutators. Change them to snake case. Also, if you&#39;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?</p><p>The other hacky solution I found was to disable attribute snake casing by using this in your models: public static $snakeAttributes = false;. 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 <a href="https://github.com/laravel/framework/blob/a47e7a21fdcdce84b17d39f49ea69523e9aef887/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L349">determine the names of model relationships</a>, and it might have implications for them as well. I think you shouldn&#39;t take this approach, and your safest bet is to just not upgrade right away the mutators/accessors that cause this issue.</p><p>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.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=10b02e59c4fc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Development Driven Testing, or testing after the fact.]]></title>
            <link>https://genijaho.medium.com/development-driven-testing-or-testing-after-the-fact-84282260fd15?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/84282260fd15</guid>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[testing]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Wed, 01 Feb 2023 15:22:59 GMT</pubDate>
            <atom:updated>2023-02-01T15:22:59.893Z</atom:updated>
            <content:encoded><![CDATA[<h3>Development Driven Testing</h3><p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P02yQgTfoD0S3SvMj_xISw.png" /></figure><h3>Testing after the fact is pretty common</h3><p>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.</p><p>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.</p><h3>Testing after the fact is often unproductive</h3><p>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?</p><p>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 😂.</p><h3>Not testing at all is the path to the dark side</h3><p>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.</p><p>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…</p><h3>Hey, but doesn’t TDD take up a lot of time?</h3><p>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.</p><p>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.</p><h3>TDD is your decision to make</h3><p>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.</p><p>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.</p><p>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.</p><h3>TDD in the era of Copilot and ChatGPT</h3><p>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.</p><p>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.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=84282260fd15" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Allowing users to send emails with their own SMTP settings in Laravel 9]]></title>
            <link>https://genijaho.medium.com/allowing-users-to-send-emails-with-their-own-smtp-settings-in-laravel-9-3da8801eba59?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/3da8801eba59</guid>
            <category><![CDATA[smtp]]></category>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[webdev]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Sun, 22 Jan 2023 22:21:42 GMT</pubDate>
            <atom:updated>2023-01-22T22:21:42.199Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CyKPNEMDRTt1Y0DF5kS2_Q.png" /></figure><p>Various guides exist on how to do this in Laravel, but all the ones I found (<a href="https://laravel-news.com/allowing-users-to-send-email-with-their-own-smtp-settings-in-laravel">this</a> and <a href="https://blog.creekorful.org/2021/11/laravel-dynamic-smtp-mail-configuration/">this</a>) worked only up to Laravel 8. The reason is that in v9 the Swift Mailer was no longer supported in favor of <a href="https://laravel.com/docs/9.x/releases#symfony-mailer">Symfony Mailer</a>. 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.</p><h3>What Laravel does behind the scenes</h3><p>The first thing we want to handle is related to a class called MailManager. 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 .env file. This MailManager has a method for that:</p><pre>protected function getConfig(string $name)<br>{<br>    return $this-&gt;app[&#39;config&#39;][&#39;mail.driver&#39;]<br>        ? $this-&gt;app[&#39;config&#39;][&#39;mail&#39;]<br>        : $this-&gt;app[&#39;config&#39;][&quot;mail.mailers.{$name}&quot;];<br>}</pre><p>This is the method that we want to override. Before we do that, we need to know how the MailManager is resolved and used by the framework. In the framework&#39;s MailServiceProvider we find this piece of code:</p><pre>protected function registerIlluminateMailer()<br>{<br>    $this-&gt;app-&gt;singleton(&#39;mail.manager&#39;, function ($app) {<br>        return new MailManager($app);<br>    });<br><br>    $this-&gt;app-&gt;bind(&#39;mailer&#39;, function ($app) {<br>        return $app-&gt;make(&#39;mail.manager&#39;)-&gt;mailer();<br>    });<br>}</pre><p>This tells us that to work with another mailer instead of the default one, we have to use another instance of a MailManager and get the mailer from it.</p><h3>Implementing the custom Mailer</h3><p>So we make this new class called App\Extensions\UserMailManager, place it or call it anything you want, that accepts a configuration when created and overrides the getConfig() method we saw earlier to use that configuration.</p><pre>namespace App\Extensions;<br><br>use Illuminate\Mail\MailManager;<br><br>class UserMailManager extends MailManager<br>{<br>    public function __construct($app, private readonly array $customConfig)<br>    {<br>        parent::__construct($app);<br>    }<br><br>    protected function getConfig(string $name)<br>    {<br>        return $this-&gt;customConfig;<br>    }<br>}</pre><p>In your AppServiceProvider, or any other provider, we bind an instance of this new MailManager to the container, which will allow us to pass custom SMTP parameters when needed. It could&#39;ve been a singleton, not sure.</p><pre>class AppServiceProvider extends ServiceProvider<br>{<br>    public function register(): void<br>    {<br>        $this-&gt;app-&gt;bind(&#39;user.mailer&#39;, function ($app, $parameters) {<br>            return (new UserMailManager($app, $parameters))-&gt;mailer();<br>        });<br>    }<br>    ...<br>}</pre><p>You would utilize our setup so far like this:</p><pre>/** @var Mailer $mailer */<br>$mailer = app(&#39;user.mailer&#39;, $smtpParams);<br>$mailer-&gt;send($mailable);</pre><p>This does not look all that handy though. Usually, you have your SMTP params in a column in your users table (or another table, doesn&#39;t matter). We&#39;re going to use that to our advantage, by creating a helper method in the User model.</p><pre>public function sendMail(Mailable $mailable)<br>{<br>    if ($this-&gt;smtp_enabled) {<br>        $smtpParams = [<br>            &#39;transport&#39; =&gt; &#39;smtp&#39;,<br>            &#39;host&#39; =&gt; $this-&gt;smtp_params[&#39;smtp_host&#39;] ?? null,<br>            &#39;port&#39; =&gt; $this-&gt;smtp_params[&#39;smtp_port&#39;] ?? null,<br>            &#39;username&#39; =&gt; $this-&gt;smtp_params[&#39;smtp_user&#39;] ?? null,<br>            &#39;password&#39; =&gt; $this-&gt;smtp_params[&#39;smtp_password&#39;] ?? null,<br>            &#39;timeout&#39; =&gt; null,<br>            &#39;verify_peer&#39; =&gt; false,<br>        ];<br><br>        /** @var Mailer $mailer */<br>        $mailer = app(&#39;creator.mailer&#39;, $smtpParams);<br>        $mailer-&gt;send($mailable);<br>    } else {<br>        Mail::send($mailable);<br>    }<br>}</pre><h3>Using the mailer with custom SMTP params</h3><p>Now, in the other parts of the code where you want to use the custom params, you can swap Mail::send(new TestMail($testVariable)) with $user-&gt;sendMail(new TestMail($testVariable)). Handy, right?</p><p>If you want to queue the mail instead, I’d suggest you add a new method called queueMail($mailable, ...), and tailor it to your needs. The only difference would probably be using $mailer-&gt;queue($mailable); inside the method.</p><p>That’s it. Leave a thumbs up if this helped you, and thank you for making it this far.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3da8801eba59" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Safely upgrade from PHP 7.4 to 8.1 using Rector]]></title>
            <link>https://genijaho.medium.com/safely-upgrade-from-php-7-4-to-8-1-using-rector-268ab3f77dfb?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/268ab3f77dfb</guid>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[rector]]></category>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Sun, 25 Sep 2022 20:43:53 GMT</pubDate>
            <atom:updated>2022-09-25T20:43:53.241Z</atom:updated>
            <content:encoded><![CDATA[<p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cQoCZ2HxG7rVSHdhGqYLRg.png" /></figure><p>With that said, we’re going to upgrade a Laravel project running on the soon-to-be-dead <a href="https://www.php.net/supported-versions.php">PHP 7.4 (Nov 28)</a>. Currently, the last stable PHP version is 8.1 and we’re going to upgrade to it using a tool called <a href="https://getrector.org/">Rector</a>. Rector will help us move on to the new syntax in a very comfortable way.</p><p>Without further ado, here are the steps I followed.</p><h4>Change composer.json to the new PHP 8.1</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eK282EcEXsrqm_DT76wWhQ.png" /></figure><p>Alongside manually changing the version in composer.json, I also changed my environment to use PHP 8.1. In Homestead, that&#39;s done simply by running php81. In Valet that command is valet use php@8.1. If you&#39;re on Windows and using Wamp or Xampp, well, good luck 😁.</p><h4>Do a composer update</h4><p>After changing the environment, I ran a nice and risky composer update. 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.</p><p>This is the point where you’ll probably get stuck and face composer errors because not all packages you use support the new PHP.</p><h4>Check for packages that might break</h4><p>In this particular project, the only package that did not have support was fzaninotto/faker, and that&#39;s because that project has said <a href="https://marmelab.com/blog/2020/10/21/sunsetting-faker.html">goodnight since 2020</a>. Good news though, there&#39;s an alternative to it called <a href="https://fakerphp.github.io/">fakerphp/faker</a>, which you can just use directly in composer.json (&quot;fzaninotto/faker&quot; =&gt; &quot;fakerphp/faker&quot;). And you don&#39;t need to change a thing in your factories, it just works.</p><h4>Run the tests</h4><p>At this point, I run the tests, and I notice nothing breaks, they’re all passing. Phew. However, if it doesn’t go as smoothly for you, or you don’t have tests, check out the deprecations <a href="https://www.php.net/releases/8.1/en.php#deprecations_and_bc_breaks">here</a>. 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.</p><h4>Install, configure, and run Rector</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ksYmUUiAXW_x5UeYpKEybA.png" /></figure><p>This command will install Rector for you and create a rector.php 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:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w7TaKxpsI01cDg3pcyUjPw.png" /></figure><p>We’ll discuss that last rule shortly, but now you should be able to run vendor/bin/rector --dry-run. If your codebase is huge, you&#39;ll have to wait a minute or two. When it&#39;s done you&#39;ll see all the changes that are to be applied, like a git diff. If you omit the --dry-run 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.</p><p>That last rule SpatieEnumClassToEnumRector is skipped because I&#39;m using the spatie/enum <a href="https://github.com/spatie/enum">package</a> and I don&#39;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&#39;s safer in my opinion to migrate some of the enums manually.</p><p>Another little gotcha I want to point out is with the match expression. It does strict comparisons, unlike switch, and that might do some damage if left unchecked. As a remedy, you can use the match (true) {...} version, which is not as beautiful but will do a loose comparison like the switch.</p><p>I’m not going to go over what’s new in 8.1 and what Rector <a href="https://blog.genijaho.dev/improve-code-quality-in-laravel-using-rector">will do for you</a>, 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.</p><p>Originally published at <a href="https://blog.genijaho.dev/safely-upgrade-from-php-74-to-81-using-rector">https://blog.genijaho.dev</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=268ab3f77dfb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Refactoring #8: What is dead may never run]]></title>
            <link>https://genijaho.medium.com/refactoring-8-what-is-dead-may-never-run-f987cd1aa7f0?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/f987cd1aa7f0</guid>
            <category><![CDATA[php]]></category>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[rector]]></category>
            <category><![CDATA[laravel]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Sun, 04 Sep 2022 19:58:53 GMT</pubDate>
            <atom:updated>2022-09-04T19:58:53.909Z</atom:updated>
            <content:encoded><![CDATA[<p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KZpgn5jasjALMyervWHzIA.png" /></figure><p>Here’s a very blatant example of a dead piece of code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5CEocxJVjR1wlC8ic97YeA.png" /></figure><p>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 <a href="https://getrector.org/">Rector</a> that is fantastic at finding dead code like this and removing it, or letting you know of its existence.</p><p>Rector also unearths other dead code that is not completely obvious at first sight. This one below is very common, actually.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tRBUGaUsMeOj5Dn6g1DWLg.png" /></figure><p>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 GeocoderInterface object, the extra comment does not tell more than that. Same for an IDE, you will get the autocomplete you need without the comment.</p><h3>PHPDocs can be liars in disguise</h3><p>One more thing I don’t like about these redundant docs is that they often ‘lie’. Take this for example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eUn_7expMuBi1gR4dyRX3g.png" /></figure><p>This keeps me up at night. This Eloquent relation has a return type of BelongsTo, but if you use it as a method $post-&gt;user() 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.</p><p>If you find yourself doing this often because you want some more autocomplete options when accessing the actual model $post-&gt;user, consider annotating it, like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2-jCTeoudbkAEpEXaFdrKA.png" /></figure><h3>Laravel’s default stubs</h3><p>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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xmPCaTwdDuXs5r01__gU6A.png" /></figure><p>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 php artisan stub:publish.</p><h3>Conditions that are always true</h3><p>Dead code candidate #4 is tricky. Usually, it’s safe to remove the redundant check like in this simple case:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*11yULtqQ8M3mDGPnoCfo6w.png" /></figure><p>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:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tvmSGEHLbouYISr95Zjdng.png" /></figure><p>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:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f8nFQ6VvGHWz5_PY-IJyvQ.png" /></figure><p>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, <a href="https://blog.genijaho.dev/improve-code-quality-in-laravel-using-rector">auto-importing class names</a> in the whole codebase, etc. Check out <a href="https://getrector.org/">their site</a>, they add refactorings weekly 🏠🔥.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f987cd1aa7f0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Upping the coding style game in PHP using Rector]]></title>
            <link>https://genijaho.medium.com/upping-the-coding-style-game-in-php-using-rector-6d37552db44c?source=rss-dde02408d820------2</link>
            <guid isPermaLink="false">https://medium.com/p/6d37552db44c</guid>
            <category><![CDATA[php]]></category>
            <category><![CDATA[webdev]]></category>
            <category><![CDATA[refactoring]]></category>
            <category><![CDATA[rector]]></category>
            <category><![CDATA[best-practices]]></category>
            <dc:creator><![CDATA[Geni Jaho]]></dc:creator>
            <pubDate>Wed, 31 Aug 2022 13:53:56 GMT</pubDate>
            <atom:updated>2022-08-31T14:12:02.285Z</atom:updated>
            <content:encoded><![CDATA[<h3>Refactoring #7: Upping the coding style game in PHP using Rector</h3><p>Continuing from <a href="https://medium.com/@genijaho/improve-code-quality-in-laravel-using-rector-ae7c10c5cb8b">the last post</a>, we’re now exploring other Rector configurations that help us improve our code.</p><p>It’s funny actually, <a href="https://getrector.org/">Rector is a refactoring tool</a>, 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.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_OdgYzDmOVlKEYISH4B_rA.png" /></figure><p>Anyway, proceeding with the SetList::CODING_STYLE configuration sets.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zaHaTevsxXe8P05n2CkxyA.png" /></figure><h3>Upgrades I absolutely love</h3><p>We’ll start with some top-notch improvements that I think we should apply to every PHP project.</p><h4>New line after statement</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MN6Y_VoEGJdWLJw-iwtgeA.png" /></figure><p>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 <a href="https://github.com/rectorphp/rector/blob/0.13.10/rules/CodingStyle/Rector/Stmt/NewlineAfterStatementRector.php#L41">the full list</a> as of Aug 2022.</p><h4>Separate multiple use statements</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X2wzrOekC7XgECb6NtJQ4A.png" /></figure><p>I love this one. Putting the use statements one under the other lets me find them more easily. Also, hidden gems, if you add/remove one of them they&#39;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.</p><p>Notice that the rule with the new lines above does not affect the use statements, they&#39;re still packed together nicely.</p><h4>Simplify quote escaping</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ik9dtLk3jI4c8TAVu_1yDQ.png" /></figure><p>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.</p><h4>Wrap variables in curly braces</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*geOuH_CmyIMFaVSGK3VI8w.png" /></figure><p>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.</p><h4>Convert switch to if-else</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NCyoGM6vmZYsWGHy3C0NBg.png" /></figure><p>This is a perfect example of a developer thinking of far-future use cases when writing code, making things harder to read. If your switch only contains one case, you&#39;re better off with the good old if. I&#39;d never think of doing this refactoring manually, by the way, this tool rocks.</p><h4>Match catch exceptions with their types</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D9D9Qdzmuxd5zhkFkrbQkw.png" /></figure><p>At first, I had some doubts about this rule. I liked the short $e version, it was nice and clean, but I guess a proper exception name provides more context after all. Alright, alright, I&#39;m keeping this one.</p><h4>Inherited method same visibility as parent</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YuuS9GWxeRH6_0RPapTWKA.png" /></figure><p>I didn’t notice this before but I had a lot of test classes where I’d set the setUp function as public. I remember doing it once in one test, and then I&#39;ve copied that method ever since 😂.</p><p>Don’t do that mistake, generate your tests with php artisan make:test ExampleTest, and modify the stubs however you like with php artisan stub:publish.</p><h3>Upgrades I have mixed feelings for</h3><h4>Static arrow functions and closures</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wl8h7Io9HLx9RhGMvd5hwg.png" /></figure><p>There are these two rules, which actually occurred quite frequently, that prefix the arrow functions and closures with static whenever possible. That is, if a closure does not need the $this reference, it can be made static. I&#39;m dropping these rules, however, since the <a href="https://wiki.php.net/rfc/arrow_functions_v2">RFC</a> notes that I probably don&#39;t need them.</p><blockquote>Static closures are rarely used: They’re mainly used to prevent $this cycles, which make GC behavior less predictable. Most code need not concern itself with this.</blockquote><p>You can skip rules by simply adding them to your rector configuration, like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4tsDnvfaoSi6gftukHYiMw.png" /></figure><h4>Convert post increments to pre increments</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5TL5nIDnaWKinWSvBU3N4g.png" /></figure><p>This one really made me hmmm big time. Why do I need this? I check in with the <a href="https://github.com/rectorphp/rector/issues/4859">issue and PR</a> 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 ++ everywhere they can, in if statements and array keys, but damn this rule made a lot of code a bit suspicious. Or I&#39;m just not used to this and it&#39;s actually pretty common. I&#39;m skipping it right now, maybe we cross paths again in the future (PostIncDecToPreIncDecRector).</p><h4>Convert inline variables to sprintfs</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*49pkz651VwY28t51yY1Pjw.png" /></figure><p>I’m not sure I understand how the sprintf 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 (EncapsedStringsToSprintfRector).</p><p>We’re pausing here for now. This list is of course, incomplete, it’s impossible to cover all the <a href="https://github.com/rectorphp/rector/blob/785f5e3b060a02fa689dd253a74ccca68cfe1f14/config/set/coding-style.php">Code Style refactorings</a> it can do. We’ll continue exploring other refactorings like dead code and early returns in another post. See ya 🏠.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6d37552db44c" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>