<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" 
     xmlns:atom="http://www.w3.org/2005/Atom" 
     xmlns:content="http://purl.org/rss/1.0/modules/content/" 
     xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Punyapal Shah's Tips</title>
    <link>https://mrpunyapal.dev/tips</link>
    <description>Curated engineering tips, testing techniques, and idiomatic snippets for Laravel, Pest PHP, PHP, JavaScript, TypeScript, and Git by Punyapal Shah.</description>
    <language>en-us</language>
    <copyright>Copyright (c) Punyapal Shah</copyright>
    <pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate>
    <lastBuildDate>Tue, 11 Aug 2026 12:38:32 GMT</lastBuildDate>
    <atom:link href="https://mrpunyapal.dev/tips/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title><![CDATA[Prevent Navigation Layout Shifts and Flickering in Web Applications]]></title>
      <link>https://mrpunyapal.dev/tips/css-prevent-navigation-layout-shift-flicker</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/css-prevent-navigation-layout-shift-flicker</guid>
      <description><![CDATA[Fix common navigation bar layout shifts, scrollbar jumps, font width shifts, and theme switcher flickering with clean CSS and HTML patterns. Navigation bars...]]></description>
      <content:encoded><![CDATA[<blockquote>Fix common navigation bar layout shifts, scrollbar jumps, font width shifts, and theme switcher flickering with clean CSS and HTML patterns.</blockquote>
<p>Navigation bars often suffer from subtle layout shifts during page reloads, theme toggles, and tab switches. Combining a few CSS and HTML techniques eliminates these visual jumps entirely.</p>
<h4>1. Lock Root Scrollbar Space</h4>
<p>Navigating between long pages with scrollbars and short pages without scrollbars causes horizontal layout jumps. Use `scrollbar-gutter: stable` on `html` with `overflow-y: auto`.</p>
<pre><code class="language-css">/* Reserve scrollbar width globally without forcing a scrollbar track on short pages */
html {
  scrollbar-gutter: stable;
  overflow-y: auto;
}

/* Prevent horizontal overflow without breaking root scrollbar gutter */
body {
  overflow-x: hidden;
}</code></pre>
<p>`scrollbar-gutter: stable` reserves the scrollbar space on short pages so layout width remains constant, while `overflow-y: auto` prevents rendering an empty scrollbar track when scrolling is not needed.</p>
<p>Do not place `overflow-x: hidden` directly on `html`, as browsers will ignore root `scrollbar-gutter` calculations when overflow is clipped at the html element level.</p>
<h4>2. Prevent View Transition Scrollbar Flicker</h4>
<p>When using native cross-document View Transitions (`@view-transition { navigation: auto; }`), browsers render outgoing and incoming page snapshots simultaneously. For a single frame during the transition, the viewport height expands, triggering a temporary scrollbar track.</p>
<pre><code class="language-css">/* Prevent temporary scrollbar flicker during cross-document view transitions */
::view-transition-group(root),
::view-transition-image-pair(root),
::view-transition-old(root),
::view-transition-new(root) {
  overflow: hidden !important;
}</code></pre>
<p>Setting `overflow: hidden` on the root view transition pseudo-elements clips snapshot layers to viewport bounds, preventing split-second scrollbar popping during page navigations.</p>
<h4>3. Lock Navigation Tab Widths with CSS Grid</h4>
<p>Active or hovered navigation tabs often expand in width when text becomes bold or changes color contrast, pushing adjacent tabs left or right. Use CSS Grid overlay with an invisible pseudo-element to reserve bold text dimensions upfront.</p>
<pre><code class="language-html">&lt;nav class=&quot;nav-tabs&quot;&gt;
  &lt;a href=&quot;/dashboard&quot; class=&quot;nav-link active&quot;&gt;
    &lt;span class=&quot;nav-label&quot; data-text=&quot;Dashboard&quot;&gt;
      &lt;span&gt;Dashboard&lt;/span&gt;
    &lt;/span&gt;
  &lt;/a&gt;
  &lt;a href=&quot;/settings&quot; class=&quot;nav-link&quot;&gt;
    &lt;span class=&quot;nav-label&quot; data-text=&quot;Settings&quot;&gt;
      &lt;span&gt;Settings&lt;/span&gt;
    &lt;/span&gt;
  &lt;/a&gt;
&lt;/nav&gt;</code></pre>
<pre><code class="language-css">.nav-tabs a {
  display: inline-flex;
  align-items: center;
}

/* Grid overlay sizes container to the widest content layer */
.nav-tabs .nav-label {
  display: inline-grid;
  grid-template-areas: &quot;label&quot;;
  align-items: center;
  justify-items: center;
}

.nav-tabs .nav-label::after,
.nav-tabs .nav-label &gt; span {
  grid-area: label;
}

/* Invisible bold pseudo-element reserves max text width */
.nav-tabs .nav-label::after {
  content: attr(data-text);
  font-weight: 700;
  visibility: hidden;
  overflow: hidden;
  user-select: none;
  pointer-events: none;
}</code></pre>
<h4>4. Pre-render Theme Toggle Icons in HTML</h4>
<p>Swapping sun and moon icons via JavaScript after page load causes visual button flickering. Pre-render both icons directly in static HTML and toggle visibility using CSS theme classes.</p>
<pre><code class="language-html">&lt;button type=&quot;button&quot; class=&quot;theme-toggle&quot; aria-label=&quot;Toggle theme&quot;&gt;
  &lt;!-- Sun icon visible in dark mode --&gt;
  &lt;svg class=&quot;hidden dark:block&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;16&quot; height=&quot;16&quot;&gt;
    &lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;5&quot; fill=&quot;currentColor&quot;/&gt;
  &lt;/svg&gt;

  &lt;!-- Moon icon visible in light mode --&gt;
  &lt;svg class=&quot;block dark:hidden&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;16&quot; height=&quot;16&quot;&gt;
    &lt;path d=&quot;M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z&quot; fill=&quot;currentColor&quot;/&gt;
  &lt;/svg&gt;
&lt;/button&gt;</code></pre>
<h4>5. Use Opacity for Text Contrast Changes</h4>
<p>Changing text color from gray to solid black or white triggers font stem-darkening in browser rendering engines, altering glyph widths by fractions of a pixel. Use constant base colors and toggle opacity instead.</p>
<pre><code class="language-css">/* Active tab */
.nav-link.active {
  color: #0f172a; /* 100% opacity */
}

/* Inactive tab uses same base color with lower opacity */
.nav-link:not(.active) {
  color: rgba(15, 23, 42, 0.6);
}</code></pre>
<h4>Key Takeaways</h4>
<ul>
  <li>Reserve scrollbar width on <code>html</code> with <code>scrollbar-gutter: stable</code> and <code>overflow-y: auto</code>.</li>
  <li>Clip View Transition root pseudo-elements with <code>overflow: hidden</code> to prevent split-second scrollbar popping.</li>
  <li>Reserve space for bold tab labels using <code>display: inline-grid</code> and <code>content: attr(data-text)</code>.</li>
  <li>Pre-render all theme toggle states in static HTML to eliminate DOM injection flicker.</li>
  <li>Adjust text opacity rather than hex colors to preserve font glyph vector calculations.</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Styling]]></category>
      <category><![CDATA[Layout Shift]]></category>
      <category><![CDATA[Frontend]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Trigger a GitHub Actions Workflow Across Repositories]]></title>
      <link>https://mrpunyapal.dev/tips/github-actions-cross-repo-workflow-dispatch</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/github-actions-cross-repo-workflow-dispatch</guid>
      <description><![CDATA[Use repositorydispatch to trigger a workflow in a target repository when commits or PRs land in a source repository. GitHub Actions workflows are scoped to a...]]></description>
      <content:encoded><![CDATA[<blockquote>Use `repository_dispatch` to trigger a workflow in a target repository when commits or PRs land in a source repository.</blockquote>
<p>GitHub Actions workflows are scoped to a single repository by default. To run a workflow in another repository after merging a PR or pushing a commit, use GitHub's `repository_dispatch` API endpoint.</p>
<h3>1. Configure the Target Repository Listener</h3>
<p>Add `repository_dispatch` to the `on` block in the target repository workflow file:</p>
<pre><code class="language-yaml"># .github/workflows/build.yml (target repo)
name: Build Site

on:
  push:
    branches:
      - main
  repository_dispatch:
    types: [content-updated]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo &quot;Build triggered&quot;</code></pre>
<h3>2. Configure the Source Repository Dispatcher</h3>
<p>In the source repository, add a workflow step that sends a POST request to GitHub's dispatches endpoint:</p>
<pre><code class="language-yaml"># .github/workflows/notify.yml (source repo)
name: Notify Target Repo

on:
  push:
    branches:
      - main

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger target build
        env:
          TOKEN: ${{ secrets.WEBSITE_DISPATCH_TOKEN }}
        run: |
          if [ -z &quot;$TOKEN&quot; ]; then
            echo &quot;WEBSITE_DISPATCH_TOKEN is not set&quot;
            exit 1
          fi
          curl --fail --show-error -X POST \
            -H &quot;Authorization: Bearer $TOKEN&quot; \
            -H &quot;Accept: application/vnd.github.v3+json&quot; \
            https://api.github.com/repos/OWNER/TARGET-REPO/dispatches \
            -d &#039;{&quot;event_type&quot;: &quot;content-updated&quot;}&#039;</code></pre>
<h3>3. Create and Assign the Access Token</h3>
<ol>
  <li>Create a Fine-Grained Personal Access Token under GitHub Developer Settings.</li>
  <li>Select the target repository under <strong>Repository Access</strong>.</li>
  <li>Set <strong>Contents</strong> permission to <strong>Read and write</strong>.</li>
  <li>Save the token as <code>WEBSITE_DISPATCH_TOKEN</code> in the source repository&#39;s Actions Secrets.</li>
</ol>
<h3>Key Considerations</h3>
<ul>
  <li>The <code>event_type</code> string in the payload must match the array value under <code>types: [...]</code>.</li>
  <li>Always pass <code>--fail --show-error</code> to <code>curl</code> so HTTP errors exit with code 1 instead of failing silently.</li>
  <li>Fine-grained tokens limit dispatch access strictly to the targeted repository.</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[GitHub Actions]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[DevOps]]></category>
    </item>
    <item>
      <title><![CDATA[Render Multiple Field Validation Errors with Flux UI and Blade]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-livewire-flux-error-handling</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-livewire-flux-error-handling</guid>
      <description><![CDATA[Suppress default single-error tooltips in Flux UI components and iterate over all validation messages per field using Blade error directives. When a single f...]]></description>
      <content:encoded><![CDATA[<blockquote>Suppress default single-error tooltips in Flux UI components and iterate over all validation messages per field using Blade error directives.</blockquote>
<p>When a single form field fails multiple validation rules (for example, a password field failing length, numbers, and special character rules), default input components only display the first error message.</p>
<p>You can pass `error:message=""` to Flux UI components to suppress the inline single-error message and render a custom error list beneath the input:</p>
<pre><code class="language-blade">&lt;!-- Password input with custom multi-error list --&gt;
&lt;flux:input
    name=&quot;password&quot;
    :label=&quot;__(&#039;Password&#039;)&quot;
    type=&quot;password&quot;
    required
    autocomplete=&quot;new-password&quot;
    :placeholder=&quot;__(&#039;Password&#039;)&quot;
    passwordrules=&quot;{{ \Illuminate\Validation\Rules\Password::defaults()-&gt;toPasswordRulesString() }}&quot;
    error:message=&quot;&quot;
    viewable
/&gt;

@if ($errors-&gt;has(&#039;password&#039;))
    &lt;ul class=&quot;mt-3 space-y-1 text-sm font-medium text-red-500 dark:text-red-400&quot;&gt;
        @foreach ($errors-&gt;get(&#039;password&#039;) as $message)
            &lt;li&gt;{{ $message }}&lt;/li&gt;
        @endforeach
    &lt;/ul&gt;
@endif</code></pre>
<ul>
  <li><code>error:message=&quot;&quot;</code> prevents double error messages in custom component setups</li>
  <li><code>$errors-&gt;get(&#39;field&#39;)</code> returns an array of all validation failures for that key</li>
  <li>Provides clear feedback for complex password policy rules</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 08 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Validation]]></category>
      <category><![CDATA[Livewire]]></category>
      <category><![CDATA[Blade]]></category>
    </item>
    <item>
      <title><![CDATA[Optimize Large Wildcard Array Validation in Laravel 13.24]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-validation-wildcard-array-performance</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-validation-wildcard-array-performance</guid>
      <description><![CDATA[Laravel 13.24 introduces dramatic performance optimizations for validating large nested arrays with wildcard rules, reducing validation execution time from 8...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 13.24 introduces dramatic performance optimizations for validating large nested arrays with wildcard rules, reducing validation execution time from 85 seconds to under 1 second.</blockquote>
<p>When validating large payloads containing thousands of array items using wildcard rules (such as `items.*.name`), earlier Laravel versions spent significant time matching nested array keys recursively.</p>
<p>Laravel 13.24 optimizes wildcard rule compilation and key evaluation under the hood:</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Validator;

$rules = [
    &#039;items&#039; =&gt; [&#039;array&#039;],
    &#039;items.*.name&#039; =&gt; [&#039;nullable&#039;, &#039;string&#039;],
    &#039;items.*.email&#039; =&gt; [&#039;nullable&#039;, &#039;email&#039;],
    &#039;items.*.phone&#039; =&gt; [&#039;nullable&#039;, &#039;string&#039;],
    &#039;items.*.address&#039; =&gt; [&#039;nullable&#039;, &#039;string&#039;],
];

$data = [
    &#039;items&#039; =&gt; array_fill(0, 8_000, [
        &#039;name&#039; =&gt; &#039;John&#039;,
        &#039;email&#039; =&gt; &#039;john@example.com&#039;,
    ]),
];

// Before Laravel 13.24: ~85 seconds
// In Laravel 13.24+: ~1 second
Validator::make($data, $rules)-&gt;passes();</code></pre>
<ul>
  <li>Wildcard array validation on 8,000+ items is over 80x faster</li>
  <li>Requires zero code changes in your existing FormRequests or Validator calls</li>
  <li>Essential optimization for bulk API imports and data synchronization endpoints</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 08 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Validation]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Never Return Statements inside Finally Blocks in PHP]]></title>
      <link>https://mrpunyapal.dev/tips/php-never-return-in-finally-block</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-never-return-in-finally-block</guid>
      <description><![CDATA[Placing a return statement inside a try-catch finally block silently overrides exceptions and previous return statements. A return statement inside a finally...]]></description>
      <content:encoded><![CDATA[<blockquote>Placing a return statement inside a try-catch finally block silently overrides exceptions and previous return statements.</blockquote>
<p>A return statement inside a finally block executes regardless of whether an exception was thrown or caught. It silently discards pending exceptions and overwrites return values from try or catch blocks.</p>
<pre><code class="language-php">// What does guess() return? It returns &#039;finally&#039;!
function guess(): string
{
    try {
        throw new Exception(&#039;Something went wrong&#039;);
    } catch (Exception $e) {
        return &#039;catch&#039;;
    } finally {
        return &#039;finally&#039;; // Silently overrides the catch return!
    }
}

// GOOD: Use finally strictly for resource cleanup
function processOrderClean(): bool
{
    try {
        return true;
    } finally {
        $this-&gt;cleanupLocks();
    }
}</code></pre>
<ul>
  <li>Return inside finally discards thrown exceptions without logging them</li>
  <li>Overwrites return values calculated in try or catch blocks</li>
  <li>Use finally strictly for resource cleanup like closing file handles or releasing locks</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 06 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Basics]]></category>
      <category><![CDATA[Exceptions]]></category>
      <category><![CDATA[Best Practices]]></category>
    </item>
    <item>
      <title><![CDATA[Extract Dominant Colors and Handle HEIC/AVIF Images in Laravel 13.24]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-dominant-color-heic-avif</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-dominant-color-heic-avif</guid>
      <description><![CDATA[Laravel 13.24 adds dominantColor() to the Image API for extracting the primary color of an image, plus native support for HEIC and AVIF formats. Extracting a...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 13.24 adds dominantColor() to the Image API for extracting the primary color of an image, plus native support for HEIC and AVIF formats.</blockquote>
<p>Extracting a dominant color for placeholders or UI backgrounds is now built directly into Laravel's Image API. Laravel 13.24 also adds native support for HEIC and AVIF uploads out of the box.</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Image;

$color = Image::read($path)-&gt;dominantColor();
// Returns hex string like &#039;#8a6f4c&#039;</code></pre>
<ul>
  <li>dominantColor() resizes to a single pixel internally and returns hex string</li>
  <li>Images with alpha channels return 8-digit hex (#rrggbbaa)</li>
  <li>HEIC/AVIF image formats are supported natively</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Architecture]]></category>
      <category><![CDATA[Image]]></category>
      <category><![CDATA[Media]]></category>
    </item>
    <item>
      <title><![CDATA[Use modelKeys() on the Eloquent Builder in Laravel 13.24]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-model-keys-eloquent-builder</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-model-keys-eloquent-builder</guid>
      <description><![CDATA[Laravel 13.24 adds modelKeys() directly to the Eloquent query builder, replacing hardcoded pluck('id') calls with a method that uses the model primary key. I...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 13.24 adds modelKeys() directly to the Eloquent query builder, replacing hardcoded pluck('id') calls with a method that uses the model primary key.</blockquote>
<p>Instead of hardcoding column strings like pluck('id'), calling modelKeys() on the query builder automatically respects custom primary key configurations defined on the target model.</p>
<pre><code class="language-php">use App\Models\User;

// Replaces User::where(&#039;active&#039;, true)-&gt;pluck(&#039;id&#039;)
$ids = User::where(&#039;active&#039;, true)-&gt;modelKeys();</code></pre>
<ul>
  <li>Replaces pluck(&#39;id&#39;) with a model-aware alternative</li>
  <li>Automatically respects custom $primaryKey definitions</li>
  <li>Previously only available on Eloquent Collections, now works on the builder</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Database]]></category>
    </item>
    <item>
      <title><![CDATA[Restrict Array Keys with the array_keys Validation Rule in Laravel 13.24]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-array-keys-validation</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-array-keys-validation</guid>
      <description><![CDATA[Laravel 13.24 adds the arraykeys validation rule to reject any unexpected keys in an array input, keeping input strictly limited to allowed keys. When buildi...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 13.24 adds the array_keys validation rule to reject any unexpected keys in an array input, keeping input strictly limited to allowed keys.</blockquote>
<p>When building API endpoints that receive settings or configuration objects, unexpected extra keys could indicate tampering or version mismatches. The array_keys rule restricts input to allowed keys only.</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Validator;

$validator = Validator::make($request-&gt;all(), [
    &#039;settings&#039;   =&gt; &#039;required|array|array_keys:theme,language,timezone&#039;,
    &#039;settings.*&#039; =&gt; &#039;string&#039;,
]);</code></pre>
<ul>
  <li>array_keys: rejects keys NOT in your list (whitelist)</li>
  <li>required_array_keys: checks that specific keys ARE present (required check)</li>
  <li>Rejects payloads containing unlisted keys</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Validation]]></category>
    </item>
    <item>
      <title><![CDATA[Move Filament Global Search to Sidebar]]></title>
      <link>https://mrpunyapal.dev/tips/filament-laracon-us-tips-povilas-korop</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/filament-laracon-us-tips-povilas-korop</guid>
      <description><![CDATA[Customize your Filament admin panel layout by moving the global search bar to the top of the navigation sidebar. In Filament admin panels, the global search...]]></description>
      <content:encoded><![CDATA[<blockquote>Customize your Filament admin panel layout by moving the global search bar to the top of the navigation sidebar.</blockquote>
<p>In Filament admin panels, the global search bar renders in the top header by default. Using render hooks, you can move global search into the navigation sidebar.</p>
<p>Register a render hook in your service provider using `PanelsRenderHook::SIDEBAR_NAV_START`:</p>
<pre><code class="language-php">namespace App\Providers;

use Filament\Support\Facades\FilamentView;
use Filament\View\PanelsRenderHook;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        FilamentView::registerRenderHook(
            PanelsRenderHook::SIDEBAR_NAV_START,
            fn (): string =&gt; Blade::render(&#039;@livewire(Filament\\Livewire\\GlobalSearch::class)&#039;)
        );
    }
}</code></pre>
<ul>
  <li>Move global search out of the header and into the sidebar navigation</li>
  <li><code>PanelsRenderHook::SIDEBAR_NAV_START</code> places elements right above navigation items</li>
  <li>Defer loading on large admin tables to keep page paint instant</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Filament]]></category>
      <category><![CDATA[Admin Panel]]></category>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Livewire]]></category>
    </item>
    <item>
      <title><![CDATA[Enable Test Impact Analysis (TIA) by Default in pest.php]]></title>
      <link>https://mrpunyapal.dev/tips/pest-enable-tia-plugin-by-default</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/pest-enable-tia-plugin-by-default</guid>
      <description><![CDATA[Configure Pest to run Test Impact Analysis by default in pest.php so only tests covering modified files execute. Running full test suites on every minor edit...]]></description>
      <content:encoded><![CDATA[<blockquote>Configure Pest to run Test Impact Analysis by default in pest.php so only tests covering modified files execute.</blockquote>
<p>Running full test suites on every minor edit slows down development feedback. Configuring TIA in pest.php automatically detects file changes and runs only affected tests.</p>
<pre><code class="language-php">// tests/Pest.php
uses()
    -&gt;compact()
    -&gt;in(__DIR__);

// Run only tests covering changed code automatically
// Terminal: ./vendor/bin/pest --tia</code></pre>
<ul>
  <li>Slashes test execution duration during rapid local iterations</li>
  <li>Tracks code coverage artifacts to identify tests covering edited lines</li>
  <li>Can be toggled via CLI flag --tia or environment config</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Pest PHP]]></category>
      <category><![CDATA[Plugins]]></category>
      <category><![CDATA[Pest]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[DX]]></category>
    </item>
    <item>
      <title><![CDATA[Automate PHP Readonly Class Refactoring with Rector]]></title>
      <link>https://mrpunyapal.dev/tips/php-rector-automate-readonly-classes</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-rector-automate-readonly-classes</guid>
      <description><![CDATA[Use Rector rules to automatically convert immutable DTOs and value objects into native PHP 8.2 readonly classes. Manually adding readonly keywords across doz...]]></description>
      <content:encoded><![CDATA[<blockquote>Use Rector rules to automatically convert immutable DTOs and value objects into native PHP 8.2 readonly classes.</blockquote>
<p>Manually adding readonly keywords across dozens of data transfer objects is repetitive. Rector automates upgrading class properties and class declarations across codebase suites.</p>
<pre><code class="language-php">// rector.php
use Rector\Config\RectorConfig;
use Rector\Php82\Rector\Class_\ReadOnlyClassRector;

return RectorConfig::configure()
    -&gt;withRules([
        ReadOnlyClassRector::class,
    ]);</code></pre>
<ul>
  <li>Converts immutable classes to native PHP 8.2 readonly class declarations</li>
  <li>Enforces immutability at compiler level for all class properties</li>
  <li>Eliminates boilerplate docblock annotations and manual checks</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Tooling]]></category>
      <category><![CDATA[Rector]]></category>
      <category><![CDATA[Refactoring]]></category>
    </item>
    <item>
      <title><![CDATA[Conditional Dependency Binding with #[BindWhen] in Laravel 13.22]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-bindwhen-conditional-binding</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-bindwhen-conditional-binding</guid>
      <description><![CDATA[Laravel 13.22 introduces the #[BindWhen] attribute for declarative, conditional service container bindings directly on implementation classes. Instead of clu...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 13.22 introduces the #[BindWhen] attribute for declarative, conditional service container bindings directly on implementation classes.</blockquote>
<p>Instead of cluttering AppServiceProvider with conditional bind logic, use the #[BindWhen] attribute directly on implementation classes. The closure receives the container instance to resolve runtime conditions.</p>
<pre><code class="language-php">use Illuminate\Container\Attributes\BindWhen;
use Illuminate\Contracts\Container\Container;

#[BindWhen(
    PaymentGateway::class,
    fn (Container $app) =&gt; $app-&gt;make(&#039;config&#039;)-&gt;get(&#039;services.gateway&#039;) === &#039;stripe&#039;
)]
class StripeGateway implements PaymentGateway {}
</code></pre>
<ul>
  <li>Closure receives Container instance for runtime decisions</li>
  <li>Can be repeated: declaration order determines priority</li>
  <li>Requires PHP 8.5 for closure-in-attribute support</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Configuration]]></category>
      <category><![CDATA[Service Container]]></category>
      <category><![CDATA[Attributes]]></category>
    </item>
    <item>
      <title><![CDATA[Format Blade Templates with Laravel Pint v1.30.0]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-pint-blade-formatter</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-pint-blade-formatter</guid>
      <description><![CDATA[Laravel Pint v1.30.0 adds native Blade template formatting via the Pint/laravelblade rule, using Prettier under the hood. Blade gets opinionated formatting i...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel Pint v1.30.0 adds native Blade template formatting via the Pint/laravel_blade rule, using Prettier under the hood.</blockquote>
<p>Blade gets opinionated formatting in Pint, keeping your markup consistent without manual tweaking.</p>
<pre><code class="language-bash">./vendor/bin/pint --blade</code></pre>
<ul>
  <li>Requires Node.js (Prettier runs under the hood)</li>
  <li>Pint auto-detects package manager for dependency installation</li>
  <li>Blade formatting is opinionated: consistent output, zero config debates</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 25 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Blade]]></category>
      <category><![CDATA[Pint]]></category>
    </item>
    <item>
      <title><![CDATA[Monthly Log Rotation with Laravel 13.23]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-monthly-log-channel</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-monthly-log-channel</guid>
      <description><![CDATA[Laravel 13.23 adds a built-in monthly logging driver that rotates log files once per month instead of daily. Daily log rotation creates hundreds of log files...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 13.23 adds a built-in monthly logging driver that rotates log files once per month instead of daily.</blockquote>
<p>Daily log rotation creates hundreds of log files over time. If your application has moderate log volume, the monthly driver keeps logs organized into one file per month (e.g. laravel-2026-08.log).</p>
<pre><code class="language-php">// config/logging.php
&#039;channels&#039; =&gt; [
    &#039;monthly&#039; =&gt; [
        &#039;driver&#039; =&gt; &#039;monthly&#039;,
        &#039;path&#039;   =&gt; storage_path(&#039;logs/laravel.log&#039;),
        &#039;days&#039;   =&gt; 12, // Retain 12 months of logs
    ],
],</code></pre>
<ul>
  <li>One log file per month (e.g. laravel-2026-08.log)</li>
  <li>days parameter controls how many months of logs to retain</li>
  <li>Ideal for production apps with moderate log volume</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 22 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Configuration]]></category>
      <category><![CDATA[Logging]]></category>
    </item>
    <item>
      <title><![CDATA[Use sole() Instead of firstOrFail() for Single Record Guarantees]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-sole-vs-firstorfail</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-sole-vs-firstorfail</guid>
      <description><![CDATA[When you expect exactly one matching record, use sole() instead of firstOrFail(). It guards against multiple records by throwing MultipleRecordsFoundExceptio...]]></description>
      <content:encoded><![CDATA[<blockquote>When you expect exactly one matching record, use sole() instead of firstOrFail(). It guards against multiple records by throwing MultipleRecordsFoundException.</blockquote>
<p>When querying unique records, firstOrFail() silently returns the first record even if multiple records match due to data integrity issues. sole() makes sure exactly one record exists.</p>
<pre><code class="language-php">// Throws ModelNotFoundException if 0, MultipleRecordsFoundException if 2+
$user = User::where(&#039;verification_token&#039;, $token)-&gt;sole();</code></pre>
<ul>
  <li>Asserts that exactly one record matches criteria</li>
  <li>Catches data integrity anomalies before bad states propagate</li>
  <li>Throws explicit MultipleRecordsFoundException</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 20 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Database]]></category>
    </item>
    <item>
      <title><![CDATA[Protect Custom Artisan Commands from AI Agents with Prohibitable]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-prohibitable-custom-commands-agents</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-prohibitable-custom-commands-agents</guid>
      <description><![CDATA[Use Laravel's Prohibitable trait on custom Artisan commands to selectively block AI agents from running destructive domain operations. While DB::prohibitDest...]]></description>
      <content:encoded><![CDATA[<blockquote>Use Laravel's Prohibitable trait on custom Artisan commands to selectively block AI agents from running destructive domain operations.</blockquote>
<p>While DB::prohibitDestructiveCommands guards core migrations, custom commands need explicit checks so AI agents don't accidentally run destructive domain actions.</p>
<pre><code class="language-php">use App\Console\Commands\DeleteInactiveUsersCommand;
use Laravel\AgentDetector\Facades\AgentDetector;

DeleteInactiveUsersCommand::prohibit(AgentDetector::detect()-&gt;isAgent);</code></pre>
<ul>
  <li>Prohibitable trait adds ::prohibit() and -&gt;isProhibited() to Artisan commands</li>
  <li>Combine with AgentDetector to let humans run commands while blocking AI agents</li>
  <li>Protects application-specific operations beyond database migrations</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Artisan]]></category>
      <category><![CDATA[AI]]></category>
    </item>
    <item>
      <title><![CDATA[Detect AI Agents in Laravel with AgentDetector and PAO]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-agent-detector-pao</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-agent-detector-pao</guid>
      <description><![CDATA[Laravel's AgentDetector detects whether an AI coding agent is interacting with your app, and PAO optimizes CLI output for agents. AI agents are part of moder...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel's AgentDetector detects whether an AI coding agent is interacting with your app, and PAO optimizes CLI output for agents.</blockquote>
<p>AI agents are part of modern dev workflows, and Laravel provides first-party tools to handle them cleanly by detecting agents and switching terminal outputs to compact JSON.</p>
<pre><code class="language-php">use Laravel\AgentDetector\Facades\AgentDetector;

if (AgentDetector::detect()-&gt;isAgent) {
    logger()-&gt;info(&#039;AI agent detected&#039;, [&#039;agent&#039; =&gt; AgentDetector::detect()-&gt;name]);
}</code></pre>
<ul>
  <li>AgentDetector ships with PAO (laravel/pao) by default in new apps</li>
  <li>Detects agents via environment variables and file markers</li>
  <li>PAO replaces verbose CLI output with compact JSON for agents</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Configuration]]></category>
      <category><![CDATA[AI]]></category>
      <category><![CDATA[DevOps]]></category>
    </item>
    <item>
      <title><![CDATA[Avoid Duplicating Authorization Logic with Gate::define() and Gate::authorize()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-gate-define-authorize-pattern</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-gate-define-authorize-pattern</guid>
      <description><![CDATA[Centralize authorization checks in Gate::define() and call Gate::authorize() in controllers instead of repeating manual if checks. Scattering authorization c...]]></description>
      <content:encoded><![CDATA[<blockquote>Centralize authorization checks in Gate::define() and call Gate::authorize() in controllers instead of repeating manual if checks.</blockquote>
<p>Scattering authorization checks across controllers leads to inconsistent security logic. Define permissions centrally using Gates and use Gate::authorize() to throw AuthorizationException automatically.</p>
<pre><code class="language-php">// AppServiceProvider::boot()
use Illuminate\Support\Facades\Gate;
use App\Models\User;
use App\Models\Post;

Gate::define(&#039;update-post&#039;, fn (User $user, Post $post) =&gt; $user-&gt;id === $post-&gt;user_id);

// Controller action
public function update(Request $request, Post $post)
{
    Gate::authorize(&#039;update-post&#039;, $post);
    // Proceeds only if authorized
}</code></pre>
<ul>
  <li>Centralizes authorization rules in AppServiceProvider or Policy classes</li>
  <li>Gate::authorize() throws HTTP 403 response automatically on failure</li>
  <li>Replaces repetitive if (! Gate::allows(...)) checks across controllers</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 12 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Authorization]]></category>
      <category><![CDATA[Security]]></category>
    </item>
    <item>
      <title><![CDATA[Stream Large Datasets: cursor() vs lazy() in Eloquent]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-cursor-vs-lazy-collections</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-cursor-vs-lazy-collections</guid>
      <description><![CDATA[cursor() hydrates single model instances sequentially using database cursors; lazy() streams records in chunks backed by LazyCollection. When iterating milli...]]></description>
      <content:encoded><![CDATA[<blockquote>cursor() hydrates single model instances sequentially using database cursors; lazy() streams records in chunks backed by LazyCollection.</blockquote>
<p>When iterating millions of records, get() exhausts PHP memory limits. cursor() uses PDO cursors to fetch records one by one, while lazy() queries records in chunks while exposing a fluent LazyCollection.</p>
<pre><code class="language-php">use App\Models\User;

// Cursors: single query, streams 1 instance at a time (lowest memory)
foreach (User::where(&#039;active&#039;, false)-&gt;cursor() as $user) {
    $user-&gt;archive();
}

// Lazy: queries in chunks of 1000 under the hood, provides LazyCollection API
User::where(&#039;active&#039;, false)-&gt;lazy(1000)-&gt;each-&gt;archive();</code></pre>
<ul>
  <li>cursor() uses a single database connection cursor for minimal RAM usage</li>
  <li>lazy() executes chunked subqueries under the hood and allows collection chaining</li>
  <li>Both prevent loading full datasets into PHP memory at once</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Avoid map() When Not Transforming: Use each()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-collection-each-vs-map</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-collection-each-vs-map</guid>
      <description><![CDATA[Use each() for iteration side effects like sending emails; use map() exclusively when returning a transformed collection. Using map() purely for side effects...]]></description>
      <content:encoded><![CDATA[<blockquote>Use each() for iteration side effects like sending emails; use map() exclusively when returning a transformed collection.</blockquote>
<p>Using map() purely for side effects builds an unused array in memory. Use each() to communicate intent when performing actions without modifying the collection elements.</p>
<pre><code class="language-php">// BAD: Builds a useless array of nulls in memory
$users-&gt;map(function ($user) {
    $user-&gt;notify(new MonthlyReport());
});

// GOOD: Expresses iteration intent clearly without allocating memory
$users-&gt;each(function ($user) {
    $user-&gt;notify(new MonthlyReport());
});</code></pre>
<ul>
  <li>map() constructs and returns a new transformed collection instance</li>
  <li>each() performs side-effect actions and returns the original collection</li>
  <li>Keeps memory footprint lower and code intent explicit</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Cache]]></category>
      <category><![CDATA[Collections]]></category>
      <category><![CDATA[Best Practices]]></category>
    </item>
    <item>
      <title><![CDATA[Choose the Right Processing Method: chunk(), lazy(), chunkById(), lazyById()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-chunk-vs-lazy-by-id</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-chunk-vs-lazy-by-id</guid>
      <description><![CDATA[Understand offset vs keyset pagination when processing large datasets to avoid missing records during updates. Updating records inside chunk() modifies the r...]]></description>
      <content:encoded><![CDATA[<blockquote>Understand offset vs keyset pagination when processing large datasets to avoid missing records during updates.</blockquote>
<p>Updating records inside chunk() modifies the result set, causing offset pagination to skip every second chunk. Use chunkById() or lazyById() when updating query columns.</p>
<pre><code class="language-php">use App\Models\User;

// BAD when updating filtered column: skips records due to offset shift!
User::where(&#039;processed&#039;, false)-&gt;chunk(100, function ($users) {
    $users-&gt;each-&gt;update([&#039;processed&#039; =&gt; true]);
});

// GOOD: Uses primary key comparison (id &gt; last_id) to avoid skipping
User::where(&#039;processed&#039;, false)-&gt;chunkById(100, function ($users) {
    $users-&gt;each-&gt;update([&#039;processed&#039; =&gt; true]);
});</code></pre>
<ul>
  <li>chunk() uses OFFSET pagination (vulnerable to skipping if queried fields change)</li>
  <li>chunkById() uses keyset pagination (WHERE id &gt; last_id), safe for updates</li>
  <li>lazyById() provides the same keyset safety wrapped in a LazyCollection</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Stop Using get() Before Collection Chains: Use lazy()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-lazy-collection-streaming</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-lazy-collection-streaming</guid>
      <description><![CDATA[Calling get() loads all matching rows into memory before filtering. Use lazy() to process database records lazily. Chaining collection methods like filter()...]]></description>
      <content:encoded><![CDATA[<blockquote>Calling get() loads all matching rows into memory before filtering. Use lazy() to process database records lazily.</blockquote>
<p>Chaining collection methods like filter() or map() after get() loads the entire dataset into RAM first. Using lazy() streams database records through collection operations on demand.</p>
<pre><code class="language-php">use App\Models\Order;

// BAD: Loads 500,000 orders into RAM before filtering
$expensive = Order::get()-&gt;filter(fn ($o) =&gt; $o-&gt;calculateTotal() &gt; 1000);

// GOOD: Streams records through filter lazily without RAM exhaustion
$expensive = Order::lazy()-&gt;filter(fn ($o) =&gt; $o-&gt;calculateTotal() &gt; 1000);</code></pre>
<ul>
  <li>get() executes SELECT * and instantiates all models immediately</li>
  <li>lazy() fetches records in chunks as collection pipeline elements demand</li>
  <li>Slashes RAM usage when applying complex collection filters to large datasets</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Collections]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Selecting Specific Columns Can Break Eloquent Relationships]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-select-columns-relationship-foreign-keys</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-select-columns-relationship-foreign-keys</guid>
      <description><![CDATA[Always include foreign key and primary key columns when using select() alongside eager loaded relationships. Using select('name') on a query with eager loade...]]></description>
      <content:encoded><![CDATA[<blockquote>Always include foreign key and primary key columns when using select() alongside eager loaded relationships.</blockquote>
<p>Using select('name') on a query with eager loaded relationships strips out foreign key columns like user_id. Without foreign key values, Eloquent cannot match child records to parent models, returning null.</p>
<pre><code class="language-php">use App\Models\Post;

// BAD: Missing user_id causes $post-&gt;author to return null!
$posts = Post::select(&#039;id&#039;, &#039;title&#039;)-&gt;with(&#039;author&#039;)-&gt;get();

// GOOD: Include foreign key &#039;user_id&#039; so relationship matching works
$posts = Post::select(&#039;id&#039;, &#039;title&#039;, &#039;user_id&#039;)-&gt;with(&#039;author&#039;)-&gt;get();</code></pre>
<ul>
  <li>Eloquent requires foreign keys in select() arrays to associate eager loaded models</li>
  <li>Always include primary key (id) and foreign key (e.g. user_id) when specifying select columns</li>
  <li>Missing keys lead to silent null relationship values</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Bug Prevention]]></category>
    </item>
    <item>
      <title><![CDATA[UI Controls Are Not Security Layers: Always Enforce Policies]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-security-ui-vs-policy-enforcement</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-security-ui-vs-policy-enforcement</guid>
      <description><![CDATA[Hiding buttons in Blade or Vue does not restrict access. Always enforce authorization policies in controller or request layers. Hiding an edit button using @...]]></description>
      <content:encoded><![CDATA[<blockquote>Hiding buttons in Blade or Vue does not restrict access. Always enforce authorization policies in controller or request layers.</blockquote>
<p>Hiding an edit button using @can or v-if only modifies visual presentation. Attackers can submit HTTP requests directly to backend endpoints. Always enforce authorization logic in backend controllers or form requests.</p>
<pre><code class="language-php">// Blade UI (Visual convenience only)
@can(&#039;update&#039;, $post)
    &lt;a href=&quot;{{ route(&#039;posts.edit&#039;, $post) }}&quot;&gt;Edit Post&lt;/a&gt;
@endcan

// Controller Action (Actual Security Layer)
public function update(UpdatePostRequest $request, Post $post)
{
    $this-&gt;authorize(&#039;update&#039;, $post); // Enforces server-side security
    $post-&gt;update($request-&gt;validated());
}</code></pre>
<ul>
  <li>UI directive checks like @can are user-experience features, not security guards</li>
  <li>Always enforce $this-&gt;authorize() or Policy checks in backend controllers</li>
  <li>Prevents unauthorized HTTP request payload tampering</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Validation]]></category>
      <category><![CDATA[Security]]></category>
      <category><![CDATA[Policies]]></category>
    </item>
    <item>
      <title><![CDATA[Don't Load Full Models for Single Columns: Use pluck() or value()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-pluck-value-over-full-models</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-pluck-value-over-full-models</guid>
      <description><![CDATA[Use value() for single scalar values and pluck() for single-column arrays instead of instantiating full Eloquent models. Querying full Eloquent model instanc...]]></description>
      <content:encoded><![CDATA[<blockquote>Use value() for single scalar values and pluck() for single-column arrays instead of instantiating full Eloquent models.</blockquote>
<p>Querying full Eloquent model instances just to read a single attribute like an email or name wastes CPU and memory. Use value() or pluck() to execute optimized database queries.</p>
<pre><code class="language-php">use App\Models\User;

// BAD: Instantiates entire User Eloquent model into RAM
$email = User::where(&#039;id&#039;, $id)-&gt;first()?-&gt;email;

// GOOD: Executes SELECT email LIMIT 1 and returns scalar string
$email = User::where(&#039;id&#039;, $id)-&gt;value(&#039;email&#039;);

// GOOD: Returns flat array of emails directly from database
$emails = User::where(&#039;active&#039;, true)-&gt;pluck(&#039;email&#039;);</code></pre>
<ul>
  <li>value(&#39;column&#39;) returns a single scalar value directly from database</li>
  <li>pluck(&#39;column&#39;) returns a flat array or collection of values</li>
  <li>Avoids model hydration overhead and reduces database payload transfer</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Dispatch Jobs After Transaction Commit with DB::afterCommit()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-db-after-commit-transaction-jobs</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-db-after-commit-transaction-jobs</guid>
      <description><![CDATA[Use DB::afterCommit() to defer job dispatching until surrounding database transactions complete successfully. Dispatching queue jobs inside active DB transac...]]></description>
      <content:encoded><![CDATA[<blockquote>Use DB::afterCommit() to defer job dispatching until surrounding database transactions complete successfully.</blockquote>
<p>Dispatching queue jobs inside active DB transactions can trigger race conditions where background workers run before the transaction commits. DB::afterCommit() delays side effects until commit finishes.</p>
<pre><code class="language-php">use Illuminate\Support\Facades\DB;
use App\Jobs\ProcessPayment;

DB::transaction(function () use ($order) {
    $order-&gt;save();
    
    // Guarantees worker receives committed database records
    DB::afterCommit(fn () =&gt; ProcessPayment::dispatch($order));
});</code></pre>
<ul>
  <li>Prevents race conditions where queue workers query uncommitted database rows</li>
  <li>Discards callbacks automatically if transaction rolls back</li>
  <li>Can be set on jobs using public $afterCommit = true;</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[Database]]></category>
    </item>
    <item>
      <title><![CDATA[Combine Filter and Eager Loading with withWhereHas()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-withwherehas-relationship-query</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-withwherehas-relationship-query</guid>
      <description><![CDATA[Use withWhereHas() to filter records based on relationship conditions and eager load the filtered relationship in a single method. Filtering models by relati...]]></description>
      <content:encoded><![CDATA[<blockquote>Use withWhereHas() to filter records based on relationship conditions and eager load the filtered relationship in a single method.</blockquote>
<p>Filtering models by relationship criteria while also eager loading the relationship previously required duplicating closures across whereHas() and with(). withWhereHas() performs both tasks in one call.</p>
<pre><code class="language-php">use App\Models\User;

// BEFORE: Duplicated relationship closure constraint
$users = User::whereHas(&#039;posts&#039;, fn ($q) =&gt; $q-&gt;where(&#039;published&#039;, true))
    -&gt;with([&#039;posts&#039; =&gt; fn ($q) =&gt; $q-&gt;where(&#039;published&#039;, true)])
    -&gt;get();

// AFTER: Combines filtering and eager loading cleanly
$users = User::withWhereHas(&#039;posts&#039;, fn ($q) =&gt; $q-&gt;where(&#039;published&#039;, true))-&gt;get();</code></pre>
<ul>
  <li>Eliminates duplicate constraint closures across whereHas and with</li>
  <li>Filters parent models while simultaneously eager loading matching children</li>
  <li>Keeps Eloquent builder queries concise and maintainable</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Prevent Skipped Records When Updating Chunks: Use chunkById()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-updating-chunk-by-id-prevention</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-updating-chunk-by-id-prevention</guid>
      <description><![CDATA[Always use chunkById() instead of chunk() when modifying columns present in your query filters to avoid offset pagination shifts. Updating records inside sta...]]></description>
      <content:encoded><![CDATA[<blockquote>Always use chunkById() instead of chunk() when modifying columns present in your query filters to avoid offset pagination shifts.</blockquote>
<p>Updating records inside standard chunk() shifts database offsets, causing every second chunk of records to be skipped silently. Using chunkById() relies on primary keys (id > last_id) preventing skipped rows.</p>
<pre><code class="language-php">use App\Models\User;

// ❌ WRONG: Standard chunk() skips records as status changes!
User::where(&#039;status&#039;, &#039;pending&#039;)-&gt;chunk(100, function ($users) {
    foreach ($users as $user) {
        $user-&gt;update([&#039;status&#039; =&gt; &#039;active&#039;]);
    }
});

// ✅ CORRECT: Keyset chunkById() updates every record safely
User::where(&#039;status&#039;, &#039;pending&#039;)-&gt;chunkById(100, function ($users) {
    foreach ($users as $user) {
        $user-&gt;update([&#039;status&#039; =&gt; &#039;active&#039;]);
    }
});</code></pre>
<ul>
  <li>Standard chunk() uses OFFSET pagination which shifts as records leave query criteria</li>
  <li>chunkById() uses keyset WHERE id &gt; last_id pagination for safe updates</li>
  <li>Prevents silent data processing omissions during background migrations</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Bug Prevention]]></category>
    </item>
    <item>
      <title><![CDATA[Never Save $request->all(): Use $request->validated()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-request-validated-over-all</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-request-validated-over-all</guid>
      <description><![CDATA[Always pass $request-validated() or $request-safe() into model creation methods to prevent mass-assignment vulnerabilities. Passing $request-all() directly i...]]></description>
      <content:encoded><![CDATA[<blockquote>Always pass $request->validated() or $request->safe() into model creation methods to prevent mass-assignment vulnerabilities.</blockquote>
<p>Passing $request->all() directly into Model::create() exposes applications to mass assignment vulnerabilities if unfillable or un-sanitized fields are submitted in HTTP request payloads.</p>
<pre><code class="language-php">use App\Http\Requests\StoreUserRequest;
use App\Models\User;

public function store(StoreUserRequest $request)
{
    // ❌ UNSAFE: Passes raw request keys including hidden payload injection
    // User::create($request-&gt;all());

    // ✅ SAFE: Passes only explicitly validated fields
    $user = User::create($request-&gt;validated());
}</code></pre>
<ul>
  <li>validated() filters HTTP input down to explicitly defined validation rules</li>
  <li>Prevents malicious form input keys from modifying un-fillable model attributes</li>
  <li>Pair with FormRequest classes for clean controller separation</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 21 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Security]]></category>
      <category><![CDATA[Validation]]></category>
    </item>
    <item>
      <title><![CDATA[Fetch Specific Related Attributes with withAggregate()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-withaggregate-subquery-optimization</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-withaggregate-subquery-optimization</guid>
      <description><![CDATA[Use withAggregate() to pull a single column value from a relationship via a SQL subquery without eager loading full model instances. When you need a single a...]]></description>
      <content:encoded><![CDATA[<blockquote>Use withAggregate() to pull a single column value from a relationship via a SQL subquery without eager loading full model instances.</blockquote>
<p>When you need a single attribute from a related model, eager loading the entire relationship wastes memory. Laravel's withAggregate() runs a subselect query directly in SQL.</p>
<pre><code class="language-php">use App\Models\Post;

// Pulls user name directly as $post-&gt;user_name via SQL subquery
$posts = Post::withAggregate(&#039;user&#039;, &#039;name&#039;)-&gt;get();</code></pre>
<ul>
  <li>Creates virtual attributes named {relation}_{column} or your alias</li>
  <li>Runs in a single database subquery instead of N+1 queries</li>
  <li>Underlies withCount(), withSum(), and withAvg() helpers</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Assert JSON Columns and Backed Enums with assertDatabaseHas]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-assert-database-has-json-enums</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-assert-database-has-json-enums</guid>
      <description><![CDATA[In Laravel and Pest tests, assertDatabaseHas() natively queries nested JSON properties using arrow syntax and accepts backed Enum instances directly. Testing...]]></description>
      <content:encoded><![CDATA[<blockquote>In Laravel and Pest tests, assertDatabaseHas() natively queries nested JSON properties using arrow syntax and accepts backed Enum instances directly.</blockquote>
<p>Testing JSON attributes or PHP backed enums requires no manual casting or encoding. assertDatabaseHas() supports arrow syntax and serializes enums automatically.</p>
<pre><code class="language-php">$this-&gt;assertDatabaseHas(&#039;users&#039;, [
    &#039;role&#039; =&gt; UserRole::Maintainer,
    &#039;settings-&gt;theme&#039; =&gt; &#039;dark&#039;,
]);</code></pre>
<ul>
  <li>Handles Backed Enums directly without -&gt;value casting</li>
  <li>Queries nested JSON keys using arrow notation</li>
  <li>Works out of the box with Pest and PHPUnit assertions</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Utilities]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[Pest]]></category>
      <category><![CDATA[Enums]]></category>
    </item>
    <item>
      <title><![CDATA[Scaffold Actions, Builders, and Collections with Artisan]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-extended-commands-scaffold-patterns</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-extended-commands-scaffold-patterns</guid>
      <description><![CDATA[Extend your generator commands to scaffold common domain patterns like Actions, Custom Query Builders, or Collections. Pushing business logic into Action cla...]]></description>
      <content:encoded><![CDATA[<blockquote>Extend your generator commands to scaffold common domain patterns like Actions, Custom Query Builders, or Collections.</blockquote>
<p>Pushing business logic into Action classes or custom Query Builders keeps controllers and models skinny. Use generator commands to scaffold these structural patterns instantly.</p>
<pre><code class="language-bash">php artisan make:builder UserBuilder
php artisan make:action CreateOrderAction</code></pre>
<ul>
  <li>Custom Builders encapsulate complex query scopes away from models</li>
  <li>Action classes provide single-responsibility execution for business operations</li>
  <li>Keeps directory structure predictable across team members</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Artisan]]></category>
      <category><![CDATA[DX]]></category>
    </item>
    <item>
      <title><![CDATA[Add a Reusable whereLike Macro for Eloquent Searching]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-custom-wherelike-macro</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-custom-wherelike-macro</guid>
      <description><![CDATA[Simplify multi-column wildcard searches across model attributes by registering a clean whereLike macro on the Eloquent Builder. Searching across multiple str...]]></description>
      <content:encoded><![CDATA[<blockquote>Simplify multi-column wildcard searches across model attributes by registering a clean whereLike macro on the Eloquent Builder.</blockquote>
<p>Searching across multiple string columns usually requires repetitive orWhere chains. Registering a macro in AppServiceProvider provides a clean API for wildcard matching.</p>
<pre><code class="language-php">use App\Models\User;

// Search across name, email, and bio in a single call
$users = User::whereLike([&#039;name&#039;, &#039;email&#039;, &#039;bio&#039;], $search)-&gt;get();</code></pre>
<ul>
  <li>Wraps multiple orWhere calls inside a grouped subquery clause</li>
  <li>Works with single column strings or arrays of columns</li>
  <li>Keeps controller search logic minimal and readable</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Macros]]></category>
    </item>
    <item>
      <title><![CDATA[Streamline Testing with Pest Test Impact Analysis (TIA)]]></title>
      <link>https://mrpunyapal.dev/tips/pest-tia-plugin-workflow</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/pest-tia-plugin-workflow</guid>
      <description><![CDATA[Enable Test Impact Analysis (TIA) in Pest to only run tests directly covering code that changed since the last commit. Running full test suites on every tiny...]]></description>
      <content:encoded><![CDATA[<blockquote>Enable Test Impact Analysis (TIA) in Pest to only run tests directly covering code that changed since the last commit.</blockquote>
<p>Running full test suites on every tiny change slows down feedback loops. Pest's TIA analyzes coverage artifacts to determine which tests cover modified lines.</p>
<pre><code class="language-bash">./vendor/bin/pest --tia</code></pre>
<ul>
  <li>Only runs tests affected by recent code changes</li>
  <li>Can be configured in pest.php or run via --tia flag</li>
  <li>Slashes test suite execution time during development</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Pest PHP]]></category>
      <category><![CDATA[Plugins]]></category>
      <category><![CDATA[Pest]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[DX]]></category>
    </item>
    <item>
      <title><![CDATA[Protect Production with Laravel Prohibitable Commands]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-prohibitable-destructive-commands</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-prohibitable-destructive-commands</guid>
      <description><![CDATA[Prevent catastrophic accidents like db:wipe or migrate:fresh in production using Laravel's Prohibitable trait and DB::prohibitDestructiveCommands(). Accident...]]></description>
      <content:encoded><![CDATA[<blockquote>Prevent catastrophic accidents like db:wipe or migrate:fresh in production using Laravel's Prohibitable trait and DB::prohibitDestructiveCommands().</blockquote>
<p>Accidentally running migrate:fresh on production is every team's nightmare. Laravel provides DB::prohibitDestructiveCommands() to disallow destructive commands in production.</p>
<pre><code class="language-php">public function boot(): void
{
    DB::prohibitDestructiveCommands($this-&gt;app-&gt;isProduction());
}</code></pre>
<ul>
  <li>Guards migrate:fresh, migrate:refresh, migrate:reset, and db:wipe</li>
  <li>Configured once in AppServiceProvider::boot()</li>
  <li>Throws command failure exit code if executed in production</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Configuration]]></category>
      <category><![CDATA[Database]]></category>
      <category><![CDATA[Security]]></category>
    </item>
    <item>
      <title><![CDATA[Customize Relative Timestamps with diffForHumans() Options]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-carbon-diffforhumans-syntax-options</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-carbon-diffforhumans-syntax-options</guid>
      <description><![CDATA[Pass Carbon options to diffForHumans() to control syntax flags, short units, and multi-part granularity. Carbon's diffForHumans() displays human-readable dat...]]></description>
      <content:encoded><![CDATA[<blockquote>Pass Carbon options to diffForHumans() to control syntax flags, short units, and multi-part granularity.</blockquote>
<p>Carbon's diffForHumans() displays human-readable dates. You can customize output by passing syntax flags for short units, removing 'ago' suffixes, or showing multiple time parts.</p>
<pre><code class="language-php">use Illuminate\Support\Carbon;

$date = now()-&gt;subDays(3)-&gt;subHours(4);

// Default: &#039;3 days ago&#039;
echo $date-&gt;diffForHumans();

// Short units: &#039;3d 4h ago&#039;
echo $date-&gt;diffForHumans([&#039;short&#039; =&gt; true, &#039;parts&#039; =&gt; 2]);

// Absolute (no &#039;ago&#039;): &#039;3 days&#039;
echo $date-&gt;diffForHumans([&#039;syntax&#039; =&gt; Carbon::DIFF_ABSOLUTE]);</code></pre>
<ul>
  <li>short option abbreviates time units (&#39;3d 4h&#39;)</li>
  <li>parts parameter displays precise multi-unit granularity</li>
  <li>DIFF_ABSOLUTE syntax removes directional &#39;ago&#39; or &#39;from now&#39; suffixes</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Utilities]]></category>
      <category><![CDATA[Carbon]]></category>
      <category><![CDATA[Helpers]]></category>
    </item>
    <item>
      <title><![CDATA[Why strip_tags() Is Not Enough for XSS Protection]]></title>
      <link>https://mrpunyapal.dev/tips/php-security-strip-tags-vs-htmlpurifier</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-security-strip-tags-vs-htmlpurifier</guid>
      <description><![CDATA[striptags() removes HTML elements but fails to sanitize inline attributes or malformed HTML payload vectors. Use HTMLPurifier for rich text input. A common s...]]></description>
      <content:encoded><![CDATA[<blockquote>strip_tags() removes HTML elements but fails to sanitize inline attributes or malformed HTML payload vectors. Use HTMLPurifier for rich text input.</blockquote>
<p>A common security misconception in PHP is relying on strip_tags() to sanitize user-submitted rich text. It allows attribute payloads like onload= or javascript: URIs through if allowed tags are specified.</p>
<pre><code class="language-php">use HTMLPurifier;

$purifier = new HTMLPurifier();
$cleanHtml = $purifier-&gt;purify($input);</code></pre>
<ul>
  <li>strip_tags() does not validate tag attributes or execution vectors</li>
  <li>Always escape plain text with e() or Blade&#39;s {{ $var }}</li>
  <li>For user-submitted rich text, use reliable HTML sanitizers like HTMLPurifier</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 18 Apr 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Basics]]></category>
      <category><![CDATA[Security]]></category>
      <category><![CDATA[XSS]]></category>
    </item>
    <item>
      <title><![CDATA[Clean Up Collections with Higher-Order Collection Messages]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-higher-order-collection-messages</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-higher-order-collection-messages</guid>
      <description><![CDATA[Use higher-order collection proxies like $users-each-archive() or $orders-sum-total to replace verbose closure callbacks. Writing closures for single method...]]></description>
      <content:encoded><![CDATA[<blockquote>Use higher-order collection proxies like $users->each->archive() or $orders->sum->total to replace verbose closure callbacks.</blockquote>
<p>Writing closures for single method invocations or attribute access across collections adds visual noise. Higher-order collection messages provide short property proxies on collections.</p>
<pre><code class="language-php">use App\Models\User;

// BEFORE: Verbose closure
$users-&gt;each(function ($user) {
    $user-&gt;archive();
});
$total = $orders-&gt;sum(function ($order) {
    return $order-&gt;total;
});

// AFTER: Clean higher-order proxies
$users-&gt;each-&gt;archive();
$total = $orders-&gt;sum-&gt;total;</code></pre>
<ul>
  <li>Provides higher-order proxies for map, each, filter, reject, sum, and more</li>
  <li>Replaces single-line closure wrappers with property syntax</li>
  <li>Works with both Eloquent model methods and attribute names</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Collections]]></category>
      <category><![CDATA[Syntax]]></category>
    </item>
    <item>
      <title><![CDATA[Clean Up Complex Multi-Step Operations with Illuminate Pipeline]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-pipeline-pattern-complex-workflows</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-pipeline-pattern-complex-workflows</guid>
      <description><![CDATA[Process complex data sequences or multi-stage order checks through Laravel's built-in Pipeline facade to replace massive controller methods. When processing...]]></description>
      <content:encoded><![CDATA[<blockquote>Process complex data sequences or multi-stage order checks through Laravel's built-in Pipeline facade to replace massive controller methods.</blockquote>
<p>When processing multi-stage workflows (like order checkout validation or user onboarding steps), controllers accumulate giant if blocks. Laravel's Pipeline facade passes objects sequentially through pipe classes.</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Pipeline;

$order = Pipeline::send($draftOrder)
    -&gt;through([VerifyStock::class, ApplyDiscountCode::class])
    -&gt;thenReturn();</code></pre>
<ul>
  <li>Each pipe is a single-responsibility class with a handle() signature</li>
  <li>Easily add, remove, or reorder pipeline stages</li>
  <li>Each step can be unit tested independently</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Architecture]]></category>
      <category><![CDATA[Design Patterns]]></category>
    </item>
    <item>
      <title><![CDATA[Extract Complex Controller Responses into Responsable Classes]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-custom-responsable-classes</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-custom-responsable-classes</guid>
      <description><![CDATA[Implement the Responsable interface to create dedicated response objects that handle headers, view data, and formatting outside controllers. When controller...]]></description>
      <content:encoded><![CDATA[<blockquote>Implement the Responsable interface to create dedicated response objects that handle headers, view data, and formatting outside controllers.</blockquote>
<p>When controller actions accumulate complex redirect logic, header building, or conditional JSON rendering, move response logic into a dedicated class implementing Illuminate\Contracts\Support\Responsable.</p>
<pre><code class="language-php">namespace App\Http\Responses;

use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\JsonResponse;

class InvoiceExportResponse implements Responsable
{
    public function __construct(private array $data) {}

    public function toResponse($request): JsonResponse
    {
        return response()-&gt;json($this-&gt;data)
            -&gt;header(&#039;X-Export-Timestamp&#039;, now()-&gt;timestamp);
    }
}

// Controller Action
return new InvoiceExportResponse($data);</code></pre>
<ul>
  <li>Implements Responsable interface with toResponse($request) signature</li>
  <li>Keeps controller actions clean and focused strictly on request handling</li>
  <li>Allows returning custom response instances directly from routes or controllers</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Routing]]></category>
      <category><![CDATA[Architecture]]></category>
      <category><![CDATA[HTTP]]></category>
    </item>
    <item>
      <title><![CDATA[Avoid whereDate() on Large Tables: Use Range Queries Instead]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-wheredate-index-performance</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-wheredate-index-performance</guid>
      <description><![CDATA[whereDate() wraps the column in a DATE() function, preventing the database from using indexes. Use whereBetween() with full timestamps for index-friendly fil...]]></description>
      <content:encoded><![CDATA[<blockquote>whereDate() wraps the column in a DATE() function, preventing the database from using indexes. Use whereBetween() with full timestamps for index-friendly filtering.</blockquote>
<p>Filtering records with whereDate() forces the database to evaluate the DATE() function on every row, bypassing indexes. Use whereBetween() with explicit timestamps.</p>
<pre><code class="language-php">$orders = Order::whereBetween(&#039;created_at&#039;, [
    $date . &#039; 00:00:00&#039;,
    $date . &#039; 23:59:59&#039;,
])-&gt;get();</code></pre>
<ul>
  <li>whereBetween() enables B-Tree index range scans</li>
  <li>Avoids wrapping indexed columns in SQL functions</li>
  <li>Crucial for high-traffic tables with millions of rows</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Remove Global Eager Loading with withoutRelation()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-without-relation-eloquent-builder</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-without-relation-eloquent-builder</guid>
      <description><![CDATA[Use withoutRelation() or unsetRelation() to remove eager loaded relationships on specific Eloquent queries or model instances. Models with $with properties e...]]></description>
      <content:encoded><![CDATA[<blockquote>Use withoutRelation() or unsetRelation() to remove eager loaded relationships on specific Eloquent queries or model instances.</blockquote>
<p>Models with $with properties eager load relations globally on every query. Use withoutRelation() on query builders to bypass global eager loads when relations are unneeded.</p>
<pre><code class="language-php">use App\Models\Post;

// Model defines protected $with = [&#039;author&#039;, &#039;comments&#039;];

// Query: Exclude &#039;comments&#039; relation for this specific query
$posts = Post::withoutRelation(&#039;comments&#039;)-&gt;get();

// Instance: Unset loaded relation on an existing model object
$post-&gt;unsetRelation(&#039;comments&#039;);</code></pre>
<ul>
  <li>Bypasses default $with eager loading definitions per-query</li>
  <li>unsetRelation() clears in-memory relation data on model instances</li>
  <li>Reduces unnecessary database subqueries for specific lightweight queries</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Relationships]]></category>
    </item>
    <item>
      <title><![CDATA[Use blank() and filled() Instead of empty() in Laravel]]></title>
      <link>https://mrpunyapal.dev/tips/php-blank-filled-over-empty</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-blank-filled-over-empty</guid>
      <description><![CDATA[PHP's empty() treats 0, '0', and false as empty. Laravel's blank() and filled() helpers handle these values intuitively without silent bugs. PHP's empty() is...]]></description>
      <content:encoded><![CDATA[<blockquote>PHP's empty() treats 0, '0', and false as empty. Laravel's blank() and filled() helpers handle these values intuitively without silent bugs.</blockquote>
<p>PHP's empty() is notoriously loose: it considers 0 and '0' empty. Laravel provides blank() and filled() as safer alternatives.</p>
<pre><code class="language-php">if (blank($request-&gt;input(&#039;quantity&#039;))) {
    return &#039;Quantity is required&#039;;
}</code></pre>
<ul>
  <li>blank() treats 0 and &#39;0&#39; as filled values, not blank</li>
  <li>filled() is the exact inverse of blank()</li>
  <li>Handles whitespace-only strings correctly</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Utilities]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Helpers]]></category>
      <category><![CDATA[Best Practices]]></category>
    </item>
    <item>
      <title><![CDATA[Match Multiple Values in a Single Match Arm in PHP]]></title>
      <link>https://mrpunyapal.dev/tips/php-match-arm-multiple-values</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-match-arm-multiple-values</guid>
      <description><![CDATA[Separate multiple comma-delimited values within a single match expression arm to group identical execution branches. When multiple inputs share the exact sam...]]></description>
      <content:encoded><![CDATA[<blockquote>Separate multiple comma-delimited values within a single match expression arm to group identical execution branches.</blockquote>
<p>When multiple inputs share the exact same output branch in a match expression, list the values separated by commas in a single match arm instead of repeating arms.</p>
<pre><code class="language-php">$status = &#039;processing&#039;;

$label = match ($status) {
    &#039;pending&#039;, &#039;processing&#039;, &#039;queued&#039; =&gt; &#039;In Progress&#039;,
    &#039;completed&#039;, &#039;delivered&#039;         =&gt; &#039;Successful&#039;,
    &#039;failed&#039;, &#039;cancelled&#039;           =&gt; &#039;Unsuccessful&#039;,
    default                           =&gt; &#039;Unknown Status&#039;,
};</code></pre>
<ul>
  <li>Groups multiple matching conditions using comma-separated expressions</li>
  <li>Eliminates duplicate result assignments across identical logic arms</li>
  <li>Evaluates using strict identity comparison (===)</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 16 Feb 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Syntax]]></category>
      <category><![CDATA[Control Flow]]></category>
    </item>
    <item>
      <title><![CDATA[Squash Legacy Database Migrations Carefully]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-squash-migrations-workflow</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-squash-migrations-workflow</guid>
      <description><![CDATA[Use php artisan schema:dump to collapse hundreds of old migration files into a single SQL schema file while preserving new migrations. As applications age, r...]]></description>
      <content:encoded><![CDATA[<blockquote>Use php artisan schema:dump to collapse hundreds of old migration files into a single SQL schema file while preserving new migrations.</blockquote>
<p>As applications age, running hundreds of individual database migration files slows down test suites and deployment setups. Use schema:dump to collapse old migrations into a clean schema file.</p>
<pre><code class="language-bash"># Dump schema and prune old migration files
php artisan schema:dump --prune

# Schema is saved to database/schema/mysql-schema.sql</code></pre>
<ul>
  <li>Collapses old migration files into a single database/schema dump file</li>
  <li>Slashes migration execution time during automated test suite runs</li>
  <li>New migration files created after squashing run sequentially after the schema dump</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 17 Jan 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Configuration]]></category>
      <category><![CDATA[Migrations]]></category>
      <category><![CDATA[DevOps]]></category>
    </item>
    <item>
      <title><![CDATA[Seed Large Database Dumps with SchemaState::load()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-schema-state-load-seeding</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-schema-state-load-seeding</guid>
      <description><![CDATA[Use SchemaState::load() to load large raw SQL dump files directly through database CLI tools instead of slow DB::unprepared() execution. Executing massive ra...]]></description>
      <content:encoded><![CDATA[<blockquote>Use SchemaState::load() to load large raw SQL dump files directly through database CLI tools instead of slow DB::unprepared() execution.</blockquote>
<p>Executing massive raw SQL dumps inside DB::unprepared() runs through PHP memory buffers and PDO statements, which is slow. SchemaState::load() invokes native database CLI tools (mysql/psql) directly.</p>
<pre><code class="language-php">use Illuminate\Support\Facades\DB;

// Fast native CLI SQL dump loading
$connection = DB::connection();
$connection-&gt;getSchemaState()-&gt;load(database_path(&#039;dumps/initial_data.sql&#039;));</code></pre>
<ul>
  <li>Uses native database CLI binaries (mysql/psql) for maximum execution speed</li>
  <li>Bypasses PHP memory buffer overhead during large data imports</li>
  <li>Ideal for seeding production-like reference datasets in test environments</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Cache]]></category>
      <category><![CDATA[Database]]></category>
      <category><![CDATA[Testing]]></category>
    </item>
    <item>
      <title><![CDATA[Simplify Class Instantiation with Constructor Property Promotion]]></title>
      <link>https://mrpunyapal.dev/tips/php-constructor-property-promotion</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-constructor-property-promotion</guid>
      <description><![CDATA[Combine property declarations and constructor parameter assignments in PHP 8 for concise class definitions. Declaring class properties, parameter signatures,...]]></description>
      <content:encoded><![CDATA[<blockquote>Combine property declarations and constructor parameter assignments in PHP 8 for concise class definitions.</blockquote>
<p>Declaring class properties, parameter signatures, and manual $this->prop = $prop assignments creates boilerplate code. Constructor property promotion combines all three steps in the parameter list.</p>
<pre><code class="language-php">namespace App\Services;

class PaymentProcessor
{
    // Property promotion combines declaration, type, and assignment
    public function __construct(
        public readonly StripeClient $client,
        private string $apiKey,
        protected int $timeout = 30,
    ) {}
}</code></pre>
<ul>
  <li>Eliminates repetitive property definitions and assignment statements</li>
  <li>Supports visibility modifiers (public, protected, private) and readonly flags</li>
  <li>Fully compatible with docblocks, attributes, and default values</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 31 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Syntax]]></category>
      <category><![CDATA[OOP]]></category>
    </item>
    <item>
      <title><![CDATA[Run Post-Request Callbacks with afterResponse() in Laravel 12.44]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-http-client-after-response-middleware</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-http-client-after-response-middleware</guid>
      <description><![CDATA[Laravel 12.44 adds the afterResponse() hook to the HTTP client, allowing you to attach response logging, metrics, and error handling callbacks cleanly inside...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 12.44 adds the afterResponse() hook to the HTTP client, allowing you to attach response logging, metrics, and error handling callbacks cleanly inside client macros.</blockquote>
<p>When building reusable API integrations with HTTP client macros, logging responses or checking status codes previously required wrapping request calls in every controller or service.</p>
<p>With `afterResponse()`, you can attach post-request callbacks directly inside your HTTP client macro definitions:</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\Response;

Http::macro(&#039;github&#039;, fn () =&gt;
    Http::baseUrl(&#039;https://api.github.com&#039;)
        -&gt;acceptJson()

        // Metrics &amp; request logging
        -&gt;afterResponse(fn (Response $response) =&gt; logger()
            -&gt;info(&#039;GitHub API response&#039;, [
                &#039;status&#039; =&gt; $response-&gt;status(),
            ])
        )

        // Conditional error handling
        -&gt;afterResponse(fn (Response $response) =&gt; $response-&gt;failed()
            &amp;&amp; logger()-&gt;error(&#039;GitHub API call failed&#039;)
        )
);

// Clean controller usage without inline log boilerplate
Http::github()-&gt;get(&#039;/repos/laravel/framework&#039;);</code></pre>
<ul>
  <li>Callbacks execute automatically after the response is received</li>
  <li>Multiple <code>afterResponse()</code> callbacks can be chained on a single request or macro</li>
  <li>Keeps API client logic self-contained inside service providers</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 29 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[HTTP & API]]></category>
      <category><![CDATA[HTTP Client]]></category>
      <category><![CDATA[Middleware]]></category>
    </item>
    <item>
      <title><![CDATA[Use Fluent Date Validation Rule Helpers in Laravel 12.44]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-date-validation-rule-helpers</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-date-validation-rule-helpers</guid>
      <description><![CDATA[Laravel 12.44 introduces fluent date validation helpers on the Rule facade, replacing string-based date comparisons with self-documenting method calls. Valid...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel 12.44 introduces fluent date validation helpers on the Rule facade, replacing string-based date comparisons with self-documenting method calls.</blockquote>
<p>Validating dates relative to the current time previously required writing string rules like `date|before:now` or `date|after_or_equal:today`.</p>
<p>Laravel 12.44 adds readable method builders under `Rule::date()`:</p>
<pre><code class="language-php">use Illuminate\Validation\Rule;

$request-&gt;validate([
    // Must be strictly in the past
    &#039;logged_at&#039; =&gt; [
        &#039;required&#039;,
        Rule::date()-&gt;past(),
    ],

    // Must be strictly in the future
    &#039;scheduled_at&#039; =&gt; [
        Rule::date()-&gt;future(),
    ],

    // Must be current timestamp or past
    &#039;completed_at&#039; =&gt; [
        Rule::date()-&gt;nowOrPast(),
    ],

    // Must be current timestamp or future
    &#039;expires_at&#039; =&gt; [
        Rule::date()-&gt;nowOrFuture(),
    ],

    // Standard date-time format builder (Y-m-d H:i:s)
    &#039;published_at&#039; =&gt; [
        Rule::dateTime(),
    ],
]);</code></pre>
<ul>
  <li>Replaces hardcoded string rules with IDE-auto-completable methods</li>
  <li><code>nowOrPast()</code> and <code>nowOrFuture()</code> handle inclusive boundary checks cleanly</li>
  <li><code>Rule::dateTime()</code> standardizes database timestamp format validation</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Validation]]></category>
    </item>
    <item>
      <title><![CDATA[Enable Compact Test Output Printer in Pest]]></title>
      <link>https://mrpunyapal.dev/tips/pest-compact-printer-output</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/pest-compact-printer-output</guid>
      <description><![CDATA[Use the --compact flag or configure compact output in pest.php for minimal single-character test progress indicators. When running large test suites with hun...]]></description>
      <content:encoded><![CDATA[<blockquote>Use the --compact flag or configure compact output in pest.php for minimal single-character test progress indicators.</blockquote>
<p>When running large test suites with hundreds of tests, verbose output fills terminal buffers. Enabling compact printer output displays dots and characters for fast console feedback.</p>
<pre><code class="language-bash"># Enable compact output via CLI
./vendor/bin/pest --compact</code></pre>
<ul>
  <li>Displays minimal test progress indicators to reduce console output clutter</li>
  <li>Highlights failures and errors prominently with detailed tracebacks</li>
  <li>Saves terminal scrollback memory during long test suite runs</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Pest PHP]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[Pest]]></category>
      <category><![CDATA[DX]]></category>
    </item>
    <item>
      <title><![CDATA[Handle Model Relationships Explicitly in Queue Jobs]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-queue-job-model-relationships</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-queue-job-model-relationships</guid>
      <description><![CDATA[Re-load or pass model primary keys into queued jobs instead of relying on stale serialized relationship collections. When Eloquent models are serialized for...]]></description>
      <content:encoded><![CDATA[<blockquote>Re-load or pass model primary keys into queued jobs instead of relying on stale serialized relationship collections.</blockquote>
<p>When Eloquent models are serialized for queue jobs, loaded relationships are serialized as well. If relationship records change before the job executes, workers operate on stale data. Pass IDs or call $model->refresh().</p>
<pre><code class="language-php">namespace App\Jobs;

use App\Models\Order;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendInvoiceJob implements ShouldQueue
{
    public function __construct(public Order $order) {}

    public function handle(): void
    {
        // Refresh model and reload relations to ensure fresh database state
        $this-&gt;order-&gt;refresh()-&gt;load(&#039;items.product&#039;);
    }
}</code></pre>
<ul>
  <li>Serialized job relations can become stale while sitting in queue backlogs</li>
  <li>Call $model-&gt;refresh() or reload relations inside handle() method</li>
  <li>Alternatively pass model primary keys (IDs) and query fresh inside job</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 14 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[Best Practices]]></category>
    </item>
    <item>
      <title><![CDATA[Clean Up Pest Test Configuration in tests/Pest.php]]></title>
      <link>https://mrpunyapal.dev/tips/pest-clean-test-setup-configuration</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/pest-clean-test-setup-configuration</guid>
      <description><![CDATA[Organize base test case bindings, helper functions, and global traits cleanly inside tests/Pest.php. Duplicate uses() declarations across every test file clu...]]></description>
      <content:encoded><![CDATA[<blockquote>Organize base test case bindings, helper functions, and global traits cleanly inside tests/Pest.php.</blockquote>
<p>Duplicate uses() declarations across every test file clutters test suites. Centralize common test traits (like RefreshDatabase or TestCase classes) inside tests/Pest.php grouped by directory.</p>
<pre><code class="language-php">// tests/Pest.php
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

uses(TestCase::class, RefreshDatabase::class)-&gt;in(&#039;Feature&#039;);
uses(TestCase::class)-&gt;in(&#039;Unit&#039;);</code></pre>
<ul>
  <li>Centralizes global traits like RefreshDatabase for specific test folders</li>
  <li>Eliminates repetitive uses() imports across individual test files</li>
  <li>Defines custom expectation helpers globally for all test suites</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 07 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Pest PHP]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[Pest]]></category>
      <category><![CDATA[Setup]]></category>
    </item>
    <item>
      <title><![CDATA[Optimize Queue Worker Polling Overheads in Production]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-queue-worker-polling-optimization</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-queue-worker-polling-optimization</guid>
      <description><![CDATA[Use queue:work with appropriate sleep configuration or Redis blocking pops to reduce database CPU polling overheads. Queue workers set to poll databases with...]]></description>
      <content:encoded><![CDATA[<blockquote>Use queue:work with appropriate sleep configuration or Redis blocking pops to reduce database CPU polling overheads.</blockquote>
<p>Queue workers set to poll databases without sleep configs execute continuous SELECT queries, driving database CPU usage up. Configure appropriate sleep intervals or use Redis queue drivers.</p>
<pre><code class="language-bash"># Wait 3 seconds when queue is empty before polling again
php artisan queue:work --sleep=3 --tries=3 --timeout=90</code></pre>
<ul>
  <li>--sleep=3 pauses worker polling when no jobs are available</li>
  <li>Reduces CPU usage and database query loads on idle queue workers</li>
  <li>Redis queue driver uses blocking pop operations for instant zero-polling dispatch</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[DevOps]]></category>
    </item>
    <item>
      <title><![CDATA[Unpack Arrays with Spread Syntax in PHP 8.1]]></title>
      <link>https://mrpunyapal.dev/tips/php-array-unpacking-square-brackets</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-array-unpacking-square-brackets</guid>
      <description><![CDATA[Use array unpacking (...) inside square bracket array literals for string-keyed and indexed array merging. PHP 8.1 expanded array unpacking to support string...]]></description>
      <content:encoded><![CDATA[<blockquote>Use array unpacking (...) inside square bracket array literals for string-keyed and indexed array merging.</blockquote>
<p>PHP 8.1 expanded array unpacking to support string keys inside array literals. This replaces array_merge() calls with clean spread syntax.</p>
<pre><code class="language-php">$defaults = [&#039;theme&#039; =&gt; &#039;dark&#039;, &#039;notifications&#039; =&gt; true];
$userCustom = [&#039;notifications&#039; =&gt; false, &#039;language&#039; =&gt; &#039;en&#039;];

// Unpacks and merges arrays natively
$options = [...$defaults, ...$userCustom];</code></pre>
<ul>
  <li>Replaces verbose array_merge() calls with clean spread operator syntax</li>
  <li>Supports string keys as of PHP 8.1 (later values overwrite earlier keys)</li>
  <li>Cleaner syntax for array composition and middleware pipelines</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Syntax]]></category>
      <category><![CDATA[Arrays]]></category>
    </item>
    <item>
      <title><![CDATA[Auto-Scale Queue Workers with --stop-when-empty-for in Laravel]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-queue-worker-stop-when-empty-for</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-queue-worker-stop-when-empty-for</guid>
      <description><![CDATA[The --stop-when-empty-for option keeps queue workers alive for a specific grace period after the queue empties, preventing rapid process churn during bursty...]]></description>
      <content:encoded><![CDATA[<blockquote>The --stop-when-empty-for option keeps queue workers alive for a specific grace period after the queue empties, preventing rapid process churn during bursty workloads.</blockquote>
<p>Running `php artisan queue:work --stop-when-empty` in serverless environments or container auto-scalers terminates the worker process immediately when no jobs remain. If new jobs arrive seconds later, new processes must spin up continuously.</p>
<p>The `--stop-when-empty-for` option adds a configurable idle timeout:</p>
<pre><code class="language-bash"># Stops worker immediately once queue is empty
php artisan queue:work --stop-when-empty

# Keeps worker running for 60 idle seconds before exiting
php artisan queue:work --stop-when-empty-for=60</code></pre>
<ul>
  <li>Prevents process start/stop churn during intermittent job spikes</li>
  <li>Ideal for AWS ECS, Kubernetes HPA, and Serverless worker instances</li>
  <li>Retains database connection pooling while waiting for trailing jobs</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[DevOps]]></category>
    </item>
    <item>
      <title><![CDATA[Simplify Default Values with Null Coalescing Assignment (??=)]]></title>
      <link>https://mrpunyapal.dev/tips/php-null-coalescing-assignment-operator</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-null-coalescing-assignment-operator</guid>
      <description><![CDATA[Replace verbose ternary checks and null coalescing reassignments with PHP null coalescing assignment operator (??=). Setting default values on nullable varia...]]></description>
      <content:encoded><![CDATA[<blockquote>Replace verbose ternary checks and null coalescing reassignments with PHP null coalescing assignment operator (??=).</blockquote>
<p>Setting default values on nullable variables often leads to repetitive variable references across statements.</p>
<p>PHP supports null coalescing assignment (`??=`) to combine evaluation and assignment into a single expression:</p>
<pre><code class="language-php">$username = null;

// Verbose ternary operator
$username = $username !== null ? $username : &#039;MrPunyapal&#039;;

// Null coalescing operator
$username = $username ?? &#039;MrPunyapal&#039;;

// Clean null coalescing assignment
$username ??= &#039;MrPunyapal&#039;;</code></pre>
<ul>
  <li>Only assigns the right-hand value if the left-hand variable is <code>null</code></li>
  <li>Prevents repeating variable names on both sides of the assignment</li>
  <li>Reduces code clutter in configuration arrays and option initialization</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Syntax]]></category>
      <category><![CDATA[Clean Code]]></category>
    </item>
    <item>
      <title><![CDATA[Prevent Duplicate Redis and Database Lookups with Cache::memo()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-cache-memo-driver-in-memory</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-cache-memo-driver-in-memory</guid>
      <description><![CDATA[Use Cache::memo() to combine persistent cache stores with per-request memory caching, preventing repetitive network roundtrips during a single HTTP request....]]></description>
      <content:encoded><![CDATA[<blockquote>Use Cache::memo() to combine persistent cache stores with per-request memory caching, preventing repetitive network roundtrips during a single HTTP request.</blockquote>
<p>Calling `Cache::get('key')` multiple times in a single request still incurs a Redis network roundtrip or database query each time.</p>
<p>`Cache::memo()` wraps your configured cache store with an in-memory array cache for the duration of the current request:</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Cache;

// Standard Cache: 3 Redis network roundtrips
$permissions = Cache::get(&#039;user.permissions&#039;); // Redis query
$permissions = Cache::get(&#039;user.permissions&#039;); // Redis query
$permissions = Cache::get(&#039;user.permissions&#039;); // Redis query

// Cache::memo(): 1 Redis query, subsequent calls read from memory
$permissions = Cache::memo()-&gt;get(&#039;user.permissions&#039;); // Redis query
$permissions = Cache::memo()-&gt;get(&#039;user.permissions&#039;); // In-memory hit
$permissions = Cache::memo()-&gt;get(&#039;user.permissions&#039;); // In-memory hit

// Mutations automatically sync the persistent store and invalidate local memory
Cache::memo()-&gt;put(&#039;user.status&#039;, &#039;active&#039;);
Cache::memo()-&gt;increment(&#039;page.views&#039;);

// Works with specific cache stores
Cache::memo(&#039;redis&#039;)-&gt;remember(&#039;expensive-report&#039;, 3600, fn () =&gt;
    $this-&gt;buildReport()
);</code></pre>
<ul>
  <li>Eliminates redundant cache store queries during heavy request lifecycles</li>
  <li>In-memory values reset automatically at the end of the HTTP request</li>
  <li>Mutation methods (<code>put</code>, <code>increment</code>, <code>forget</code>) keep memory and storage in sync</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[HTTP & API]]></category>
      <category><![CDATA[Cache]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Automate Short Closure Conversions with Laravel Pint]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-pint-use-arrow-functions-rule</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-pint-use-arrow-functions-rule</guid>
      <description><![CDATA[Enable the usearrowfunctions rule in pint.json to automatically refactor single-line closures into arrow functions. Manually converting single-line function...]]></description>
      <content:encoded><![CDATA[<blockquote>Enable the use_arrow_functions rule in pint.json to automatically refactor single-line closures into arrow functions.</blockquote>
<p>Manually converting single-line function () use ($var) callbacks to fn () arrow functions across codebases takes time. Laravel Pint's use_arrow_functions rule automates this conversion.</p>
<pre><code class="language-json">// pint.json
{
    &quot;rules&quot;: {
        &quot;use_arrow_functions&quot;: true
    }
}</code></pre>
<ul>
  <li>Converts single-line closures to short arrow functions fn () automatically</li>
  <li>Auto-captures outer scope variables without explicit use () bindings</li>
  <li>Enforces modern short closure conventions across your codebase</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 11 Aug 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Utilities]]></category>
      <category><![CDATA[Pint]]></category>
      <category><![CDATA[Code Quality]]></category>
    </item>
    <item>
      <title><![CDATA[Combine Multiple Alpine.data Objects Inside a Single x-data]]></title>
      <link>https://mrpunyapal.dev/tips/alpinejs-multiple-data-objects-xdata</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/alpinejs-multiple-data-objects-xdata</guid>
      <description><![CDATA[Spread multiple Alpine.data component factories into a single x-data directive to compose modular frontend state. When a single UI element requires state fro...]]></description>
      <content:encoded><![CDATA[<blockquote>Spread multiple Alpine.data component factories into a single x-data directive to compose modular frontend state.</blockquote>
<p>When a single UI element requires state from multiple Alpine components (e.g. dropdown state + search state), use spread syntax inside x-data to merge component factories.</p>
<pre><code class="language-html">&lt;script&gt;
  document.addEventListener(&#039;alpine:init&#039;, () =&gt; {
    Alpine.data(&#039;dropdown&#039;, () =&gt; ({ open: false, toggle() { this.open = !this.open } }));
    Alpine.data(&#039;search&#039;, () =&gt; ({ query: &#039;&#039;, clear() { this.query = &#039;&#039; } }));
  });
&lt;/script&gt;

&lt;!-- Merge multiple data objects via spread operator --&gt;
&lt;div x-data=&quot;{ ...dropdown(), ...search() }&quot;&gt;
  &lt;input x-model=&quot;query&quot;&gt;
  &lt;button @click=&quot;toggle()&quot;&gt;Toggle Menu&lt;/button&gt;
&lt;/div&gt;</code></pre>
<ul>
  <li>Composes multiple Alpine.data component factories into one container</li>
  <li>Keeps JavaScript state definitions modular and reusable</li>
  <li>Prevents deeply nested wrapper div structures in HTML markup</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 27 Jul 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Frameworks]]></category>
      <category><![CDATA[Alpine.js]]></category>
      <category><![CDATA[Frontend]]></category>
    </item>
    <item>
      <title><![CDATA[Use Real-Time Facades with the Facades Prefix]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-real-time-facades</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-real-time-facades</guid>
      <description><![CDATA[Prefix any application class import with Facades\ to instantly treat it as a mockable Laravel Facade. Creating explicit Facade classes for internal services...]]></description>
      <content:encoded><![CDATA[<blockquote>Prefix any application class import with Facades\ to instantly treat it as a mockable Laravel Facade.</blockquote>
<p>Creating explicit Facade classes for internal services adds boilerplate. Laravel's Real-Time Facades generate mockable facades on the fly by prefixing namespace imports with Facades\.</p>
<pre><code class="language-php">namespace App\Http\Controllers;

// Import class with Facades\ prefix for instant facade capabilities
use Facades\App\Services\PaymentGateway;

class CheckoutController
{
    public function store()
    {
        PaymentGateway::charge(100); // Executed as real-time facade
    }
}</code></pre>
<ul>
  <li>Eliminates boilerplate dedicated Facade class files</li>
  <li>Allows instant test mocking via PaymentGateway::shouldReceive()</li>
  <li>Resolves underlying service class instance from container automatically</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 12 Jul 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[HTTP & API]]></category>
      <category><![CDATA[Facades]]></category>
      <category><![CDATA[Testing]]></category>
    </item>
    <item>
      <title><![CDATA[Master Essential PHP Predefined and Magic Constants]]></title>
      <link>https://mrpunyapal.dev/tips/php-predefined-constants-cheat-sheet</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-predefined-constants-cheat-sheet</guid>
      <description><![CDATA[A practical cheat sheet covering PHP magic constants, filesystem path helpers, error reporting masks, and CLI stream handles. PHP provides built-in magic and...]]></description>
      <content:encoded><![CDATA[<blockquote>A practical cheat sheet covering PHP magic constants, filesystem path helpers, error reporting masks, and CLI stream handles.</blockquote>
<p>PHP provides built-in magic and environment constants that provide system context and platform independent path formatting.</p>
<pre><code class="language-php">// Filesystem &amp; Path Constants
DIRECTORY_SEPARATOR; // &#039;/&#039; on Unix, &#039;\&#039; on Windows
PATH_SEPARATOR;      // &#039;:&#039; on Unix, &#039;;&#039; on Windows
__FILE__;            // Full file path of current file
__DIR__;             // Directory of current file
__LINE__;            // Current line number

// Magic Compile Constants
__FUNCTION__;        // Current function name
__CLASS__;           // Current class name
__TRAIT__;           // Current trait name
__METHOD__;          // Current class method name
__NAMESPACE__;       // Current namespace name

// Environment &amp; Platform
PHP_VERSION;         // e.g. &quot;8.4.1&quot;
PHP_OS_FAMILY;       // &quot;Windows&quot;, &quot;Linux&quot;, &quot;BSD&quot;, &quot;Darwin&quot;
PHP_EOL;             // End-of-line character for host OS
PHP_INT_MAX;         // Maximum integer supported

// CLI Stream Handles (available in CLI mode)
STDIN;               // Standard input stream
STDOUT;              // Standard output stream
STDERR;              // Standard error stream</code></pre>
<ul>
  <li>Always use <code>DIRECTORY_SEPARATOR</code> or forward slashes for cross-platform file paths</li>
  <li>Use <code>PHP_OS_FAMILY</code> instead of checking <code>PHP_OS</code> strings</li>
  <li>Magic constants resolve at compile time, making sure minimal runtime overhead</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 29 Jun 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Tooling]]></category>
      <category><![CDATA[Constants]]></category>
      <category><![CDATA[Reference]]></category>
    </item>
    <item>
      <title><![CDATA[Lint PHP Files Instantly with php -l CLI Syntax Check]]></title>
      <link>https://mrpunyapal.dev/tips/php-cli-syntax-check-linting</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-cli-syntax-check-linting</guid>
      <description><![CDATA[Run fast, zero-dependency PHP syntax validation from the terminal using php -l on individual files or recursively across entire projects. Before running stat...]]></description>
      <content:encoded><![CDATA[<blockquote>Run fast, zero-dependency PHP syntax validation from the terminal using php -l on individual files or recursively across entire projects.</blockquote>
<p>Before running static analysis tools or unit test suites, you can instantly verify that your PHP files contain no syntax errors directly from the terminal.</p>
<p>The `-l` (lint) flag checks code syntax without executing the script:</p>
<pre><code class="language-bash"># Check syntax of a single file
php -l app/Models/User.php
# Output: No syntax errors detected in app/Models/User.php

# Check all PHP files in project recursively (Linux/macOS)
find . -name &quot;*.php&quot; -exec php -l {} \;

# Check all PHP files recursively using PowerShell (Windows)
Get-ChildItem -Recurse -Filter *.php | ForEach-Object { php -l $_.FullName }</code></pre>
<ul>
  <li>Runs instantly with 0 external dependencies</li>
  <li>Catches parse errors, missing semicolons, and bracket mismatches early</li>
  <li>Easy addition to git pre-commit hooks and lightweight CI pipelines</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 28 Jun 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Syntax]]></category>
      <category><![CDATA[CLI]]></category>
      <category><![CDATA[Tooling]]></category>
    </item>
    <item>
      <title><![CDATA[Selectively Fake Jobs in Tests with Queue::fakeExceptFor()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-queue-fake-except-for-testing</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-queue-fake-except-for-testing</guid>
      <description><![CDATA[Use Queue::fakeExceptFor() to execute specific background jobs synchronously during a test while preventing all other jobs from running. When testing a featu...]]></description>
      <content:encoded><![CDATA[<blockquote>Use Queue::fakeExceptFor() to execute specific background jobs synchronously during a test while preventing all other jobs from running.</blockquote>
<p>When testing a feature that dispatches multiple background jobs, using `Queue::fake()` prevents all jobs from executing. If your test relies on a critical job running inline, total faking breaks test setup.</p>
<p>`Queue::fakeExceptFor()` lets you specify jobs that should execute normally while faking the rest:</p>
<pre><code class="language-php">use App\Jobs\CriticalSystemJob;
use App\Jobs\EmailNotification;
use Illuminate\Support\Facades\Queue;

test(&#039;queue dispatches email while executing critical system job inline&#039;, function () {
    Queue::fakeExceptFor(function () {
        Queue::push(new CriticalSystemJob); // Executes inline
        Queue::push(new EmailNotification);  // Faked

        // Faked jobs are tracked by assertion helpers
        Queue::assertPushed(EmailNotification::class);
    }, [CriticalSystemJob::class]);
});</code></pre>
<ul>
  <li>Allows real execution for essential side effects during integration tests</li>
  <li>Keeps unneeded jobs (emails, webhooks) isolated and faked</li>
  <li>Also available on <code>Event::fakeExceptFor()</code> and <code>Bus::fakeExceptFor()</code></li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 25 Jun 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[Pest PHP]]></category>
    </item>
    <item>
      <title><![CDATA[Focus Test Runs Instantly with Pest ->only() Method]]></title>
      <link>https://mrpunyapal.dev/tips/pest-only-method-test-focus</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/pest-only-method-test-focus</guid>
      <description><![CDATA[Append -only() to any Pest test declaration to execute only that specific test without writing long CLI filter strings. Filtering specific test names via CLI...]]></description>
      <content:encoded><![CDATA[<blockquote>Append ->only() to any Pest test declaration to execute only that specific test without writing long CLI filter strings.</blockquote>
<p>Filtering specific test names via CLI flags like --filter requires typing exact strings. Appending ->only() to a test declaration silences all other tests in the file.</p>
<pre><code class="language-php">test(&#039;calculates order total with discounts&#039;, function () {
    expect(true)-&gt;toBeTrue();
})-&gt;only(); // Only this test will run!

test(&#039;another test&#039;, function () {
    // Skipped
});</code></pre>
<ul>
  <li>Focuses test runner execution strictly on tagged test cases</li>
  <li>Avoids typing long CLI --filter flags during debugging sessions</li>
  <li>Remember to remove -&gt;only() before committing changes</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 21 Jun 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Pest PHP]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[Pest]]></category>
      <category><![CDATA[DX]]></category>
    </item>
    <item>
      <title><![CDATA[Use $model->touch() Instead of Manual Timestamp Updates]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-model-touch-timestamp-update</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-model-touch-timestamp-update</guid>
      <description><![CDATA[Use the built-in touch() method to update updatedat timestamps on Eloquent records cleanly. Manually assigning $model-updatedat = now(); $model-save(); is re...]]></description>
      <content:encoded><![CDATA[<blockquote>Use the built-in touch() method to update updated_at timestamps on Eloquent records cleanly.</blockquote>
<p>Manually assigning $model->updated_at = now(); $model->save(); is repetitive. Calling $model->touch() updates timestamps and persists changes in a single line.</p>
<pre><code class="language-php">use App\Models\Post;

$post = Post::find($id);

// Replaces $post-&gt;updated_at = now(); $post-&gt;save();
$post-&gt;touch();</code></pre>
<ul>
  <li>Updates updated_at column to current timestamp and saves model</li>
  <li>Triggers cascading timestamp touches defined on $touches parent relations</li>
  <li>Fires model saving and saved event listeners</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 20 Jun 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Database]]></category>
    </item>
    <item>
      <title><![CDATA[Reduce Indentation with Early Returns and Guard Clauses]]></title>
      <link>https://mrpunyapal.dev/tips/php-early-returns-guard-clauses</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-early-returns-guard-clauses</guid>
      <description><![CDATA[Replace deeply nested if statements with guard clauses that exit functions early when preconditions fail. Deeply nested if-else blocks make code hard to read...]]></description>
      <content:encoded><![CDATA[<blockquote>Replace deeply nested if statements with guard clauses that exit functions early when preconditions fail.</blockquote>
<p>Deeply nested if-else blocks make code hard to read and track. Guard clauses validate edge cases at the top of functions and return early, keeping happy-path code un-indented.</p>
<pre><code class="language-php">// BAD: Deeply nested happy path
public function process(User $user): bool
{
    if ($user-&gt;isActive()) {
        if ($user-&gt;hasSubscription()) {
            // Business logic
            return true;
        }
    }
    return false;
}

// GOOD: Guard clauses with early returns
public function processClean(User $user): bool
{
    if (! $user-&gt;isActive()) return false;
    if (! $user-&gt;hasSubscription()) return false;

    // Business logic at root indentation level
    return true;
}</code></pre>
<ul>
  <li>Flattens code indentation to root level for happy-path execution</li>
  <li>Handles failure conditions and validation edge cases upfront</li>
  <li>Significantly improves readability and static analysis maintainability</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 16 Jun 2025 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Basics]]></category>
      <category><![CDATA[Refactoring]]></category>
      <category><![CDATA[Clean Code]]></category>
    </item>
    <item>
      <title><![CDATA[Prefer dispatch(new Job(...)) Over Static Job::dispatch()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-job-dispatch-new-instance</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-job-dispatch-new-instance</guid>
      <description><![CDATA[Use dispatch(new ProcessReport($data)) for superior IDE constructor auto-completion and static analysis type checking. Static Job::dispatch(...) relies on ma...]]></description>
      <content:encoded><![CDATA[<blockquote>Use dispatch(new ProcessReport($data)) for superior IDE constructor auto-completion and static analysis type checking.</blockquote>
<p>Static Job::dispatch(...) relies on magical __callStatic methods which can bypass IDE parameter type checking. Instantiating job classes directly via dispatch(new Job(...)) guarantees strict type checking.</p>
<pre><code class="language-php">use App\Jobs\ProcessReport;

// ❌ Static magic method: weak IDE constructor type completion
// ProcessReport::dispatch($reportId);

// ✅ Explicit instantiation: full IDE auto-completion &amp; static analysis
dispatch(new ProcessReport($reportId));</code></pre>
<ul>
  <li>Provides full static analysis parameter validation in PHPStan and Psalm</li>
  <li>makes sure IDE auto-completes constructor parameters accurately</li>
  <li>Avoids relying on magic __callStatic methods</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 23 Dec 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Validation]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[Type Safety]]></category>
    </item>
    <item>
      <title><![CDATA[Accelerate TALL Stack Development with FilamentPHP]]></title>
      <link>https://mrpunyapal.dev/tips/tall-stack-filament-admin-panel</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/tall-stack-filament-admin-panel</guid>
      <description><![CDATA[Use FilamentPHP to build full-featured administrative panels, forms, and tables using Livewire and Alpine.js. Building custom admin dashboards from scratch r...]]></description>
      <content:encoded><![CDATA[<blockquote>Use FilamentPHP to build full-featured administrative panels, forms, and tables using Livewire and Alpine.js.</blockquote>
<p>Building custom admin dashboards from scratch requires writing repetitive Blade components, Livewire tables, and form validation. Filament provides pre-built TALL stack components out of the box.</p>
<pre><code class="language-php">use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;

public static function form(Form $form): Form
{
    return $form-&gt;schema([
        TextInput::make(&#039;name&#039;)-&gt;required(),
        Select::make(&#039;role&#039;)-&gt;options([
            &#039;admin&#039; =&gt; &#039;Admin&#039;,
            &#039;editor&#039; =&gt; &#039;Editor&#039;,
        ]),
    ]);
}</code></pre>
<ul>
  <li>First-party TALL stack admin framework built on Livewire and Alpine.js</li>
  <li>Includes form builders, data tables, notifications, and dashboard widgets</li>
  <li>Extremely extensible via custom Livewire components</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Filament]]></category>
      <category><![CDATA[Admin Panel]]></category>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[TALL Stack]]></category>
    </item>
    <item>
      <title><![CDATA[Ditch sleep() in Tests: Use Laravel Time Travel Helpers]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-test-time-travel-freeze</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-test-time-travel-freeze</guid>
      <description><![CDATA[Use $this-travelTo() or freezeTime() in test suites to test date-sensitive logic without slowing down test execution with sleep(). Using sleep() in tests slo...]]></description>
      <content:encoded><![CDATA[<blockquote>Use $this->travelTo() or freezeTime() in test suites to test date-sensitive logic without slowing down test execution with sleep().</blockquote>
<p>Using sleep() in tests slows down execution suites significantly. Laravel's time travel helpers let you manipulate Carbon's internal clock instantaneously without real-world delays.</p>
<pre><code class="language-php">test(&#039;trial expires after 14 days&#039;, function () {
    $user = User::factory()-&gt;create([&#039;trial_ends_at&#039; =&gt; now()-&gt;addDays(14)]);

    // Instantly jump 15 days into the future
    $this-&gt;travel(15)-&gt;days();

    expect($user-&gt;fresh()-&gt;hasExpiredTrial())-&gt;toBeTrue();
});</code></pre>
<ul>
  <li>Replaces slow real-time sleep() calls with instant mock clock jumps</li>
  <li>travel(15)-&gt;days() moves time forward dynamically</li>
  <li>freezeTime() locks current time to prevent test flickering</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 12 Sep 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Configuration]]></category>
      <category><![CDATA[Testing]]></category>
      <category><![CDATA[Time]]></category>
    </item>
    <item>
      <title><![CDATA[Integer Enums: Start Indexing from 1, Avoid 0]]></title>
      <link>https://mrpunyapal.dev/tips/php-backed-integer-enums-indexing</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-backed-integer-enums-indexing</guid>
      <description><![CDATA[When creating integer-backed PHP Enums, index starting from 1 to avoid false-y evaluation bugs in loose comparisons. In PHP, 0 evaluates as false-y in loose...]]></description>
      <content:encoded><![CDATA[<blockquote>When creating integer-backed PHP Enums, index starting from 1 to avoid false-y evaluation bugs in loose comparisons.</blockquote>
<p>In PHP, 0 evaluates as false-y in loose condition checks like empty() or if ($enumValue). Starting integer enum values at 1 prevents accidental false-y evaluation bugs.</p>
<pre><code class="language-php">// BAD: Case 0 is false-y in loose checks!
enum Priority: int
{
    case Low = 0;
    case Medium = 1;
    case High = 2;
}

// GOOD: Start indexing at 1
enum Priority: int
{
    case Low = 1;
    case Medium = 2;
    case High = 3;
}</code></pre>
<ul>
  <li>Prevents false-y evaluation pitfalls when checking enum integer values in loose conditionals</li>
  <li>Aligns with standard 1-based database primary key indexing conventions</li>
  <li>makes sure empty() checks on enum raw values behave predictably</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 31 Aug 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Basics]]></category>
      <category><![CDATA[Enums]]></category>
      <category><![CDATA[Best Practices]]></category>
    </item>
    <item>
      <title><![CDATA[Retrieve and Override Configurations with the config() Helper]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-config-helper-get-set</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-config-helper-get-set</guid>
      <description><![CDATA[Use config('app.name') to retrieve settings and config(['app.debug' =true]) to set runtime overrides. The config() helper accesses application configuration...]]></description>
      <content:encoded><![CDATA[<blockquote>Use config('app.name') to retrieve settings and config(['app.debug' => true]) to set runtime overrides.</blockquote>
<p>The config() helper accesses application configuration options defined in config/*.php files. You can also pass key-value arrays to override configurations dynamically during testing or request execution.</p>
<pre><code class="language-php">// Retrieve config value with fallback default
$appName = config(&#039;app.name&#039;, &#039;Laravel&#039;);

// Override config value dynamically at runtime
config([&#039;services.stripe.key&#039; =&gt; &#039;pk_test_12345&#039;]);</code></pre>
<ul>
  <li>Dot-notation syntax accesses nested configuration array keys</li>
  <li>Passing key-value arrays overrides configurations dynamically for current request</li>
  <li>Always use config() instead of env() outside config directory files</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 21 Aug 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Configuration]]></category>
      <category><![CDATA[Helpers]]></category>
    </item>
    <item>
      <title><![CDATA[Format Single-Line Property Promotion Constructors with Pint]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-pint-single-line-empty-constructors</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-pint-single-line-empty-constructors</guid>
      <description><![CDATA[Configure Laravel Pint to collapse empty constructor bodies with promoted properties onto a single line for compact PHP 8 class declarations. PHP 8 construct...]]></description>
      <content:encoded><![CDATA[<blockquote>Configure Laravel Pint to collapse empty constructor bodies with promoted properties onto a single line for compact PHP 8 class declarations.</blockquote>
<p>PHP 8 constructor property promotion eliminates explicit property declarations and assignments. However, empty constructor body braces `{}` can still take up 3 vertical lines.</p>
<p>Laravel Pint formats empty promoted constructors into clean single-line declarations:</p>
<pre><code class="language-diff">class DatabaseNotificationSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

-   public function __construct(public DatabaseNotification $notification)
-   {
-   }
+   public function __construct(public DatabaseNotification $notification) {}
}</code></pre>
<ul>
  <li>Compacts class definitions without losing readability</li>
  <li>Enforced automatically across your project via <code>./vendor/bin/pint</code></li>
  <li>Keeps event, job, and DTO constructor signatures minimal</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 27 Jun 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Pint]]></category>
      <category><![CDATA[PHP 8]]></category>
    </item>
    <item>
      <title><![CDATA[Enforce Strict Typing Across PHP Classes and Methods]]></title>
      <link>https://mrpunyapal.dev/tips/php-strict-types-property-declarations</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-strict-types-property-declarations</guid>
      <description><![CDATA[Declare declare(stricttypes=1); at the top of PHP files to prevent unexpected scalar type coercions. By default, PHP coerces types scalar values (e.g. string...]]></description>
      <content:encoded><![CDATA[<blockquote>Declare declare(strict_types=1); at the top of PHP files to prevent unexpected scalar type coercions.</blockquote>
<p>By default, PHP coerces types scalar values (e.g. string '1' to int 1). Adding declare(strict_types=1); enforces strict type constraints on function arguments and return values.</p>
<pre><code class="language-php">&lt;?php

declare(strict_types=1);

namespace App\Services;

class TaxCalculator
{
    public function calculate(int $amount, float $rate): float
    {
        return $amount * $rate;
    }
}</code></pre>
<ul>
  <li>Prevents silent scalar type coercions (like string to integer conversion)</li>
  <li>Must be declared at the absolute top of PHP files before code execution</li>
  <li>Helps static analysis tools like PHPStan catch type bugs early</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 26 May 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Syntax]]></category>
      <category><![CDATA[Type Safety]]></category>
      <category><![CDATA[Best Practices]]></category>
    </item>
    <item>
      <title><![CDATA[Subquery Attribute Loading with withAttribute() in Eloquent]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-eloquent-with-attribute-proposal</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-eloquent-with-attribute-proposal</guid>
      <description><![CDATA[Load specific relationship column values directly into model attributes using withAttribute() without loading complete child model instances. When you only n...]]></description>
      <content:encoded><![CDATA[<blockquote>Load specific relationship column values directly into model attributes using withAttribute() without loading complete child model instances.</blockquote>
<p>When you only need a single field from a related model (such as a author name or category title), eager loading the full model creates unnecessary object allocation overhead.</p>
<p>Using `withAttribute()` attaches subquery selected values directly onto the primary model:</p>
<pre><code class="language-php">use App\Models\Post;

// Eager load only the category name directly as an attribute
$post = Post::withAttribute(&#039;category&#039;, &#039;name&#039;)-&gt;first();
echo $post-&gt;category_attribute_name;

// Lazy load attribute on an existing model instance
$post = Post::first();
$post-&gt;loadAttribute(&#039;category&#039;, &#039;name&#039;);

// Advanced subquery callback formatting
Post::query()
    -&gt;select(&#039;id&#039;)
    -&gt;withAttribute([&#039;comments as last_comment&#039; =&gt; fn ($q) =&gt; $q-&gt;latest()], &#039;content&#039;)
    -&gt;get();</code></pre>
<ul>
  <li>Avoids instantiating nested relationship model objects for simple display values</li>
  <li>Converts nested queries into efficient sub-select SQL statements</li>
  <li>Keeps API payload responses lightweight and flat</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 31 Mar 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Automate Laravel 11 casts() Method Upgrade with Rector]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-rector-automate-casts-method-upgrade</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-rector-automate-casts-method-upgrade</guid>
      <description><![CDATA[Use Rector to automatically refactor legacy protected $casts array properties into the modern casts() method across your entire Laravel codebase. Laravel 11...]]></description>
      <content:encoded><![CDATA[<blockquote>Use Rector to automatically refactor legacy protected $casts array properties into the modern casts() method across your entire Laravel codebase.</blockquote>
<p>Laravel 11 introduced the `casts()` method on Eloquent models, allowing fluent cast definitions, class references, and method calls inside model classes.</p>
<p>Rector automates converting legacy `protected $casts` properties to the new method:</p>
<pre><code class="language-diff">1) app/Models/Post.php

- protected $casts = [
-     &#039;tags&#039; =&gt; &#039;array&#039;,
-     &#039;published_at&#039; =&gt; &#039;datetime&#039;,
-     &#039;is_featured&#039; =&gt; FeaturedStatus::class,
- ];

+ protected function casts(): array
+ {
+     return [
+         &#039;tags&#039; =&gt; &#039;array&#039;,
+         &#039;published_at&#039; =&gt; &#039;datetime&#039;,
+         &#039;is_featured&#039; =&gt; FeaturedStatus::class,
+     ];
+ }</code></pre>
<ul>
  <li>Runs across hundreds of models in seconds during framework upgrades</li>
  <li>Enables calling static methods directly inside cast definitions (e.g. <code>AsEnumCollection::of(...)</code>)</li>
  <li>Eliminates typos in array property names</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 30 Mar 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Rector]]></category>
      <category><![CDATA[Tooling]]></category>
    </item>
    <item>
      <title><![CDATA[Customize Artisan Code Generators with stub:publish]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-artisan-stubs-custom-namespaces</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-artisan-stubs-custom-namespaces</guid>
      <description><![CDATA[Publish and customize Artisan generator stubs using php artisan stub:publish to enforce custom code conventions across your team. Standard php artisan make:c...]]></description>
      <content:encoded><![CDATA[<blockquote>Publish and customize Artisan generator stubs using php artisan stub:publish to enforce custom code conventions across your team.</blockquote>
<p>Standard php artisan make:controller or make:model commands generate default templates. Running stub:publish exports stub files to stubs/ so you can customize scaffolded code.</p>
<pre><code class="language-bash"># Publish default Artisan code stubs to stubs/ directory
php artisan stub:publish</code></pre>
<ul>
  <li>Exports code stubs to stubs/ folder for custom modification</li>
  <li>Enforces team code standards (strict types, custom traits, imports)</li>
  <li>Automatically used by artisan make:* commands once published</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 24 Mar 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Artisan]]></category>
      <category><![CDATA[DX]]></category>
    </item>
    <item>
      <title><![CDATA[Cast Model Attributes to Backed Enums in Eloquent]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-model-attribute-backed-enums</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-model-attribute-backed-enums</guid>
      <description><![CDATA[Define PHP Backed Enums on model $casts to automatically hydrate strings and integers into typed Enums. Storing status strings like 'active' or 'pending' as...]]></description>
      <content:encoded><![CDATA[<blockquote>Define PHP Backed Enums on model $casts to automatically hydrate strings and integers into typed Enums.</blockquote>
<p>Storing status strings like 'active' or 'pending' as raw strings leads to typos. Defining Backed Enums inside model $casts makes sure typed enum object hydration.</p>
<pre><code class="language-php">namespace App\Models;

use App\Enums\PostStatus;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected function casts(): array
    {
        return [
            &#039;status&#039; =&gt; PostStatus::class,
        ];
    }
}</code></pre>
<ul>
  <li>Automatically casts database scalar values into typed Backed Enum objects</li>
  <li>Provides full type safety and IDE auto-completion on model attributes</li>
  <li>Throws ValueError when database contains unmapped enum scalar values</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 16 Mar 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Enums]]></category>
    </item>
    <item>
      <title><![CDATA[Clear Query Order Constraints with reorder()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-eloquent-reorder-query-builder</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-eloquent-reorder-query-builder</guid>
      <description><![CDATA[Use reorder() to clear previous orderBy clauses from a query builder before applying new sorting constraints. When modifying existing query builders or model...]]></description>
      <content:encoded><![CDATA[<blockquote>Use reorder() to clear previous orderBy clauses from a query builder before applying new sorting constraints.</blockquote>
<p>When modifying existing query builders or model scopes that already contain orderBy clauses, appending another orderBy appends a secondary sort. reorder() strips existing ordering rules.</p>
<pre><code class="language-php">use App\Models\User;

$query = User::orderBy(&#039;name&#039;, &#039;asc&#039;);

// Replaces &#039;name&#039; sorting with &#039;created_at&#039; sorting
$users = $query-&gt;reorder(&#039;created_at&#039;, &#039;desc&#039;)-&gt;get();</code></pre>
<ul>
  <li>Strips all existing orderBy clauses from the query builder</li>
  <li>Accepts optional new column and direction arguments to re-apply sorting</li>
  <li>Essential when overriding default sorting rules defined in model scopes</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 07 Mar 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Database]]></category>
    </item>
    <item>
      <title><![CDATA[Build Conditional Queries Cleanly with when() and unless()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-eloquent-when-and-unless-conditional-queries</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-eloquent-when-and-unless-conditional-queries</guid>
      <description><![CDATA[Use when() and unless() on query builders to apply conditional clauses without breaking method chains with if statements. Building dynamic search queries oft...]]></description>
      <content:encoded><![CDATA[<blockquote>Use when() and unless() on query builders to apply conditional clauses without breaking method chains with if statements.</blockquote>
<p>Building dynamic search queries often breaks method chains with if ($search) { $query->where(...) }. The when() and unless() methods evaluate conditions inline within query chains.</p>
<pre><code class="language-php">use App\Models\User;

$users = User::query()
    -&gt;when($request-&gt;search, fn ($q, $search) =&gt; $q-&gt;where(&#039;name&#039;, &#039;like&#039;, &quot;%{$search}%&quot;))
    -&gt;unless($request-&gt;include_archived, fn ($q) =&gt; $q-&gt;whereNull(&#039;archived_at&#039;))
    -&gt;get();</code></pre>
<ul>
  <li>Keeps query builder method chains fluent without breaking into if statements</li>
  <li>when() executes closure if first parameter evaluates to truthy</li>
  <li>unless() executes closure if first parameter evaluates to falsey</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 01 Feb 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Query Builder]]></category>
    </item>
    <item>
      <title><![CDATA[Combine Custom Casts and Enums for Complex Attributes]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-custom-casts-enum-json</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-custom-casts-enum-json</guid>
      <description><![CDATA[Use Eloquent custom casts (CastsAttributes) to handle complex JSON serialization and Backed Enum arrays cleanly. When model attributes contain complex JSON s...]]></description>
      <content:encoded><![CDATA[<blockquote>Use Eloquent custom casts (CastsAttributes) to handle complex JSON serialization and Backed Enum arrays cleanly.</blockquote>
<p>When model attributes contain complex JSON structures or collections of Enums, implement CastsAttributes to handle custom database transformation and object hydration.</p>
<pre><code class="language-php">namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use App\Enums\Permission;

class PermissionCast implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes): array
    {
        return array_map(fn ($val) =&gt; Permission::from($val), json_decode($value, true) ?? []);
    }

    public function set($model, string $key, $value, array $attributes): string
    {
        return json_encode(array_map(fn ($enum) =&gt; $enum-&gt;value, $value));
    }
}</code></pre>
<ul>
  <li>Implements CastsAttributes with get() and set() transformation signatures</li>
  <li>Handles custom JSON encoding and object hydration transparently</li>
  <li>Keeps model classes free of manual JSON encoding logic</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 30 Jan 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Casts]]></category>
    </item>
    <item>
      <title><![CDATA[Queue Heavy Artisan Commands with Artisan::queue()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-artisan-queue-background-commands</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-artisan-queue-background-commands</guid>
      <description><![CDATA[Use Artisan::queue() to push long-running Artisan commands to background queues instead of blocking HTTP requests. Running heavy Artisan commands like databa...]]></description>
      <content:encoded><![CDATA[<blockquote>Use Artisan::queue() to push long-running Artisan commands to background queues instead of blocking HTTP requests.</blockquote>
<p>Running heavy Artisan commands like database backups or report generators inside HTTP controller requests blocks the user web server thread. Artisan::queue() dispatches the command to queue workers.</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Artisan;

// Pushes Artisan command execution to default background queue
Artisan::queue(&#039;reports:generate&#039;, [
    &#039;--user&#039; =&gt; $user-&gt;id,
]);</code></pre>
<ul>
  <li>Dispatches command execution as a background queue job</li>
  <li>Prevents HTTP request timeouts during long-running tasks</li>
  <li>Accepts command arguments and option arrays</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 29 Jan 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[Artisan]]></category>
    </item>
    <item>
      <title><![CDATA[Filter Records by JSON Array Size with whereJsonLength()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-wherejsonlength-eloquent</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-wherejsonlength-eloquent</guid>
      <description><![CDATA[Use whereJsonLength() to query database records based on the number of elements in a JSON array attribute. Filtering records by the number of elements stored...]]></description>
      <content:encoded><![CDATA[<blockquote>Use whereJsonLength() to query database records based on the number of elements in a JSON array attribute.</blockquote>
<p>Filtering records by the number of elements stored in a JSON array column is straightforward in Eloquent using whereJsonLength().</p>
<pre><code class="language-php">use App\Models\User;

// Select users with more than 2 items in their JSON options-&gt;tags array
$users = User::whereJsonLength(&#039;options-&gt;tags&#039;, &#039;&gt;&#039;, 2)-&gt;get();</code></pre>
<ul>
  <li>Queries database JSON array length using native database JSON functions</li>
  <li>Supports comparison operators (=, &gt;, &lt;, &gt;=, &lt;=)</li>
  <li>Works with MySQL, PostgreSQL, and SQLite database drivers</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 20 Jan 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Database]]></category>
    </item>
    <item>
      <title><![CDATA[Copy Datasets Between Tables Instantly with insertUsing()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-query-insertusing-table-copy</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-query-insertusing-table-copy</guid>
      <description><![CDATA[Use insertUsing() to execute INSERT INTO ... SELECT queries for copying data between database tables in SQL. Fetching records into PHP memory and re-insertin...]]></description>
      <content:encoded><![CDATA[<blockquote>Use insertUsing() to execute INSERT INTO ... SELECT queries for copying data between database tables in SQL.</blockquote>
<p>Fetching records into PHP memory and re-inserting them into another table via loops is slow. insertUsing() runs a single INSERT INTO ... SELECT query directly inside the database.</p>
<pre><code class="language-php">use Illuminate\Support\Facades\DB;

// Fast database-level copy without loading records into PHP RAM
DB::table(&#039;archived_orders&#039;)-&gt;insertUsing(
    [&#039;order_id&#039;, &#039;total&#039;, &#039;created_at&#039;],
    DB::table(&#039;orders&#039;)-&gt;select(&#039;id&#039;, &#039;total&#039;, &#039;created_at&#039;)-&gt;where(&#039;status&#039;, &#039;completed&#039;)
);</code></pre>
<ul>
  <li>Executes high-speed INSERT INTO ... SELECT statements directly in database</li>
  <li>Avoids loading records into PHP memory buffers</li>
  <li>Ideal for data archiving, activity logging, and snapshot tables</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 19 Jan 2024 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Database]]></category>
      <category><![CDATA[Performance]]></category>
    </item>
    <item>
      <title><![CDATA[Build Framework-Free Dropdowns with Tailwind and nextElementSibling]]></title>
      <link>https://mrpunyapal.dev/tips/javascript-tailwind-dropdown-next-element-sibling</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/javascript-tailwind-dropdown-next-element-sibling</guid>
      <description><![CDATA[Create lightweight, zero-dependency toggle dropdowns using inline JavaScript nextElementSibling calls with Tailwind CSS hidden classes. For simple marketing...]]></description>
      <content:encoded><![CDATA[<blockquote>Create lightweight, zero-dependency toggle dropdowns using inline JavaScript nextElementSibling calls with Tailwind CSS hidden classes.</blockquote>
<p>For simple marketing sites or static pages where full JavaScript frameworks like Alpine.js or Vue are not installed, you can toggle dropdown visibility using native DOM methods.</p>
<p>By calling `this.nextElementSibling.classList.toggle('hidden')`, the button toggles the adjacent menu element directly:</p>
<pre><code class="language-html">&lt;div class=&quot;relative inline-block text-left ms-2&quot;&gt;
    &lt;button type=&quot;button&quot; 
            onclick=&quot;this.nextElementSibling.classList.toggle(&#039;hidden&#039;)&quot;
            class=&quot;bg-gray-300 text-gray-700 font-bold py-2 px-4 rounded inline-flex items-center&quot;&gt;
        &lt;span&gt;Languages &amp;#x25BE;&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&quot;origin-top-right absolute right-0 mt-2 w-40 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 hidden&quot;
         role=&quot;menu&quot;&gt;
        &lt;a class=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot; href=&quot;#&quot;&gt;English&lt;/a&gt;
        &lt;a class=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot; href=&quot;#&quot;&gt;French&lt;/a&gt;
        &lt;a class=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot; href=&quot;#&quot;&gt;Gujarati&lt;/a&gt;
    &lt;/div&gt;
&lt;/div&gt;</code></pre>
<ul>
  <li>Adds interactive dropdown behavior with zero npm dependencies</li>
  <li>Works natively across all modern desktop and mobile browsers</li>
  <li>Clean solution for simple landing page navigation elements</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 24 Dec 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Frameworks]]></category>
      <category><![CDATA[Tailwind CSS]]></category>
      <category><![CDATA[DOM]]></category>
    </item>
    <item>
      <title><![CDATA[Import Multiple Classes in Blade with Array @use Syntax]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-blade-use-directive-array-imports</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-blade-use-directive-array-imports</guid>
      <description><![CDATA[Import multiple PHP classes or custom aliases inside Blade templates using array arguments in the @use directive. The Blade @use directive allows importing P...]]></description>
      <content:encoded><![CDATA[<blockquote>Import multiple PHP classes or custom aliases inside Blade templates using array arguments in the @use directive.</blockquote>
<p>The Blade `@use` directive allows importing PHP classes directly inside templates. Punyapal Shah contributed a Pull Request to Laravel expanding `@use` to accept array arguments.</p>
<p>You can import multiple classes or define custom aliases in a single directive:</p>
<pre><code class="language-blade">{{-- Import multiple model classes --}}
@use([&#039;App\Models\User&#039;, &#039;App\Models\Post&#039;])

{{-- Import with custom aliases --}}
@use([&#039;App\Models\User&#039; =&gt; &#039;ModelUser&#039;, &#039;App\Models\Post&#039; =&gt; &#039;ModelPost&#039;])

{{-- Single class import still works --}}
@use(&#039;App\Models\User&#039;, &#039;ModelUser&#039;)</code></pre>
<ul>
  <li>Replaces repetitive <code>@php use ... @endphp</code> blocks at the top of Blade templates</li>
  <li>Keeps Blade template imports grouped in a clean, readable structure</li>
  <li>Supports alias mapping directly inside array keys and values</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 22 Dec 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Blade]]></category>
    </item>
    <item>
      <title><![CDATA[Never Use env() Outside Config Files + Enforce with Pest Arch]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-avoid-env-outside-config-pest-arch</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-avoid-env-outside-config-pest-arch</guid>
      <description><![CDATA[env() returns null when config is cached in production. Always use config() and enforce this with Pest architecture tests. In production environments running...]]></description>
      <content:encoded><![CDATA[<blockquote>env() returns null when config is cached in production. Always use config() and enforce this with Pest architecture tests.</blockquote>
<p>In production environments running php artisan config:cache, calling env() outside files in config/*.php returns null. Enforce this rule across your codebase using Pest architecture testing.</p>
<pre><code class="language-php">// tests/ArchitectureTest.php
arch(&#039;avoid env outside config&#039;)
    -&gt;expect(&#039;env&#039;)
    -&gt;not-&gt;toBeUsed()
    -&gt;ignoring(&#039;config&#039;);</code></pre>
<ul>
  <li>env() returns null in production when config:cache is active</li>
  <li>Always define configuration keys in config/*.php files and call config(&#39;key&#39;)</li>
  <li>Pest arch tests catch leftover env() calls before deployment</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 30 Nov 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Cache]]></category>
      <category><![CDATA[Architecture]]></category>
      <category><![CDATA[Pest]]></category>
    </item>
    <item>
      <title><![CDATA[Essential Laravel Production Deployment Command Sequence]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-production-deployment-commands</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-production-deployment-commands</guid>
      <description><![CDATA[Execute standard caching and optimization Artisan commands during production CI/CD deployment scripts. Deploying Laravel applications without caching routes,...]]></description>
      <content:encoded><![CDATA[<blockquote>Execute standard caching and optimization Artisan commands during production CI/CD deployment scripts.</blockquote>
<p>Deploying Laravel applications without caching routes, views, and configuration degrades performance. Run this standard production optimization command sequence during deployment.</p>
<pre><code class="language-bash">php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
php artisan queue:restart</code></pre>
<ul>
  <li>migrate --force runs database migrations without interaction prompts</li>
  <li>config:cache, route:cache, and view:cache pre-compile core assets</li>
  <li>queue:restart signals background workers to reload updated code</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 09 Nov 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Queue]]></category>
      <category><![CDATA[Deployment]]></category>
      <category><![CDATA[DevOps]]></category>
    </item>
    <item>
      <title><![CDATA[Avoid auth() and session() Inside Event Listeners]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-listeners-decouple-auth-session</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-listeners-decouple-auth-session</guid>
      <description><![CDATA[Pass authenticated user objects or session data explicitly inside Event payloads instead of relying on global session helpers in listeners. Accessing global...]]></description>
      <content:encoded><![CDATA[<blockquote>Pass authenticated user objects or session data explicitly inside Event payloads instead of relying on global session helpers in listeners.</blockquote>
<p>Accessing global auth() or session() helpers inside event listeners breaks if the listener is pushed to a background queue where HTTP context is absent. Pass required data in event payloads.</p>
<pre><code class="language-php">namespace App\Events;

use App\Models\User;

class UserRegistered
{
    // Pass user model explicitly in event constructor
    public function __construct(public User $user) {}
}</code></pre>
<ul>
  <li>Queued event listeners execute outside HTTP request cycles with no session context</li>
  <li>Pass authenticated user models and contextual data inside Event constructors</li>
  <li>makes sure event listeners are decoupled and safely queueable</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 28 Sep 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Events]]></category>
      <category><![CDATA[Architecture]]></category>
    </item>
    <item>
      <title><![CDATA[Understand dispatch() vs dispatch_sync() in Laravel Jobs]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-dispatch-vs-dispatch-sync</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-dispatch-vs-dispatch-sync</guid>
      <description><![CDATA[Know when to push background jobs to asynchronous queues with dispatch() versus executing them immediately in the current HTTP request process using dispatch...]]></description>
      <content:encoded><![CDATA[<blockquote>Know when to push background jobs to asynchronous queues with dispatch() versus executing them immediately in the current HTTP request process using dispatch_sync().</blockquote>
<p>Laravel provides helper functions for pushing jobs to workers or executing them synchronously in the current process.</p>
<p>Understanding the difference makes sure background operations execute at the right time:</p>
<pre><code class="language-php">use App\Jobs\ProcessPdfExport;
use App\Jobs\UpdateUserStatus;

// Async: Pushes job to queue driver (Redis/Database) for worker execution
dispatch(new ProcessPdfExport($document));

// Sync: Executes job immediately inside current HTTP request process
dispatch_sync(new UpdateUserStatus($user));</code></pre>
<ul>
  <li><code>dispatch()</code>: Non-blocking, offloads long-running tasks to queue workers</li>
  <li><code>dispatch_sync()</code>: Blocking, runs immediately inline without requiring an active queue worker</li>
  <li>Handy for CLI commands and testing where immediate execution is required</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 28 Sep 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Queue]]></category>
    </item>
    <item>
      <title><![CDATA[Work Around MySQL Subquery Restrictions on Target Tables]]></title>
      <link>https://mrpunyapal.dev/tips/mysql-subquery-same-table-update-restriction</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/mysql-subquery-same-table-update-restriction</guid>
      <description><![CDATA[MySQL prevents updating a table while selecting from it in a subquery. Wrap subqueries in an intermediate alias table. Executing UPDATE table WHERE id IN (SE...]]></description>
      <content:encoded><![CDATA[<blockquote>MySQL prevents updating a table while selecting from it in a subquery. Wrap subqueries in an intermediate alias table.</blockquote>
<p>Executing UPDATE table WHERE id IN (SELECT id FROM table) throws MySQL Error 1093. Work around this by wrapping the subquery in an intermediate derived table alias.</p>
<pre><code class="language-sql">-- ❌ FAILS in MySQL: Error 1093
-- UPDATE users SET status = &#039;inactive&#039; WHERE id IN (SELECT id FROM users WHERE last_login &lt; &#039;2023-01-01&#039;);

-- ✅ WORKS: Intermediate alias subquery
UPDATE users SET status = &#039;inactive&#039;
WHERE id IN (
    SELECT id FROM (
        SELECT id FROM users WHERE last_login &lt; &#039;2023-01-01&#039;
    ) AS temp_users
);</code></pre>
<ul>
  <li>MySQL forbids modifying a target table used directly in a subquery clause</li>
  <li>Wrapping subquery in SELECT * FROM (...) AS alias resolves Error 1093</li>
  <li>Alternative: Use JOIN syntax for multi-table updates</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 22 Sep 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[Queries]]></category>
      <category><![CDATA[Database]]></category>
      <category><![CDATA[SQL]]></category>
    </item>
    <item>
      <title><![CDATA[Preview Mailables Instantly in Browser Routes]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-mailable-browser-preview</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-mailable-browser-preview</guid>
      <description><![CDATA[Return Mailable instances directly from route callbacks to preview compiled HTML emails in your browser without sending test emails. Testing email layouts by...]]></description>
      <content:encoded><![CDATA[<blockquote>Return Mailable instances directly from route callbacks to preview compiled HTML emails in your browser without sending test emails.</blockquote>
<p>Testing email layouts by sending real test emails to inboxes slows down design iterations. Returning a Mailable object directly from a route renders the HTML output live in your browser.</p>
<pre><code class="language-php">use App\Mail\OrderShipped;
use App\Models\Order;
use Illuminate\Support\Facades\Route;

Route::get(&#039;/mailable-preview&#039;, function () {
    $order = Order::first();
    return new OrderShipped($order); // Renders compiled HTML email directly in browser
});</code></pre>
<ul>
  <li>Renders compiled Blade HTML email template live in browser</li>
  <li>Speeds up email design and responsive layout iterations</li>
  <li>No external mail server or SMTP trap configuration needed</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 07 Sep 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Mail]]></category>
      <category><![CDATA[DX]]></category>
    </item>
    <item>
      <title><![CDATA[Adopt Tailwind CSS for Utility-First Component Styling]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-tailwind-utility-css-workflow</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-tailwind-utility-css-workflow</guid>
      <description><![CDATA[Leverage Tailwind CSS utility classes to compose custom responsive designs directly in HTML without CSS stylesheet bloat. Writing custom CSS classes for ever...]]></description>
      <content:encoded><![CDATA[<blockquote>Leverage Tailwind CSS utility classes to compose custom responsive designs directly in HTML without CSS stylesheet bloat.</blockquote>
<p>Writing custom CSS classes for every UI component leads to bloated stylesheet files. Tailwind CSS provides utility classes that streamline responsive styling directly inside templates.</p>
<pre><code class="language-html">&lt;div class=&quot;p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4&quot;&gt;
  &lt;div class=&quot;shrink-0&quot;&gt;
    &lt;img class=&quot;h-12 w-12&quot; src=&quot;/img/logo.svg&quot; alt=&quot;Logo&quot;&gt;
  &lt;/div&gt;
  &lt;div&gt;
    &lt;div class=&quot;text-xl font-medium text-black&quot;&gt;Punyapal Shah&lt;/div&gt;
    &lt;p class=&quot;text-slate-500&quot;&gt;Software Engineer&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre>
<ul>
  <li>Eliminates named CSS class abstraction debates</li>
  <li>Purges unused CSS styles automatically in production builds</li>
  <li>makes sure consistent spacing, typography, and color tokens</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 03 Sep 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Tailwind CSS]]></category>
      <category><![CDATA[Styling]]></category>
      <category><![CDATA[Tailwind]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Frontend]]></category>
    </item>
    <item>
      <title><![CDATA[Simplify Nested Routes with Shallow Resource Controllers]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-nested-resource-controllers-shallow-nesting</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-nested-resource-controllers-shallow-nesting</guid>
      <description><![CDATA[Use shallow nesting on resource routes to keep URLs concise while preserving parent-child relationships for creation and listing endpoints. Deeply nested res...]]></description>
      <content:encoded><![CDATA[<blockquote>Use shallow nesting on resource routes to keep URLs concise while preserving parent-child relationships for creation and listing endpoints.</blockquote>
<p>Deeply nested resource routes (such as `/posts/{post}/comments/{comment}/edit`) create unnecessarily long URLs and controller parameter signatures.</p>
<p>By chaining `->shallow()`, child resource routes that operate on a unique primary key automatically drop the parent URI segment:</p>
<pre><code class="language-php">use App\Http\Controllers\CommentController;
use Illuminate\Support\Facades\Route;

// Shallow resource routes
Route::resource(&#039;posts.comments&#039;, CommentController::class)-&gt;shallow();

/*
Generated URIs:
GET    /posts/{post}/comments           -&gt; index (needs parent)
POST   /posts/{post}/comments           -&gt; store (needs parent)
GET    /comments/{comment}              -&gt; show (shallow)
GET    /comments/{comment}/edit         -&gt; edit (shallow)
PUT    /comments/{comment}              -&gt; update (shallow)
DELETE /comments/{comment}              -&gt; destroy (shallow)
*/</code></pre>
<ul>
  <li>Eliminates redundant parent ID parameters in controller show, edit, update, and destroy actions</li>
  <li>Keeps nested resource URLs clean and user-friendly</li>
  <li>Retains parent routing context for index and store endpoints</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 29 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Routing]]></category>
      <category><![CDATA[Architecture]]></category>
    </item>
    <item>
      <title><![CDATA[Stream and Download Files Efficiently in Laravel Controllers]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-file-downloads-streamed-responses</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-file-downloads-streamed-responses</guid>
      <description><![CDATA[Return proper HTTP file responses using response()-download(), response()-streamDownload(), and response()-file() for downloads and inline previews. Serving...]]></description>
      <content:encoded><![CDATA[<blockquote>Return proper HTTP file responses using response()->download(), response()->streamDownload(), and response()->file() for downloads and inline previews.</blockquote>
<p>Serving files to users requires setting correct HTTP disposition headers and content types.</p>
<p>Laravel provides explicit response helpers for file handling:</p>
<pre><code class="language-php">use Illuminate\Support\Facades\Storage;

// Download local file directly
public function downloadContract()
{
    return response()-&gt;download(storage_path(&#039;app/contract.pdf&#039;), &#039;Agreement.pdf&#039;);
}

// Stream generated content without storing to disk
public function exportCsv()
{
    return response()-&gt;streamDownload(function () {
        echo &quot;id,name\n1,John&quot;;
    }, &#039;users.csv&#039;);
}

// Display file inline in browser (e.g. PDF preview)
public function previewInvoice()
{
    return response()-&gt;file(storage_path(&#039;app/invoice.pdf&#039;));
}</code></pre>
<ul>
  <li><code>download()</code>: Triggers browser save dialog with custom filename</li>
  <li><code>streamDownload()</code>: Streams dynamic output directly to browser with 0 disk storage overhead</li>
  <li><code>file()</code>: Renders image or PDF inline inside browser tab</li>
</ul>
]]></content:encoded>
      <pubDate>Tue, 27 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[HTTP & API]]></category>
      <category><![CDATA[HTTP]]></category>
      <category><![CDATA[Filesystem]]></category>
    </item>
    <item>
      <title><![CDATA[Customize Missing Model Binding Behavior in Laravel]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-custom-missing-model-binding-behavior</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-custom-missing-model-binding-behavior</guid>
      <description><![CDATA[Use missing() callbacks on route model bindings to return custom JSON responses or specialized error pages when records are not found. By default, implicit r...]]></description>
      <content:encoded><![CDATA[<blockquote>Use missing() callbacks on route model bindings to return custom JSON responses or specialized error pages when records are not found.</blockquote>
<p>By default, implicit route model binding throws a 404 ModelNotFoundException when a record missing from the database is requested.</p>
<p>You can customize this missing behavior per route by attaching the `->missing()` closure:</p>
<pre><code class="language-php">use App\Http\Controllers\PostController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get(&#039;/posts/{post:slug}&#039;, [PostController::class, &#039;show&#039;])
    -&gt;missing(function (Request $request) {
        if ($request-&gt;wantsJson()) {
            return response()-&gt;json([&#039;error&#039; =&gt; &#039;Post no longer exists&#039;], 404);
        }

        return redirect()-&gt;route(&#039;posts.index&#039;)
            -&gt;with(&#039;warning&#039;, &#039;Requested post was not found.&#039;);
    });</code></pre>
<ul>
  <li>Customizes 404 fallback logic per individual route or resource</li>
  <li>Prevents raw 404 exception screens for friendly user redirects</li>
  <li>Works directly with custom route binding columns like <code>{post:slug}</code></li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 26 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Routing]]></category>
    </item>
    <item>
      <title><![CDATA[Understand Input Trimming & Empty String Normalization]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-trim-strings-convert-empty-strings-middleware</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-trim-strings-convert-empty-strings-middleware</guid>
      <description><![CDATA[Laravel automatically trims request strings and converts empty string inputs to null via default global middleware. Laravel includes TrimStrings and ConvertE...]]></description>
      <content:encoded><![CDATA[<blockquote>Laravel automatically trims request strings and converts empty string inputs to null via default global middleware.</blockquote>
<p>Laravel includes TrimStrings and ConvertEmptyStringsToNull middleware globally. Incoming string inputs are automatically trimmed of whitespace and empty strings ('') become null.</p>
<pre><code class="language-php">// Request input: [&#039;name&#039; =&gt; &#039;  Punyapal  &#039;, &#039;bio&#039; =&gt; &#039;&#039;]

$name = $request-&gt;input(&#039;name&#039;); // Returns &#039;Punyapal&#039;
$bio  = $request-&gt;input(&#039;bio&#039;);  // Returns null</code></pre>
<ul>
  <li>TrimStrings removes leading and trailing whitespace automatically</li>
  <li>ConvertEmptyStringsToNull normalizes empty form inputs (&#39;&#39;) to null</li>
  <li>Can be bypassed for specific fields (like passwords) via $except property</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 26 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Validation]]></category>
      <category><![CDATA[Middleware]]></category>
    </item>
    <item>
      <title><![CDATA[Flatten Transformed Collections with flatMap()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-collection-flatmap-transformation</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-collection-flatmap-transformation</guid>
      <description><![CDATA[Use flatMap() to map over a collection and collapse the nested array result into a flat collection in a single operation. Mapping over a collection where cal...]]></description>
      <content:encoded><![CDATA[<blockquote>Use flatMap() to map over a collection and collapse the nested array result into a flat collection in a single operation.</blockquote>
<p>Mapping over a collection where callbacks return sub-arrays creates nested array structures. Combining map() and collapse() into flatMap() simplifies transformations.</p>
<pre><code class="language-php">use App\Models\User;

// Maps over users and flattens all roles into a single flat collection
$roles = $users-&gt;flatMap(fn ($user) =&gt; $user-&gt;roles);</code></pre>
<ul>
  <li>Combines map() transformation and collapse() flattening in a single call</li>
  <li>Flattens nested array structures returned by mapping callbacks</li>
  <li>Keeps collection pipeline code concise</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 25 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Collections]]></category>
      <category><![CDATA[Syntax]]></category>
    </item>
    <item>
      <title><![CDATA[Improve Large Number Readability with Numeric Literal Separators]]></title>
      <link>https://mrpunyapal.dev/tips/php-numeric-literal-separators</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/php-numeric-literal-separators</guid>
      <description><![CDATA[Use underscores () as visual numeric literal separators in PHP code to improve number readability. Reading large numbers like 1000000000 requires counting ze...]]></description>
      <content:encoded><![CDATA[<blockquote>Use underscores (_) as visual numeric literal separators in PHP code to improve number readability.</blockquote>
<p>Reading large numbers like 1000000000 requires counting zeroes manually. PHP allows underscores as visual separators inside numeric literals without affecting value evaluation.</p>
<pre><code class="language-php">// Hard to read
$bytes = 1073741824;

// Clean and readable with numeric separators
$bytes = 1_073_741_824; // 1 GB
$price = 1_999_99;       // 1999.99 in cents</code></pre>
<ul>
  <li>Underscores serve purely as visual code separators and are ignored at runtime</li>
  <li>Works with integers, floats, binary (0b1010_0001), and hex (0xFF_00_FF)</li>
  <li>Prevents misreading large numbers during code reviews</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 23 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Syntax]]></category>
      <category><![CDATA[Readability]]></category>
    </item>
    <item>
      <title><![CDATA[Eager Load Nested Relationships with Dot Notation]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-eager-load-nested-dot-notation</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-eager-load-nested-dot-notation</guid>
      <description><![CDATA[Use dot-notation strings inside with() to eager load deeply nested relationships in single database query chains. When fetching models that require multi-lev...]]></description>
      <content:encoded><![CDATA[<blockquote>Use dot-notation strings inside with() to eager load deeply nested relationships in single database query chains.</blockquote>
<p>When fetching models that require multi-level relationships (e.g. Posts -> Comments -> Author), use dot-notation strings in with() to eager load all levels efficiently.</p>
<pre><code class="language-php">use App\Models\Post;

// Eager loads post author, comments, and comment authors
$posts = Post::with([&#039;author&#039;, &#039;comments.author&#039;])-&gt;get();</code></pre>
<ul>
  <li>Eager loads multi-level nested relationships cleanly in dot-notation strings</li>
  <li>Prevents cascading N+1 query problems across nested views</li>
  <li>Allows applying constraints to nested levels using array key closures</li>
</ul>
]]></content:encoded>
      <pubDate>Thu, 22 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Relationships]]></category>
    </item>
    <item>
      <title><![CDATA[Dynamically Append Model Casts with mergeCasts()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-eloquent-merge-casts-dynamic</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-eloquent-merge-casts-dynamic</guid>
      <description><![CDATA[Use mergeCasts() to dynamically append attribute casting rules to Eloquent model instances at runtime. When working with dynamic attributes or traits on mode...]]></description>
      <content:encoded><![CDATA[<blockquote>Use mergeCasts() to dynamically append attribute casting rules to Eloquent model instances at runtime.</blockquote>
<p>When working with dynamic attributes or traits on models, hardcoding all casts in $casts can be inflexible. Use mergeCasts() to add casting definitions dynamically.</p>
<pre><code class="language-php">use App\Models\User;

$user = new User();
$user-&gt;mergeCasts([
    &#039;options&#039; =&gt; &#039;array&#039;,
    &#039;verified_at&#039; =&gt; &#039;datetime&#039;,
]);</code></pre>
<ul>
  <li>Appends cast rules to existing model casts dynamically at runtime</li>
  <li>Ideal for reusable model traits that require specific attribute casts</li>
  <li>Does not overwrite existing cast declarations for un-specified attributes</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Casts]]></category>
    </item>
    <item>
      <title><![CDATA[Query JSON and Array ID Columns with Array-Column Relationships]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-array-column-relationships-hasmanyarraycolumn</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-array-column-relationships-hasmanyarraycolumn</guid>
      <description><![CDATA[Define relationships on models where foreign keys are stored as JSON arrays or comma-separated lists rather than traditional single-id foreign keys. When dea...]]></description>
      <content:encoded><![CDATA[<blockquote>Define relationships on models where foreign keys are stored as JSON arrays or comma-separated lists rather than traditional single-id foreign keys.</blockquote>
<p>When dealing with legacy schemas or denormalized database designs where a model stores multiple related IDs inside a JSON array column (e.g. `[1, 2, 5]`), standard Eloquent relationships fail.</p>
<p>Using array column relationship packages or custom query scope join helpers allows seamless querying:</p>
<pre><code class="language-php">use App\Models\Tag;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $casts = [
        &#039;tag_ids&#039; =&gt; &#039;array&#039;,
    ];

    // Query products that contain specific tag IDs inside JSON array
    public function scopeWithTag($query, int $tagId)
    {
        return $query-&gt;whereJsonContains(&#039;tag_ids&#039;, $tagId);
    }
}

// Fetch products matching tag array
$products = Product::withTag(5)-&gt;get();</code></pre>
<ul>
  <li>Enables relational queries on denormalized JSON array fields</li>
  <li>Uses database-native <code>whereJsonContains()</code> for optimized index searching</li>
  <li>Ideal for tag lists, permission arrays, and multi-category selections</li>
</ul>
]]></content:encoded>
      <pubDate>Mon, 19 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Database]]></category>
    </item>
    <item>
      <title><![CDATA[Convert Nested Arrays to URL Query Strings with Arr::query()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-arr-query-build-url-params</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-arr-query-build-url-params</guid>
      <description><![CDATA[Use Arr::query() to build properly encoded URL query strings directly from nested associative arrays. Building complex query parameters manually using httpbu...]]></description>
      <content:encoded><![CDATA[<blockquote>Use Arr::query() to build properly encoded URL query strings directly from nested associative arrays.</blockquote>
<p>Building complex query parameters manually using http_build_query() can be clunky. Laravel's Arr::query() helper converts arrays into clean URL-encoded query strings.</p>
<pre><code class="language-php">use Illuminate\Support\Arr;

$array = [&#039;filter&#039; =&gt; [&#039;status&#039; =&gt; &#039;active&#039;, &#039;role&#039; =&gt; &#039;admin&#039;], &#039;page&#039; =&gt; 2];

// Returns: &#039;filter%5Bstatus%5D=active&amp;filter%5Brole%5D=admin&amp;page=2&#039;
$queryString = Arr::query($array);</code></pre>
<ul>
  <li>Converts nested associative arrays to URL-encoded query string format</li>
  <li>Handles nested key structures cleanly</li>
  <li>Ideal for constructing dynamic filter URLs for API or web links</li>
</ul>
]]></content:encoded>
      <pubDate>Sun, 18 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[HTTP & API]]></category>
      <category><![CDATA[Arr]]></category>
      <category><![CDATA[Helpers]]></category>
    </item>
    <item>
      <title><![CDATA[Simplify App Translations with JSON Translation Files]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-localization-json-translation-keys</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-localization-json-translation-keys</guid>
      <description><![CDATA[Use JSON files in lang/ for localization to write default language strings directly as translation keys. Managing translation key strings like messages.welco...]]></description>
      <content:encoded><![CDATA[<blockquote>Use JSON files in lang/ for localization to write default language strings directly as translation keys.</blockquote>
<p>Managing translation key strings like messages.welcome in short PHP translation files is tedious. Using JSON files (e.g. lang/es.json) lets you use full English sentences as translation keys.</p>
<pre><code class="language-json">// lang/es.json
{
    &quot;Welcome to our application&quot;: &quot;Bienvenido a nuestra aplicación&quot;,
    &quot;Hello :name&quot;: &quot;Hola :name&quot;
}</code></pre>
<ul>
  <li>Uses full default language strings as keys (no abstract keys like home.title)</li>
  <li>Blade helper __(&#39;Welcome to our application&#39;) falls back to key if translation is missing</li>
  <li>Supports dynamic parameters like __(&#39;Hello :name&#39;, [&#39;name&#39; =&gt; &#39;Punyapal&#39;])</li>
</ul>
]]></content:encoded>
      <pubDate>Sat, 17 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Routing]]></category>
      <category><![CDATA[Localization]]></category>
      <category><![CDATA[i18n]]></category>
    </item>
    <item>
      <title><![CDATA[Control Pagination Link Density with onEachSide()]]></title>
      <link>https://mrpunyapal.dev/tips/laravel-pagination-oneachside-links</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/laravel-pagination-oneachside-links</guid>
      <description><![CDATA[Use onEachSide() on paginated Eloquent queries to adjust the number of page links shown beside the current page. Default pagination controls can show too man...]]></description>
      <content:encoded><![CDATA[<blockquote>Use onEachSide() on paginated Eloquent queries to adjust the number of page links shown beside the current page.</blockquote>
<p>Default pagination controls can show too many numeric page links on mobile viewports. Calling onEachSide(1) limits pagination controls to a clean, minimal set of links.</p>
<pre><code class="language-php">use App\Models\User;

// Displays 1 page link on each side of the active page
$users = User::paginate(15)-&gt;onEachSide(1);</code></pre>
<ul>
  <li>Controls how many numeric page buttons display beside active page number</li>
  <li>Prevents pagination controls from overflowing narrow mobile viewports</li>
  <li>Works out of the box with default Blade pagination templates</li>
</ul>
]]></content:encoded>
      <pubDate>Wed, 14 Jun 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Eloquent]]></category>
      <category><![CDATA[Pagination]]></category>
      <category><![CDATA[UI]]></category>
    </item>
    <item>
      <title><![CDATA[Use CSS Container Queries for Modular Component Layouts]]></title>
      <link>https://mrpunyapal.dev/tips/css-container-queries-responsive-components</link>
      <guid isPermaLink="true">https://mrpunyapal.dev/tips/css-container-queries-responsive-components</guid>
      <description><![CDATA[Use CSS Container Queries (@container) to adjust component layouts based on parent container width rather than viewport size. Media queries (@media) check to...]]></description>
      <content:encoded><![CDATA[<blockquote>Use CSS Container Queries (@container) to adjust component layouts based on parent container width rather than viewport size.</blockquote>
<p>Media queries (@media) check total screen width, which breaks when a component is placed inside narrow sidebars versus wide main content areas. Container queries adjust styles based on element parent container width.</p>
<pre><code class="language-css">/* Define container context on parent */
.card-container {
  container-type: inline-size;
}

/* Adjust card layout based on container width */
@container (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}</code></pre>
<ul>
  <li>Adapts component layouts based on parent container dimensions instead of viewport width</li>
  <li>Allows building truly self-contained, context-aware UI components</li>
  <li>Supported natively in all modern web browsers</li>
</ul>
]]></content:encoded>
      <pubDate>Fri, 24 Feb 2023 00:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Punyapal Shah]]></dc:creator>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Styling]]></category>
      <category><![CDATA[Responsive]]></category>
      <category><![CDATA[Frontend]]></category>
    </item>
  </channel>
</rss>
