<?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:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Pritesh Bhanushali]]></title><description><![CDATA[Pritesh Bhanushali]]></description><link>https://newsletter.priteshbhanushali.com</link><image><url>https://substackcdn.com/image/fetch/$s_!DiOV!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92a0aac7-a3c0-44be-97d4-f77aaa58306c_593x593.png</url><title>Pritesh Bhanushali</title><link>https://newsletter.priteshbhanushali.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 06 May 2026 12:03:38 GMT</lastBuildDate><atom:link href="https://newsletter.priteshbhanushali.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Pritesh Bhanushali]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[devpritesh@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[devpritesh@substack.com]]></itunes:email><itunes:name><![CDATA[Pritesh Bhanushali]]></itunes:name></itunes:owner><itunes:author><![CDATA[Pritesh Bhanushali]]></itunes:author><googleplay:owner><![CDATA[devpritesh@substack.com]]></googleplay:owner><googleplay:email><![CDATA[devpritesh@substack.com]]></googleplay:email><googleplay:author><![CDATA[Pritesh Bhanushali]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[🎯Laravel Database Seeders: Beyond Dummy Data]]></title><description><![CDATA[How to manage roles, permissions, and master data safely across local, staging, and production environments]]></description><link>https://newsletter.priteshbhanushali.com/p/laravel-database-seeders-beyond-dummy</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/laravel-database-seeders-beyond-dummy</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 03 Jan 2026 05:00:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!I-HD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Setting up consistent data across environments is one of the most common challenges in Laravel projects. Whether you are onboarding a new developer, preparing demo data for QA, or running automated tests, manually inserting records into the database is error-prone and time-consuming.</p><p>Laravel solves this problem elegantly using <strong>Database Seeders</strong>.</p><div><hr></div><h2>&#128304;What Is Database Seeding in Laravel?</h2><p><strong>Database seeding</strong> is the process of populating your database with predefined or fake data using PHP classes.</p><p>Laravel seeders allow you to:</p><ul><li><p>Insert default records (roles, permissions, admin users)</p></li><li><p>Generate dummy data for testing</p></li><li><p>Maintain consistent data across environments</p></li><li><p>Avoid repetitive manual database setup</p></li></ul><p>Seeders are especially useful in <strong>local</strong>, <strong>testing</strong>, and <strong>staging</strong> environments.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!I-HD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I-HD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I-HD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2524044,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/183123429?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!I-HD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#9881;&#65039;How to Create a Database Seeder in Laravel</h2><p>Creating a database seeder in Laravel is straightforward and follows a clear, standardized workflow. Seeders allow you to define database data using PHP code, making your setup repeatable and version-controlled.</p><h4>&#128736;&#65039; Laravel provides an Artisan command to generate a seeder.</h4><pre><code>php artisan make:seeder TransactionStatusSeeder</code></pre><p>This command creates a new seeder file inside the <code>database/seeders</code> directory.</p><pre><code>database/seeders/TransactionStatusSeeder.php</code></pre><h4>&#129513; Understand the Seeder Structure</h4><p>A basic seeder class looks like this:</p><pre><code>use Illuminate\Database\Seeder;

class TransactionStatusSeeder extends Seeder
{
    public function run()
    {
        // Seeding logic goes here
    }
}</code></pre><ul><li><p>Every seeder extends the <code>Seeder</code> class</p></li><li><p>The <code>run()</code> method is executed when the seeder runs</p></li><li><p>All database insert logic lives inside this method</p></li></ul><h4>&#129514;Add Data to the Seeder</h4><p>Assume you have a table called <code>transaction_statuses</code> with columns:</p><ul><li><p><code>code</code></p></li><li><p><code>label</code></p></li><li><p><code>is_active</code></p></li></ul><p>Now define the seeding logic:</p><pre><code>use Illuminate\Database\Seeder;
use App\Models\TransactionStatus;

class TransactionStatusSeeder extends Seeder
{
    public function run()
    {
        $statuses = [
            ['code' =&gt; 'PENDING',   'label' =&gt; 'Pending',   'is_active' =&gt; true],
            ['code' =&gt; 'POSTED',    'label' =&gt; 'Posted',    'is_active' =&gt; true],
            ['code' =&gt; 'REVERSED',  'label' =&gt; 'Reversed',  'is_active' =&gt; true],
            ['code' =&gt; 'VOIDED',    'label' =&gt; 'Voided',    'is_active' =&gt; false],
        ];

        foreach ($statuses as $status) {
            TransactionStatus::updateOrCreate(
                ['code' =&gt; $status['code']],
                $status
            );
        }
    }
}</code></pre><h5>Why <code>updateOrCreate()</code>?</h5><ul><li><p>Prevents duplicate records</p></li><li><p>Allows controlled updates to labels or flags</p></li><li><p>Safe to run in production</p></li><li><p>Ideal for <strong>master data</strong></p></li></ul><p>This pattern is widely used in <strong>finance, insurance, and accounting systems</strong>.</p><div><hr></div><h4>&#9997;&#65039; Register the Seeder</h4><pre><code>class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            TransactionStatusSeeder::class,
        ]);
    }
}</code></pre><p>The <code>DatabaseSeeder</code> class is the <strong>central entry point</strong> for all database seeders in a Laravel application. Whenever you run a seeding command, Laravel starts execution from this class and then calls all registered seeders in the defined order.</p><p>Understanding <code>DatabaseSeeder</code> is essential because it controls <strong>what data gets seeded, when it gets seeded, and in which environment</strong>.</p><h4>&#128193; Where Is DatabaseSeeder Located?</h4><p>Laravel automatically creates this file at:</p><pre><code><code>database/seeders/DatabaseSeeder.php</code></code></pre><pre><code>database/seeders/
 &#9500;&#9472;&#9472; DatabaseSeeder.php
 &#9500;&#9472;&#9472; TransactionStatusSeeder.php
 &#9492;&#9472;&#9472; RoleSeeder.php</code></pre><p>This file exists by default in every Laravel project and acts as the <strong>orchestrator</strong> for all other seeders.</p><h4>&#128196; Basic Structure of DatabaseSeeder</h4><pre><code>use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        // Call individual seeders here
    }
}</code></pre><h5>Key Points</h5><ul><li><p><code>DatabaseSeeder</code> extends the base <code>Seeder</code> class</p></li><li><p>The <code>run()</code> method is the <strong>starting point</strong></p></li><li><p>All child seeders are invoked from here</p></li></ul><h4>&#9654;&#65039; How DatabaseSeeder Works</h4><p>When you run:</p><pre><code>php artisan db:seed</code></pre><p>Laravel performs the following steps:</p><ol><li><p>Bootstraps the application</p></li><li><p>Loads <code>DatabaseSeeder</code></p></li><li><p>Executes the <code>run()</code> method</p></li><li><p>Calls all registered seeders in sequence</p></li><li><p>Executes each seeder&#8217;s <code>run()</code> method</p></li></ol><p>This makes <code>DatabaseSeeder</code> the <strong>single source of truth</strong> for seeding.</p><h4>&#128313;Registering Seeders in DatabaseSeeder</h4><p>To execute a seeder, it must be registered using the <code>call()</code> method.</p><pre><code>class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            RoleSeeder::class,
            PermissionSeeder::class,
            UserSeeder::class,
        ]);
    }
}</code></pre><h5>Why Order Matters</h5><ul><li><p>Roles must exist before permissions are assigned</p></li><li><p>Permissions must exist before user-role mapping</p></li><li><p>Prevents foreign key constraint errors</p></li></ul><h4>&#128313;Calling Seeders Conditionally</h4><p>You can control which seeders run based on the environment.</p><pre><code>public function run()
{
    $this-&gt;call([
        RoleSeeder::class,
        PermissionSeeder::class,
    ]);

    if (app()-&gt;environment(['local', 'staging'])) {
        $this-&gt;call(UserSeeder::class);
    }
}</code></pre><p>This ensures:</p><ul><li><p>Production stays clean</p></li><li><p>Test users are not created in live systems</p></li></ul><h2>&#128450;&#65039; Grouping Seeders for Better Organization</h2><p>In large applications, it&#8217;s a good practice to group related seeders.</p><pre><code>public function run()
{
    $this-&gt;call([
        MasterDataSeeder::class,
        AuthorizationSeeder::class,
        ConfigurationSeeder::class,
    ]);
}</code></pre><p>Each group seeder can internally call smaller seeders.</p><pre><code>class AuthorizationSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            RoleSeeder::class,
            PermissionSeeder::class,
            RolePermissionSeeder::class,
        ]);
    }
}</code></pre><p>This improves:</p><ul><li><p>Readability</p></li><li><p>Maintainability</p></li><li><p>Scalability</p></li></ul><div><hr></div><h2>&#9654;&#65039; Running the Seeder Explicitly via Artisan</h2><pre><code>php artisan db:seed --class=TransactionStatusSeeder</code></pre><h4>Running DatabaseSeeder Explicitly</h4><p>Although <code>DatabaseSeeder</code> runs by default, you can explicitly call it:</p><pre><code>php artisan db:seed --class=DatabaseSeeder</code></pre><p>This is useful in:</p><ul><li><p>CI/CD pipelines</p></li><li><p>Controlled production deployments</p></li><li><p>Multi-tenant seeding workflows</p></li></ul><div><hr></div><h2>&#128452;&#65039;Database Seeders: A Game-Changer for Multi-Tenant Applications</h2><p>In a <strong>multi-tenant architecture</strong>, a single application serves <strong>multiple tenants</strong>, but each tenant&#8217;s data must remain <strong>isolated</strong>.<br>This makes database seeding fundamentally different from a single-database application.</p><p>Laravel seeders play a <strong>critical role</strong> in initializing and maintaining <strong>consistent data across all tenants</strong>.</p><h4>&#128313; The Core Challenge in Multi-Tenant Seeding</h4><p>In a normal Laravel app:</p><ul><li><p>One database</p></li><li><p>One <code>DatabaseSeeder</code></p></li><li><p>Seed once &#8594; done</p></li></ul><p>In a <strong>multi-tenant system</strong>:</p><ul><li><p>Multiple tenant databases or schemas</p></li><li><p>Each tenant requires <strong>the same base data</strong></p></li><li><p>Seeding must happen <strong>per tenant</strong>, not globally</p></li></ul><p>&#128204; Examples of tenant-specific data:</p><ul><li><p>Roles &amp; permissions</p></li><li><p>Default users</p></li><li><p>Configuration settings</p></li><li><p>Accounting rules</p></li><li><p>Commission slabs</p></li><li><p>Feature flags</p></li></ul><div><hr></div><p>The Tenancy for Laravel package, along with the Spatie package, helps manage tenant isolation and provides tenant-aware features for Laravel applications.</p><p><strong>Tenant-aware Artisan Commands</strong></p><ul><li><p>You can run migrations or seeders <strong>per tenant</strong>.</p></li><li><p>Example:</p></li></ul><pre><code>php artisan tenants:migrate
php artisan tenants:seed --class=SomeSeeder</code></pre><p>Without this, running <code>php artisan db:seed</code> would only affect the central database.</p><ul><li><p>The package automatically switches the database connection and context based on the current tenant.</p></li><li><p>This makes queries &#8220;tenant-aware&#8221; without changing your code everywhere.</p></li></ul><p><strong>&#128221; Seed all tenants with default DatabaseSeeder</strong>:</p><pre><code>php artisan tenants:seed</code></pre><p><strong>&#128221; Seed a specific tenant seeder</strong>:</p><pre><code>php artisan tenants:seed --class=TransactionStatusSeeder</code></pre><p><strong>&#128221; Seed a specific tenant (if supported by package)</strong>:</p><pre><code>php artisan tenants:seed --tenant=1 --class=TransactionStatusSeeder</code></pre><div><hr></div><h2>&#128221;Best Practices for Laravel Seeders</h2><p>&#9989; Keep seeders <strong>small and focused</strong><br>&#9989; Use <strong>factories</strong> for large datasets<br>&#9989; Separate <strong>master data</strong> (roles, permissions)<br>&#9989; Use <strong>environment checks</strong><br>&#10060; Never seed sensitive production data<br>&#10060; Avoid running destructive commands in production</p><div><hr></div><h2>&#9888;&#65039;Common Mistakes and Troubleshooting</h2><h4>Seeder not running?</h4><ul><li><p>Ensure it is registered in <code>DatabaseSeeder</code></p></li></ul><h4>Foreign key constraint errors?</h4><ul><li><p>Disable foreign key checks temporarily</p></li><li><p>Ensure correct seeding order</p></li></ul><h4>Duplicate records?</h4><ul><li><p>Add existence checks before inserting</p></li></ul><div><hr></div><h2>&#127760;Real-World Use Cases</h2><ul><li><p>Creating default admin users</p></li><li><p>Seeding roles and permissions</p></li><li><p>Generating transactions for QA testing</p></li><li><p>Preparing demo data for stakeholders</p></li><li><p>Speeding up onboarding for new developers</p></li></ul><div><hr></div><h2>&#129525; Wrapping Up</h2><p>Laravel Database Seeders are a powerful yet simple tool that can save hours of manual setup and ensure consistency across environments.</p><p>When used correctly&#8212;with factories, best practices, and environment awareness&#8212;seeders become an essential part of any professional Laravel project.</p><blockquote><p><strong>A well-designed seeding strategy is not just about dummy data&#8212;it&#8217;s about predictable, repeatable, and reliable development environments.</strong></p></blockquote><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🔐 Encryption vs Hashing vs Encoding in Laravel]]></title><description><![CDATA[Same Data. Three Techniques. One Costly Mistake Away from Disaster.]]></description><link>https://newsletter.priteshbhanushali.com/p/encryption-vs-hashing-vs-encoding</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/encryption-vs-hashing-vs-encoding</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Wed, 17 Dec 2025 04:31:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NhMa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>&#128680; Introduction: One Wrong Choice Can Break Your Security</h3><p>Every Laravel developer works with sensitive data.</p><p>Passwords.<br>API keys.<br>Bank accounts.<br>Personal identifiers.</p><p>Yet one mistake is shockingly common:</p><blockquote><p>Treating <strong>encryption</strong>, <strong>hashing</strong>, and <strong>encoding</strong> as the same thing.</p></blockquote><p>They&#8217;re not.</p><p>Using the wrong technique doesn&#8217;t just cause bugs &#8212; it can lead to <strong>security breaches, failed audits, compliance violations, and irreversible data loss</strong>.</p><p>In this guide, we&#8217;ll clearly break down:</p><ul><li><p>&#128269; What <strong>Encryption</strong>, <strong>Hashing</strong>, and <strong>Encoding</strong> really are</p></li><li><p>&#129504; When you should use each one (and when you absolutely shouldn&#8217;t)</p></li><li><p>&#128736;&#65039; <strong>Real Laravel examples</strong> you can copy-paste today</p></li><li><p>&#128683; Costly mistakes even experienced developers make</p></li></ul><p>This isn&#8217;t theory &#8212; it&#8217;s written for <strong>real Laravel applications</strong>: APIs, SaaS platforms, fintech products, and multi-tenant systems.</p><div><hr></div><h3>1&#65039;&#8419; Encryption: Lock It, Unlock It (When You Must)</h3><h4>&#128272; What Is Encryption?</h4><p>Encryption transforms <strong>readable data (plaintext)</strong> into <strong>unreadable data (ciphertext)</strong> using a <strong>secret key</strong>.</p><p>&#128073; <strong>It is reversible</strong> &#8212; if (and only if) you have the correct key.</p><p>Think of encryption as a <strong>secure locker</strong>:</p><ul><li><p>You lock the data</p></li><li><p>You unlock it only when truly necessary</p></li></ul><h4>&#9989; When Should You Use Encryption?</h4><p>Use encryption <strong>only</strong> when the original value must be retrieved later.</p><p>&#10004; Bank account numbers<br>&#10004; API tokens &amp; secrets<br>&#10004; OAuth credentials<br>&#10004; Personally Identifiable Information (PII)</p><h4>&#128736;&#65039; Laravel Encryption (Built-In &amp; Battle-Tested)</h4><p>Laravel ships with strong encryption using <strong>AES-256-CBC</strong> &#8212; no setup required.</p><h4>Encrypting Data</h4><pre><code>use Illuminate\Support\Facades\Crypt;

$encrypted = Crypt::encryptString(&#8217;1234-5678-9012-3456&#8217;);</code></pre><h4>Decrypting Data</h4><pre><code>$decrypted = Crypt::decryptString($encrypted);
</code></pre><p>&#10004; Key-based<br>&#10004; Secure<br>&#10004; Fully reversible</p><h4>&#127757; Real-World Example: Storing Bank Details Securely</h4><pre><code>Schema::create(&#8217;client_bank_accounts&#8217;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;string(&#8217;account_holder_name&#8217;);
    $table-&gt;text(&#8217;account_number&#8217;); // encrypted
    $table-&gt;text(&#8217;ifsc_code&#8217;);       // encrypted
    $table-&gt;timestamps();
});</code></pre><pre><code>$bank-&gt;account_number = Crypt::encryptString($request-&gt;account_number);
$bank-&gt;ifsc_code = Crypt::encryptString($request-&gt;ifsc_code);
$bank-&gt;save();</code></pre><p>&#128274; Even if your database is leaked, the data remains useless without <code>APP_KEY</code>.</p><div><hr></div><h3>2&#65039;&#8419; Hashing: Verify Without Ever Knowing</h3><h4>&#128273; What Is Hashing?</h4><p>Hashing converts data into a <strong>fixed-length string</strong> using a mathematical algorithm.</p><p>&#128073; <strong>It is irreversible</strong> &#8212; forever.</p><p>You don&#8217;t unlock hashed data.<br>You <strong>compare</strong> against it.</p><h4>&#9989; When Should You Use Hashing?</h4><p>Use hashing when you <strong>never need the original value again</strong>.</p><p>&#10004; Passwords<br>&#10004; PINs<br>&#10004; Security answers</p><h4>&#128736;&#65039; Laravel Hashing Example</h4><p>Laravel uses <strong>bcrypt</strong> or <strong>Argon2</strong>, both industry-approved.</p><h4>Hashing a Password</h4><pre><code>use Illuminate\Support\Facades\Hash;

$hashed = Hash::make(&#8217;my-secret-password&#8217;);
</code></pre><h4>Verifying a Password</h4><pre><code>Hash::check(&#8217;my-secret-password&#8217;, $hashed); // true</code></pre><p>&#10004; One-way<br>&#10004; Automatically salted<br>&#10004; Resistant to brute-force attacks</p><h4>&#128683; The Biggest Security Mistake: Encrypting Passwords</h4><p>&#10060; <strong>Never do this</strong>:</p><pre><code><code>Crypt::encryptString(&#8217;password123&#8217;);
</code></code></pre><p>Why this is dangerous:</p><ul><li><p>Anyone with <code>APP_KEY</code> can decrypt every password</p></li><li><p>Violates OWASP standards</p></li><li><p>Fails security audits instantly</p></li></ul><p>&#9989; <strong>Passwords must always be hashed &#8212; never encrypted.</strong></p><div><hr></div><h3>3&#65039;&#8419; Encoding: Data Travel, Not Data Protection</h3><h4>&#128260; What Is Encoding?</h4><p>Encoding transforms data into another format so it can be <strong>safely transmitted or stored</strong>.</p><p>&#128073; <strong>It provides ZERO security.</strong></p><p>Encoding is about <strong>compatibility</strong>, not protection.</p><h4>&#9989; When Should You Use Encoding?</h4><p>&#10004; Sending data over APIs<br>&#10004; URLs<br>&#10004; JSON responses<br>&#10004; File uploads</p><h4>&#128736;&#65039; Common Encoding Examples</h4><h4>Base64 Encoding</h4><pre><code><code>$encoded = base64_encode(&#8217;Hello World&#8217;);
$decoded = base64_decode($encoded);
</code></code></pre><h4>JSON Encoding</h4><pre><code><code>$data = [&#8217;name&#8217; =&gt; &#8216;John&#8217;, &#8216;role&#8217; =&gt; &#8216;admin&#8217;];

$json  = json_encode($data);
$array = json_decode($json, true);
</code></code></pre><p>&#9888;&#65039; Anyone can decode encoded data in seconds.</p><div><hr></div><h3>&#128269; Encryption vs Hashing vs Encoding (At a Glance)</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NhMa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NhMa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 424w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 848w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NhMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png" width="1200" height="1200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1200,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:550378,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/181507383?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NhMa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 424w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 848w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128683; Costly Mistakes Developers Still Make</h3><p>&#10060; Treating encoding as security<br>&#10060; Encrypting passwords<br>&#10060; Logging decrypted sensitive data<br>&#10060; Sharing <code>APP_KEY</code> across environments<br>&#10060; Skipping key rotation policies</p><div><hr></div><h3>&#9989; Laravel Security Best Practices</h3><p>&#10004; Hash all passwords with <code>Hash::make()</code><br>&#10004; Encrypt sensitive data with <code>Crypt</code><br>&#10004; Protect your <code>APP_KEY</code> like production credentials<br>&#10004; Restrict access to decrypted data<br>&#10004; Rotate keys if compromised</p><div><hr></div><h3>&#129504; Final Thoughts: Choose Wisely or Pay Later</h3><p>These three concepts are <strong>not interchangeable</strong> &#8212; and confusing them is expensive.</p><ul><li><p>&#128272; <strong>Encryption</strong> &#8594; for data you must retrieve</p></li><li><p>&#128273; <strong>Hashing</strong> &#8594; for secrets you must never see again</p></li><li><p>&#128260; <strong>Encoding</strong> &#8594; for data movement, not protection</p></li></ul><p>If you&#8217;re building <strong>fintech, healthcare, SaaS, or multi-tenant systems</strong>, this knowledge isn&#8217;t optional &#8212; it&#8217;s foundational.</p><p><strong>Secure code is not about more tools.<br>It&#8217;s about using the right one.</strong></p><p>Happy coding &#8212; and stay secure &#128272;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🔒Mastering Encryption in Laravel: A Complete Guide with Real-World Examples]]></title><description><![CDATA[A Real-World Guide to Securely Storing Sensitive Data (Like Client Bank Info) in Laravel]]></description><link>https://newsletter.priteshbhanushali.com/p/mastering-encryption-in-laravel-a</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/mastering-encryption-in-laravel-a</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 13 Dec 2025 09:30:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!QIYY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today&#8217;s digital world, data is one of the most valuable assets &#8212; and also one of the most vulnerable. Whether you&#8217;re building a SaaS platform, a healthcare portal, or a financial app, protecting sensitive information is <strong>not optional</strong>. Laravel makes this easier with its built-in, developer-friendly <strong>encryption system</strong>.</p><p>This guide will help you understand:</p><ul><li><p><strong>What</strong> encryption is</p></li><li><p><strong>How</strong> Laravel handles encryption</p></li><li><p><strong>When</strong> you should use it</p></li><li><p><strong>Best practices</strong> every developer must follow</p></li><li><p><strong>Real-world use cases</strong> to apply in your projects</p></li></ul><p>Let&#8217;s dive in! &#129504;</p><div><hr></div><h2>&#129513; <strong>1. What Is Encryption?</strong></h2><p><strong>Encryption is the process of converting readable data (plaintext) into unreadable data (ciphertext)</strong> using a key.<br>Only someone with the correct key can decrypt the data.</p><h3>Why We Need Encryption?</h3><ul><li><p>To protect sensitive data</p></li><li><p>To comply with security standards</p></li><li><p>To secure database-stored information</p></li><li><p>To prevent identity theft, fraud, or data leaks</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QIYY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QIYY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QIYY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg" width="1200" height="628" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:628,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87583,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/181415377?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438b597b-e083-43cc-8da3-a53e06735453_1200x1200.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QIYY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3>Laravel&#8217;s Encryption</h3><p>Laravel uses <strong>OpenSSL</strong> and the industry-standard <strong>AES-256-CBC</strong> or <strong>AES-128-CBC</strong> encryption.</p><p>It is:</p><ul><li><p>Secure</p></li><li><p>Fast</p></li><li><p>Easy to use</p></li><li><p>Opinionated (removes complexity)</p></li></ul><div><hr></div><h2>&#9881;&#65039; <strong>2. How Laravel Encryption Works</strong></h2><p>Laravel uses a secret key stored in <code>.env</code> &#8594; <code>APP_KEY</code>.</p><pre><code>APP_KEY=base64:JQ1XC77tVRa1Tt56Zl................</code></pre><p>This key is used to encrypt/decrypt values.</p><h3>&#10004;&#65039; <strong>Encrypting Data</strong></h3><pre><code>use Illuminate\Support\Facades\Crypt;

$encrypted = Crypt::encryptString(&#8217;Hello Pritesh&#8217;);</code></pre><h3>&#10004;&#65039; <strong>Decrypting Data</strong></h3><pre><code>use Illuminate\Support\Facades\Crypt;

$decrypted = Crypt::decryptString($encrypted);</code></pre><div><hr></div><h2>&#128338; <strong>3. When Should You Use Encryption?</strong></h2><p>You should NOT encrypt everything.</p><p>Use encryption when storing or transmitting <strong>highly sensitive information</strong> such as:</p><h3>&#10004;&#65039; Personally identifiable information (PII)</h3><ul><li><p>Aadhaar / SSN</p></li><li><p>Phone numbers</p></li><li><p>Government IDs</p></li><li><p>Passport numbers</p></li></ul><h3>&#10004;&#65039; Financial data</h3><ul><li><p>Bank account details</p></li><li><p>Credit card numbers</p></li><li><p>Transaction tokens</p></li></ul><h3>&#10004;&#65039; Authentication-related data</h3><ul><li><p>API keys</p></li><li><p>Access tokens</p></li><li><p>Refresh tokens</p></li><li><p>OAuth secrets</p></li></ul><h3>&#10004;&#65039; Business confidential data</h3><ul><li><p>Internal notes</p></li><li><p>Private documents</p></li><li><p>Signed agreements</p></li></ul><h3>&#10060; When NOT to Use Encryption</h3><ul><li><p>Fields used for search or filtering (<code>LIKE</code>, <code>WHERE</code>)</p></li><li><p>Sorting fields</p></li><li><p>Non-sensitive fields</p></li></ul><p>Because encrypted data is unreadable, <strong>database-level queries become impossible</strong>.</p><div><hr></div><h2>&#11088; <strong>4. Best Practices for Using Encryption in Laravel</strong></h2><h3>&#9989; <strong>1. Always protect your APP_KEY</strong></h3><p>Never share your <code>.env</code> file<br>Never commit it to GitHub<br>Rotate keys during breaches<br>Use server-level secrets management (AWS/GCP/Vault)</p><h3>&#9989; <strong>2. Use model encryption casts</strong></h3><p>Cleaner, automatically applied, and standardized.</p><pre><code><code>protected $casts = [
   &#8216;pan_number&#8217; =&gt; &#8216;encrypted&#8217;,
];
</code></code></pre><h3>&#9989; <strong>3. Encrypt only what is necessary</strong></h3><p>Avoid performance overhead and indexing problems.</p><h3>&#9989; <strong>4. Store hash values for searchable encrypted fields</strong></h3><p>Example: store hashed email for lookup.</p><pre><code>email_encrypted
email_hash (for searching)</code></pre><h3>&#9989; <strong>5. Use Queue Encryption for Queued Jobs</strong></h3><p>Never put raw sensitive data in queues.</p><pre><code>$job-&gt;data = Crypt::encrypt($sensitive);</code></pre><h3>&#9989; <strong>6. Secure backups</strong></h3><p>Encrypted data must remain encrypted even in backups.</p><h3>&#9989; <strong>7. Don&#8217;t log sensitive information</strong></h3><p>Disable logging for encrypted sensitive data.</p><div><hr></div><h2>&#127974; <strong>Real-World Scenario: Storing Client Bank Details (EFT)</strong></h2><p>Imagine you&#8217;re building an insurance or loan management system where clients can set up their bank account for EFT payouts.</p><p>You want to store:</p><ul><li><p>Account holder name</p></li><li><p>Bank name</p></li><li><p>Account number (Sensitive &#128680;)</p></li><li><p>Routing number (Sensitive &#128680;)</p></li><li><p>Bank identifiers (IFSC / SWIFT / Branch code)</p></li></ul><p>This must be safe.</p><p>Let&#8217;s build it the secure way.</p><h3>&#129513; <strong>Step 1: Create the Migration</strong></h3><p>Here&#8217;s the migration you&#8217;ll use:</p><pre><code>Schema::create(&#8217;client_bank_details&#8217;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;unsignedBigInteger(&#8217;client_id&#8217;);

    $table-&gt;string(&#8217;account_holder_name&#8217;);
    $table-&gt;string(&#8217;bank_name&#8217;);

    // Sensitive fields - stored encrypted
    $table-&gt;text(&#8217;account_number&#8217;);
    $table-&gt;text(&#8217;routing_number&#8217;);

    $table-&gt;enum(&#8217;account_type&#8217;, [&#8217;checking&#8217;, &#8216;savings&#8217;, &#8216;business&#8217;]);
    $table-&gt;boolean(&#8217;is_primary&#8217;)-&gt;default(false);
    $table-&gt;boolean(&#8217;is_verified&#8217;)-&gt;default(false);
    $table-&gt;enum(&#8217;status&#8217;, [&#8217;active&#8217;, &#8216;inactive&#8217;])-&gt;default(&#8217;active&#8217;);

    $table-&gt;string(&#8217;branch_code&#8217;)-&gt;nullable();
    $table-&gt;string(&#8217;swift_code&#8217;)-&gt;nullable();
    $table-&gt;string(&#8217;country&#8217;)-&gt;nullable();
    $table-&gt;string(&#8217;currency&#8217;)-&gt;nullable();

    $table-&gt;json(&#8217;metadata&#8217;)-&gt;nullable();

    $table-&gt;timestamps();
});</code></pre><h3>&#128737;&#65039; <strong>Step 2: Apply Laravel Encryption Using Model Casts</strong></h3><p>This is the secret sauce.</p><p>Laravel provides a beautiful, painless way to automatically encrypt/decrypt model attributes.</p><p>Just add:</p><pre><code>protected $casts = [
    &#8216;account_number&#8217; =&gt; &#8216;encrypted&#8217;,
    &#8216;routing_number&#8217; =&gt; &#8216;encrypted&#8217;,
];</code></pre><p>Your model becomes:</p><pre><code>class ClientBankDetail extends Model
{
    protected $fillable = [
        &#8216;client_id&#8217;,
        &#8216;account_holder_name&#8217;,
        &#8216;bank_name&#8217;,
        &#8216;account_number&#8217;,
        &#8216;routing_number&#8217;,
        &#8216;account_type&#8217;,
        &#8216;is_primary&#8217;,
        &#8216;is_verified&#8217;,
        &#8216;status&#8217;,
        &#8216;branch_code&#8217;,
        &#8216;swift_code&#8217;,
        &#8216;country&#8217;,
        &#8216;currency&#8217;,
        &#8216;metadata&#8217;,
    ];

    protected $casts = [
        &#8216;account_number&#8217; =&gt; &#8216;encrypted&#8217;,
        &#8216;routing_number&#8217; =&gt; &#8216;encrypted&#8217;,
        &#8216;metadata&#8217; =&gt; &#8216;array&#8217;,
        &#8216;is_primary&#8217; =&gt; &#8216;boolean&#8217;,
        &#8216;is_verified&#8217; =&gt; &#8216;boolean&#8217;,
    ];
}</code></pre><h3>&#128640; <strong>Step 3: Storing Encrypted Bank Information</strong></h3><pre><code>ClientBankDetail::create([
    &#8216;client_id&#8217; =&gt; 101,
    &#8216;account_holder_name&#8217; =&gt; &#8216;John Doe&#8217;,
    &#8216;bank_name&#8217; =&gt; &#8216;Wells Fargo&#8217;,
    &#8216;account_number&#8217; =&gt; &#8216;9876543210&#8217;,
    &#8216;routing_number&#8217; =&gt; &#8216;021000021&#8217;,
    &#8216;account_type&#8217; =&gt; &#8216;checking&#8217;,
    &#8216;country&#8217; =&gt; &#8216;USA&#8217;,
    &#8216;currency&#8217; =&gt; &#8216;USD&#8217;
]);</code></pre><p>Laravel will:</p><ul><li><p>Encrypt the data before storing it</p></li><li><p>Decrypt it when you retrieve it</p></li><li><p>Protect it using application key (<code>APP_KEY</code>)</p></li></ul><p>All automatically.</p><h3>&#128269; <strong>Step 4: Reading Decrypted Values</strong></h3><pre><code>$bank = ClientBankDetail::find(1);
echo $bank-&gt;account_number;</code></pre><p>You get a clean value like:</p><pre><code>9876543210
</code></pre><p>But if you open the database, you see unreadable encrypted content &#8212; <strong>exactly what we want</strong>.</p><div><hr></div><h2>&#128220; Compliance &amp; Legal Benefits</h2><p>Encryption helps meet:</p><ul><li><p><strong>PCI-DSS</strong></p></li><li><p><strong>GDPR</strong></p></li><li><p><strong>SOC 2</strong></p></li></ul><p>Encryption &#8800; compliance<br>But <strong>compliance is impossible without encryption</strong>.</p><div><hr></div><h2>&#128204; Conclusion</h2><p>Laravel makes encryption incredibly simple without compromising security.</p><p>If you&#8217;re dealing with sensitive data, encryption is non-negotiable.</p><p>This blog covered:</p><p>&#10004;&#65039; What encryption is</p><p>&#10004;&#65039; How Laravel handles it</p><p>&#10004;&#65039; When to use it</p><p>&#10004;&#65039; Best practices</p><p>&#10004;&#65039; Real-world examples</p><p>&#10004;&#65039; Practical code snippets</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[🗄️Database Migrations in Laravel: The Secret Weapon for Team Productivity]]></title><description><![CDATA[How migrations prevent conflicts, streamline deployments, and boost collaboration.]]></description><link>https://newsletter.priteshbhanushali.com/p/database-migrations-in-laravel-the</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/database-migrations-in-laravel-the</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 23 Nov 2025 09:30:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!shFt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you&#8217;re building modern applications, you know that <strong>databases evolve constantly</strong> tables change, new columns get added, constraints get updated, and entire structures may require rethinking.<br>Managing this manually becomes messy, error-prone, and almost impossible when a team is involved.</p><p>This is where <strong>Database Migrations</strong> become a game-changer.</p><p>Laravel provides one of the most elegant migration systems in the industry, turning database changes into <strong>version-controlled, repeatable, trackable code</strong>.</p><p>In this article, you&#8217;ll learn:</p><ul><li><p>What a database migration is</p></li><li><p>How migrations work in Laravel</p></li><li><p>Advantages &amp; disadvantages</p></li><li><p>Best practices (with real examples)</p></li><li><p>Why migrations are essential for long-term project health</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!shFt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!shFt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!shFt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!shFt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2155196,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/179696166?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!shFt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!shFt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128204; What Is a Database Migration?</h2><p>A <strong>Database Migration</strong> is simply a <strong>version control system for your database schema</strong>.</p><p>Just like Git tracks code changes, migrations track <strong>database structure changes</strong> such as:</p><ul><li><p>Creating tables</p></li><li><p>Adding or removing columns</p></li><li><p>Adding indexes or foreign keys</p></li><li><p>Changing data types</p></li><li><p>Renaming tables</p></li></ul><p>Each migration is a small PHP file that modifies the database in a controlled and reversible way.</p><div><hr></div><h2>&#128193; What Does a Migration File Look Like?</h2><p>When you run:</p><pre><code>php artisan make:migration create_users_table</code></pre><p>Laravel generates a file like this:</p><pre><code>&lt;?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create(&#8217;users&#8217;, function (Blueprint $table) {
            $table-&gt;id();
            $table-&gt;string(&#8217;name&#8217;);
            $table-&gt;string(&#8217;email&#8217;)-&gt;unique();
            $table-&gt;string(&#8217;password&#8217;);
            $table-&gt;boolean(&#8217;is_active&#8217;)-&gt;default(true);
            $table-&gt;timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists(&#8217;users&#8217;);
    }
};</code></pre><h3>&#128998; <code>up()</code></h3><p>Describes <strong>what to do</strong> &#8212; create/modify tables.</p><h3>&#128997; <code>down()</code></h3><p>Describes <strong>how to roll back</strong> the changes.</p><p>Simple. Clean. Powerful.</p><div><hr></div><h2>&#9881;&#65039; How Do Migrations Work in Laravel?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!djl3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!djl3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 424w, https://substackcdn.com/image/fetch/$s_!djl3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 848w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1272w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!djl3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png" width="1092" height="614" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:614,&quot;width&quot;:1092,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89130,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/179696166?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!djl3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 424w, https://substackcdn.com/image/fetch/$s_!djl3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 848w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1272w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Laravel stores the history of executed migrations in a special table called <code>migrations</code>.</p><p>When you run:</p><pre><code>php artisan migrate</code></pre><p>Laravel:</p><ol><li><p>Reads all migration files</p></li><li><p>Checks which ones are <strong>not executed yet</strong></p></li><li><p>Runs them in order</p></li><li><p>Records them in the <code>migrations</code> table</p></li></ol><p>When you run:</p><pre><code><code>php artisan migrate:rollback</code></code></pre><p>Laravel executes the <code>down()</code> method of recent migrations.</p><p>This makes migrations <strong>safe, reversible, and manageable</strong> across environments like:</p><ul><li><p>local</p></li><li><p>staging</p></li><li><p>production</p></li></ul><div><hr></div><h2>Create Migration with Table:</h2><p>You can create a new migration by defining the table name.</p><pre><code>php artisan make:migration add_columns_to_orders_table --table=orders</code></pre><h5>&#9989; <strong>Generated Migration File</strong></h5><h5>File Name (Laravel will create something like)</h5><p><code>2025_11_23_000000_add_columns_to_orders_table.php</code><br>(The timestamp will match the moment you run the command.)</p><h5><strong>Full Migration File Content</strong></h5><pre><code>&lt;?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table(&#8217;orders&#8217;, function (Blueprint $table) {
            //
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table(&#8217;orders&#8217;, function (Blueprint $table) {
            //
        });
    }
};</code></pre><h5>&#128221; <strong>What this means</strong></h5><ul><li><p>Laravel <strong>will NOT guess</strong> which columns you want to add.</p></li><li><p>It simply creates an <strong>empty Schema::table(&#8217;orders&#8217;)</strong> migration.</p></li><li><p>You need to fill in the columns manually.</p></li></ul><p>Example (you will add this yourself):</p><pre><code>$table-&gt;string(&#8217;status&#8217;)-&gt;default(&#8217;pending&#8217;);
$table-&gt;date(&#8217;delivery_date&#8217;)-&gt;nullable();</code></pre><div><hr></div><h2>&#9989; Advantages of Using Migrations</h2><h4>1. <strong>Version Control for Your Database</strong></h4><p>Every schema change is stored as code.<br>No more &#8220;Did you update your DB?&#8221; chaos.</p><h4>2. <strong>Team Collaboration Becomes Easy</strong></h4><p>Developers simply pull the latest code and run:</p><pre><code><code>php artisan migrate</code></code></pre><p>Boom &#8212; database synced.</p><h4>3. <strong>Automated Deployment</strong></h4><p>CI/CD pipelines can apply migrations automatically.</p><h4>4. <strong>Rollback Support</strong></h4><p>Accidentally deployed a breaking change?<br>Rollback with one command.</p><h4>5. <strong>No More Manual SQL</strong></h4><p>You define schema changes in PHP, not raw SQL.</p><h4>6. <strong>Infrastructure Consistency</strong></h4><p>Same database structure across all environments.</p><div><hr></div><h2>&#10060; Disadvantages (Yes, They Exist)</h2><h4>1. <strong>Bad Migrations Can Break Production</strong></h4><p>If not tested properly, migrations can cause downtime.</p><h4>2. <strong>Large Tables Can Lock During Alter Operations</strong></h4><p>Adding indexes or columns to big tables may cause slowdowns.</p><h4>3. <strong>Migrations Require Discipline</strong></h4><p>Everyone in the team needs to follow the migration workflow.</p><h4>4. <strong>Rollback May Fail for Complex Logic</strong></h4><p>Some operations (like data migrations) aren&#8217;t perfectly reversible.</p><div><hr></div><h2>.&#129504; Best Practices for Laravel Migrations</h2><h4>&#10004;&#65039; 1. <strong>Never Edit a Migration That Has Already Been Run</strong></h4><p>Wrong &#10060;<br>Editing old migrations breaks other developers.</p><p>Right &#10004;&#65039;<br>Create a new migration:</p><pre><code><code>php artisan make:migration add_status_to_users_table</code></code></pre><h4>&#10004;&#65039; 2. <strong>Use Meaningful Migration Names</strong></h4><p>Good:</p><pre><code><code>2025_11_20_101010_add_status_column_to_users_table.php</code></code></pre><p>Bad:</p><pre><code><code>xyz_migration.php</code></code></pre><h4>&#10004;&#65039; 3. <strong>Keep Migrations Small and Focused</strong></h4><p>One responsibility per migration.</p><h4>&#10004;&#65039; 4. <strong>Always Provide a Rollback Method</strong></h4><p>A <code>down()</code> method should undo changes cleanly.</p><h4>&#10004;&#65039; 5. <strong>Use Database Indexes Wisely</strong></h4><p>Example:</p><pre><code><code>$table-&gt;index(&#8217;email&#8217;);</code></code></pre><p>They improve performance significantly.</p><h4>&#10004;&#65039; 6. <strong>Use </strong><code>php artisan migrate:fresh</code><strong> Only in Local Development</strong></h4><p>Never run it on staging/production &#8212; it will drop all tables.</p><h4>&#10004;&#65039; 7. <strong>Run Migrations in Transactions (If Supported)</strong></h4><p>Laravel can wrap migrations in a transaction:</p><pre><code><code>public $withinTransaction = true;</code></code></pre><p>This keeps migrations atomic.</p><div><hr></div><h2>&#127919; Benefits of Using Migrations in Real Projects</h2><ul><li><p>Faster onboarding of new developers</p></li><li><p>Zero manual DB setup</p></li><li><p>Consistent schema across all servers</p></li><li><p>Easy recovery in case of issues</p></li><li><p>Better CI/CD automation</p></li><li><p>Database and code stay in sync</p></li><li><p>Change history is preserved forever</p></li></ul><div><hr></div><h2>&#128293; Conclusion</h2><p>Database migrations are one of the most powerful features in Laravel. They turn database management into a <strong>predictable, trackable, and reversible</strong> process.</p><p>Whether you&#8217;re working solo or on a large team, migrations help ensure your application remains stable, scalable, and easier to maintain.</p><p>If you&#8217;re still editing databases manually &#8212; it&#8217;s time to level up.<br>Let your database evolve <strong>with confidence</strong> using Laravel migrations. &#128640;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 How One Laravel Mistake Can Trigger 300 Queries (and How to Fix It)]]></title><description><![CDATA[The Hidden Laravel Performance Bug: Understanding the N+1 Problem]]></description><link>https://newsletter.priteshbhanushali.com/p/how-one-laravel-mistake-can-trigger</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/how-one-laravel-mistake-can-trigger</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 16 Nov 2025 12:30:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!auPw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you work with databases and applications, you may have heard of the <strong>N+1 problem</strong>. It&#8217;s a common performance issue that happens when an application makes too many database queries instead of fetching data efficiently. This can slow down your app and make it harder to scale.</p><h2><strong>&#128269;What Is the N+1 Problem?</strong></h2><p>Imagine you have a list of <strong>blog posts</strong>, and each post has multiple <strong>comments</strong>. This is a classic one-to-many relationship because <strong>one post can have many comments</strong>.</p><p>Now, let&#8217;s say you need to fetch <strong>all posts along with their comments</strong>.<br>If your application isn&#8217;t optimized, it might do something like this:</p><h4><strong>1. First, fetch all posts:</strong></h4><pre><code>$posts = Post::all();</code></pre><h4>2. Then, for each post, fetch its comments one by one:</h4><pre><code>foreach ($posts as $post) {
    // Query for each post: SELECT * FROM comments WHERE post_id = ?
    $comments = Comment::where(&#8217;post_id&#8217;, $post-&gt;id)-&gt;get();
}</code></pre><p>If you have <strong>100 posts</strong>, this results in <strong>101 queries</strong><br>(1 query for posts + 100 queries for comments).</p><p>That&#8217;s why it&#8217;s called the <strong>N+1 problem</strong> &#8212;<br>you run <strong>N queries (one for each post) + 1 main query</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!auPw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!auPw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 424w, https://substackcdn.com/image/fetch/$s_!auPw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 848w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1272w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!auPw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png" width="808" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:808,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:404140,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/179036656?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!auPw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 424w, https://substackcdn.com/image/fetch/$s_!auPw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 848w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1272w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2>&#9888;&#65039;Why Is This a Problem?</h2><p>The N+1 problem can seriously slow down your application for several reasons:</p><ul><li><p><strong>Too many database queries overload your database server.</strong><br>Fetching each post&#8217;s comments separately forces the database to run the same query again and again.</p></li><li><p><strong>Increased network traffic between your Laravel app and the database.</strong><br>Instead of sending 2 queries (posts + all comments), your app may send 101 queries or more.</p></li><li><p><strong>Slower response times because the application waits for multiple comment queries to finish.</strong><br>Each post triggers its own query, adding delay for every request.</p></li><li><p><strong>Performance gets worse as the dataset grows.</strong><br>For example, if you have <strong>1,000 posts</strong>, Laravel may execute <strong>1,001 queries</strong><br>(1 for posts + 1 for each post&#8217;s comments).</p></li></ul><p>In short, the more posts you have, the slower your page becomes.</p><div><hr></div><h2><strong>&#129517;How to Fix the N+1 Problem</strong></h2><h3><strong>1. Use Joins to Fetch Data in One Query</strong></h3><p>Instead of making a separate query for each post&#8217;s comments, you can fetch everything in a <strong>single database query</strong> using a JOIN.</p><pre><code>SELECT posts.*, comments.*
FROM posts
LEFT JOIN comments ON posts.id = comments.post_id;</code></pre><p>This way, you avoid running 101 queries (1 for posts + 100 for comments).<br>Everything is retrieved in <strong>one efficient request</strong>.</p><p><strong>Laravel Query Builder Equivalent:</strong></p><pre><code>$posts = DB::table(&#8217;posts&#8217;)
    -&gt;leftJoin(&#8217;comments&#8217;, &#8216;posts.id&#8217;, &#8216;=&#8217;, &#8216;comments.post_id&#8217;)
    -&gt;select(&#8217;posts.*&#8217;, &#8216;comments.*&#8217;)
    -&gt;get();</code></pre><h3><strong>2. Batch Queries Instead of Fetching One by One</strong></h3><p>If you prefer not to use a JOIN, another solution is <strong>batch querying</strong>&#8212;fetching all comments for multiple posts in one call.</p><p><strong>SQL Example:</strong></p><pre><code>SELECT * FROM comments WHERE post_id IN (1, 2, 3, 4, 5);</code></pre><p>This allows you to:</p><ul><li><p>First fetch all posts</p></li><li><p>Then fetch all comments for those posts in a single batch query</p></li></ul><p>Instead of 100 separate queries, you now perform:</p><ul><li><p><strong>1 query</strong> &#8594; posts</p></li><li><p><strong>1 query</strong> &#8594; all their comments</p></li></ul><p>Total = <strong>2 queries</strong> instead of 101.</p><p><strong>Laravel Eloquent Equivalent:</strong></p><pre><code>$posts = Post::all(); // Query 1

$comments = Comment::whereIn(&#8217;post_id&#8217;, $posts-&gt;pluck(&#8217;id&#8217;))-&gt;get(); // Query 2</code></pre><h3>&#9989; The Laravel fix: Eager loading (<code>with()</code>)</h3><p>Laravel&#8217;s built-in solution is to <em>eager-load</em> relations before looping:</p><pre><code><code>// Controller (fixed)
$posts = Post::with(&#8217;comments&#8217;)-&gt;get(); // Query #1 (posts) + Query #2 (comments for all posts)

// Now in the view, no extra queries:
@foreach($posts as $post)
  @foreach($post-&gt;comments as $comment)
    {{ $comment-&gt;body }}
  @endforeach
@endforeach
</code></code></pre><p>Result: 2 queries total no matter how many posts.</p><div><hr></div><h2>&#128270; Detect N+1 in Laravel</h2><ul><li><p><strong>Laravel Debugbar</strong> (dev): shows each query and count.</p></li><li><p><strong>Laravel Telescope</strong>: records queries for requests.</p></li><li><p><strong>DB::listen()</strong>: log queries in tests or a temporary debug route.</p></li><li><p>Add assertions in tests to monitor query count (use sparingly).</p></li></ul><p>Quick example using <code>DB::listen</code>:</p><pre><code>DB::listen(function ($query) {
    logger($query-&gt;sql, $query-&gt;bindings);
});</code></pre><h2>&#128736; Handling large datasets</h2><p>Eager-loading everything for massive datasets can blow memory. Use chunking or cursors:</p><pre><code>Post::chunkById(100, function ($posts) {
    $posts-&gt;load(&#8217;comments&#8217;); // eager-load per chunk
    foreach ($posts as $post) {
        // process
    }
});</code></pre><p><code>cursor()</code> streams models but you can only eager-load per batch (collect ids, then load in batches).</p><h2>&#128064; Common gotchas &amp; pitfalls</h2><p>If you <code>select()</code> custom columns on a relation, include the foreign key so Eloquent can map relations:</p><pre><code>$posts = Post::with([&#8217;comments&#8217; =&gt; function ($q) {
    $q-&gt;select(&#8217;id&#8217;, &#8216;post_id&#8217;, &#8216;body&#8217;, &#8216;created_at&#8217;);
}])-&gt;select(&#8217;id&#8217;, &#8216;title&#8217;)-&gt;get();</code></pre><ul><li><p><code>limit()</code> inside eager load is not the same as &#8220;limit per parent&#8221; in every DB. Use <code>latestOfMany()</code> or subquery patterns for latest-N-per-parent.</p></li><li><p>Polymorphic relations require careful eager-loading; Laravel groups by type and runs efficient queries, but always test.</p></li><li><p>Over-eager-loading deep relations (<code>with(&#8217;a.b.c.d&#8217;)</code>) can increase memory and do too much work &#8212; load only what&#8217;s required.</p></li></ul><div><hr></div><h2>&#9989; Best practices checklist (senior devs use this)</h2><p>Before merging a PR, ask:</p><ul><li><p>Am I accessing a relation inside a loop? If yes, ensure that relation is eager-loaded.</p></li><li><p>Do I only need aggregates (count, sum)? Use <code>withCount()</code>, <code>withSum()</code>.</p></li><li><p>Am I selecting only the columns needed for the view/API? (Use <code>select()</code>).</p></li><li><p>Is this endpoint high-traffic? Consider caching or denormalized read models.</p></li><li><p>For large datasets, did I use chunking or streaming?</p></li><li><p>Did I profile queries with Debugbar or Telescope?</p></li><li><p>Are there joins/subqueries that could replace heavy eager-loading?</p></li></ul><div><hr></div><h2>&#127937; Final thoughts</h2><p>The N+1 problem is simple to understand and easy to fix once you know where to look. In Laravel, <code>with()</code>, <code>withCount()</code>, constrained eager loading, and smart use of joins/subqueries give you a powerful toolbox to fetch data efficiently.</p><p>If you:</p><ul><li><p>identify relations used in views,</p></li><li><p>eager-load what&#8217;s required,</p></li><li><p>prefer aggregates when possible,</p></li><li><p>and profile query counts regularly,</p></li></ul><p>you&#8217;ll keep your app fast and scalable as data grows.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🧠Understanding ACID Properties in Databases (With Laravel + MySQL Examples)]]></title><description><![CDATA[How to build reliable, consistent, and safe database operations in modern web applications]]></description><link>https://newsletter.priteshbhanushali.com/p/understanding-acid-properties-in</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/understanding-acid-properties-in</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Thu, 13 Nov 2025 04:30:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Y90z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>&#128161; Introduction: Why ACID Matters</h2><p>Every modern web application from eCommerce platforms to financial systems relies on <strong>data integrity</strong>.<br>When users transfer money, create orders, or update profiles, they expect one thing: <strong>the data should be accurate, consistent, and safe</strong>, no matter what happens in the backend.</p><p>This is where <strong>ACID properties</strong> come in.</p><p>In relational databases like <strong>MySQL</strong>, ACID ensures that each database transaction behaves predictably, even in the face of power failures, concurrency issues, or application crashes.</p><p>Laravel, with its expressive Eloquent ORM and database abstractions, gives developers easy access to these principles without needing to write low-level SQL.</p><div><hr></div><h2>&#9881;&#65039; What Does ACID Stand For?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y90z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y90z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y90z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png" width="1024" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2466999,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/178703485?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Y90z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>ACID is an acronym for four key properties that define reliable database transactions:</p><p><strong>A &#8212; Atomicity</strong>              A transaction is all or nothing. Either all operations succeed, or none do. </p><p><strong>C &#8212; Consistency</strong>          The database must remain in a valid state before and after a transaction. </p><p><strong>I &#8212; Isolation</strong>                 Concurrent transactions should not interfere with each other. <strong>D &#8212; Durability</strong>             Once committed, data remains safe even after failures.</p><p>Let&#8217;s explore each property in detail with <strong>Laravel and MySQL examples</strong>.</p><div><hr></div><h3>&#128313; 1. Atomicity &#8212; All or Nothing</h3><p><strong>Atomicity</strong> ensures that either <em>all steps of a transaction succeed</em> or <em>none do</em>.</p><h4>&#128172; Example: Transferring Money Between Accounts</h4><p>Imagine a banking system where we transfer &#8377;100 from <strong>Account A</strong> to <strong>Account B</strong>.<br>We must <strong>decrease</strong> the balance of A and <strong>increase</strong> the balance of B in a single atomic transaction.</p><p>If the system crashes after debiting A but before crediting B &#8212; the transaction must <strong>roll back</strong>.</p><h5>&#9989; Laravel Example</h5><pre><code>use Illuminate\Support\Facades\DB;

try {
    DB::beginTransaction();

    // Debit from Account A
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 1)-&gt;decrement(&#8217;balance&#8217;, 100);

    // Credit to Account B
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 2)-&gt;increment(&#8217;balance&#8217;, 100);

    DB::commit();
} catch (\Exception $e) {
    DB::rollBack();
    Log::error(&#8217;Transaction failed: &#8216; . $e-&gt;getMessage());
}</code></pre><p>Or more elegantly:</p><pre><code>DB::transaction(function () {
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 1)-&gt;decrement(&#8217;balance&#8217;, 100);
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 2)-&gt;increment(&#8217;balance&#8217;, 100);
});</code></pre><p>&#128161; <strong>Best Practice:</strong><br>Use <code>DB::transaction()</code> whenever you perform multiple related writes that must succeed or fail together.</p><div><hr></div><h3>&#128313; 2. Consistency &#8212; Maintaining Data Integrity</h3><p><strong>Consistency</strong> ensures that the database transitions from one valid state to another.</p><p>This is often maintained using <strong>constraints</strong>, <strong>foreign keys</strong>, <strong>validation</strong>, and <strong>business logic</strong>.</p><h4>&#128172; Example: Creating an Order and Order Items</h4><p>If you create an order but fail to insert the related order items, your database becomes inconsistent.</p><h5>&#9989; Laravel Example</h5><pre><code>DB::transaction(function () {
    $order = Order::create([
        &#8216;user_id&#8217; =&gt; 1,
        &#8216;total&#8217; =&gt; 500,
    ]);

    OrderItem::create([
        &#8216;order_id&#8217; =&gt; $order-&gt;id,
        &#8216;product_id&#8217; =&gt; 42,
        &#8216;quantity&#8217; =&gt; 2,
        &#8216;price&#8217; =&gt; 250,
    ]);
});</code></pre><h4>&#128161; Database-Level Consistency</h4><p>At the schema level, we ensure consistency with <strong>foreign key constraints</strong> and <strong>unique indexes</strong>:</p><pre><code>Schema::create(&#8217;order_items&#8217;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;foreignId(&#8217;order_id&#8217;)-&gt;constrained()-&gt;onDelete(&#8217;cascade&#8217;);
    $table-&gt;foreignId(&#8217;product_id&#8217;)-&gt;constrained();
    $table-&gt;integer(&#8217;quantity&#8217;);
    $table-&gt;decimal(&#8217;price&#8217;, 10, 2);
});</code></pre><p>If someone tries to insert an order item with an invalid <code>order_id</code>, MySQL will reject it maintaining consistency.</p><div><hr></div><h3>&#128313; 3. Isolation &#8212; Safe Concurrent Transactions</h3><p><strong>Isolation</strong> means that concurrent transactions don&#8217;t affect each other&#8217;s results.</p><p>MySQL supports multiple <strong>isolation levels</strong>:</p><p><strong>READ UNCOMMITTED               </strong>Can read uncommitted data (dirty reads).</p><p><strong>READ COMMITTED                      </strong>Reads only committed data.</p><p><strong>REPEATABLE READ</strong> <em>(default in MySQL)     </em>Same result for repeated reads within a transaction.</p><p><strong>SERIALIZABLE                            </strong>Highest isolation; transactions run one after another.</p><h4>&#128172; Example: Preventing Double Spending</h4><p>Two users try to withdraw from the same account simultaneously.<br>Isolation ensures that one transaction waits until the other completes.</p><h5>&#9989; Laravel Example</h5><pre><code>DB::transaction(function () {
    $account = DB::table(&#8217;accounts&#8217;)
        -&gt;where(&#8217;id&#8217;, 1)
        -&gt;lockForUpdate()
        -&gt;first();

    if ($account-&gt;balance &gt;= 100) {
        DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 1)-&gt;decrement(&#8217;balance&#8217;, 100);
    } else {
        throw new \Exception(&#8217;Insufficient balance.&#8217;);
    }
});</code></pre><p><code>lockForUpdate()</code> prevents other transactions from reading or updating the same row until this one finishes  ensuring <strong>isolation</strong>.</p><div><hr></div><h3>&#128313; 4. Durability &#8212; Data That Stays</h3><p><strong>Durability</strong> ensures that once a transaction is committed, it will remain saved even if a power failure or crash occurs.</p><p>In MySQL&#8217;s <strong>InnoDB engine</strong>, durability is maintained using:</p><ul><li><p><strong>Redo logs (write-ahead logs)</strong></p></li><li><p><strong>Automatic crash recovery</strong></p></li><li><p><strong>fsync() writes to disk after commit</strong></p></li></ul><h4>&#128172; Laravel&#8217;s Role</h4><p>Laravel delegates durability to MySQL &#8212; but you can ensure safer commits by:</p><ul><li><p>Using <code>DB::transaction()</code> to avoid half-committed writes</p></li><li><p>Configuring <strong>InnoDB</strong> as the storage engine</p></li><li><p>Setting proper <code>innodb_flush_log_at_trx_commit</code> (default <code>1</code> ensures maximum durability)</p></li></ul><div><hr></div><h3>&#129513; Putting It All Together: A Real-World Scenario</h3><h4>Scenario: Order Creation with Payment Logging</h4><pre><code>DB::transaction(function () use ($request) {
    $order = Order::create([
        &#8216;user_id&#8217; =&gt; $request-&gt;user_id,
        &#8216;total&#8217; =&gt; $request-&gt;total,
    ]);

    foreach ($request-&gt;items as $item) {
        OrderItem::create([
            &#8216;order_id&#8217; =&gt; $order-&gt;id,
            &#8216;product_id&#8217; =&gt; $item[&#8217;product_id&#8217;],
            &#8216;quantity&#8217; =&gt; $item[&#8217;qty&#8217;],
            &#8216;price&#8217; =&gt; $item[&#8217;price&#8217;],
        ]);
    }

    Payment::create([
        &#8216;order_id&#8217; =&gt; $order-&gt;id,
        &#8216;amount&#8217; =&gt; $order-&gt;total,
        &#8216;status&#8217; =&gt; &#8216;SUCCESS&#8217;,
    ]);
});</code></pre><p>If any insert fails, Laravel automatically rolls back ensuring <strong>Atomicity</strong>, <strong>Consistency</strong>, and <strong>Durability</strong>.<br>MySQL&#8217;s <strong>transaction isolation</strong> ensures that other users see consistent data while this operation is in progress.</p><div><hr></div><h3>&#129517; Best Practices for Handling Transactions in Laravel</h3><ol><li><p>&#9989; <strong>Use </strong><code>DB::transaction()</code> for multi-step operations.</p></li><li><p>&#128257; <strong>Handle Exceptions Gracefully</strong> - rollback on failure and log errors.</p></li><li><p>&#129521; <strong>Leverage Database Constraints</strong> (foreign keys, unique keys).</p></li><li><p>&#9881;&#65039; <strong>Tune Isolation Levels</strong> - use <code>REPEATABLE READ</code> for safety, or <code>READ COMMITTED</code> for performance.</p></li><li><p>&#129513; <strong>Keep Transactions Short</strong> - long-running transactions can cause locks and deadlocks.</p></li><li><p>&#128190; <strong>Always Use InnoDB</strong> - for full ACID compliance.</p></li></ol><div><hr></div><h3>&#129534; Summary &amp; Key Takeaways</h3><ul><li><p><strong>Atomicity</strong> ensures operations succeed together.</p></li><li><p><strong>Consistency</strong> keeps your data valid and rules enforced.</p></li><li><p><strong>Isolation</strong> prevents race conditions in concurrent access.</p></li><li><p><strong>Durability</strong> guarantees data safety after commits.</p></li></ul><p>When combined, these principles make your <strong>Laravel + MySQL applications reliable, predictable, and production-safe</strong>.</p><p>Building with ACID in mind means your users can trust your app - every time they hit &#8220;Submit&#8221;.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🧠 Subquery vs CTE vs Temporary Table — The Ultimate Comparison with Practical Examples]]></title><description><![CDATA[Understand When and Why to Use Each for Better Query Performance]]></description><link>https://newsletter.priteshbhanushali.com/p/subquery-vs-cte-vs-temporary-table</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/subquery-vs-cte-vs-temporary-table</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Thu, 06 Nov 2025 04:30:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q1X0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>When writing SQL queries, choosing between a Subquery, CTE, or Temporary Table can make or break your query&#8217;s performance and readability.</strong><br>Though they all help you break complex problems into smaller chunks, each has its own strengths and ideal use cases.</p><p>In this post, we&#8217;ll explore:</p><ul><li><p>What each of them is</p></li><li><p>How they differ</p></li><li><p>Practical SQL examples</p></li><li><p>When to use which one</p></li></ul><div><hr></div><h3>&#128313; 1. What is a Subquery?</h3><p>A <strong>subquery</strong> (or <em>nested query</em>) is a query inside another query.<br>It can appear in the <code>SELECT</code>, <code>FROM</code>, or <code>WHERE</code> clause and is used when you need to use the result of one query inside another.</p><h4>&#129513; Example:</h4><p>Let&#8217;s say you want to fetch all employees who work in the <strong>IT department</strong>.</p><pre><code>SELECT first_name, last_name
FROM employees
WHERE department_id = (
    SELECT department_id
    FROM departments
    WHERE department_name = &#8216;IT&#8217;
);</code></pre><p>Here, the inner query:</p><pre><code>SELECT department_id FROM departments WHERE department_name = &#8216;IT&#8217;;</code></pre><p>runs first, and its result is used by the outer query.</p><h4>&#9989; When to Use:</h4><ul><li><p>For simple filtering or lookups.</p></li><li><p>When you need the result only once.</p></li></ul><h4>&#9888;&#65039; Be Careful:</h4><ul><li><p>Can become <strong>slow</strong> if used inside loops or complex joins.</p></li><li><p>Not ideal for <strong>reusing</strong> results.</p></li></ul><div><hr></div><h3>&#128313; 2. What is a CTE (Common Table Expression)?</h3><p>A <strong>CTE</strong> is a temporary named result set that exists only during the execution of a single SQL statement.<br>Think of it as a <strong>named subquery</strong> that improves readability and maintainability.</p><p>It starts with the <code>WITH</code> keyword.</p><h4>&#129513; Example:</h4><pre><code>WITH IT_Employees AS (
    SELECT employee_id, first_name, last_name, department_id
    FROM employees
    WHERE department_id = (
        SELECT department_id FROM departments WHERE department_name = &#8216;IT&#8217;
    )
)
SELECT first_name, last_name
FROM IT_Employees
WHERE last_name LIKE &#8216;S%&#8217;;</code></pre><p>Here, the CTE <code>IT_Employees</code> holds the intermediate result which we later query again.</p><h4>&#9989; Benefits:</h4><ul><li><p><strong>Readable</strong> and <strong>modular</strong> &#8212; perfect for long, multi-step queries.</p></li><li><p>Can be <strong>recursive</strong>, great for hierarchical data (like org charts).</p></li></ul><h4>&#9888;&#65039; Limitations:</h4><ul><li><p>Exists only within that query.</p></li><li><p>Cannot be indexed or reused across multiple queries.</p></li></ul><div><hr></div><h3>&#128313; 3. What is a Temporary Table?</h3><p>A <strong>Temporary Table</strong> is like a normal table, but it&#8217;s stored temporarily in your database (e.g., in <code>tempdb</code> in SQL Server).<br>You can <strong>create</strong>, <strong>index</strong>, <strong>update</strong>, and <strong>reuse</strong> it across multiple queries within the same session.</p><h4>&#129513; Example:</h4><pre><code>CREATE TEMPORARY TABLE temp_it_employees AS
SELECT employee_id, first_name, last_name, department_id
FROM employees
WHERE department_id = (
    SELECT department_id FROM departments WHERE department_name = &#8216;IT&#8217;
);

SELECT * FROM temp_it_employees WHERE last_name LIKE &#8216;S%&#8217;;</code></pre><p>You can even index it for faster performance:</p><pre><code>CREATE INDEX idx_lastname ON temp_it_employees(last_name);</code></pre><h4>&#9989; Benefits:</h4><ul><li><p>Can be reused multiple times.</p></li><li><p>Supports indexing &#8212; improves performance on large datasets.</p></li><li><p>Ideal for <strong>complex transformations</strong> or <strong>multi-step logic</strong>.</p></li></ul><h4>&#9888;&#65039; Limitations:</h4><ul><li><p>Needs manual creation and cleanup.</p></li><li><p>Slight overhead for disk/memory usage.</p></li></ul><div><hr></div><h3>&#128312; 4. Comparison Summary</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q1X0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q1X0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 424w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 848w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1272w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q1X0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png" width="1042" height="453" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:453,&quot;width&quot;:1042,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41525,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177801281?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q1X0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 424w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 848w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1272w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#129504; 5. Real-World Analogy</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nQ-k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nQ-k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 424w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 848w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1272w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png" width="969" height="255" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:255,&quot;width&quot;:969,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21789,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177801281?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nQ-k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 424w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 848w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1272w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128313; 6. Practical Performance Example</h3><p>Let&#8217;s consider a real-world scenario:</p><blockquote><p>You need to find all employees who worked more than 200 hours in the past 3 months and belong to departments with average salary &gt; 80,000.</p></blockquote><p>You can do it in <strong>three ways</strong>:</p><h4>&#128312; Subquery:</h4><pre><code>SELECT e.employee_id, e.first_name, e.last_name
FROM employees e
WHERE e.department_id IN (
    SELECT department_id FROM departments WHERE avg_salary &gt; 80000
)
AND e.employee_id IN (
    SELECT employee_id FROM work_hours
    WHERE total_hours &gt; 200
);</code></pre><h4>&#128312; CTE:</h4><pre><code>WITH HighSalaryDepartments AS (
    SELECT department_id FROM departments WHERE avg_salary &gt; 80000
),
ActiveEmployees AS (
    SELECT employee_id FROM work_hours WHERE total_hours &gt; 200
)
SELECT e.employee_id, e.first_name, e.last_name
FROM employees e
JOIN HighSalaryDepartments d ON e.department_id = d.department_id
JOIN ActiveEmployees a ON e.employee_id = a.employee_id;</code></pre><h4>&#128312; Temporary Table:</h4><pre><code>CREATE TEMPORARY TABLE high_salary_departments AS
SELECT department_id FROM departments WHERE avg_salary &gt; 80000;

CREATE TEMPORARY TABLE active_employees AS
SELECT employee_id FROM work_hours WHERE total_hours &gt; 200;

SELECT e.employee_id, e.first_name, e.last_name
FROM employees e
JOIN high_salary_departments d ON e.department_id = d.department_id
JOIN active_employees a ON e.employee_id = a.employee_id;</code></pre><p>&#128073; In this example:</p><ul><li><p><strong>Subquery</strong> works fine for small datasets.</p></li><li><p><strong>CTE</strong> is cleaner and easier to maintain.</p></li><li><p><strong>Temporary Table</strong> gives the best performance for large datasets or repeated queries.</p></li></ul><div><hr></div><h3>&#128313; 7. Best Practices</h3><p>&#9989; <strong>Use Subqueries</strong> for quick and simple conditions.<br>&#9989; <strong>Use CTEs</strong> for readable, modular logic and recursive problems.<br>&#9989; <strong>Use Temporary Tables</strong> for large or reusable intermediate data sets.<br>&#9989; Always test performance with <code>EXPLAIN</code> or query plans.<br>&#9989; Clean up temporary tables if not automatically dropped.</p><div><hr></div><h3>&#128161; Final Thoughts</h3><p>Choosing between a <strong>Subquery</strong>, <strong>CTE</strong>, and <strong>Temporary Table</strong> depends on <strong>what you value most</strong> &#8212; readability, performance, or reusability.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cB_7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cB_7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 424w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 848w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1272w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cB_7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png" width="846" height="225" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:225,&quot;width&quot;:846,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9982,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177801281?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cB_7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 424w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 848w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1272w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Remember &#8212; <strong>clean SQL is not just about running faster, but also about being maintainable</strong>.<br>Start small, measure performance, and evolve your query structure as your data grows.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 MySQL UNION vs UNION ALL - Key Differences and When to Use Each]]></title><description><![CDATA[Learn how these two simple keywords can make or break your SQL performance]]></description><link>https://newsletter.priteshbhanushali.com/p/mysql-union-vs-union-all-key-differences</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/mysql-union-vs-union-all-key-differences</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 02 Nov 2025 10:06:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!s-T8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When working with SQL queries, we often need to combine results from multiple tables or queries into one unified dataset.<br>In MySQL, this is where <code>UNION</code> and <code>UNION ALL</code> come in handy.</p><p>Although they look similar, they behave differently under the hood and have performance implications that every developer should understand.</p><div><hr></div><h3>&#9881;&#65039; What is <code>UNION</code>?</h3><p>The <code>UNION</code> operator <strong>combines the results of two or more SELECT statements</strong> and <strong>removes duplicate rows</strong> from the final result set.</p><h4>&#9989; Syntax:</h4><pre><code>SELECT column_list FROM table1
UNION
SELECT column_list FROM table2;</code></pre><h3>&#128269; Example:</h3><p>Let&#8217;s say we have two tables &#8212; <code>employees_2024</code> and <code>employees_2025</code>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s-T8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s-T8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 424w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 848w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1272w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s-T8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png" width="875" height="452" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:452,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16145,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s-T8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 424w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 848w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1272w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Now, if we run:</p><pre><code>SELECT name FROM employees_2024
UNION
SELECT name FROM employees_2025;</code></pre><h4>&#129534; Output:</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ktVQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ktVQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 424w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 848w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1272w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png" width="723" height="278.3611584327087" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:226,&quot;width&quot;:587,&quot;resizeWidth&quot;:723,&quot;bytes&quot;:5142,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ktVQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 424w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 848w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1272w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#10145;&#65039; Notice how <strong>&#8220;Alice&#8221; appears only once</strong> &#8212; duplicates are removed automatically.</p><div><hr></div><h3>&#9889; What is <code>UNION ALL</code>?</h3><p>The <code>UNION ALL</code> operator also combines the results of multiple <code>SELECT</code> statements &#8212;<br><strong>but it does not remove duplicates.</strong></p><h4>&#9989; Syntax:</h4><pre><code>SELECT column_list FROM table1
UNION ALL
SELECT column_list FROM table2;</code></pre><h3>&#128269; Example:</h3><p>Using the same tables:</p><pre><code>SELECT name FROM employees_2024
UNION ALL
SELECT name FROM employees_2025;</code></pre><h4>&#129534; Output:</h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jkOW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jkOW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 424w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 848w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1272w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jkOW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png" width="829" height="246" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:246,&quot;width&quot;:829,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6840,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jkOW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 424w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 848w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1272w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#10145;&#65039; Here, <strong>&#8220;Alice&#8221; appears twice</strong> &#8212; once from each table.</p><div><hr></div><h3>&#9878;&#65039; Key Differences Between UNION and UNION ALL</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IxEZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IxEZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 424w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 848w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1272w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png" width="624" height="708" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:708,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65039,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IxEZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 424w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 848w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1272w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128640; Performance Example</h3><p>Let&#8217;s run a quick comparison with a large dataset.</p><pre><code>-- UNION
SELECT name FROM sales_2023
UNION
SELECT name FROM sales_2024;

-- UNION ALL
SELECT name FROM sales_2023
UNION ALL
SELECT name FROM sales_2024;</code></pre><h3>&#9881;&#65039; Behind the scenes:</h3><ul><li><p><code>UNION</code> performs an <strong>implicit </strong><code>DISTINCT</code> operation &#8212; MySQL creates a temporary table, sorts results, and removes duplicates.</p></li><li><p><code>UNION ALL</code> simply <strong>concatenates results</strong>, making it much faster for large data sets.</p></li></ul><p><strong>Rule of thumb:</strong><br>If you don&#8217;t need to eliminate duplicates &#8594; <strong>always prefer </strong><code>UNION ALL</code> for better performance.</p><div><hr></div><h3>&#127757; Real-World Examples</h3><h4>&#127970; Example 1: Combining Customer Lists from Multiple Branches</h4><p>Let&#8217;s say you have separate databases or tables for different store branches:</p><ul><li><p><code>customers_mumbai</code></p></li><li><p><code>customers_delhi</code></p></li></ul><p>Each table stores customer names. Some customers shop in both branches.</p><h5>&#9989; Using UNION (to get unique customers)</h5><pre><code>SELECT customer_name FROM customers_mumbai
UNION
SELECT customer_name FROM customers_delhi;</code></pre><h5>Output:</h5><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LKCL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LKCL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 424w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 848w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1272w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LKCL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png" width="728" height="254.687306501548" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:226,&quot;width&quot;:646,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:6644,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LKCL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 424w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 848w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1272w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#10145;&#65039; This gives you a <strong>unique list</strong> of customers across both branches &#8212; useful for CRM or marketing campaigns where duplicates are not needed.</p><h5>&#9889; Using UNION ALL (to track total visits)</h5><pre><code>SELECT customer_name FROM customers_mumbai
UNION ALL
SELECT customer_name FROM customers_delhi;</code></pre><h5>Output:</h5><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iiTu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iiTu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 424w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 848w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1272w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iiTu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png" width="796" height="265" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:265,&quot;width&quot;:796,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8233,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iiTu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 424w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 848w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1272w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#10145;&#65039; This shows <strong>each occurrence</strong>, meaning Priya visited both branches &#8212; helpful when you want to <strong>count total visits or analyze activity</strong>.</p><div><hr></div><h3>&#128161; When to Use Which?</h3><h4>&#9989; Use <code>UNION</code> When:</h4><ul><li><p>You need <strong>unique results</strong> only.<br>Example: Combining customer lists from multiple regions.</p></li></ul><h4>&#9989; Use <code>UNION ALL</code> When:</h4><ul><li><p>You&#8217;re sure data won&#8217;t overlap or duplicates are acceptable.<br>Example: Merging monthly logs or combining partitions.</p></li></ul><div><hr></div><h3>&#9888;&#65039; Common Mistakes to Avoid</h3><ol><li><p><strong>Mismatched columns:</strong><br>Each SELECT must return the <strong>same number of columns</strong> with <strong>compatible data types</strong>.</p></li></ol><pre><code>-- &#10060; This will fail
SELECT id, name FROM table1
UNION
SELECT name FROM table2;</code></pre><ol start="2"><li><p><strong>Expecting automatic sorting:</strong><br>The result of <code>UNION</code> or <code>UNION ALL</code> is <strong>not guaranteed to be ordered</strong>.<br>Use <code>ORDER BY</code> after the last query:</p></li></ol><pre><code>SELECT name FROM table1
UNION ALL
SELECT name FROM table2
ORDER BY name;</code></pre><div><hr></div><h4>&#129504; Pro Tip</h4><p>If you need to count duplicates or analyze overlaps, you can simulate both behaviors:</p><pre><code>SELECT name, COUNT(*) AS occurrence
FROM (
  SELECT name FROM employees_2024
  UNION ALL
  SELECT name FROM employees_2025
) AS combined
GROUP BY name;</code></pre><p>This helps detect duplicates even when using <code>UNION ALL</code>.</p><div><hr></div><h2>&#127937; Conclusion</h2><p>Both <code>UNION</code> and <code>UNION ALL</code> are powerful tools in MySQL for combining data from multiple queries &#8212; but understanding their differences is essential for writing efficient SQL.</p><ul><li><p><code>UNION</code> gives you a <em>clean, unique</em> result set by removing duplicates &#8212; ideal for cases where you care about distinct data (like unique customers, leads, or products).</p></li><li><p><code>UNION ALL</code>, on the other hand, is <em>faster and lighter</em> because it doesn&#8217;t check for duplicates &#8212; making it perfect for analytics, reporting, and log data where every record matters.</p></li></ul><p>In short:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HEJp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HEJp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 424w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 848w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1272w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HEJp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png" width="882" height="155" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:155,&quot;width&quot;:882,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9496,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HEJp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 424w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 848w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1272w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Think of it this way &#8212;</p><blockquote><p>Use <code>UNION</code> for <em>accuracy</em>, and <code>UNION ALL</code> for <em>speed</em>.</p></blockquote><p>The best SQL developers know when to choose between the two, balancing <strong>data correctness</strong> and <strong>query performance</strong> based on the context.</p><p>So next time you merge data across tables, pause for a second and ask yourself &#8212;<br><strong>Do I really need to remove duplicates?</strong><br>If not, give <code>UNION ALL</code> the green light. &#128678;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[✍️ The Ultimate Guide to MySQL JOINs — Simplified with Practical Use Cases]]></title><description><![CDATA[Making MySQL JOINs Easy, Intuitive, and Actionable]]></description><link>https://newsletter.priteshbhanushali.com/p/the-ultimate-guide-to-mysql-joins</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/the-ultimate-guide-to-mysql-joins</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Tue, 28 Oct 2025 05:00:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gVEh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>&#129513; Introduction &#8212; Why JOINs Matter</h3><p>Databases are built on relationships &#8212; and <strong>JOINs are how you bring those relationships to life.</strong></p><p>Imagine running an e-commerce platform. You have one table for customers, one for orders, and one for products. To know <em>which customer bought what</em>, you need to connect these tables. That&#8217;s where <strong>JOINs</strong> come in.</p><p>In this post, we&#8217;ll walk through different types of MySQL JOINs with clear diagrams, real-world examples, and best practices to make your data relationships clean and efficient.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gVEh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gVEh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 424w, https://substackcdn.com/image/fetch/$s_!gVEh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 848w, https://substackcdn.com/image/fetch/$s_!gVEh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 1272w, https://substackcdn.com/image/fetch/$s_!gVEh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gVEh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png" width="605" height="551" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:551,&quot;width&quot;:605,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:186874,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gVEh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 424w, https://substackcdn.com/image/fetch/$s_!gVEh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 848w, https://substackcdn.com/image/fetch/$s_!gVEh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 1272w, https://substackcdn.com/image/fetch/$s_!gVEh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1385b139-20c3-4799-9020-7f4cc9a4d6c3_605x551.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128269; What is a JOIN in MySQL?</h2><p>A <strong>JOIN</strong> in MySQL is used to combine rows from two or more tables based on a related column &#8212; usually a <strong>foreign key</strong>.</p><p><strong>Syntax:</strong></p><pre><code>SELECT columns
FROM table1
JOIN table2
ON table1.common_column = table2.common_column;</code></pre><div><hr></div><h3>&#127978; Real-World Example Setup</h3><p>Let&#8217;s say you have a small <strong>online store</strong> with these two tables:</p><h4>customers</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Rzme!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Rzme!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 424w, https://substackcdn.com/image/fetch/$s_!Rzme!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 848w, https://substackcdn.com/image/fetch/$s_!Rzme!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 1272w, https://substackcdn.com/image/fetch/$s_!Rzme!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Rzme!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png" width="838" height="212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:212,&quot;width&quot;:838,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8711,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Rzme!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 424w, https://substackcdn.com/image/fetch/$s_!Rzme!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 848w, https://substackcdn.com/image/fetch/$s_!Rzme!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 1272w, https://substackcdn.com/image/fetch/$s_!Rzme!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff85abe73-a725-4fd1-8692-f8dcdc87008d_838x212.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4>orders</h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!68Vn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!68Vn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 424w, https://substackcdn.com/image/fetch/$s_!68Vn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 848w, https://substackcdn.com/image/fetch/$s_!68Vn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 1272w, https://substackcdn.com/image/fetch/$s_!68Vn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!68Vn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png" width="897" height="289" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1cfebc6-6663-42cc-8bca-34116923d391_897x289.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:289,&quot;width&quot;:897,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14636,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!68Vn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 424w, https://substackcdn.com/image/fetch/$s_!68Vn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 848w, https://substackcdn.com/image/fetch/$s_!68Vn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 1272w, https://substackcdn.com/image/fetch/$s_!68Vn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1cfebc6-6663-42cc-8bca-34116923d391_897x289.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128279; 1. INNER JOIN &#8212; &#8220;Only Matching Data&#8221;</h3><p>The <strong>INNER JOIN</strong> returns only the records that have matching values in both tables.</p><h5><strong>Example:</strong></h5><pre><code>SELECT customers.name, orders.product, orders.amount
FROM customers
INNER JOIN orders
ON customers.id = orders.customer_id;</code></pre><h5>Result:</h5><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!58VF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!58VF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 424w, https://substackcdn.com/image/fetch/$s_!58VF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 848w, https://substackcdn.com/image/fetch/$s_!58VF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 1272w, https://substackcdn.com/image/fetch/$s_!58VF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!58VF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png" width="797" height="179" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:179,&quot;width&quot;:797,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8515,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!58VF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 424w, https://substackcdn.com/image/fetch/$s_!58VF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 848w, https://substackcdn.com/image/fetch/$s_!58VF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 1272w, https://substackcdn.com/image/fetch/$s_!58VF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8169d072-3287-4b41-9aed-ac7745ad5edb_797x179.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#129504; <strong>Use Case:</strong> When you want only customers <em>who have placed orders.</em></p><p>&#128161; <strong>Tip:</strong> INNER JOIN is the most common join type used in reporting, analytics, and dashboards.</p><div><hr></div><h3>&#128279; 2. LEFT JOIN &#8212; &#8220;Include All from Left, Even Without a Match&#8221;</h3><p>The <strong>LEFT JOIN</strong> returns all records from the left table (<code>customers</code>), and the matched rows from the right table (<code>orders</code>).<br>If no match exists, you&#8217;ll still get the customer &#8212; with NULLs in order columns.</p><h5>Example:</h5><pre><code>SELECT customers.name, orders.product
FROM customers
LEFT JOIN orders
ON customers.id = orders.customer_id;</code></pre><h5>Result:</h5><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q9PT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q9PT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 424w, https://substackcdn.com/image/fetch/$s_!q9PT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 848w, https://substackcdn.com/image/fetch/$s_!q9PT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 1272w, https://substackcdn.com/image/fetch/$s_!q9PT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q9PT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png" width="769" height="224" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:224,&quot;width&quot;:769,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8322,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q9PT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 424w, https://substackcdn.com/image/fetch/$s_!q9PT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 848w, https://substackcdn.com/image/fetch/$s_!q9PT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 1272w, https://substackcdn.com/image/fetch/$s_!q9PT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F639216f0-b1a2-4d81-83fe-c85a0a674b4b_769x224.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#129504; <strong>Use Case:</strong> You want a list of all customers &#8212; even those who haven&#8217;t placed any orders yet.</p><p>&#128161; <strong>Pro Tip:</strong> Great for reports like &#8220;inactive customers&#8221; or &#8220;potential leads.&#8221;</p><div><hr></div><h3>&#128279; 3. RIGHT JOIN &#8212; &#8220;Include All from Right Table&#8221;</h3><p>The <strong>RIGHT JOIN</strong> is the mirror of LEFT JOIN. It returns all records from the right table, plus matching ones from the left.</p><h5><strong>Example:</strong></h5><pre><code>SELECT customers.name, orders.product
FROM customers
RIGHT JOIN orders
ON customers.id = orders.customer_id;</code></pre><h5>Result:</h5><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9lOU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9lOU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 424w, https://substackcdn.com/image/fetch/$s_!9lOU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 848w, https://substackcdn.com/image/fetch/$s_!9lOU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 1272w, https://substackcdn.com/image/fetch/$s_!9lOU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9lOU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png" width="758" height="217" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:217,&quot;width&quot;:758,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8491,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9lOU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 424w, https://substackcdn.com/image/fetch/$s_!9lOU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 848w, https://substackcdn.com/image/fetch/$s_!9lOU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 1272w, https://substackcdn.com/image/fetch/$s_!9lOU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e7edcd0-5d01-49b8-92e2-48bc9bac0f81_758x217.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#129504; <strong>Use Case:</strong> When you want to see <em>all orders</em> &#8212; even if some don&#8217;t have valid customers (e.g., deleted accounts).</p><p>&#128161; <strong>Note:</strong> RIGHT JOIN is less used in practice; LEFT JOIN is more readable and maintainable.</p><div><hr></div><h3>&#128279; 4. FULL OUTER JOIN &#8212; &#8220;All Records, Matched or Not&#8221;</h3><p>MySQL doesn&#8217;t support <strong>FULL OUTER JOIN</strong> natively, but you can simulate it using <code>UNION</code>.</p><h5><strong>Example:</strong></h5><pre><code>SELECT customers.name, orders.product
FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id
UNION
SELECT customers.name, orders.product
FROM customers
RIGHT JOIN orders ON customers.id = orders.customer_id;</code></pre><p>&#129504; <strong>Use Case:</strong> You want <em>everything</em> &#8212; all customers and all orders, matched or unmatched.</p><div><hr></div><h3>&#128279; 5. CROSS JOIN &#8212; &#8220;Every Combination&#8221;</h3><p>The <strong>CROSS JOIN</strong> returns the <strong>Cartesian product</strong> &#8212; every combination of rows between the two tables.</p><h5><strong>Example:</strong></h5><pre><code>SELECT customers.name, orders.product
FROM customers
CROSS JOIN orders;</code></pre><p>&#129504; <strong>Use Case:</strong> Generating combinations &#8212; like product bundles or testing joins in analytics.</p><p>&#9888;&#65039; <strong>Caution:</strong> If you have 1,000 customers and 1,000 orders, this will return <strong>1,000,000 rows</strong> &#8212; use carefully.</p><div><hr></div><h3>&#128279; 6. SELF JOIN &#8212; &#8220;Join a Table to Itself&#8221;</h3><p>A <strong>SELF JOIN</strong> allows comparing rows within the same table.</p><h5>Example:</h5><pre><code>SELECT A.name AS customer1, B.name AS customer2, A.city
FROM customers A
JOIN customers B
ON A.city = B.city
AND A.id &lt;&gt; B.id;</code></pre><h5><strong>Result:</strong></h5><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iG0E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iG0E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 424w, https://substackcdn.com/image/fetch/$s_!iG0E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 848w, https://substackcdn.com/image/fetch/$s_!iG0E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 1272w, https://substackcdn.com/image/fetch/$s_!iG0E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iG0E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png" width="740" height="82" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:82,&quot;width&quot;:740,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4308,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iG0E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 424w, https://substackcdn.com/image/fetch/$s_!iG0E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 848w, https://substackcdn.com/image/fetch/$s_!iG0E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 1272w, https://substackcdn.com/image/fetch/$s_!iG0E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb86a52a6-abff-402b-adfe-1adbf765f53b_740x82.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#129504; <strong>Use Case:</strong> Finding customers from the same city, or comparing hierarchical data like employees and managers.</p><div><hr></div><h3>&#129520; Best Practices for Using JOINs</h3><p>&#9989; <strong>1. Always use aliases (</strong><code>c</code><strong>, </strong><code>o</code><strong>)</strong> to make queries readable.<br>&#9989; <strong>2. Use INNER JOIN by default</strong> unless you explicitly need unmatched rows.<br>&#9989; <strong>3. Index foreign key columns</strong> to improve performance.<br>&#9989; **4. Avoid SELECT *** &#8212; choose only needed columns.<br>&#9989; <strong>5. Use </strong><code>EXPLAIN</code> to analyze query performance for complex joins.</p><div><hr></div><h3>&#128640; Real-World Use Case: &#8220;Customer Insights Dashboard&#8221;</h3><p>You&#8217;re building a dashboard that shows <strong>total orders and spend per customer</strong>.</p><h5><strong>Query:</strong></h5><pre><code>SELECT c.name, COUNT(o.id) AS total_orders, SUM(o.amount) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id;</code></pre><h5>Result:</h5><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ox2M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ox2M!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 424w, https://substackcdn.com/image/fetch/$s_!ox2M!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 848w, https://substackcdn.com/image/fetch/$s_!ox2M!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 1272w, https://substackcdn.com/image/fetch/$s_!ox2M!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ox2M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png" width="724" height="170" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:170,&quot;width&quot;:724,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7369,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177071573?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ox2M!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 424w, https://substackcdn.com/image/fetch/$s_!ox2M!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 848w, https://substackcdn.com/image/fetch/$s_!ox2M!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 1272w, https://substackcdn.com/image/fetch/$s_!ox2M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79de6191-69e2-42ae-a8bc-0c5c7ff0487b_724x170.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>&#129504; <strong>Insight:</strong> LEFT JOIN ensures even customers with zero orders appear in the results.</p><div><hr></div><h3>&#129513; Conclusion &#8212; JOINs Are the Language of Relationships</h3><p>Mastering JOINs is essential for turning raw data into insight.<br>Whether you&#8217;re building dashboards, APIs, or reports, understanding how to <strong>combine, filter, and relate data</strong> efficiently will make you a stronger database engineer.</p><p>JOINs are not just SQL syntax &#8212; they&#8217;re the <em>bridge</em> between your data and meaningful answers.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[💻Laravel Collections: The Hidden Superpower in Your Toolkit]]></title><description><![CDATA[How Laravel Collections simplify data transformations, boost readability, and cut boilerplate.]]></description><link>https://newsletter.priteshbhanushali.com/p/laravel-collections-the-hidden-superpower</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/laravel-collections-the-hidden-superpower</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 18 Oct 2025 08:07:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!0HTM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Collections are one of Laravel&#8217;s best developer-experience features. They turn array/iteration-heavy code into fluent, expressive pipelines and make common transforms readable and composable. In this post I&#8217;ll walk you through the <em>most important</em> collection methods.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0HTM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0HTM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!0HTM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!0HTM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!0HTM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0HTM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1218573,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/174027225?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0HTM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!0HTM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!0HTM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!0HTM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc41fa7dd-429c-459e-928b-298840c9ba0c_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#9889;Why Collections matter</h2><ul><li><p>They improve readability: intent is clearer than nested loops and temporary variables.</p></li><li><p>They are chainable: easy to compose small transformations.</p></li><li><p>They reduce bug surface: fewer manual index fiddles and less mutation.</p></li><li><p>They integrate with Eloquent and many Laravel APIs (many return <code>Collection</code> by default).</p></li></ul><p>Tradeoff: Collections have small overhead vs raw arrays. For extreme hot loops or microbenchmarks arrays may be slightly faster &#8212; but for application-level logic the clarity win is usually worth it. Use <code>LazyCollection</code> or streaming DB cursors when data size risks blowing memory.</p><div><hr></div><h2>&#128073;Quick setup examples</h2><pre><code>use Illuminate\Support\Collection;

// create from array
$col = collect([1,2,3,4]);

// from Eloquent
$users = App\Models\User::where('active', true)-&gt;get(); // returns Collection

// lazy streaming (when iterating large datasets)
use Illuminate\Support\LazyCollection;
LazyCollection::make(function () {
    // yield rows...
});</code></pre><div><hr></div><h2>&#128142;Essential Collection methods (with examples)</h2><h3>1) <code>map()</code> &#8212; transform each item</h3><p>Transforms items and returns a new Collection.</p><pre><code>$names = collect([' alice ', 'bob', ' carol '])
    -&gt;map(fn($n) =&gt; trim($n)-&gt;ucfirst());
// ['Alice','Bob','Carol']</code></pre><p>When to use: value transformation, mapping models to DTOs or arrays. Avoid heavy side-effects inside <code>map()</code> &#8212; prefer pure transforms.</p><p>The callback should ideally be a "pure function" &#8212; meaning it takes an input, returns an output, and does not produce side effects like DB calls, API requests, or logging.</p><h5>&#9989; Good:</h5><pre><code>$users = User::all()-&gt;map(fn($u) =&gt; $u-&gt;only(['id', 'name']));</code></pre><h5>&#10060; Risky:</h5><pre><code>$users = User::all()-&gt;map(fn($u) =&gt; Mail::to($u-&gt;email)-&gt;send(...));</code></pre><h4>&#128313; When to Use <code>map()</code></h4><h5><strong>Value transformation</strong></h5><p>Clean/format raw values:</p><pre><code>$prices = collect([1000, 2500, 3999])
    -&gt;map(fn($p) =&gt; '$' . number_format($p / 100, 2));

// ['$10.00', '$25.00', '$39.99']</code></pre><h5><strong>Mapping Models to DTOs (Data Transfer Objects)</strong></h5><p>Suppose you don&#8217;t want to pass whole Eloquent models to the frontend:</p><pre><code>$users = User::all()-&gt;map(fn($u) =&gt; [
    'id' =&gt; $u-&gt;id,
    'name' =&gt; $u-&gt;name,
    'email' =&gt; $u-&gt;email,
]);</code></pre><h5>Mapping API Response / Normalizing Data</h5><pre><code>$apiData = collect($response['items'])
    -&gt;map(fn($item) =&gt; [
        'title' =&gt; $item['name'],
        'url'   =&gt; $item['link'],
    ]);</code></pre><div><hr></div><h3>2) <code>filter()</code> / <code>reject()</code> &#8212; keep / remove by predicate</h3><p>Both methods are used to <strong>select items from a collection based on a condition (predicate)</strong>:</p><ul><li><p><code>filter()</code> &#8594; <strong>keeps items</strong> where the condition is <strong>true</strong></p></li><li><p><code>reject()</code> &#8594; <strong>removes items</strong> where the condition is <strong>true</strong> (inverse of filter)</p></li></ul><p>They <strong>return a new collection</strong> and do not modify the original one.</p><pre><code>$numbers = collect([1, 2, 3, 4, 5]);

$even = $numbers-&gt;filter(fn($n) =&gt; $n % 2 === 0);
// Result: [1 =&gt; 2, 3 =&gt; 4]

$nonEven = $numbers-&gt;reject(fn($n) =&gt; $n % 2 === 0);
// Result: [0 =&gt; 1, 2 =&gt; 3, 4 =&gt; 5]</code></pre><p>Notice the keys:</p><ul><li><p><code>filter()</code> keeps <strong>original keys</strong> &#8594; <code>[1 =&gt; 2, 3 =&gt; 4]</code></p></li><li><p><code>reject()</code> also keeps <strong>original keys</strong> &#8594; <code>[0 =&gt; 1, 2 =&gt; 3, 4 =&gt; 5]</code></p></li></ul><p>If you want <strong>0-based indexes</strong>, just call <code>.values()</code> afterwards:</p><pre><code>$even = $numbers-&gt;filter(fn($n) =&gt; $n % 2 === 0)-&gt;values();
// Result: [0 =&gt; 2, 1 =&gt; 4]</code></pre><h4>&#128313; When to Use <code>filter()</code> / <code>reject()</code></h4><h5>Filtering collections of models or arrays</h5><pre><code>$orders = collect([
    ['id' =&gt; 1, 'status' =&gt; 'paid'],
    ['id' =&gt; 2, 'status' =&gt; 'pending'],
    ['id' =&gt; 3, 'status' =&gt; 'paid'],
]);

$paidOrders = $orders-&gt;filter(fn($o) =&gt; $o['status'] === 'paid');
// [['id' =&gt; 1, 'status' =&gt; 'paid'], ['id' =&gt; 3, 'status' =&gt; 'paid']]</code></pre><h5>Rejecting invalid or null values</h5><pre><code>$data = collect([10, null, 20, '', 30]);

$cleaned = $data-&gt;filter(fn($n) =&gt; !empty($n));
// [0 =&gt; 10, 2 =&gt; 20, 4 =&gt; 30]</code></pre><p>Or using reject:</p><pre><code>$cleaned = $data-&gt;reject(fn($n) =&gt; empty($n));</code></pre><h5><strong>Filtering based on relationships</strong></h5><p>Suppose you have users with posts:</p><pre><code>$users = User::with('posts')-&gt;get();

$withPosts = $users-&gt;filter(fn($u) =&gt; $u-&gt;posts-&gt;isNotEmpty());</code></pre><p>Now <code>$withPosts</code> contains only users who have at least one post.</p><div><hr></div><h3>3) <code>pluck()</code> &#8212; get a single field (or nested)</h3><p>Very handy for extracting a column from arrays or Eloquent collections.</p><pre><code>$users = collect([
    ['id' =&gt; 1, 'email' =&gt; 'a@example.com'],
    ['id' =&gt; 2, 'email' =&gt; 'b@example.com'],
]);

$emails = $users-&gt;pluck('email');
// =&gt; Collection: ['a@example.com', 'b@example.com']

// With Eloquent (query builder)
$emails = User::pluck('email');      // issues SELECT email FROM users
// =&gt; Collection of emails</code></pre><h5>Nested (dot) notation</h5><pre><code>$data = collect([
    ['user' =&gt; ['id' =&gt; 1, 'name' =&gt; 'Alice']],
    ['user' =&gt; ['id' =&gt; 2, 'name' =&gt; 'Bob']],
]);

$data-&gt;pluck('user.name');
// =&gt; Collection: ['Alice', 'Bob']</code></pre><h4>Collection vs Query Builder <code>pluck()</code> &#8212; performance tip</h4><ul><li><p><code>User::pluck('email')</code> on the query builder executes a SQL <code>SELECT email</code> and returns the values directly &#8212; <strong>efficient</strong> (no full models).</p></li><li><p><code>$users = User::get(); $users-&gt;pluck('email')</code> loads full models then extracts &#8212; <strong>less efficient</strong>.</p></li></ul><p>So prefer query-builder <code>pluck()</code> when you only need a column from DB.</p><div><hr></div><h3>4) <code>where()</code> / <code>whereIn()</code> &#8212; simple filtering by key/value</h3><p>Shorthand for common equality filters.</p><ul><li><p><code>where($key, $operator = null, $value = null)</code><br>Filters items in the collection where the given <code>$key</code> matches <code>$value</code>.<br>By default, uses <strong>loose comparison (</strong><code>==</code><strong>)</strong>.</p></li><li><p><code>whereIn($key, $values)</code><br>Filters items in the collection where the <code>$key</code> is <strong>in an array of allowed values</strong>.</p></li></ul><p>Both return <strong>new collections</strong> (original is unchanged).</p><h4>&#128313; Example with <code>where()</code></h4><pre><code>$users = collect([
    ['id' =&gt; 1, 'name' =&gt; 'Alice', 'status' =&gt; 'active'],
    ['id' =&gt; 2, 'name' =&gt; 'Bob', 'status' =&gt; 'inactive'],
    ['id' =&gt; 3, 'name' =&gt; 'Carol', 'status' =&gt; 'active'],
]);

$active = $users-&gt;where('status', 'active');

// Result (Collection):
// [
//   ['id' =&gt; 1, 'name' =&gt; 'Alice', 'status' =&gt; 'active'],
//   ['id' =&gt; 3, 'name' =&gt; 'Carol', 'status' =&gt; 'active']
// ]</code></pre><p>&#128073; Same as writing:</p><pre><code>$active = $users-&gt;filter(fn($u) =&gt; $u['status'] == 'active');</code></pre><p>But <strong>shorter and cleaner</strong>.</p><h4>&#128313; Example with <code>whereIn()</code></h4><pre><code>$orders = collect([
    ['id' =&gt; 10, 'total' =&gt; 100],
    ['id' =&gt; 11, 'total' =&gt; 200],
    ['id' =&gt; 12, 'total' =&gt; 300],
    ['id' =&gt; 13, 'total' =&gt; 400],
]);

$selected = $orders-&gt;whereIn('id', [10, 12]);

// Result:
// [
//   ['id' =&gt; 10, 'total' =&gt; 100],
//   ['id' =&gt; 12, 'total' =&gt; 300]
// ]</code></pre><p>&#128073; Same as:</p><pre><code>$selected = $orders-&gt;filter(fn($o) =&gt; in_array($o['id'], [10, 12]));</code></pre><h4>&#128313; When to use <code>where()</code> / <code>whereIn()</code></h4><p>&#9989; Use when filtering by a <strong>single field&#8217;s value</strong> (cleaner than <code>filter()</code>).<br>&#9989; Great for <strong>status checks</strong>, <strong>id lookups</strong>, <strong>simple equality conditions</strong>.<br>&#9989; Use <code>whereIn()</code> when you want to allow <strong>multiple values</strong> in one shot.</p><p>&#10060; Avoid when:</p><ul><li><p>You need strict comparison (<code>===</code>) &#8594; use <code>filter()</code>.</p></li><li><p>You need complex conditions (multiple fields, computed logic) &#8594; use <code>filter()</code>.</p></li></ul><div><hr></div><h3>5) <code>groupBy()</code> &#8212; group items by a callback or key</h3><p><code>groupBy()</code> takes a <strong>key</strong> (string) or a <strong>callback</strong> and groups items of the collection into <strong>sub-collections</strong>, keyed by that value.</p><p>Think of it like SQL&#8217;s <code>GROUP BY</code>, but instead of reducing, it organizes items into buckets.</p><h5>&#128313; Example with Key</h5><pre><code>$posts = collect([
    ['category' =&gt; 'php', 'title' =&gt; 'x'],
    ['category' =&gt; 'js',  'title' =&gt; 'y'],
    ['category' =&gt; 'php', 'title' =&gt; 'z'],
]);

$byCat = $posts-&gt;groupBy('category');

// Result:
// [
//   'php' =&gt; Collection([
//       ['category' =&gt; 'php', 'title' =&gt; 'x'],
//       ['category' =&gt; 'php', 'title' =&gt; 'z'],
//   ]),
//   'js' =&gt; Collection([
//       ['category' =&gt; 'js', 'title' =&gt; 'y'],
//   ]),
// ]</code></pre><h5>&#128313; Example with Callback</h5><p>You can pass a callback to dynamically decide the grouping key:</p><pre><code>$numbers = collect([1, 2, 3, 4, 5, 6]);

$grouped = $numbers-&gt;groupBy(fn($n) =&gt; $n % 2 === 0 ? 'even' : 'odd');

// Result:
// [
//   'odd' =&gt; Collection([1, 3, 5]),
//   'even' =&gt; Collection([2, 4, 6]),
// ]</code></pre><h5>&#128313; Nested Grouping</h5><p>You can group by multiple keys by passing an array of keys:</p><pre><code>$items = collect([
    ['team' =&gt; 'A', 'type' =&gt; 'frontend'],
    ['team' =&gt; 'A', 'type' =&gt; 'backend'],
    ['team' =&gt; 'B', 'type' =&gt; 'frontend'],
]);

$grouped = $items-&gt;groupBy(['team', 'type']);

// Result:
// [
//   'A' =&gt; [
//       'frontend' =&gt; Collection([['team'=&gt;'A','type'=&gt;'frontend']]),
//       'backend'  =&gt; Collection([['team'=&gt;'A','type'=&gt;'backend']]),
//   ],
//   'B' =&gt; [
//       'frontend' =&gt; Collection([['team'=&gt;'B','type'=&gt;'frontend']]),
//   ],
// ]</code></pre><h5>&#128313; When to Use <code>groupBy()</code></h5><p>&#9989; When you want to <strong>organize items into categories/buckets</strong>.<br>&#9989; Preparing API responses with <strong>sectioned data</strong>.<br>&#9989; Batch processing (e.g., per user, per category).<br>&#9989; Reporting and analytics (e.g., counts per status).</p><p>&#10060; Don&#8217;t use if you only want <strong>filtering</strong> (use <code>filter()</code> / <code>where()</code> instead).<br>&#10060; Not ideal if you need <strong>aggregation</strong> (like SUM, AVG). For that, pair with <code>map()</code> or use DB-side aggregation.</p><div><hr></div><h3>6) first() / firstWhere() / last()</h3><h4>&#128313; <code>first()</code></h4><p><strong>What it does:</strong><br>Returns the <em>first element</em> in the collection.</p><ul><li><p>By default, it just takes the <strong>first item</strong> (based on order in the collection).</p></li><li><p>But you can also pass a <strong>callback (predicate)</strong> to filter.</p></li></ul><pre><code>$numbers = collect([10, 20, 30, 40]);

// get first item (no callback)
$first = $numbers-&gt;first();  
// 10

// get first item greater than 15
$firstGreater = $numbers-&gt;first(fn($n) =&gt; $n &gt; 15);
// 20</code></pre><p>&#128073; If no element matches the callback, it returns <strong>null</strong>.</p><h4>&#128313; <code>firstWhere()</code></h4><p><strong>What it does:</strong><br>A shorthand for filtering by a <strong>field/value condition</strong> and returning the first match.</p><p>Think of it as:</p><pre><code>$collection-&gt;where($key, $value)-&gt;first();</code></pre><p>Example with models:</p><pre><code>$users = collect([
    ['id' =&gt; 1, 'name' =&gt; 'Alice', 'active' =&gt; true],
    ['id' =&gt; 2, 'name' =&gt; 'Bob', 'active' =&gt; false],
    ['id' =&gt; 3, 'name' =&gt; 'Carol', 'active' =&gt; true],
]);

// get first user with active = true
$activeUser = $users-&gt;firstWhere('active', true);
// ['id'=&gt;1,'name'=&gt;'Alice','active'=&gt;true]

// get first user by id
$user2 = $users-&gt;firstWhere('id', 2);
// ['id'=&gt;2,'name'=&gt;'Bob','active'=&gt;false]</code></pre><p>&#128161; Handy when you want a single record without writing <code>where()-&gt;first()</code> manually.</p><h4><br>&#128313; <code>last()</code></h4><p><strong>What it does:</strong><br>The opposite of <code>first()</code>. It grabs the <strong>last item</strong> in the collection.</p><ul><li><p>Like <code>first()</code>, it accepts an optional <strong>callback</strong>.</p></li></ul><pre><code>$numbers = collect([10, 20, 30, 40]);

// get last item (no callback)
$last = $numbers-&gt;last();  
// 40

// get last item less than 35
$lastLess = $numbers-&gt;last(fn($n) =&gt; $n &lt; 35);
// 30</code></pre><h4>&#9989; Best Practices</h4><ul><li><p>Use <code>first()</code> when you want logic-driven retrieval (with callback).</p></li><li><p>Use <code>firstWhere()</code> when you&#8217;re checking a field against a value (simpler &amp; cleaner).</p></li><li><p>Use <code>last()</code> when your dataset is <strong>ordered</strong> (like logs, dates, sequences) and you want the most recent or last entry.</p></li></ul><div><hr></div><h2>&#127937; Conclusion</h2><p>Laravel&#8217;s Collection class gives you a powerful, expressive toolset to work with arrays, model sets, and data structures in memory. Methods like <code>map</code>, <code>filter</code> / <code>reject</code>, <code>pluck</code>, <code>where</code> / <code>whereIn</code>, <code>groupBy</code>, <code>first</code>, <code>last</code>, etc., let you write clean, readable, declarative code rather than lots of loops and conditionals.</p><p>A few guiding principles:</p><ul><li><p>Use <strong>pure transformations</strong> (<code>map</code>) when you want to convert or reshape data.</p></li><li><p>Use <strong>filtering</strong> (<code>filter</code>, <code>reject</code>, <code>where</code>, <code>whereIn</code>) when you want to include/exclude items.</p></li><li><p>Use <strong>pluck</strong> to extract specific fields or build small key/value mappings.</p></li><li><p>Use <strong>groupBy</strong> when you want to organise or batch items by a common attribute.</p></li><li><p>Use <strong>first / last / firstWhere</strong> to pull out a single item when you don&#8217;t need the full collection.</p></li></ul><p>For full reference, examples, edge cases and all available methods, you can always check the official documentation:</p><p>https://laravel.com/docs/12.x/collections#available-methods</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 Stop Repeating Yourself: How to Supercharge Your Laravel Code with Traits]]></title><description><![CDATA[Smarter Laravel Code with Traits]]></description><link>https://newsletter.priteshbhanushali.com/p/stop-repeating-yourself-how-to-supercharge</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/stop-repeating-yourself-how-to-supercharge</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 20 Sep 2025 04:30:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DiOV!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92a0aac7-a3c0-44be-97d4-f77aaa58306c_593x593.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When working on mid-to-large Laravel projects, one challenge we all face is <strong>keeping code DRY (Don&#8217;t Repeat Yourself)</strong> while still keeping things readable and maintainable. That&#8217;s where <strong>PHP Traits</strong> come in.</p><p>If you&#8217;ve been using Laravel for a while, you&#8217;ve probably encountered traits&#8212;maybe in Eloquent models (<code>SoftDeletes</code>, <code>HasFactory</code>, <code>Notifiable</code>) or in your own helper code. Traits let you group reusable methods and inject them into classes without the rigidity of inheritance.</p><p>In this article, we&#8217;ll explore how traits work in Laravel, when you should use them, and best practices to avoid common pitfalls.</p><div><hr></div><h2>&#128073; What Are Traits in Laravel?</h2><p>In PHP, a <strong>Trait</strong> is a mechanism for code reuse. Think of it like a &#8220;shared toolkit&#8221; you can drop into multiple classes without inheritance.</p><p>Instead of duplicating logic, you can define it once inside a trait and reuse it wherever needed.</p><p>A simple example:</p><pre><code>trait HasUuid {
    protected static function bootHasUuid()
    {
        static::creating(function ($model) {
            $model-&gt;uuid = (string) \Illuminate\Support\Str::uuid();
        });
    }
}</code></pre><p>Now any model can automatically get a UUID when created:</p><pre><code>class User extends Model {
    use HasUuid;
}

class Post extends Model {
    use HasUuid;
}</code></pre><p>No copy-paste, just <strong>plug and play</strong>.</p><div><hr></div><h2>&#127775; Benefits of Using Traits in Laravel (with Examples)</h2><h4>1. <strong>Code Reusability</strong></h4><p>The biggest advantage of traits is that you can write a piece of logic once and reuse it anywhere.</p><p>&#128073; Example: Imagine you want to add a method that generates a <strong>slug</strong> for models like <code>Post</code>, <code>Category</code>, and <code>Tag</code>.</p><p>Instead of writing the same code three times, you put it in a trait:</p><pre><code>&lt;?php
namespace App\Traits;

use Illuminate\Support\Str;

trait HasSlug
{
    public function generateSlug($string)
    {
        return Str::slug($string, '-');
    }
}</code></pre><p>Use it in multiple models:</p><pre><code>class Post extends Model {
    use HasSlug;
}

class Category extends Model {
    use HasSlug;
}</code></pre><p>Now both models can generate slugs:</p><pre><code>$post-&gt;generateSlug('My First Blog Post'); 
// Output: "my-first-blog-post"</code></pre><p>&#9989; Saves time and avoids duplicate code.</p><h4>2. <strong>Keeps Code Clean &amp; Organized</strong></h4><p>Controllers and models should focus on their <strong>main responsibility</strong>. Traits let you move repeated logic into separate, reusable blocks.</p><p>&#128073; Example: Instead of filling controllers with API response formatting logic:</p><pre><code>return response()-&gt;json([
    'status' =&gt; 'success',
    'data' =&gt; $data,
], 200);</code></pre><p>You can create a trait:</p><pre><code>&lt;?php
namespace App\Traits;

trait ApiResponse
{
    public function successResponse($data, $message = "Success")
    {
        return response()-&gt;json([
            'status' =&gt; 'success',
            'message' =&gt; $message,
            'data' =&gt; $data,
        ], 200);
    }

    public function errorResponse($message, $code = 400)
    {
        return response()-&gt;json([
            'status' =&gt; 'error',
            'message' =&gt; $message,
        ], $code);
    }
}</code></pre><p>And use it in any controller:</p><pre><code>class UserController extends Controller
{
    use ApiResponse;

    public function show($id)
    {
        $user = User::find($id);

        if (! $user) {
            return $this-&gt;errorResponse("User not found", 404);
        }

        return $this-&gt;successResponse($user, "User fetched successfully");
    }
}</code></pre><p>&#9989; Cleaner controller, better readability, reusable responses.</p><h4>3. <strong>Consistency Across Application</strong></h4><p>Traits ensure you don&#8217;t accidentally write slightly different versions of the same logic in different places.</p><p>&#128073; Example: Suppose you log user actions. If you write the logging code in multiple controllers, small differences (like log format) might creep in.</p><p>Using a trait ensures <strong>consistent logging</strong>:</p><pre><code>$this-&gt;logAction('User Deleted', ['user_id' =&gt; $id]);</code></pre><p>Everywhere you use it &#8594; logs will always look the same.</p><p>&#9989; Prevents mistakes, keeps logs standardized.</p><h4>4. <strong>Faster Development</strong></h4><p>When your team builds features, traits save time since developers don&#8217;t have to rewrite logic.</p><p>&#128073; Example: A team working on <strong>different modules</strong> (Posts, Orders, Products) may need <strong>soft delete logging</strong>.</p><p>Instead of rewriting:</p><pre><code>Log::info("Record deleted", ['id' =&gt; $id, 'model' =&gt; self::class]);</code></pre><p>They can just:</p><pre><code>$this-&gt;logDelete($id);</code></pre><p>&#9989; Faster coding, less debugging, fewer bugs.</p><h4>5. <strong>Works Across Unrelated Classes</strong></h4><p>Inheritance in PHP is limited (a class can only extend one parent). Traits solve this by letting you reuse code across <strong>completely unrelated classes</strong>.</p><p>&#128073; Example:</p><ul><li><p><code>Post</code> model (extends <code>Model</code>)</p></li><li><p><code>UserController</code> (extends <code>Controller</code>)</p></li></ul><p>Both can use the same <code>UserActionLogger</code> trait, even though they don&#8217;t share a parent class.</p><p>&#9989; Traits break the limitations of inheritance.</p><h4>6. <strong>Testability &amp; Maintainability</strong></h4><p>Traits make your codebase easier to maintain and test.</p><p>&#128073; Example:<br>If you have a <strong>trait for payment gateway logging</strong>, you can write <strong>unit tests for the trait</strong> itself instead of testing every class that uses it.</p><pre><code>use Tests\TestCase;
use App\Traits\PaymentLogger;

class PaymentLoggerTest extends TestCase
{
    use PaymentLogger;

    public function test_it_logs_payment()
    {
        $this-&gt;logPayment('PayPal', 100);
        $this-&gt;assertTrue(true); // Check log file manually or mock logging
    }
}</code></pre><p>&#9989; Test once, confidence everywhere.</p><div><hr></div><h2>&#128683; Don&#8217;t Abuse Traits in Laravel</h2><p>Traits are powerful, but if you start using them for <strong>everything</strong>, your code will become confusing, hard to maintain, and even buggy.</p><p>&#128073; The rule of thumb is:</p><blockquote><p><strong>Use traits for small, reusable behaviors.</strong><br>If logic is big, complex, or belongs to a specific domain &#8594; use a <strong>service class</strong> or <strong>inheritance</strong> instead.</p><div><hr></div></blockquote><h3>&#10060; Bad Example &#8211; Abusing Traits</h3><p>Imagine you&#8217;re building an <strong>e-commerce app</strong> and you put <strong>all payment logic</strong> inside a trait:</p><pre><code>&lt;?php
namespace App\Traits;

trait PaymentTrait
{
    public function processStripePayment($amount) {
        // Stripe API logic
    }

    public function processPaypalPayment($amount) {
        // PayPal API logic
    }

    public function applyDiscount($cart, $code) {
        // Discount logic
    }

    public function calculateTax($cart) {
        // Tax calculation logic
    }

    public function generateInvoice($order) {
        // Invoice PDF logic
    }
}</code></pre><p>Then you dump this trait into multiple controllers:</p><pre><code>class CheckoutController extends Controller {
    use PaymentTrait;
}

class OrderController extends Controller {
    use PaymentTrait;
}</code></pre><p>&#9888;&#65039; Problems:</p><ul><li><p>Trait is doing <strong>too much</strong> (payment, discount, tax, invoices).</p></li><li><p>Hard to test &#8212; you can&#8217;t easily mock/replace one piece of logic.</p></li><li><p>Changes in payment logic may accidentally break unrelated features.</p></li><li><p>Controllers lose their focus (business logic sneaks in).</p></li></ul><p>This is <strong>trait abuse</strong>.</p><h3>&#9989; Good Example &#8211; Proper Trait Usage</h3><p>Instead, keep <strong>small, focused traits</strong>.<br>For example, create a trait just for <strong>logging payments</strong>:</p><pre><code>&lt;?php
namespace App\Traits;

use Illuminate\Support\Facades\Log;

trait PaymentLogger
{
    public function logPayment($method, $amount)
    {
        Log::info("Payment processed", [
            'method' =&gt; $method,
            'amount' =&gt; $amount,
            'user_id' =&gt; auth()-&gt;id(),
            'time' =&gt; now(),
        ]);
    }
}</code></pre><p>Use it in your services:</p><pre><code>class StripeService {
    use PaymentLogger;

    public function pay($amount) {
        // Stripe API logic
        $this-&gt;logPayment('stripe', $amount);
    }
}

class PaypalService {
    use PaymentLogger;

    public function pay($amount) {
        // PayPal API logic
        $this-&gt;logPayment('paypal', $amount);
    }
}</code></pre><p>&#128073; Benefits:</p><ul><li><p><strong>Focused trait</strong>: Only does logging.</p></li><li><p><strong>Complex logic</strong> (Stripe/PayPal payments) stays in proper service classes.</p></li><li><p>Easy to test &#8212; you can test the <code>PaymentLogger</code> trait separately.</p></li><li><p>Code remains clean and organized.</p></li></ul><div><hr></div><h2>&#127919; Key Takeaways</h2><ul><li><p>&#9989; Use traits for <strong>small, reusable behaviors</strong> (logging, slugs, API responses).</p></li><li><p>&#9989; Keep traits <strong>focused</strong> (one responsibility).</p></li><li><p>&#10060; Don&#8217;t put <strong>business logic</strong> (like payment, discounts, or order handling) inside traits.</p></li><li><p>&#10060; Don&#8217;t use traits as a &#8220;dumping ground&#8221; for random functions.</p></li><li><p>&#9989; If logic grows too large &#8594; create a <strong>service class</strong> instead.</p></li></ul><div><hr></div><h2>&#127916;Conclusion</h2><p>Traits in Laravel are a fantastic way to <strong>reuse code, simplify cross-cutting concerns, and keep your classes lean.</strong> Used wisely, they can save you from duplication and keep your code elegant.</p><p>But remember:</p><ul><li><p>Keep traits small and focused.</p></li><li><p>Don&#8217;t use them as a dumping ground.</p></li><li><p>Consider service classes or observers for more complex logic.</p></li></ul><p>Next time you find yourself writing the same methods across multiple models, ask yourself: <em>&#8220;Is this a good fit for a trait, or should it live in a service?&#8221;</em></p><p>Used thoughtfully, traits can make your Laravel codebase more modular, expressive, and maintainable.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[7 PHP Operators That Will Change the Way You Write Laravel Code]]></title><description><![CDATA[Level up your code with modern, high&#8209;impact syntax]]></description><link>https://newsletter.priteshbhanushali.com/p/7-php-operators-that-will-change</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/7-php-operators-that-will-change</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 06 Sep 2025 13:35:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DiOV!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92a0aac7-a3c0-44be-97d4-f77aaa58306c_593x593.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Like many Laravel developers, I used to wrestle with walls of <code>if</code> statements and endless conditionals that made my code feel heavier than it should be. Then I discovered a handful of PHP operators that felt like secret weapons. Suddenly, my code was leaner, more readable, and &#8212; dare I say &#8212; fun to write again. Here are seven operators that can do the same for you.</p><div><hr></div><h2>&#11088; Null&#8209;Safe Operator (?-&gt;)</h2><p>The <strong>null-safe operator (</strong><code>?-&gt;</code><strong>)</strong> is a feature in PHP 8+ that makes handling nullable values much cleaner. Instead of writing long <code>if</code> conditions or using <code>isset</code>, you can safely "chain" method calls or property access even if something in the chain is <code>null</code>.</p><h4>&#128678; The Problem Without <code>?-&gt;</code></h4><p>Suppose you&#8217;re working on a <strong>Laravel insurance app</strong> where a <code>User</code> may or may not have a <code>Policy</code>.</p><pre><code>$policyNumber = null;

if ($user-&gt;policy) {
    if ($user-&gt;policy-&gt;details) {
        $policyNumber = $user-&gt;policy-&gt;details-&gt;number;
    }
}</code></pre><p>This works, but look at the <strong>nested if checks</strong> &#128531; &#8212; very verbose.</p><h4>&#9989; Using the Null-Safe Operator</h4><p>With <code>?-&gt;</code>, you can collapse all that:</p><pre><code>$policyNumber = $user-&gt;policy?-&gt;details?-&gt;number;</code></pre><ul><li><p>If <code>policy</code> is <code>null</code>, it immediately stops and returns <code>null</code>.</p></li><li><p>If <code>details</code> is <code>null</code>, it also stops and returns <code>null</code>.</p></li><li><p>Otherwise, it gives you the <code>number</code>.</p></li></ul><p>So <code>$policyNumber</code> will either be the policy number <strong>or</strong> <code>null</code> without throwing an error.</p><h4>&#10024; Best Practice</h4><ul><li><p>Use <code>?-&gt;</code> for optional relationships or deeply nested data.</p></li><li><p>Don&#8217;t overuse it where you <strong>expect data to always exist</strong> &#8212; in those cases, better to throw an error than silently return <code>null</code>.</p></li><li><p>Combine it with the null coalescing operator <code>??</code> to provide defaults:</p></li></ul><div><hr></div><h2>&#11088;Null Coalescing Assignment (??=)</h2><p><strong>Null Coalescing Assignment (</strong><code>??=</code><strong>)</strong> in PHP 7.4+ &#8212; it&#8217;s like a shortcut for setting a default value <strong>only when a variable is </strong><code>null</code><strong> or not set</strong>.</p><h4>&#128678; The Problem Without <code>??=</code></h4><p>Imagine you want to set a default <strong>timezone</strong> for users in a Laravel app:</p><pre><code>if (!isset($user-&gt;timezone)) {
    $user-&gt;timezone = 'UTC';
}</code></pre><p>Or, using the <strong>null coalescing operator (</strong><code>??</code><strong>)</strong>:</p><pre><code>$user-&gt;timezone = $user-&gt;timezone ?? 'UTC';</code></pre><p>That&#8217;s already shorter &#8212; but it <strong>reassigns the variable explicitly</strong>.</p><h4>&#9989; Using <code>??=</code></h4><p>With <code>??=</code>, you can make it even cleaner:</p><pre><code>$user-&gt;timezone ??= 'UTC';</code></pre><p>This means:</p><ul><li><p>If <code>$user-&gt;timezone</code> is <code>null</code> or not set &#8594; assign <code>'UTC'</code>.</p></li><li><p>Otherwise, leave it as is.</p></li></ul><h4>&#10024; Best Practice</h4><ul><li><p>Use <code>??=</code> for <strong>default values</strong> in configs, request handling, or optional variables.</p></li><li><p>Avoid using it where <strong>values must always be present</strong> &#8212; in those cases, validation is better (<code>$request-&gt;validate()</code> in Laravel).</p></li><li><p>Combine it with arrays when filling optional keys:</p></li></ul><pre><code>$options['limit'] ??= 10;
$options['sort'] ??= 'asc';</code></pre><div><hr></div><h2>&#11088;Spread Operator (...)</h2><p>The spread operator (<code>...</code>) allows you to <strong>&#8220;unpack&#8221; arrays or arguments</strong> into a function, array, or constructor.<br>Think of it as taking a list of items and "spreading" them out one by one.</p><h4>1&#65039;&#8419; Function Arguments</h4><p>Instead of manually passing every array element, you can unpack it:</p><pre><code>function sendNotification($user, $message, $type) {
    echo "Sending {$type} notification to {$user}: {$message}";
}

$args = ['John', 'Your order is ready!', 'email'];

// Without spread
sendNotification($args[0], $args[1], $args[2]);

// With spread
sendNotification(...$args);</code></pre><h4>2&#65039;&#8419; Array Merging</h4><p>Before PHP 7.4, merging arrays required <code>array_merge()</code>:</p><pre><code>$defaults = ['status' =&gt; 'active', 'role' =&gt; 'user'];
$overrides = ['role' =&gt; 'admin'];

// Old way
$final = array_merge($defaults, $overrides);

// Spread operator
$final = [...$defaults, ...$overrides];</code></pre><p>&#9989; Much shorter, and it feels like JavaScript&#8217;s spread syntax.</p><h4>&#10024; Best Practices</h4><ul><li><p>Use it for <strong>clean array merging</strong> (instead of <code>array_merge()</code> everywhere).</p></li><li><p>Great for <strong>seeding, configs, and middleware stacking</strong> in Laravel.</p></li><li><p>Don&#8217;t overuse in deep nested structures &#8212; readability can suffer.</p></li></ul><div><hr></div><h2>&#11088;Match Expression (match)</h2><h4>&#128678; The Problem Without (match)</h4><p>Suppose you want to determine the <strong>order status label</strong> in a Laravel e-commerce app:</p><pre><code>$status = 'shipped';

switch ($status) {
    case 'pending':
        $label = 'Order Pending';
        break;
    case 'shipped':
        $label = 'Order Shipped';
        break;
    case 'delivered':
        $label = 'Order Delivered';
        break;
    default:
        $label = 'Unknown Status';
}</code></pre><p>Problems:</p><ul><li><p>Lots of boilerplate (<code>break;</code> everywhere).</p></li><li><p>Can accidentally <strong>fall through</strong> if you forget <code>break</code>.</p></li><li><p>Verbose compared to modern syntax.</p></li></ul><h4>&#9989; With <code>match</code></h4><pre><code>$status = 'shipped';

$label = match ($status) {
    'pending'   =&gt; 'Order Pending',
    'shipped'   =&gt; 'Order Shipped',
    'delivered' =&gt; 'Order Delivered',
    default     =&gt; 'Unknown Status',
};</code></pre><p><strong>Why it&#8217;s better:</strong></p><ul><li><p><strong>No fallthrough</strong> risk &#8212; each case is <strong>strictly compared (</strong><code>===</code><strong>)</strong>.</p></li><li><p>More compact and <strong>returns a value directly</strong>.</p></li><li><p>Cleaner, especially for mappings.</p></li></ul><h4>&#10024; Best Practices</h4><ul><li><p>Use <code>match</code> instead of <code>switch</code> for <strong>value mapping</strong>.</p></li><li><p>Great for <strong>status codes, enums, role-based UI, and service resolution</strong>.</p></li><li><p>Always handle the <code>default</code> case (unless you&#8217;re 100% sure you&#8217;ve covered all).</p></li><li><p>Combine with <strong>exceptions</strong> in default if unexpected values are a bug.</p></li></ul><div><hr></div><h2>&#11088; Spaceship Operator (&lt;=&gt;)</h2><p><strong>Spaceship Operator (</strong><code>&lt;=&gt;</code><strong>)</strong> is another modern PHP feature that&#8217;s small but super useful, especially for comparisons and sorting.</p><p>The spaceship operator (<code>&lt;=&gt;</code>) is a <strong>combined comparison operator</strong> that returns:</p><ul><li><p><code>-1</code> if the left side is <strong>less than</strong> the right</p></li><li><p><code>0</code> if they are <strong>equal</strong></p></li><li><p><code>1</code> if the left side is <strong>greater than</strong> the right</p></li></ul><p>So instead of writing multiple <code>if/else</code> comparisons, you can do it in <strong>one line</strong>.</p><h4>&#128678; The Problem Without &lt;=&gt;</h4><p>Suppose you want to compare two numbers:</p><pre><code>$a = 5;
$b = 10;

if ($a &lt; $b) {
    $result = -1;
} elseif ($a == $b) {
    $result = 0;
} else {
    $result = 1;
}</code></pre><p>That&#8217;s <strong>too much boilerplate</strong> just to compare values.</p><h4>&#9989; With <code>&lt;=&gt;</code></h4><pre><code>$a = 5;
$b = 10;

$result = $a &lt;=&gt; $b; // -1</code></pre><ul><li><p>If <code>$a = 5</code> and <code>$b = 10</code>, result is <code>-1</code></p></li><li><p>If <code>$a = 10</code> and <code>$b = 10</code>, result is <code>0</code></p></li><li><p>If <code>$a = 15</code> and <code>$b = 10</code>, result is <code>1</code></p></li></ul><p>Clean and elegant &#128076;</p><h4>1&#65039;&#8419; Sorting Arrays</h4><p>Imagine you want to sort products by <strong>price</strong>:</p><pre><code>$products = [
    ['name' =&gt; 'Product A', 'price' =&gt; 200],
    ['name' =&gt; 'Product B', 'price' =&gt; 100],
    ['name' =&gt; 'Product C', 'price' =&gt; 300],
];

// Without spaceship
usort($products, function ($a, $b) {
    if ($a['price'] == $b['price']) return 0;
    return ($a['price'] &lt; $b['price']) ? -1 : 1;
});

// With spaceship
usort($products, fn($a, $b) =&gt; $a['price'] &lt;=&gt; $b['price']);</code></pre><h4>2&#65039;&#8419; Multi-level Sorting</h4><p>Sort users first by <strong>role</strong>, then by <strong>name</strong>:</p><pre><code>$users = [
    ['name' =&gt; 'Alice', 'role' =&gt; 'editor'],
    ['name' =&gt; 'Bob', 'role' =&gt; 'admin'],
    ['name' =&gt; 'Charlie', 'role' =&gt; 'admin'],
];

usort($users, function ($a, $b) {
    return [$a['role'], $a['name']] &lt;=&gt; [$b['role'], $b['name']];
});</code></pre><p>&#9989; Both comparisons are done in one go.</p><h4>3&#65039;&#8419;Comparing Dates</h4><pre><code>$date1 = new DateTime('2024-01-01');
$date2 = new DateTime('2025-01-01');

$result = $date1 &lt;=&gt; $date2; // -1</code></pre><p>&#9989; Works with numbers, strings, and objects that implement <code>compareTo</code>.</p><h4>&#10024; Best Practices</h4><ul><li><p>Use <code>&lt;=&gt;</code> in <strong>sorting callbacks</strong> &#8212; it reduces clutter dramatically.</p></li><li><p>Great for <strong>multi-field sorting</strong> (combine values in an array).</p></li><li><p>Avoid using it for <strong>simple boolean checks</strong> (e.g., <code>$a &gt; $b</code>) &#8212; regular operators are more readable.</p></li></ul><div><hr></div><h2>&#11088;Null Coalescing (??) vs. Elvis (?:)</h2><p><strong>Null Coalescing (</strong><code>??</code><strong>)</strong> and the <strong>Elvis operator (</strong><code>?:</code><strong>)</strong> look similar but behave differently. Many devs mix them up, so let&#8217;s break it down with <strong>clear examples.</strong></p><h3>&#9889; Null Coalescing (<code>??</code>)</h3><ul><li><p>Introduced in PHP 7.</p></li><li><p>Checks if a variable is <strong>set and not null</strong>.</p></li><li><p>If it exists and isn&#8217;t null &#8594; returns the value.</p></li><li><p>Otherwise &#8594; returns the fallback.</p></li></ul><h4>Example</h4><pre><code>$username = $_GET['user'] ?? 'Guest';</code></pre><ul><li><p>If <code>$_GET['user']</code> exists and is not null &#8594; <code>$username = $_GET['user']</code>.</p></li><li><p>Otherwise &#8594; <code>$username = 'Guest'</code>.</p></li></ul><p>&#9989; Safe for <strong>undefined variables/array keys</strong> (no notice error).</p><h2>&#9889; Elvis Operator (<code>?:</code>)</h2><ul><li><p>A shorthand for <strong>ternary without the middle part</strong>.</p></li><li><p>Evaluates <strong>truthiness</strong> (not just null).</p></li><li><p>If the value is truthy &#8594; returns it.</p></li><li><p>If falsy (null, false, 0, <code>''</code>, <code>[]</code>) &#8594; returns the fallback.</p></li></ul><h3>Example</h3><pre><code><code>$username = $_GET['user'] ?: 'Guest';</code></code></pre><ul><li><p>If <code>$_GET['user']</code> is truthy &#8594; <code>$username = $_GET['user']</code>.</p></li><li><p>If falsy (<code>null</code>, empty string, <code>0</code>, <code>false</code>) &#8594; <code>$username = 'Guest'</code>.</p></li></ul><p>&#10071; Difference: Here, even an <strong>empty string</strong> or <strong>0</strong> will fall back, unlike <code>??</code>.<br></p><h4>&#128269; Side-by-Side Difference</h4><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hNA0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hNA0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 424w, https://substackcdn.com/image/fetch/$s_!hNA0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 848w, https://substackcdn.com/image/fetch/$s_!hNA0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 1272w, https://substackcdn.com/image/fetch/$s_!hNA0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hNA0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png" width="944" height="154" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:154,&quot;width&quot;:944,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14208,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/172799593?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hNA0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 424w, https://substackcdn.com/image/fetch/$s_!hNA0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 848w, https://substackcdn.com/image/fetch/$s_!hNA0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 1272w, https://substackcdn.com/image/fetch/$s_!hNA0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cd327e2-02e4-47f3-9a0c-e2500654f8dd_944x154.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Examples-2</h3><h4>1. Null Coalescing (<code>??</code>) for Defaults</h4><pre><code><code>$limit = $request-&gt;input('limit') ?? 10;</code></code></pre><ul><li><p>If <code>limit</code> is missing/null &#8594; use <code>10</code>.</p></li><li><p>If <code>limit = 0</code> &#8594; still <code>0</code> (valid).</p></li></ul><h4>2. Elvis (<code>?:</code>) for Falsy Checks</h4><pre><code><code>$username = $request-&gt;input('username') ?: 'Guest';</code></code></pre><ul><li><p>If <code>username = ""</code> (empty string) &#8594; fallback to <code>'Guest'</code>.</p></li><li><p>If <code>username = "John"</code> &#8594; keep <code>"John"</code>.</p></li></ul><h4>&#10024; Best Practice</h4><ul><li><p>Use <code>??</code> when you care about <strong>null/undefined only</strong> (like request params, config values).</p></li><li><p>Use <code>?:</code> when you care about <strong>any falsy value</strong> (like optional form fields).</p></li><li><p>Always think: <em>&#8220;Do I want </em><code>0</code><em> or </em><code>''</code><em> to be considered valid or not?&#8221;</em></p></li></ul><div><hr></div><h2>&#11088;Null Coalescing Nesting &amp; Chaining</h2><h4>&#128678; The Problem Multiple fallbacks with nested ternaries get messy.</h4><pre><code>$value = isset($a) ? $a : (isset($b) ? $b : $c);</code></pre><h4>&#9989; With</h4><pre><code>$value = $a ?? $b ?? $c;</code></pre><p>&#9889; <strong>Why It Rocks:</strong> Chain any number of defaults cleanly &#8212; no parentheses or nested calls.</p><h4>Example</h4><p>Suppose you want to get a user&#8217;s phone number. The phone can come from multiple places:</p><ol><li><p>Profile phone</p></li><li><p>Contact phone</p></li><li><p>Fallback: <code>"Not Provided"</code></p></li></ol><h5>Old Way</h5><pre><code>$phone = null;

if (isset($user-&gt;profile) &amp;&amp; isset($user-&gt;profile-&gt;phone)) {
    $phone = $user-&gt;profile-&gt;phone;
} elseif (isset($user-&gt;contact) &amp;&amp; isset($user-&gt;contact-&gt;phone)) {
    $phone = $user-&gt;contact-&gt;phone;
} else {
    $phone = 'Not Provided';
}</code></pre><p>&#128553; Lots of <code>if/else</code>, very verbose.</p><h5>&#9989; With <code>??</code> (Clean Way)</h5><pre><code>$phone = $user-&gt;profile?-&gt;phone 
      ?? $user-&gt;contact?-&gt;phone 
      ?? 'Not Provided';</code></pre><ul><li><p>First tries <code>$user-&gt;profile-&gt;phone</code>.</p></li><li><p>If null, tries <code>$user-&gt;contact-&gt;phone</code>.</p></li><li><p>If still null, defaults to <code>'Not Provided'</code>.</p></li></ul><p>&#9989; Short, expressive, and safe.</p><h4>&#10024; Best Practices</h4><ul><li><p>Use <strong>nesting (</strong><code>??</code><strong>)</strong> for <strong>layered defaults</strong> (multi-source input, config).</p></li><li><p>Combine with <strong>null-safe operator (</strong><code>?-&gt;</code><strong>)</strong> for safe chaining of object/relationship access.</p></li><li><p>Don&#8217;t over-nest &#8212; if you have more than 2&#8211;3 fallbacks, extract into a method for readability.</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 Mastering Laravel HTTP Client: Build Scalable API Integrations the Right Way]]></title><description><![CDATA[Learn how to structure reusable service classes, handle errors gracefully, and scale API integrations with ease.]]></description><link>https://newsletter.priteshbhanushali.com/p/mastering-laravel-http-client-build</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/mastering-laravel-http-client-build</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 31 Aug 2025 12:32:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HnDv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HnDv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HnDv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 424w, https://substackcdn.com/image/fetch/$s_!HnDv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 848w, https://substackcdn.com/image/fetch/$s_!HnDv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 1272w, https://substackcdn.com/image/fetch/$s_!HnDv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HnDv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png" width="1010" height="503" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:503,&quot;width&quot;:1010,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:132976,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/172325512?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HnDv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 424w, https://substackcdn.com/image/fetch/$s_!HnDv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 848w, https://substackcdn.com/image/fetch/$s_!HnDv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 1272w, https://substackcdn.com/image/fetch/$s_!HnDv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b2a1979-4e84-4b34-ab12-6a57b56e8867_1010x503.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When building modern web applications, interacting with external APIs has become a core requirement. Whether you&#8217;re integrating with <strong>payment gateways, third-party CRMs, or fetching weather data</strong>, you need a reliable way to send and receive HTTP requests.</p><p>In Laravel, the <strong>HTTP Client</strong> (introduced in Laravel 7, powered by Guzzle under the hood) makes working with APIs <strong>elegant, expressive, and developer-friendly</strong>.</p><div><hr></div><h2>&#128313; Why Use Laravel&#8217;s HTTP Client?</h2><p>Before Laravel 7, developers often used <strong>Guzzle directly</strong> or other libraries. But it required extra setup and didn&#8217;t feel very "Laravel-ish."</p><p>Laravel&#8217;s HTTP Client solves this by providing:<br>&#9989; A <strong>clean, expressive API</strong><br>&#9989; Built-in <strong>JSON handling</strong><br>&#9989; <strong>Automatic retries &amp; error handling</strong><br>&#9989; Support for <strong>asynchronous requests</strong><br>&#9989; Easy integration with <strong>testing and fakes</strong></p><div><hr></div><h2>&#128313; What is Guzzle?</h2><p>Guzzle is a powerful PHP HTTP client. It&#8217;s feature-rich and widely used outside Laravel.</p><p>With Guzzle, you get:<br>&#10004; Full control over requests<br>&#10004; Middleware support<br>&#10004; Streaming large files<br>&#10004; Cookie/session handling<br>&#10004; Advanced async workflows</p><p>Laravel developers usually don&#8217;t need raw Guzzle unless doing something advanced &#8212; but it&#8217;s good to know both.</p><div><hr></div><h3>&#9989; Using Laravel HTTP Client</h3><pre><code>use Illuminate\Support\Facades\Http;

$response = Http::get('https://jsonplaceholder.typicode.com/posts/1');

$post = $response-&gt;json();</code></pre><p>&#128073; <code>$response-&gt;json()</code> instantly gives you an array.</p><h3>&#9989; Using Guzzle</h3><pre><code>use GuzzleHttp\Client;

$client = new Client(['base_uri' =&gt; 'https://jsonplaceholder.typicode.com']);

$response = $client-&gt;request('GET', '/posts/1');

$post = json_decode($response-&gt;getBody()-&gt;getContents(), true);</code></pre><p>&#128073; With Guzzle, you need to <strong>manually decode</strong> JSON.</p><div><hr></div><h3>&#128313; Real-World Example : Payment Gateway &#128179;</h3><h5>Laravel HTTP Client</h5><pre><code>$response = Http::withHeaders([
    'Authorization' =&gt; 'Bearer ' . config('services.payment.api_key'),
])-&gt;post('https://api.paymentprovider.com/payments', [
    'amount' =&gt; 1000,
    'currency' =&gt; 'USD',
    'customer_id' =&gt; 12345,
]);

if ($response-&gt;failed()) {
    Log::error('Payment failed', ['response' =&gt; $response-&gt;body()]);
} else {
    $paymentData = $response-&gt;json();
}</code></pre><h5>Guzzle Alternative</h5><pre><code>$client = new Client();

$response = $client-&gt;request('POST', 'https://api.paymentprovider.com/payments', [
    'headers' =&gt; [
        'Authorization' =&gt; 'Bearer ' . env('PAYMENT_API_KEY')
    ],
    'form_params' =&gt; [
        'amount' =&gt; 1000,
        'currency' =&gt; 'USD',
        'customer_id' =&gt; 12345
    ]
]);

$paymentData = json_decode($response-&gt;getBody()-&gt;getContents(), true);</code></pre><div><hr></div><h3>&#128680; Error Handling</h3><p>Laravel HTTP Client makes error handling easier:</p><pre><code>$response-&gt;successful();  // true if 200-level
$response-&gt;failed();      // true if 400 or 500
$response-&gt;clientError(); // true if 400-level
$response-&gt;serverError(); // true if 500-level</code></pre><p>In Guzzle, you&#8217;d need to <strong>catch exceptions</strong>:</p><pre><code>try {
    $response = $client-&gt;request('GET', $url);
} catch (\GuzzleHttp\Exception\ClientException $e) {
    // Handle 400-level errors
} catch (\GuzzleHttp\Exception\ServerException $e) {
    // Handle 500-level errors
}</code></pre><div><hr></div><h3>&#128338;Retries &amp; Timeouts</h3><p>Laravel HTTP Client</p><pre><code>$response = Http::retry(3, 200)-&gt;timeout(10)-&gt;get($url);</code></pre><p>Guzzle</p><pre><code>$client = new Client([
    'timeout' =&gt; 10,
    'retry' =&gt; 3 // Requires middleware for custom retry logic
]);</code></pre><p>&#128073; Retry logic is <strong>built-in</strong> for Laravel, but requires <strong>extra setup in Guzzle</strong>.</p><div><hr></div><h3>&#128313; Testing with Fakes</h3><p>Laravel HTTP Client makes API testing easy:</p><pre><code>Http::fake([
    'api.paymentprovider.com/*' =&gt; Http::response(['status' =&gt; 'success'], 200),
]);

$response = Http::post('https://api.paymentprovider.com/payments', [
    'amount' =&gt; 500,
]);

$response-&gt;json(); // ['status' =&gt; 'success']</code></pre><p>In Guzzle, you&#8217;d need <strong>MockHandler</strong>, which is more verbose.</p><div><hr></div><h2>&#128313; Which Should You Use?</h2><ul><li><p><strong>Laravel HTTP Client</strong> &#128073; If you&#8217;re working inside a Laravel app (cleaner syntax, testing support, retries, easy JSON handling).</p></li><li><p><strong>Guzzle</strong> &#128073; If you need <strong>advanced features</strong> (streaming, complex middleware, custom transports, or if you&#8217;re working outside Laravel).</p></li></ul><div><hr></div><h3>&#128260; Building a Reusable HTTP Client for Multiple Services in Laravel</h3><p>Modern Laravel applications often need to talk to <strong>multiple APIs</strong> &#8212; payment gateways, weather services, CRMs, notification systems, etc.</p><p>If you have <strong>10 different services</strong> to integrate with, you don&#8217;t want to copy-paste the same <code>Http::get()</code> logic everywhere. That would be:<br>&#10060; Hard to maintain<br>&#10060; Difficult to test<br>&#10060; Messy when adding retries/logging</p><p>Instead, we can design a <strong>clean, centralized HTTP client approach</strong> that is:</p><p>&#9989; <strong>Reusable</strong> across all services<br>&#9989; <strong>Configurable</strong> with <code>.env</code> variables<br>&#9989; <strong>Testable</strong> with <code>Http::fake()</code><br>&#9989; <strong>Maintainable</strong> when scaling from 2 &#8594; 10+ services</p><h4>&#128313; Step 1: Store API Configurations in <code>config/services.php</code></h4><p>Instead of hardcoding API keys or URLs, keep them in <code>.env</code> and <code>config/services.php</code>.</p><pre><code>// config/services.php
return [
    'payment' =&gt; [
        'base_uri' =&gt; env('PAYMENT_API_URL', 'https://api.paymentprovider.com'),
        'token'    =&gt; env('PAYMENT_API_KEY'),
    ],

    'weather' =&gt; [
        'base_uri' =&gt; env('WEATHER_API_URL', 'https://api.openweathermap.org/data/2.5'),
        'token'    =&gt; env('WEATHER_API_KEY'),
    ],

    // ... add other 8 services here
];</code></pre><p>&#128073; Benefits:</p><ul><li><p>Easy to manage all API credentials in <strong>one place</strong></p></li><li><p>No sensitive data in code (kept in <code>.env</code>)</p></li><li><p>Simple to switch between staging/production APIs</p></li></ul><h4>&#128313; Step 2: Create a Base HTTP Service</h4><p>We&#8217;ll create a <strong>base class</strong> that defines how our app talks to APIs (headers, retries, timeouts, logging).</p><pre><code>// app/Services/BaseHttpService.php
namespace App\Services;

use Illuminate\Support\Facades\Http;

abstract class BaseHttpService
{
    protected string $baseUri;
    protected array $headers = [];

    protected function client()
    {
        return Http::withHeaders($this-&gt;headers)
            -&gt;baseUrl($this-&gt;baseUri)
            -&gt;retry(3, 200) // retry 3 times, 200ms apart
            -&gt;timeout(10);  // 10 second timeout
    }
}</code></pre><p>&#128073; Every service (Payment, Weather, etc.) will <strong>extend this base class</strong>, so we don&#8217;t repeat retry/timeout logic.</p><h4>&#128313; Step 3: Create Individual Service Classes</h4><p>Each API gets its <strong>own service class</strong> for clarity and single responsibility.</p><pre><code>// app/Services/PaymentService.php
namespace App\Services;

class PaymentService extends BaseHttpService
{
    public function __construct()
    {
        $this-&gt;baseUri = config('services.payment.base_uri');
        $this-&gt;headers = [
            'Authorization' =&gt; 'Bearer ' . config('services.payment.token'),
            'Accept'        =&gt; 'application/json',
        ];
    }

    public function createPayment(array $data)
    {
        return $this-&gt;client()-&gt;post('/payments', $data)-&gt;json();
    }

    public function getPayment(string $id)
    {
        return $this-&gt;client()-&gt;get("/payments/{$id}")-&gt;json();
    }
}</code></pre><pre><code>// app/Services/WeatherService.php
namespace App\Services;

class WeatherService extends BaseHttpService
{
    public function __construct()
    {
        $this-&gt;baseUri = config('services.weather.base_uri');
        $this-&gt;headers = [
            'Accept' =&gt; 'application/json',
        ];
    }

    public function getWeather(string $city)
    {
        $response = $this-&gt;client()-&gt;get('/weather', [
            'q' =&gt; $city,
            'appid' =&gt; config('services.weather.token'),
            'units' =&gt; 'metric',
        ]);

        return $response-&gt;successful()
            ? $response-&gt;json()
            : ['error' =&gt; 'Unable to fetch weather data'];
    }
}</code></pre><h4>&#128313; Step 4: Use Dependency Injection in Controllers</h4><p>Since each service is a dedicated class, you can inject it directly into your controllers.</p><pre><code>use App\Services\PaymentService;
use App\Services\WeatherService;

class ApiController extends Controller
{
    public function checkout(PaymentService $paymentService)
    {
        $payment = $paymentService-&gt;createPayment([
            'amount' =&gt; 1500,
            'currency' =&gt; 'USD',
            'customer_id' =&gt; 123,
        ]);

        return response()-&gt;json($payment);
    }

    public function forecast(WeatherService $weatherService)
    {
        $weather = $weatherService-&gt;getWeather('New York');
        return response()-&gt;json($weather);
    }
}</code></pre><p>&#128073; Now, your controllers stay <strong>clean</strong> and don&#8217;t care about API logic.</p><div><hr></div><h1>&#128313; Best Practices</h1><p>&#10004; Keep all API credentials in <code>.env</code> &amp; <code>config/services.php</code><br>&#10004; Use a <strong>BaseHttpService</strong> to avoid repeated code<br>&#10004; Create <strong>dedicated service classes</strong> for each API<br>&#10004; Use <strong>Dependency Injection</strong> instead of <code>new Class()</code><br>&#10004; Always handle errors with <code>failed()</code>, <code>throw()</code>, or retries<br>&#10004; Use <code>Http::fake()</code> in tests to avoid hitting real APIs</p><div><hr></div><h2>&#127919; Wrapping Up</h2><p>Both <strong>Laravel HTTP Client</strong> and <strong>Guzzle</strong> are excellent choices for consuming APIs.</p><ul><li><p><strong>Laravel HTTP Client</strong> gives you an expressive, developer-friendly wrapper for common tasks &#8212; perfect for clean, testable, and maintainable code.</p></li><li><p><strong>Guzzle</strong> gives you full power and flexibility when you need advanced features or fine-grained control.</p></li></ul><p>For most Laravel projects, the <strong>HTTP Client</strong> will cover everything you need while keeping your code elegant and simple. When things get complex, you always have the option to drop down into Guzzle&#8217;s full capabilities.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🔢 Stop Using Magic Numbers in Code — Use Constants for Clarity and Maintainability]]></title><description><![CDATA[Magic Numbers Are Killing Your Code &#8212; Here&#8217;s How to Fix It in Laravel]]></description><link>https://newsletter.priteshbhanushali.com/p/stop-using-magic-numbers-in-code</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/stop-using-magic-numbers-in-code</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 23 Aug 2025 14:03:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DiOV!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92a0aac7-a3c0-44be-97d4-f77aaa58306c_593x593.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When building applications in Laravel, making your code work is only the first step &#8212; keeping it clean and maintainable is what really matters in the long run. One of the easiest mistakes developers make, even experienced ones, is relying on <em>magic numbers</em> &#8212; hard-coded values scattered throughout the code.</p><p>They might feel like a quick fix, but as your project grows, those numbers turn into confusion, slow down debugging, and make future updates painful. Picture opening your Laravel project months later and seeing values like <code>7</code>, <code>365</code>, or <code>5000</code> with no explanation &#8212; were they IDs, limits, or timeouts? This is the trap of magic numbers. By replacing them with clear constants, you instantly give meaning to your values, improve readability, and write code that feels professional and easy to maintain.</p><p>&#9203; <em>&#8220;Magic numbers save seconds now, but cost hours later.&#8221;</em></p><div><hr></div><p>Ever looked at a piece of code and thought:</p><pre><code>if ($user-&gt;role_id === 3) {
    // do something
}</code></pre><p>At first glance, you might understand the logic. But two weeks later, or when another developer joins the project, the question arises:</p><p>&#128073; &#8220;What does <code>3</code> even mean here?&#8221;</p><p>That&#8217;s the danger of <strong>magic numbers</strong> &#8212; numeric (or string) literals sprinkled throughout your code without context.</p><p>In this article, I&#8217;ll explain why magic numbers are harmful, and how <strong>using constants</strong> makes your Laravel projects more readable, maintainable, and bug-free.</p><div><hr></div><h2>&#128680; The Problem with Magic Numbers</h2><p>Magic numbers are values used directly in code without explanation. They may work in the short term, but they create confusion and technical debt in the long run.</p><h3>Why they&#8217;re bad:</h3><ul><li><p>&#10067; <strong>Lack of clarity</strong>: Nobody knows what <code>3</code> or <code>7</code> represents.</p></li><li><p>&#128260; <strong>Hard to update</strong>: If you need to change the value, you&#8217;ll have to hunt down every occurrence.</p></li><li><p>&#128027; <strong>Prone to bugs</strong>: Copy-paste errors and inconsistent values creep in.</p></li><li><p>&#129489;&#8205;&#129309;&#8205;&#129489; <strong>Poor collaboration</strong>: New developers on the team will struggle to understand the code.</p></li></ul><p>Let&#8217;s look at a quick example:</p><pre><code>// Controller
public function store(Request $request)
{
    // 365 represents subscription days, but it&#8217;s unclear
    $expiryDate = now()-&gt;addDays(365);

    // 2 represents user type (e.g., admin), but nobody knows that by just looking at it
    if ($request-&gt;user_type == 2) {
        // do something for admin
    }
}</code></pre><p>At first glance, this code works. But if someone new joins your team &#8212; or if you revisit it months later &#8212; it&#8217;s not clear what <code>365</code> or <code>2</code> mean. You&#8217;ll waste time figuring it out.</p><div><hr></div><h2>&#9989; The Solution: Use Constants</h2><p>Here&#8217;s the same code refactored with constants:</p><pre><code>// constants.php (or config / enums in Laravel)
const SUBSCRIPTION_VALIDITY_DAYS = 365;
const USER_TYPE_ADMIN = 2;

// Controller
public function store(Request $request)
{
    $expiryDate = now()-&gt;addDays(SUBSCRIPTION_VALIDITY_DAYS);

    if ($request-&gt;user_type == USER_TYPE_ADMIN) {
        // do something for admin
    }
}</code></pre><p>Now, your code is <strong>self-explanatory</strong>. Anyone can understand what those values mean at a glance.</p><div><hr></div><h2>&#9881;&#65039; Managing Constants in Laravel Projects</h2><p>There are multiple ways to manage constants in a Laravel project and which approach you choose depends on <strong>project size, maintainability, and usage scope</strong>.:</p><h4>&#128313; 1. Using <code>config/</code> Files (Best for Large Projects)</h4><p>Laravel encourages keeping constants inside <strong>config files</strong> since they are <strong>cached</strong> and easily accessible.</p><p>&#128073; Example: create a file <code>config/constants.php</code></p><pre><code>&lt;?php

return [
    'ROLES' =&gt; [
        'ADMIN' =&gt; 'admin',
        'USER' =&gt; 'user',
        'MANAGER' =&gt; 'manager',
    ],

    'STATUS' =&gt; [
        'ACTIVE' =&gt; 1,
        'INACTIVE' =&gt; 0,
    ],

    'CURRENCY' =&gt; 'USD',
];</code></pre><p>Usage:</p><pre><code>// Anywhere in your code
$role = config('constants.ROLES.ADMIN');
$status = config('constants.STATUS.ACTIVE');</code></pre><p>&#9989; <strong>When to use?</strong></p><ul><li><p>Large projects with many constants.</p></li><li><p>When you want to change values without touching the codebase (just config).</p></li><li><p>Works perfectly with <strong>config caching (</strong><code>php artisan config:cache</code><strong>)</strong>.</p><p></p></li></ul><h4>&#128313; 2. Using PHP Class Constants (For Domain-Specific Values)</h4><p>Create a <code>Constants</code> class or multiple domain-specific classes.</p><p>&#128073; Example: <code>app/Constants/UserRoles.php</code></p><pre><code>&lt;?php

namespace App\Constants;

class UserRoles
{
    public const ADMIN = 'admin';
    public const USER = 'user';
    public const MANAGER = 'manager';
}</code></pre><p>Usage:</p><pre><code>use App\Constants\UserRoles;

if ($user-&gt;role === UserRoles::ADMIN) {
    // Do something
}</code></pre><p>&#9989; <strong>When to use?</strong></p><ul><li><p>Constants are tied to a <strong>domain entity</strong> (like roles, statuses).</p></li><li><p>You want <strong>strict namespacing</strong> and <strong>static analysis support</strong>.</p><p></p></li></ul><h4>&#128313; 3. Using PHP Enums (Laravel 9+ &amp; PHP 8.1+)</h4><p>PHP 8.1 introduced <strong>Enums</strong>, which are much cleaner than class constants for predefined values.</p><p>&#128073; Example: <code>app/Enums/OrderStatus.php</code></p><pre><code>&lt;?php

namespace App\Enums;

enum OrderStatus: string
{
    case PENDING = 'pending';
    case COMPLETED = 'completed';
    case CANCELED = 'canceled';
}</code></pre><p>Usage:</p><pre><code>use App\Enums\OrderStatus;

$order-&gt;status = OrderStatus::PENDING-&gt;value;

if ($order-&gt;status === OrderStatus::COMPLETED-&gt;value) {
    // ...
}</code></pre><p>&#9989; <strong>When to use?</strong></p><ul><li><p>Laravel 9+ with PHP 8.1+.</p></li><li><p>Best for <strong>status codes, roles, types</strong> where the value set is <strong>strictly limited</strong>.</p></li></ul><div><hr></div><h2>&#127919; Best Practices for Constants in Laravel</h2><p>&#10004;&#65039; <strong>Use meaningful names</strong> &#8594; <code>MAX_LOGIN_ATTEMPTS</code> is better than <code>5</code>.<br>&#10004;&#65039; <strong>Group related constants</strong> &#8594; Don&#8217;t mix user roles with timeout values.<br>&#10004;&#65039; <strong>Keep them DRY (Don&#8217;t Repeat Yourself)</strong> &#8594; Define once, use everywhere.<br>&#10004;&#65039; <strong>Use Enums where possible</strong> &#8594; Cleaner and type-safe.</p><div><hr></div><h2>&#127942; Benefits of Replacing Magic Numbers</h2><p>&#9989; <strong>Readability</strong> &#8594; Your code tells a story without extra comments.<br>&#9989; <strong>Maintainability</strong> &#8594; Change the constant in one place, and it reflects everywhere.<br>&#9989; <strong>Collaboration</strong> &#8594; Team members instantly understand what values mean.<br>&#9989; <strong>Bug Prevention</strong> &#8594; No risk of mismatched or forgotten values.</p><div><hr></div><h2>&#127873; Wrapping Up</h2><p>Magic numbers might feel convenient in the moment, but they become costly as your project grows. By replacing them with <strong>constants</strong>, you write code that is <strong>clean, maintainable, and professional</strong>.</p><p>Next time you&#8217;re tempted to hard-code a number, stop and ask:<br>&#128073; <em>&#8220;Will I (or my team) understand this six months from now?&#8221;</em></p><p>If the answer is <strong>no</strong>, make it a constant.<br>Your future self &#8212; and your teammates &#8212; will thank you.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[🔍 Tracing Ghosts in Distributed Systems: Correlation IDs in Laravel Made Simple]]></title><description><![CDATA[Tracking Requests with Correlation ID in Laravel microservices]]></description><link>https://newsletter.priteshbhanushali.com/p/tracing-ghosts-in-distributed-systems</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/tracing-ghosts-in-distributed-systems</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 17 Aug 2025 14:31:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!tTmH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In distributed systems or microservices architecture, a single user request often flows through multiple services and logs. When an error occurs or performance issues arise, it becomes critical to <strong>trace the entire request flow</strong>. This is where <strong>Correlation IDs</strong> come in.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tTmH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tTmH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 424w, https://substackcdn.com/image/fetch/$s_!tTmH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 848w, https://substackcdn.com/image/fetch/$s_!tTmH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 1272w, https://substackcdn.com/image/fetch/$s_!tTmH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tTmH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png" width="1051" height="707" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:707,&quot;width&quot;:1051,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:163860,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/171050942?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tTmH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 424w, https://substackcdn.com/image/fetch/$s_!tTmH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 848w, https://substackcdn.com/image/fetch/$s_!tTmH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 1272w, https://substackcdn.com/image/fetch/$s_!tTmH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F329bddfd-bee1-4319-8960-1aac075c5279_1051x707.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>&#129504; What Is a Correlation ID?</h2><p>A <strong>Correlation ID</strong> is a unique identifier (usually a UUID) assigned to each incoming request. It helps trace that request across services and systems by tagging it in logs and headers.</p><p>Think of it like a request &#8220;tracking number.&#8221; You can search logs and trace the full journey of a request by this ID.</p><div><hr></div><h2>&#9989; Why Use Correlation IDs?</h2><p>Imagine a front-end request that touches:</p><pre><code>Frontend &#8594; Laravel API &#8594; Microservice A &#8594; Microservice B &#8594; DB</code></pre><p>Without a correlation ID, debugging is painful. With it, you can trace:</p><ul><li><p>Where the error occurred</p></li><li><p>How long each step took</p></li><li><p>What services were called</p></li></ul><p>And all this with just one search term &#8212; the correlation ID.</p><div><hr></div><h2>&#128736; How to Implement Correlation ID in Laravel</h2><h4>&#129513;Create Middleware</h4><p>Let&#8217;s build a middleware that will:</p><ul><li><p>Read <code>X-Correlation-ID</code> from the incoming request header (if provided)</p></li><li><p>Generate one if missing</p></li><li><p>Store it in the request attributes</p></li><li><p>Add it to logs and the response headers</p></li></ul><p>&#128196; <code>app/Http/Middleware/CorrelationIdMiddleware.php</code></p><pre><code>&lt;?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;

class CorrelationIdMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $correlationId = $request-&gt;header('X-Correlation-ID') ?? (string) Str::uuid();

        // Set the correlation ID in the request for future use
        $request-&gt;attributes-&gt;set('correlationId', $correlationId);

        // Attach correlation ID to the logging context
        Log::withContext([
            'correlationId' =&gt; $correlationId,
        ]);

        $response = $next($request);

        // Add correlation ID to response headers
        $response-&gt;headers-&gt;set('X-Correlation-ID', $correlationId);

        return $response;
    }
}</code></pre><h4>&#129513;Register the Middleware</h4><p>Register it globally in your app so it runs on every request.</p><p>&#128196; <code>app/Http/Kernel.php</code></p><pre><code>protected $middleware = [
    // ...
    \App\Http\Middleware\CorrelationIdMiddleware::class,
];</code></pre><h4>&#129513;Logging With Correlation ID</h4><p>Now, anywhere in your Laravel app &#8212; controller, service, model &#8212; you can simply use:</p><pre><code>Log::info('Processing payment');</code></pre><p>You don't need to manually pass the <code>correlationId</code>. It&#8217;s already part of the global logging context thanks to <code>Log::withContext()</code>.</p><div><hr></div><h2>Example:</h2><h5>OrderController.php</h5><pre><code>&lt;?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\OrderService;

class OrderController extends Controller
{
    protected $orderService;

    public function __construct(OrderService $orderService)
    {
        $this-&gt;orderService = $orderService;
    }

    public function create(Request $request)
    {
        $correlationId = $request-&gt;attributes-&gt;get('correlationId');

        $order = $this-&gt;orderService-&gt;createOrder($request-&gt;all());

        return response()-&gt;json([
            'message' =&gt; 'Order created successfully',
            'order' =&gt; $order,
            'correlationId' =&gt; $correlationId
        ]);
    }
}</code></pre><h5>PaymentController.php</h5><pre><code>&lt;?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\PaymentService;

class PaymentController extends Controller
{
    protected $paymentService;

    public function __construct(PaymentService $paymentService)
    {
        $this-&gt;paymentService = $paymentService;
    }

    public function process(Request $request)
    {
        $correlationId = $request-&gt;attributes-&gt;get('correlationId');

        $payment = $this-&gt;paymentService-&gt;processPayment($request-&gt;all());

        return response()-&gt;json([
            'message' =&gt; 'Payment processed successfully',
            'payment' =&gt; $payment,
            'correlationId' =&gt; $correlationId
        ]);
    }
}</code></pre><h5>OrderService.php</h5><pre><code>&lt;?php

namespace App\Services;

use Illuminate\Support\Facades\Log;

class OrderService
{
    public function createOrder(array $data)
    {
        Log::info("Creating new order");

        // Simulate order creation
        $order = [
            'id' =&gt; rand(1000, 9999),
            'items' =&gt; $data['items'] ?? [],
            'total' =&gt; $data['total'] ?? 0
        ];

        Log::info("Order created successfully", ['orderId' =&gt; $order['id']]);

        return $order;
    }
}</code></pre><h5>PaymentService.php</h5><pre><code>&lt;?php

namespace App\Services;

use Illuminate\Support\Facades\Log;

class PaymentService
{
    public function processPayment(array $data)
    {
        Log::info("Processing payment");

        // Simulate payment processing
        $payment = [
            'payment_id' =&gt; rand(10000, 99999),
            'order_id' =&gt; $data['order_id'] ?? null,
            'amount' =&gt; $data['amount'] ?? 0,
            'status' =&gt; 'success'
        ];

        Log::info("Payment processed successfully", [
            'paymentId' =&gt; $payment['payment_id'],
            'orderId' =&gt; $payment['order_id'],
            
        ]);

        return $payment;
    }
}</code></pre><h5>routes/api.php</h5><pre><code>use App\Http\Controllers\OrderController;
use App\Http\Controllers\PaymentController;

Route::post('/order', [OrderController::class, 'create'])-&gt;middleware('correlation');
Route::post('/payment', [PaymentController::class, 'process'])-&gt;middleware('correlation');</code></pre><h5>Logs:</h5><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lwWg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lwWg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 424w, https://substackcdn.com/image/fetch/$s_!lwWg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 848w, https://substackcdn.com/image/fetch/$s_!lwWg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 1272w, https://substackcdn.com/image/fetch/$s_!lwWg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lwWg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png" width="1456" height="476" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:476,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:159237,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/171050942?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lwWg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 424w, https://substackcdn.com/image/fetch/$s_!lwWg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 848w, https://substackcdn.com/image/fetch/$s_!lwWg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 1272w, https://substackcdn.com/image/fetch/$s_!lwWg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37e48120-3107-4711-83aa-9f0fd9a6c6a3_2048x670.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#9989; Now both <strong>Order</strong> and <strong>Payment logs</strong> are tied with the same <code>correlationId</code>.<br>This helps you trace the entire request lifecycle across services.</p><div><hr></div><h2>&#128257; Passing Correlation ID to External Services</h2><p>If your Laravel app calls other services, be sure to forward the correlation ID:</p><pre><code>use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-Correlation-ID' =&gt; request()-&gt;attributes-&gt;get('correlationId')
])-&gt;get('http://api.internal/payment');</code></pre><div><hr></div><h2>&#128073; <strong>&#8220;But What About Queued Jobs?&#8221;</strong></h2><p>So far, correlation IDs have been flowing smoothly across requests and services. But what happens when you dispatch a job to the queue?<br>Surprise: your correlationId doesn&#8217;t automatically follow along. Why? Because jobs run in a completely separate process from the web request. That means our precious correlationId is left behind&#8230; unless we explicitly carry it forward.</p><p>Laravel queues <strong>don&#8217;t automatically carry the correlation ID</strong>. If you need it in jobs, pass it manually:</p><pre><code>// Dispatch job with correlationId
dispatch(new MyJob($data, correlation_id()));

// In the Job
public function handle()
{
    Log::withContext(['correlationId' =&gt; $this-&gt;correlationId]);
    Log::info("Processing payment inside job...");
}</code></pre><p>And store it in the job to set the log context again when the job runs.</p><div><hr></div><h2>&#128230; Bonus: Helper Function (Optional)</h2><p>Add this to a global helpers file:</p><pre><code>if (!function_exists('correlation_id')) {
    function correlation_id(): ?string {
        return request()?-&gt;attributes-&gt;get('correlationId');
    }
}</code></pre><p>Now you can use <code>correlation_id()</code> anywhere in your app.</p><div><hr></div><h2>&#10024; Final Thoughts</h2><p>Implementing a Correlation ID strategy in Laravel improves:</p><ul><li><p>Debugging across services</p></li><li><p>Consistency in logs</p></li><li><p>Traceability in production environments</p></li></ul><p>This is especially useful in microservice systems, APIs, and any complex application where logs are your best source of truth.</p><p>With just one middleware and a few config changes, you gain powerful observability into your Laravel app. Start small, and you&#8217;ll thank yourself the next time you&#8217;re debugging a production issue!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[⏱The 2 AM Debugging Nightmare That Taught Me the Power of Logging]]></title><description><![CDATA[When &#8216;Something Went Wrong&#8217; Isn&#8217;t Enough &#8212; Why Logging Matters More Than You Think]]></description><link>https://newsletter.priteshbhanushali.com/p/the-2-am-debugging-nightmare-that</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/the-2-am-debugging-nightmare-that</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 16 Aug 2025 10:40:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fblz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>&#8220;Something went wrong.&#8221;</em><br>Three words that can ruin a developer&#8217;s day &#8212; or night.<br>At 2 AM, staring at a blinking cursor in the terminal, you realize those are the only words your application decided to leave you. No context. No clues. Just an expensive game of hide-and-seek with the root cause.</p><p>Logging isn&#8217;t glamorous. It&#8217;s rarely the star of the sprint demo, and it doesn&#8217;t get applause in release notes. But when your system goes rogue, <strong>logs are your only map through the chaos</strong>.</p><p>If code is the heart of your application, logs are its memory. Without logs, debugging is guesswork, troubleshooting takes forever, and security incidents can go unnoticed.<br>Good logging isn&#8217;t just about writing messages &#8212; it&#8217;s about recording the <em>right information</em> in the <em>right way</em> at the <em>right time</em>.</p><h2>&#128221;What is Logging?</h2><p>In software development, <strong>logging</strong> means recording information about a program&#8217;s execution so you can review it later. These records (logs) can include:</p><ul><li><p>Events (e.g., "User logged in", "Order placed")</p></li><li><p>Errors and exceptions</p></li><li><p>Performance metrics</p></li><li><p>System states and variable values</p></li></ul><p>Logs are usually written to files, databases, or centralized log management tools like <strong>Datadog, Splunk, ELK (Elasticsearch + Logstash + Kibana), CloudWatch</strong>, etc.</p><p>A log entry usually contains:</p><ul><li><p><strong>Timestamp</strong> &#8211; When it happened</p></li><li><p><strong>Severity level</strong> &#8211; DEBUG, INFO, WARN, ERROR, etc.</p></li><li><p><strong>Message</strong> &#8211; What happened</p></li><li><p><strong>Context</strong> &#8211; User ID, request ID, related data</p></li></ul><div><hr></div><h2>&#9989;Why is Logging Important?</h2><p>Without logs, debugging is like trying to find a black cat in a dark room &#8212; when there might not even be a cat. &#128049;&#8205;&#128187;</p><p><strong>Benefits of Logging:</strong></p><ul><li><p><strong>Debugging</strong> &#8211; Find and fix issues faster.</p></li><li><p><strong>Auditing</strong> &#8211; Track who did what and when.</p></li><li><p><strong>Monitoring</strong> &#8211; Spot unusual patterns or security breaches.</p></li><li><p><strong>Compliance</strong> &#8211; Meet regulatory requirements in finance, healthcare, etc.</p></li><li><p><strong>Performance Analysis</strong> &#8211; Identify bottlenecks and optimize.</p></li></ul><div><hr></div><h2>&#128295;How Logging Works in Laravel</h2><p>Laravel&#8217;s logging configuration lives in <code>config/logging.php</code>.<br>You define <strong>channels</strong> &#8212; think of them as destinations for your logs.</p><p>Example: Basic Logging</p><pre><code>use Illuminate\Support\Facades\Log;

Log::info('User logged in.', ['user_id' =&gt; auth()-&gt;id()]);
Log::warning('Profile update failed.', ['user_id' =&gt; 5]);
Log::error('Payment failed.', ['order_id' =&gt; 12345, 'amount' =&gt; 250]);</code></pre><h4>&#129513; Laravel Log Levels</h4><ul><li><p><code>emergency</code></p></li><li><p><code>alert</code></p></li><li><p><code>critical</code></p></li><li><p><code>error</code></p></li><li><p><code>warning</code></p></li><li><p><code>notice</code></p></li><li><p><code>info</code></p></li><li><p><code>debug</code></p></li></ul><h4>&#129514; Custom Log Channel</h4><p>in <code>logging.php</code></p><pre><code>'channels' =&gt; [
    'payment' =&gt; [
        'driver' =&gt; 'single',
        'path' =&gt; storage_path('logs/payment.log'),
        'level' =&gt; 'info',
    ],
],</code></pre><p>Usage:</p><pre><code>Log::channel('payment')-&gt;info('Payment initiated.', ['order_id' =&gt; 6789]);</code></pre><div><hr></div><h2>&#129504;How to Log Like a Pro</h2><p>Logging is easy &#8212; logging well is a skill. Here&#8217;s how:</p><h4>&#129513;Use a Logging Framework</h4><p>Don&#8217;t reinvent the wheel with <code>echo</code> or <code>print_r</code>. Use built-in or external logging tools:</p><ul><li><p><strong>Laravel:</strong> <code>Log</code> facade</p></li><li><p><strong>Python:</strong> <code>logging</code> module</p></li><li><p><strong>Node.js:</strong> <code>winston</code>, <code>pino</code></p></li></ul><h4>&#129513;Choose the Right Log Level</h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fblz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fblz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 424w, https://substackcdn.com/image/fetch/$s_!fblz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 848w, https://substackcdn.com/image/fetch/$s_!fblz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 1272w, https://substackcdn.com/image/fetch/$s_!fblz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fblz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png" width="1034" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1034,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fblz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 424w, https://substackcdn.com/image/fetch/$s_!fblz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 848w, https://substackcdn.com/image/fetch/$s_!fblz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 1272w, https://substackcdn.com/image/fetch/$s_!fblz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F34b3fcac-b855-4be2-99a9-db9817230d65_1034x728.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>&#129513;Log in a Structured Format</h4><p>Structured logs (like JSON) are easier to search and analyze:</p><pre><code>{
  "timestamp": "2025-08-11T10:15:22Z",
  "level": "ERROR",
  "message": "Payment failed",
  "order_id": 7891,
  "user_id": 102,
  "error": "Insufficient funds",
  "correlation_id": "ac3f5c1d-4b91-4c91-b5ea-9baf1b90b2d4"
}</code></pre><h4>&#129513;Add Context</h4><p>Every log should answer:</p><ul><li><p>What happened?</p></li><li><p>Where did it happen?</p></li><li><p>Who was involved?</p></li><li><p>What was the state at the time?</p></li></ul><pre><code>use Illuminate\Support\Facades\Log;

try {
    // Simulate a payment processing failure
    $paymentGateway-&gt;charge($order-&gt;amount, $order-&gt;payment_method);
} catch (\Exception $e) {
    Log::error('Payment processing failed', [
        // &#9989; What happened?
        'event' =&gt; 'payment_failed',

        // &#9989; Where did it happen?
        'service' =&gt; 'PaymentService',
        'method' =&gt; __METHOD__,
        'file' =&gt; __FILE__,
        'line' =&gt; __LINE__,

        // &#9989; Who was involved?
        'user_id' =&gt; auth()-&gt;id(),
        'order_id' =&gt; $order-&gt;id,

        // &#9989; What was the state at the time?
        'order_amount' =&gt; $order-&gt;amount,
        'payment_method' =&gt; $order-&gt;payment_method,
        'gateway_response' =&gt; $e-&gt;getMessage(),

        // Extra good practice: correlation/tracking ID
        'correlation_id' =&gt; request()-&gt;header('X-Correlation-ID') ?? Str::uuid()
    ]);
}</code></pre><h4>&#129513;Use Correlation IDs</h4><p>In distributed systems, a <strong>correlation ID</strong> lets you trace a request across multiple services.</p><pre><code>public function handle($request, Closure $next)
{
    $correlationId = $request-&gt;header('X-Correlation-ID') ?? Str::uuid();
    Log::withContext(['correlation_id' =&gt; $correlationId]);
    return $next($request);
}</code></pre><div><hr></div><h2>&#128230;Best Practices for Effective Logging</h2><p>&#9989; <strong>Log only what matters</strong> &#8212; Too much noise hides real issues.<br>&#9989; <strong>Avoid sensitive data</strong> &#8212; Never log passwords, tokens, or personal info.<br>&#9989; <strong>Use the right level</strong> &#8212; Don&#8217;t mark everything as <code>ERROR</code>.<br>&#9989; <strong>Centralize logs</strong> &#8212; Use ELK, Datadog, or CloudWatch for aggregation.<br>&#9989; <strong>Rotate and archive</strong> &#8212; Prevent storage bloat.<br>&#9989; <strong>Set up alerts</strong> &#8212; Notify on repeated errors or suspicious activity.</p><div><hr></div><h2>&#128683;Common Logging Mistakes</h2><p>&#10060; Logging everything at DEBUG and INFO in production (hurts performance).<br>&#10060; Using vague messages like &#8220;Something failed&#8221; without details.<br>&#10060; Dumping large objects without summarizing.<br>&#10060; Ignoring log retention and storage limits.</p><div><hr></div><h2>&#128161;Benefits of Logging Like a Pro</h2><ul><li><p><strong>Fast troubleshooting</strong> &#8211; Fewer &#8220;where did it break?&#8221; moments.</p></li><li><p><strong>Better collaboration</strong> &#8211; Ops, QA, and dev teams read the same story.</p></li><li><p><strong>Security visibility</strong> &#8211; Detect anomalies early.</p></li><li><p><strong>Compliance-ready</strong> &#8211; Easy to audit.</p></li><li><p><strong>Performance insights</strong> &#8211; Identify slow spots without intrusive profiling.</p></li></ul><div><hr></div><h2>&#129496;Final Thoughts</h2><p>Logging is the <strong>black box recorder</strong> of your application. When implemented thoughtfully, it becomes your most reliable ally in debugging, monitoring, and securing your system.</p><p>So next time you write a log, ask yourself:</p><blockquote><p>&#8220;Will this message help me (or someone else) understand the full story later?&#8221;</p></blockquote><p>Logging is not just recording history &#8212; it&#8217;s writing tomorrow&#8217;s answers today.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Silent But Powerful: Laravel Observers]]></title><description><![CDATA[Invisible Architects of Laravel Applications]]></description><link>https://newsletter.priteshbhanushali.com/p/silent-but-powerful-laravel-observers</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/silent-but-powerful-laravel-observers</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Tue, 12 Aug 2025 05:01:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!MXu0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#8220;In software, as in life, the most powerful systems are those that work quietly in the background.&#8221; As Laravel developers, we often focus on building elegant controllers, expressive routes, and beautifully designed frontends. But behind every elegant system lies <strong>invisible intelligence</strong> &#8212; logic that responds to events without being manually triggered every time. In Laravel, this invisible layer is brought to life by <strong>Observers</strong>.</p><div><hr></div><h2>&#129504; What Is a Laravel Observer?</h2><p>A Laravel Observer is a <strong>dedicated class</strong> that listens for <strong>model events</strong> such as:</p><ul><li><p><code>creating</code>, <code>created</code></p></li><li><p><code>updating</code>, <code>updated</code></p></li><li><p><code>saving</code>, <code>saved</code></p></li><li><p><code>deleting</code>, <code>deleted</code></p></li><li><p><code>restoring</code>, <code>restored</code></p></li></ul><p>Think of it like a <strong>watcher</strong> assigned to a specific Eloquent model. When something happens to that model &#8212; a record is created, updated, or deleted &#8212; the observer steps in to run predefined logic.</p><div><hr></div><h2>&#128640; Why Use Observers?</h2><h3>Without Observers:</h3><p>You write the same logic (e.g., logging or notifications) in multiple controllers or services. Your code becomes scattered and hard to maintain.</p><h3>With Observers:</h3><p>You encapsulate all that logic in one dedicated class that responds <strong>automatically</strong> to changes &#8212; like a rulebook attached to a model&#8217;s life.</p><h4>&#128269; Real-World Examples:</h4><ul><li><p>Send a welcome email when a user is created</p></li><li><p>Log updates to sensitive user data</p></li><li><p>Track when a record is soft deleted for audit</p></li><li><p>Create a record in a secondary table on update</p></li><li><p>Sync external APIs on creation</p></li></ul><div><hr></div><h2>&#128295; How to Implement an Observer</h2><p>Let&#8217;s say we have a model <code>User</code>. Every time a new user is created, we want to:</p><ul><li><p>Send a welcome email.</p></li><li><p>Log the registration in an activity log table.</p></li></ul><h4>&#128736; Step 1: Create the Observer</h4><pre><code>php artisan make:observer UserObserver --model=User</code></pre><p>This generates <code>app/Observers/UserObserver.php</code>.</p><h4>&#128736; Step 2: Define Observer Methods</h4><pre><code>namespace App\Observers;

use App\Models\User;
use App\Jobs\SendWelcomeEmail;
use Illuminate\Support\Facades\Log;

class UserObserver
{
    public function created(User $user)
    {
        // Send welcome email
        SendWelcomeEmail::dispatch($user);

        // Log the creation
        Log::info("New user registered: " . $user-&gt;email);
    }

    public function updated(User $user)
    {
        Log::info("User updated: " . $user-&gt;email);
    }

    public function deleted(User $user)
    {
        Log::warning("User deleted: " . $user-&gt;email);
    }
}</code></pre><h4>&#128736; Step 3: Register the Observer</h4><p>In <code>App\Providers\EventServiceProvider.php</code></p><pre><code>use App\Models\User;
use App\Observers\UserObserver;

public function boot(): void
{
    User::observe(UserObserver::class);
}</code></pre><div><hr></div><h2>&#9888;&#65039; Gotchas: When Observers Are Not Triggered</h2><p>While observers are powerful, they do <strong>not</strong> trigger in every case. Here are some common situations where they remain silent:</p><h4>&#10060; 1. Using <code>insert()</code> Instead of <code>create()</code></h4><pre><code>$data = [
    ['name' =&gt; 'Alice', 'email' =&gt; 'alice@example.com'],
    ['name' =&gt; 'Bob', 'email' =&gt; 'bob@example.com'],
];

User::insert($data); // &#10060; Observer won't trigger</code></pre><p><code>insert()</code> performs a bulk insert without creating Eloquent models &#8212; so no <code>creating</code> or <code>created</code> events are fired.</p><h4>&#10060; 2. Using Query Builder or Raw SQL</h4><pre><code>DB::table('persons')-&gt;update(['email' =&gt; 'test@example.com']); // &#10060; No events</code></pre><p>Only Eloquent model operations like <code>create()</code>, <code>update()</code>, <code>save()</code>, <code>delete()</code> trigger observers.</p><p></p><div><hr></div><h2>&#128188; When Should You Use Observers?</h2><h4>Use Observers When:</h4><p>&#9989; You want to centralize repetitive logic (e.g., audit logging, notifications)<br>&#9989; You want to enforce lifecycle behavior for a model<br>&#9989; You need to act automatically on model changes across the system</p><h4>Avoid Observers When:</h4><p>&#10060; You&#8217;re doing heavy bulk operations<br>&#10060; You want to manually control side-effects<br>&#10060; Your logic is complex and tightly coupled to other models (prefer services instead)</p><div><hr></div><h2>&#127760; Real-World Use Cases</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MXu0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MXu0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 424w, https://substackcdn.com/image/fetch/$s_!MXu0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 848w, https://substackcdn.com/image/fetch/$s_!MXu0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 1272w, https://substackcdn.com/image/fetch/$s_!MXu0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MXu0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png" width="852" height="618" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:618,&quot;width&quot;:852,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MXu0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 424w, https://substackcdn.com/image/fetch/$s_!MXu0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 848w, https://substackcdn.com/image/fetch/$s_!MXu0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 1272w, https://substackcdn.com/image/fetch/$s_!MXu0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e9b8fb-0e35-44f6-acc8-12585f2e7556_852x618.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#129718; Final Thoughts</h2><p>When used well, <strong>Laravel Observers</strong> help you write elegant, scalable code while embracing the age-old wisdom:</p><blockquote><p><em>&#8220;Do not chase every moment. Instead, build systems that respond when the moment comes.&#8221;</em></p></blockquote><p>They are your trusted watchmen &#8212; always present, always ready. Give them the right tasks, and they will repay you with stability, clarity, and peace of mind.</p><p>In the end, Observers remind us that precision doesn&#8217;t have to be noisy. The quietest parts of a system often carry the heaviest responsibilities.</p><p>So the next time you build a Laravel model, ask yourself:<br><strong>What silent responsibilities can I hand over to an Observer?</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletters ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🛡️ How Laravel Helps You Filter Bad Data]]></title><description><![CDATA[Stop Dirty Data: Laravel Validation Tips You Can&#8217;t Ignore]]></description><link>https://newsletter.priteshbhanushali.com/p/how-laravel-helps-you-filter-bad</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/how-laravel-helps-you-filter-bad</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 03 Aug 2025 06:01:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!m2H3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m2H3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m2H3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 424w, https://substackcdn.com/image/fetch/$s_!m2H3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 848w, https://substackcdn.com/image/fetch/$s_!m2H3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 1272w, https://substackcdn.com/image/fetch/$s_!m2H3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m2H3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png" width="1035" height="542" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:542,&quot;width&quot;:1035,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:127393,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/169918939?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!m2H3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 424w, https://substackcdn.com/image/fetch/$s_!m2H3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 848w, https://substackcdn.com/image/fetch/$s_!m2H3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 1272w, https://substackcdn.com/image/fetch/$s_!m2H3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F630698a4-23dd-4ffe-a1d2-73b4a9f4514e_1035x542.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In any modern web application, <strong>validating user input</strong> is not just a best practice&#8212;it's essential. Whether you're building a small blog or a massive enterprise API, <strong>Laravel makes input validation elegant, powerful, and flexible</strong>.</p><h2>&#9989; What is Request Validation?</h2><p>Request validation is the process of ensuring that incoming data is in the expected format before processing it. Laravel provides a fluent, expressive interface for defining validation rules directly within your controllers, form requests, or even custom classes.</p><div><hr></div><h2>&#128640; Why Validation is Important</h2><p>Here are just a few reasons why validation is non-negotiable:</p><ul><li><p><strong>Security</strong>: Prevent malicious input like SQL injection or XSS attacks.</p></li><li><p><strong>Data Integrity</strong>: Keep your database clean and consistent.</p></li><li><p><strong>User Experience</strong>: Provide clear error messages when something goes wrong.</p></li><li><p><strong>Reliability</strong>: Avoid processing incomplete or incorrect data.</p></li></ul><div><hr></div><h2>&#129504; Laravel Validation Strategies</h2><p>Laravel gives us multiple ways to handle validation. Let&#8217;s explore each with examples and when to use them:</p><h4>1. <strong>Inline Validation in Controllers (Quick &amp; Simple)</strong></h4><p>Add validation logic directly inside the controller:</p><pre><code>public function store(Request $request)
{
    $validated = $request-&gt;validate([
        'name' =&gt; 'required|string|max:255',
        'email' =&gt; 'required|email|unique:users,email',
    ]);

    // Continue with your logic...
}</code></pre><h4>2. <strong>Form Request Validation (Recommended)</strong></h4><pre><code>Use <code>php artisan make:request StoreUserRequest</code></code></pre><pre><code>class StoreUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' =&gt; 'required|string|max:255',
            'email' =&gt; 'required|email|unique:users,email',
        ];
    }

    public function authorize(): bool
    {
        return true; // add permission logic if needed
    }
}</code></pre><p>In your controller:</p><pre><code>public function store(StoreUserRequest $request)
{
    $validated = $request-&gt;validated();
}</code></pre><h4>&#128172; Customize the Error Message</h4><p>add in <code>StoreUserRequest</code></p><pre><code>public function messages()
{
    return [
        'name.required' =&gt; 'Please provide your name.',
        'name.min' =&gt; 'Your name must be at least :min characters.',
        'email.required' =&gt; 'We need your email address.',
        'email.email' =&gt; 'This is not a valid email format.',
        'email.unique' =&gt; 'This email is already registered.',
        'password.required' =&gt; 'A password is mandatory.',
        'password.min' =&gt; 'Password must be at least :min characters.',
        'password.confirmed' =&gt; 'Passwords do not match.',
    ];
}</code></pre><div><hr></div><h3>&#129514; How to Add Custom Validation Rule</h3><p>Create a custom rule called <code>AlphaSpace</code> that ensures a field only contains <strong>letters (a&#8211;z)</strong> and <strong>spaces</strong>. We'll apply this rule to a user's <strong>full name</strong> field.</p><h5>&#129513; Step 1: Generate the Custom Rule</h5><pre><code>php artisan make:rule AlphaSpace</code></pre><p>This will create a file at:</p><pre><code>app/Rules/AlphaSpace.php</code></pre><h5>&#129513; Step 2: Define the Rule Logic</h5><p>Open <code>AlphaSpace.php</code> and update it like this:</p><pre><code>namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class AlphaSpace implements Rule
{
    public function passes($attribute, $value): bool
    {
        // Allow only letters and spaces
        return preg_match('/^[a-zA-Z\s]+$/', $value);
    }

    public function message(): string
    {
        return 'The :attribute may only contain letters and spaces.';
    }
}</code></pre><h5>&#129513; Step 3: Use the Rule in a Form Request</h5><p>In <code>RegisterUserRequest.php</code></p><pre><code>use App\Rules\AlphaSpace;

public function rules(): array
{
    return [
        'full_name' =&gt; ['required', new AlphaSpace],
        'email' =&gt; 'required|email|unique:users,email',
        'password' =&gt; 'required|string|min:8|confirmed',
    ];
}</code></pre><h5>&#129513; Step 4: Use the Request in Controller</h5><pre><code>use App\Http\Requests\RegisterUserRequest;

public function register(RegisterUserRequest $request)
{
    $validated = $request-&gt;validated();

    // You can now safely use $validated['full_name']
}</code></pre><h5>&#129514; Example Inputs</h5><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3tEk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3tEk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 424w, https://substackcdn.com/image/fetch/$s_!3tEk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 848w, https://substackcdn.com/image/fetch/$s_!3tEk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 1272w, https://substackcdn.com/image/fetch/$s_!3tEk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3tEk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png" width="982" height="224" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:224,&quot;width&quot;:982,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16282,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/169918939?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3tEk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 424w, https://substackcdn.com/image/fetch/$s_!3tEk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 848w, https://substackcdn.com/image/fetch/$s_!3tEk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 1272w, https://substackcdn.com/image/fetch/$s_!3tEk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F82420b6a-f0c7-442f-b64f-f179e23db78a_982x224.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>&#9989; Benefits of Custom Rules</h2><ul><li><p>Encapsulates logic in a reusable class</p></li><li><p>Makes your validation cleaner and modular</p></li><li><p>Great for domain-specific validation (e.g., policy number, PAN, GSTIN, etc.)</p></li></ul><div><hr></div><h2>&#9989; Benefits of Using Laravel Validation</h2><ul><li><p>&#128683; Prevents bad data from entering your system</p></li><li><p>&#128161; Clear and readable syntax</p></li><li><p>&#128230; Reusable with Form Requests</p></li><li><p>&#128295; Customizable with custom rules</p></li><li><p>&#128260; Automatically returns JSON errors for APIs</p></li></ul><div><hr></div><h2>&#129496; Wrapping Up</h2><p>In this article, we covered how to implement and organize request validation effectively in Laravel. You learned:</p><ul><li><p>&#9989; <strong>Inline Validation</strong><br>A quick and simple method perfect for small tasks, one-off features, or prototyping.</p></li><li><p>&#128196; <strong>Form Request Validation</strong><br>A clean and reusable approach ideal for larger applications, keeping your controllers slim and focused.</p></li><li><p>&#129513; <strong>Custom Validation Rules</strong><br>A powerful way to enforce domain-specific or business-critical logic that goes beyond built-in Laravel rules.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[How API Versioning Can Save You from Production Disasters]]></title><description><![CDATA[Evolve Without Breaking: Smarter API Versioning Strategies for Laravel]]></description><link>https://newsletter.priteshbhanushali.com/p/how-api-versioning-can-save-you-from</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/how-api-versioning-can-save-you-from</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 02 Aug 2025 09:31:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-sS1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>Ever faced this nightmare?</strong></h3><p>You&#8217;ve built and tested your application rigorously. Every new API endpoint is working like a charm. All tests are green, and you feel ready for a successful release. </p><p>But just a few hours after deploying to production, chaos erupts.</p><p>Customers start flooding your inbox:<br>&#128241; Their mobile apps are crashing<br>&#128257; Automated scripts are failing<br>&#128721; Critical business operations are down</p><p>You rush to debug the issue&#8230; and discover the root cause:<br><strong>Your latest API update wasn&#8217;t backward compatible</strong>.</p><p>You renamed a field, changed a response structure, or retired an endpoint &#8212; without considering that dozens of client applications still depend on the old behavior.</p><p>This situation is <strong>more common than you'd think</strong>, and it all boils down to one key oversight:<br>&#128073; <strong>Lack of proper API versioning</strong>.</p><p>Without versioning, even the smallest change can break everything for your users.</p><h3>&#128269; What is API Versioning</h3><p>API versioning is the practice of managing changes to your API without breaking existing client integrations. It allows you to <strong>introduce new features</strong>, <strong>deprecate old ones</strong>, and <strong>maintain backward compatibility</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-sS1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-sS1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 424w, https://substackcdn.com/image/fetch/$s_!-sS1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 848w, https://substackcdn.com/image/fetch/$s_!-sS1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 1272w, https://substackcdn.com/image/fetch/$s_!-sS1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-sS1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png" width="774" height="555" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c32bda43-f57a-4119-a01f-16df3802a02b_774x555.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:555,&quot;width&quot;:774,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:144870,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/169901906?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-sS1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 424w, https://substackcdn.com/image/fetch/$s_!-sS1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 848w, https://substackcdn.com/image/fetch/$s_!-sS1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 1272w, https://substackcdn.com/image/fetch/$s_!-sS1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc32bda43-f57a-4119-a01f-16df3802a02b_774x555.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>&#128640; Why Use API Versioning?</h3><ul><li><p>To avoid breaking changes for existing users</p></li><li><p>To allow parallel development of new API features</p></li><li><p>To cleanly deprecate outdated functionality</p></li></ul><div><hr></div><h3>&#9989; Common API Versioning Strategies</h3><h5>1. <strong>URI Versioning (most common)</strong></h5><pre><code>GET /api/v1/users</code></pre><ul><li><p><strong>Pros</strong>: Easy to understand, widely supported by tools</p></li><li><p><strong>Cons</strong>: Version becomes part of the resource identifier</p></li></ul><pre><code>Route::prefix('v1')-&gt;group(function () {
    Route::get('/users', [UserController::class, 'index']);
});</code></pre><h5>2. <strong>Header Versioning</strong></h5><pre><code>GET /api/users
Accept: application/vnd.myapp.v1+json</code></pre><ul><li><p><strong>Pros</strong>: Keeps URL clean, flexible</p></li><li><p><strong>Cons</strong>: Harder to debug, less browser-friendly</p></li></ul><p>Laravel (custom middleware required):</p><pre><code>php artisan make:middleware ApiVersionMiddleware</code></pre><pre><code>// app/Http/Middleware/ApiVersionMiddleware.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ApiVersionMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $acceptHeader = $request-&gt;header('Accept');

        preg_match('/vnd\.myapp\.v(\d+)\+json/', $acceptHeader, $matches);
        $version = $matches[1] ?? '1'; // default to v1

        $request-&gt;attributes-&gt;set('api_version', 'v' . $version);

        return $next($request);
    }
}</code></pre><p>Register the Middleware In <code>app/Http/Kernel.php</code></p><pre><code>'api.version' =&gt; \App\Http\Middleware\ApiVersionMiddleware::class,</code></pre><p>Define a Common Route and Dispatch to Version</p><pre><code>// routes/api.php

Route::middleware(['api.version'])-&gt;group(function () {
    Route::get('/users', function (\Illuminate\Http\Request $request) {
        $version = $request-&gt;get('api_version');

        $controllerClass = "\\App\\Http\\Controllers\\Api\\" . ucfirst($version) . "\\UserController";

        return app()-&gt;make($controllerClass)-&gt;index($request);
    });
});</code></pre><h5>3. <strong>Query Parameter Versioning</strong></h5><pre><code>GET /api/users?version=1</code></pre><ul><li><p><strong>Pros</strong>: Easy to implement and test</p></li><li><p><strong>Cons</strong>: Considered less RESTful</p></li></ul><h5>4. <strong>Subdomain Versioning</strong></h5><pre><code>GET https://v1.api.myapp.com/users</code></pre><ul><li><p><strong>Pros</strong>: Clean separation</p></li><li><p><strong>Cons</strong>: More DNS/config complexity</p></li></ul><div><hr></div><h2>&#128197; When to Create a New Version</h2><ul><li><p>You&#8217;re making breaking changes</p></li><li><p>You&#8217;re significantly changing response structure</p></li><li><p>You want to deprecate an existing method</p></li></ul><h3>&#128269; Real-World Scenarios</h3><h4>&#9989; <strong>1. Changing Response Structure</strong></h4><h5>&#129534; Use Case:</h5><p>You have a mobile app using this API:</p><p><strong>Version 1</strong></p><pre><code>GET /api/v1/users/1
{
  "id": 1,
  "name": "Pritesh",
  "email": "pritesh@example.com"
}</code></pre><p>Later, you want to return more structured data in <strong>Version 2</strong>:</p><pre><code>GET /api/v2/users/1
{
  "data": {
    "user_id": 1,
    "full_name": "Pritesh Bhanushali",
    "email_address": "pritesh@example.com"
  },
  "meta": {
    "version": "2.0"
  }
}</code></pre><p>&#128257; <strong>Why version?</strong><br>Changing key names like <code>name</code> &#8594; <code>full_name</code> will break existing clients that expect <code>name</code>.</p><h4>&#9989; <strong>2. Deprecating or Removing Endpoints</strong></h4><h5>&#129534; Use Case:</h5><p>In <strong>v1</strong>, you allow users to delete their account:</p><pre><code>DELETE /api/v1/users/1</code></pre><p>In <strong>v2</strong>, business rules change and you disallow account deletion. Instead, you allow only deactivation:</p><pre><code>PATCH /api/v2/users/1/deactivate</code></pre><p>&#128257; <strong>Why version?</strong><br>Removing or repurposing the DELETE endpoint would break clients expecting that functionality.</p><h4>&#9989; <strong>3. Adding New Features Without Affecting Old Clients</strong></h4><h5>&#129534; Use Case:</h5><p>You introduce a new search filter in <strong>v2</strong>:</p><pre><code>GET /api/v2/products?category=electronics&amp;sort=price_desc</code></pre><p>But in <strong>v1</strong>, you want to keep things simple:</p><pre><code>GET /api/v1/products</code></pre><p>&#128257; <strong>Why version?</strong><br>New clients can benefit from advanced features; old clients stay unaffected.</p><h4>&#9989; <strong>4. Business Rule Changes</strong></h4><h5>&#129534; Use Case:</h5><p>In an insurance app:</p><ul><li><p><strong>v1</strong>: Returns policy premium <strong>including tax</strong></p></li><li><p><strong>v2</strong>: Returns premium <strong>excluding tax</strong>, and tax info separately</p></li></ul><pre><code>// v1
{ "premium": 1200 }

// v2
{
  "premium": 1000,
  "tax": 200
}</code></pre><p>&#128257; <strong>Why version?</strong><br>Financial calculations might depend on how values are presented &#8212; older apps must not break.</p><div><hr></div><h2>&#129517; Conclusion</h2><p><strong>Yes, API versioning is a good practice</strong>, especially when:</p><ul><li><p>Your API is public or consumed by multiple apps</p></li><li><p>You want long-term stability and maintainability</p></li><li><p>You want to avoid breaking changes for users</p></li></ul><p>&#128204; <em>It&#8217;s not about if you need versioning, but <strong>when</strong> you&#8217;ll need it &#8212; and being ready for that moment.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletters ! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[🚦How to Prevent API Abuse in Laravel with Throttle Middleware]]></title><description><![CDATA[Learn to apply smart, scalable throttling strategies in Laravel to protect your API from abuse.]]></description><link>https://newsletter.priteshbhanushali.com/p/how-to-prevent-api-abuse-in-laravel</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/how-to-prevent-api-abuse-in-laravel</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Thu, 31 Jul 2025 04:30:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jf2U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today's API-driven applications, security, scalability, and stability are key. One way Laravel helps developers achieve this is through <strong>API rate limiting</strong>. Whether you're building an internal API or a public-facing one, <strong>rate limiting protects your backend from abuse and overload</strong>.<br></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jf2U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jf2U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 424w, https://substackcdn.com/image/fetch/$s_!jf2U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 848w, https://substackcdn.com/image/fetch/$s_!jf2U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 1272w, https://substackcdn.com/image/fetch/$s_!jf2U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jf2U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png" width="630" height="210.85089141004863" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:413,&quot;width&quot;:1234,&quot;resizeWidth&quot;:630,&quot;bytes&quot;:67615,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/169663187?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jf2U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 424w, https://substackcdn.com/image/fetch/$s_!jf2U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 848w, https://substackcdn.com/image/fetch/$s_!jf2U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 1272w, https://substackcdn.com/image/fetch/$s_!jf2U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7421e864-335d-4259-bd25-e89945d20e4b_1234x413.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><h3>&#128269; What is API Rate Limiting?</h3><p><strong>Rate limiting</strong> is a technique used to control how many requests a user (or client) can make to your API in a given period.</p><p>For example:</p><blockquote><p>"You can only send 60 requests per minute."</p></blockquote><p>Laravel uses a powerful middleware system to enforce these limits.</p><h3><br>&#128737;&#65039; Why Should You Use Rate Limiting?</h3><p>Rate limiting is crucial for:</p><ul><li><p>&#128272; Preventing brute force login attacks</p></li><li><p>&#128721; Stopping spamming bots or scrapers</p></li><li><p>&#9878;&#65039; Ensuring fair use among all users</p></li><li><p>&#128260; Avoiding server overload or denial-of-service (DoS)</p></li><li><p>&#128176; Tiered API limits for free vs premium users</p></li></ul><h3>&#128736;&#65039; How Laravel Handles Rate Limiting</h3><p>Laravel includes <code>ThrottleRequests</code> middleware out of the box.</p><h4>&#129513; 1. <code>Route::middleware('throttle:60,1')-&gt;group(...)</code></h4><h5>&#9989; Best For:</h5><ul><li><p><strong>Simple, quick rate limiting</strong></p></li><li><p>When <strong>all routes in a group</strong> share the same limit</p></li><li><p>Great for <strong>small to medium-sized APIs</strong> with no user-based customization.</p><pre><code>Route::middleware('throttle:60,1')-&gt;group(function () {
    Route::get('/posts', [PostController::class, 'index']);
    Route::post('/comments', [CommentController::class, 'store']);
});</code></pre><h5>&#128161; Use case:</h5><p>You want to <strong>limit all visitors to 60 requests per minute</strong> to avoid abuse.</p></li></ul><div><hr></div><h4>&#129513; 2. <code>Route::middleware('throttle:10,1')</code> on <strong>single routes</strong></h4><h5>&#9989; Best For:</h5><ul><li><p><strong>Sensitive endpoints</strong> (login, registration)</p></li><li><p>Limiting abuse <strong>only on specific actions</strong></p><pre><code>Route::post('/login', [AuthController::class, 'login'])
    -&gt;middleware('throttle:5,1');</code></pre><h5>&#128161; Use case:</h5><p>Allow <strong>only 5 login attempts per minute</strong> per user/IP. (Limit login attempts to <strong>prevent brute-force</strong>).</p><div><hr></div></li></ul><h4>&#129513; 3. <strong>Custom </strong><code>RateLimiter::for()</code><strong> in </strong><code>RouteServiceProvider</code></h4><h5>&#9989; Best For:</h5><ul><li><p><strong>Dynamic logic</strong> based on user role, API key, IP, or even route</p></li><li><p><strong>Centralized, scalable configuration</strong></p></li><li><p>Required if you want <strong>different limits for different users or plans.</strong></p></li></ul><h5>&#128216; Example 1: Role-based limits (premium vs free users)</h5><pre><code>// RouteServiceProvider.php
RateLimiter::for('user-api', function (Request $request) {
    return $request-&gt;user()?-&gt;is_premium
        ? Limit::perMinute(100)-&gt;by($request-&gt;user()-&gt;id)
        : Limit::perMinute(20)-&gt;by($request-&gt;user()-&gt;id);
});</code></pre><p>Then in <code>api.php</code>:</p><pre><code>Route::middleware('throttle:user-api')-&gt;get('/dashboard', [DashboardController::class, 'index']);</code></pre><h5>&#128161; Use case:</h5><p>Premium users get <strong>higher rate limits</strong> than free users.</p><h5>&#128216; Example 2: API key-based throttling</h5><pre><code>RateLimiter::for('api-key', function (Request $request) {
    $apiKey = $request-&gt;header('X-API-KEY');
    return Limit::perHour(1000)-&gt;by($apiKey);
});</code></pre><p>In routes:</p><pre><code>Route::middleware('throttle:api-key')-&gt;get('/v1/public-data', [ApiController::class, 'index']);</code></pre><h5>&#128161; Use case:</h5><p>Public API where users authenticate using API keys &#8212; enforce <strong>hourly limits per API key</strong>.</p><div><hr></div><h3>&#129504; Best Practices for Rate Limiting</h3><ul><li><p>&#9888;&#65039; Return proper <code>429 Too Many Requests</code> responses.</p></li><li><p>&#128257; Use <code>Retry-After</code> header to tell clients when they can retry.</p></li><li><p>&#128272; Apply stricter limits to login, registration, and sensitive endpoints.</p></li><li><p>&#128202; Use monitoring tools like Telescope or Laravel Debugbar for insight.</p></li></ul><div><hr></div><h3>&#10024; Conclusion</h3><p>Rate limiting is a simple yet powerful way to <strong>secure your Laravel APIs</strong> and <strong>maintain server performance</strong>. With Laravel's built-in support, implementing smart limits is just a few lines of code away.<br></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item></channel></rss>