<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Hafiq Iqmal</title>
    <description>The latest articles on Forem by Hafiq Iqmal (@afiqiqmal).</description>
    <link>https://forem.com/afiqiqmal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F6719%2Fbc612c98-0b7d-45fb-92d0-427222458c8b.png</url>
      <title>Forem: Hafiq Iqmal</title>
      <link>https://forem.com/afiqiqmal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/afiqiqmal"/>
    <language>en</language>
    <item>
      <title>I Built a Claude Code Slash Command for OWASP Top 10:2025, NIST CSF 2.0 and 850+ Security Checks</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Mon, 02 Mar 2026 01:33:51 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/claude-security-audit-a-claude-code-slash-command-for-owasp-top-102025-nist-csf-20-and-850-4mjn</link>
      <guid>https://forem.com/afiqiqmal/claude-security-audit-a-claude-code-slash-command-for-owasp-top-102025-nist-csf-20-and-850-4mjn</guid>
      <description>&lt;p&gt;Security audits should happen on every project before every major release. In practice, they happen whenever someone puts it in the budget, which is usually once a year if the project is lucky.&lt;/p&gt;

&lt;p&gt;I built &lt;code&gt;claude-security-audit&lt;/code&gt; to close that gap. It is a Claude Code slash command that runs a comprehensive white-box and gray-box security audit on any project, maps every finding to the compliance frameworks your clients and auditors care about and saves the report to your project root.&lt;/p&gt;

&lt;p&gt;One command. No setup per project. No external service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Is
&lt;/h2&gt;

&lt;p&gt;A Claude Code slash command (&lt;code&gt;/security-audit&lt;/code&gt;) that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads your entire codebase (or just the diff from a PR)&lt;/li&gt;
&lt;li&gt;Runs 850+ security checks across 18 attack categories&lt;/li&gt;
&lt;li&gt;Detects your framework and loads tailored checklists&lt;/li&gt;
&lt;li&gt;Maps every finding to OWASP Top 10:2025, CWE, NIST CSF 2.0 and more&lt;/li&gt;
&lt;li&gt;Outputs a structured report to &lt;code&gt;./security-audit-report.md&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/afiqiqmal/claude-security-audit" rel="noopener noreferrer"&gt;https://github.com/afiqiqmal/claude-security-audit&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/afiqiqmal/claude-security-audit/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs the slash command globally at &lt;code&gt;~/.claude/commands/security-audit.md&lt;/code&gt; and the reference files at &lt;code&gt;~/.claude/security-audit-references/&lt;/code&gt;. Available in every project from that point forward.&lt;/p&gt;

&lt;p&gt;For per-project install only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; .claude/commands/security-audit.md /path/to/your-project/.claude/commands/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use &lt;code&gt;/project:security-audit&lt;/code&gt; inside that project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full audit: white-box + gray-box + hotspots + code smells&lt;/span&gt;
/security-audit

&lt;span class="c"&gt;# Quick scan: CRITICAL and HIGH only, no gray-box&lt;/span&gt;
/security-audit quick

&lt;span class="c"&gt;# Diff mode: scan only changed files (good for PR reviews)&lt;/span&gt;
/security-audit diff
/security-audit diff:main
/security-audit diff:develop

&lt;span class="c"&gt;# Focused deep dives&lt;/span&gt;
/security-audit focus:auth     &lt;span class="c"&gt;# Auth and authorization&lt;/span&gt;
/security-audit focus:api      &lt;span class="c"&gt;# API security and input validation&lt;/span&gt;
/security-audit focus:config   &lt;span class="c"&gt;# Config, supply chain, infrastructure&lt;/span&gt;

&lt;span class="c"&gt;# Run individual phases&lt;/span&gt;
/security-audit phase:1        &lt;span class="c"&gt;# Reconnaissance only&lt;/span&gt;
/security-audit phase:2        &lt;span class="c"&gt;# White-box analysis only&lt;/span&gt;
/security-audit phase:3        &lt;span class="c"&gt;# Gray-box testing only&lt;/span&gt;
/security-audit phase:4        &lt;span class="c"&gt;# Security hotspots only&lt;/span&gt;
/security-audit phase:5        &lt;span class="c"&gt;# Code smells only&lt;/span&gt;

&lt;span class="c"&gt;# Include remediation code blocks (off by default)&lt;/span&gt;
/security-audit &lt;span class="nt"&gt;--fix&lt;/span&gt;
/security-audit quick &lt;span class="nt"&gt;--fix&lt;/span&gt;
/security-audit diff:main &lt;span class="nt"&gt;--fix&lt;/span&gt;

&lt;span class="c"&gt;# Lite mode: reduces token usage significantly&lt;/span&gt;
/security-audit &lt;span class="nt"&gt;--lite&lt;/span&gt;         &lt;span class="c"&gt;# OWASP + CWE + NIST only&lt;/span&gt;
/security-audit quick &lt;span class="nt"&gt;--lite&lt;/span&gt;   &lt;span class="c"&gt;# Cheapest useful scan&lt;/span&gt;
/security-audit diff:main &lt;span class="nt"&gt;--lite&lt;/span&gt; &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Frameworks Covered
&lt;/h2&gt;

&lt;p&gt;Every finding is tagged to one or more of these:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OWASP Top 10&lt;/td&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CWE&lt;/td&gt;
&lt;td&gt;4.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NIST CSF&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SANS/CWE Top 25&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OWASP ASVS&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCI DSS&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MITRE ATT&amp;amp;CK&lt;/td&gt;
&lt;td&gt;v15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SOC 2&lt;/td&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISO 27001&lt;/td&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  OWASP Top 10:2025 Coverage
&lt;/h2&gt;

&lt;p&gt;The audit explicitly tests all ten 2025 categories. Two of them are new:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;OWASP ID&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Broken Access Control&lt;/td&gt;
&lt;td&gt;A01:2025&lt;/td&gt;
&lt;td&gt;Now includes SSRF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Security Misconfiguration&lt;/td&gt;
&lt;td&gt;A02:2025&lt;/td&gt;
&lt;td&gt;Moved up from #5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Software Supply Chain Failures&lt;/td&gt;
&lt;td&gt;A03:2025&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;NEW&lt;/strong&gt; - expands "Vulnerable Components"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Cryptographic Failures&lt;/td&gt;
&lt;td&gt;A04:2025&lt;/td&gt;
&lt;td&gt;Moved from #2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Injection&lt;/td&gt;
&lt;td&gt;A05:2025&lt;/td&gt;
&lt;td&gt;Moved from #3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Insecure Design&lt;/td&gt;
&lt;td&gt;A06:2025&lt;/td&gt;
&lt;td&gt;Moved from #4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Identification and Auth Failures&lt;/td&gt;
&lt;td&gt;A07:2025&lt;/td&gt;
&lt;td&gt;Unchanged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Software and Data Integrity Failures&lt;/td&gt;
&lt;td&gt;A08:2025&lt;/td&gt;
&lt;td&gt;Unchanged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Security Logging and Alerting Failures&lt;/td&gt;
&lt;td&gt;A09:2025&lt;/td&gt;
&lt;td&gt;Renamed, emphasis on alerting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Mishandling of Exceptional Conditions&lt;/td&gt;
&lt;td&gt;A10:2025&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;NEW&lt;/strong&gt; - fail-open logic, silent failures&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Framework-Specific Checklists
&lt;/h2&gt;

&lt;p&gt;Claude detects your framework and loads a tailored checklist alongside the base checks. Currently supported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Laravel&lt;/li&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;FastAPI&lt;/li&gt;
&lt;li&gt;Express&lt;/li&gt;
&lt;li&gt;Django&lt;/li&gt;
&lt;li&gt;Rails&lt;/li&gt;
&lt;li&gt;Spring Boot&lt;/li&gt;
&lt;li&gt;ASP.NET Core&lt;/li&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;li&gt;Flask&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the Laravel checklist covers Eloquent mass assignment vectors, Laravel authorization bypass patterns, &lt;code&gt;.env&lt;/code&gt; exposure, Sanctum and Passport token handling and queue job deserialization. Generic web checks miss these because they are framework-specific failure modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gray-Box Testing
&lt;/h2&gt;

&lt;p&gt;Beyond static analysis, the audit runs a gray-box phase that looks at your code the way a tester with partial knowledge would:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;What It Tests&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Role-Based Access&lt;/td&gt;
&lt;td&gt;Can lower-privilege roles reach higher-privilege endpoints?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Probing&lt;/td&gt;
&lt;td&gt;Verb tampering, undocumented params, over-fetching, mass assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credential Boundaries&lt;/td&gt;
&lt;td&gt;Expired tokens, revoked sessions, tenant isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial Knowledge&lt;/td&gt;
&lt;td&gt;Hidden endpoints from routes, IDOR via migration schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate Limit Verification&lt;/td&gt;
&lt;td&gt;Is rate limiting actually enforced or just documented?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Differentials&lt;/td&gt;
&lt;td&gt;Do your errors leak resource existence?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The error differential check is one worth calling out specifically. If your application returns "User not found" for a nonexistent user ID and "Access denied" for a valid ID that belongs to someone else, you have just given an attacker a user enumeration tool. The audit flags this even if the authentication logic itself is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI/LLM Security Checks
&lt;/h2&gt;

&lt;p&gt;If your project includes AI or LLM features, the audit runs additional checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt injection vectors&lt;/li&gt;
&lt;li&gt;Output sanitization before rendering&lt;/li&gt;
&lt;li&gt;RAG poisoning paths&lt;/li&gt;
&lt;li&gt;Tool calling permission scopes&lt;/li&gt;
&lt;li&gt;Cost monitoring and DoS via large context inputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This maps to A05:2025 and A01:2025. As more projects add AI features, these attack surfaces are becoming increasingly relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Report Structure
&lt;/h2&gt;

&lt;p&gt;The report at &lt;code&gt;./security-audit-report.md&lt;/code&gt; is organized for both technical and non-technical readers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Executive Summary (finding counts, risk assessment)&lt;/li&gt;
&lt;li&gt;OWASP Top 10:2025 Coverage Matrix&lt;/li&gt;
&lt;li&gt;NIST CSF 2.0 Coverage Matrix&lt;/li&gt;
&lt;li&gt;Critical and High Findings (with vulnerable code block)&lt;/li&gt;
&lt;li&gt;Medium Findings&lt;/li&gt;
&lt;li&gt;Low and Informational&lt;/li&gt;
&lt;li&gt;Gray-Box Findings (role, endpoint, expected vs actual)&lt;/li&gt;
&lt;li&gt;Security Hotspots (PR review guidance)&lt;/li&gt;
&lt;li&gt;Code Smells (patterns that breed security bugs)&lt;/li&gt;
&lt;li&gt;Recommendations Summary (grouped by OWASP)&lt;/li&gt;
&lt;li&gt;Methodology&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By default the report shows findings and descriptions. Append &lt;code&gt;--fix&lt;/code&gt; to include copy-paste remediation code blocks. This is off by default because it adds roughly 50% to the output token count and is not always needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token Usage
&lt;/h2&gt;

&lt;p&gt;This is a token-intensive command. Be aware of this before running full audits on large codebases.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Estimated Total Tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quick --lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~30-80K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;diff --lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~20-40K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quick&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~50-100K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;focus:auth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~40-75K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;diff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~35-60K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;full --lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~75-170K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;full&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~90-190K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;full --fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~100-210K&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Recommended workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;diff --lite&lt;/code&gt; for every PR review&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;quick --lite&lt;/code&gt; for regular development checks&lt;/li&gt;
&lt;li&gt;Save &lt;code&gt;full&lt;/code&gt; for pre-release audits or client deliveries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Custom Checks
&lt;/h2&gt;

&lt;p&gt;Add your own security checklists to extend the built-in ones.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Folder&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/security-audit-custom/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Global - all projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/security-audit-custom/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Project-level only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Format your checks with OWASP and NIST tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Internal API Standards [A01:2025, A05:2025 | PR.AA, PR.DS]&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] All internal endpoints require service-to-service auth tokens
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Response bodies never include internal database IDs
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Deprecated endpoints return 410 Gone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A template file is installed at &lt;code&gt;~/.claude/security-audit-custom/custom-template.md&lt;/code&gt; during setup.&lt;/p&gt;

&lt;p&gt;Both global and project-level custom checks run alongside the built-in checks. Project-level checks do not override global ones, they are merged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;claude-security-audit/
├── .claude/
│   └── commands/
│       └── security-audit.md       # The /security-audit slash command
├── references/
│   ├── attack-vectors.md           # 850+ checks tagged to OWASP/NIST/CWE
│   ├── nist-csf-mapping.md         # OWASP 2025-to-NIST cross-reference
│   ├── compliance-mapping.md       # CWE, SANS, ASVS, PCI DSS, ATT&amp;amp;CK, SOC 2, ISO 27001
│   ├── custom-template.md          # Template for custom checks
│   └── frameworks/                 # Framework-specific checklists
│       ├── laravel.md
│       ├── nextjs.md
│       ├── fastapi.md
│       └── ...
├── security-audit-guidelines.md    # Severity ratings and conventions
├── install.sh                      # One-command installer
├── CLAUDE.md                       # Project context for Claude Code
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What It Is Not
&lt;/h2&gt;

&lt;p&gt;This is not a replacement for a proper penetration test done by human security researchers. A skilled pentester will find things a checklist-driven audit will not, because they bring creativity and adversarial thinking that goes beyond predefined checks.&lt;/p&gt;

&lt;p&gt;What this replaces is the security check you were not doing at all. The code review that focused on feature correctness but skipped the security angle. The audit that was supposed to happen before the release and did not.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;diff --lite&lt;/code&gt; on every PR is a realistic habit that costs almost nothing and catches the class of vulnerabilities that show up repeatedly in production incidents: missing authorization checks, unvalidated inputs, sensitive data in logs, rate limiting that exists in documentation but not in code.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/afiqiqmal/claude-security-audit" rel="noopener noreferrer"&gt;https://github.com/afiqiqmal/claude-security-audit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MIT licensed. Pull requests welcome, especially for additional framework-specific checklists.&lt;/p&gt;

&lt;p&gt;If you build something on top of this or extend it for your stack, open a PR or drop an issue. The reference files are designed to be extended.&lt;/p&gt;

</description>
      <category>security</category>
      <category>claudecode</category>
      <category>owasp</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Preventing Duplicate Requests in Laravel: The AtomicLockMiddleware</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Mon, 21 Oct 2024 08:12:49 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/preventing-duplicate-requests-in-laravel-the-atomiclockmiddleware-j46</link>
      <guid>https://forem.com/afiqiqmal/preventing-duplicate-requests-in-laravel-the-atomiclockmiddleware-j46</guid>
      <description>&lt;h3&gt;
  
  
  How to Handle Multiple Request Collisions and Keep Your Laravel App Running Smoothly??
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;/blockquote&gt;




&lt;p&gt;If you ever developed a web app in Laravel, you must have faced one such problem which is rather common: &lt;strong&gt;handling of duplicate requests currently being processed&lt;/strong&gt;. It's that trivial problem, but it may lead to data inconsistency, a failed transaction, or-what's worse-a poor user experience. Imagine your application handling some form submission twice. You'd end up with duplicate records or maybe even duplicated payments, which would be a huge headache both for you and your users.&lt;/p&gt;

&lt;p&gt;For this, we need to ensure each request should be processed one at a time, especially when the action is sensitive, such as making any payment or updating something. One of the solutions is by using an atomic lock. In this post, I am going to walk you through how I implemented a simple &lt;strong&gt;middleware&lt;/strong&gt; in Laravel to handle such scenarios. This middleware will allow processing of only one request from any particular user at one time for specific actions.&lt;/p&gt;

&lt;p&gt;Let's take a closer look into how it works and why you might want to use it in your projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Should You Care About Duplicate Requests?
&lt;/h2&gt;

&lt;p&gt;Before getting into the code, let's talk about why handling duplicate requests is important. It's easy to think of a web request as a one-time thing, but there are several scenarios where duplicate requests can sneak in, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slow internet connections&lt;/strong&gt;: Users might click the same button multiple times out of impatience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page reloads&lt;/strong&gt;: If a user refreshes the page quickly after submitting a form, it might send the same request again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated scripts or bots&lt;/strong&gt;: These can sometimes trigger requests in a way you don't expect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these cases, Laravel could end up processing 2 (or more) identical requests at the same time. That's where the atomic lock middleware comes in.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Middleware
&lt;/h2&gt;

&lt;p&gt;The middleware is simple and its  ensures only one request from a user is processed at a time. It works by locking the request URL with a unique key in Laravel's cache. If another request with the same URL comes in before the first one finishes, the middleware throws an error, preventing the duplicate request from being processed.&lt;/p&gt;

&lt;p&gt;Here's the full code of the AtomicLockMiddleware:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AtomicLockMiddleware&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Handle an incoming request.
     *
     * @param  Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Ensure that no duplicate request is being processed&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ensureSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Proceed with the original request&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Release the lock after the request is processed&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;releaseSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Generate a unique key for the request.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'atomic_lock_middleware_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Handle request termination and release lock.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;releaseSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Ensure that only one request is processed at a time by locking it.
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;ensureSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Check if the request is already locked&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// HTTP status code for "Too Many Requests"&lt;/span&gt;
                &lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Multiple duplicate request. Wait until the previous request is completed.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Lock the request for 60 seconds&lt;/span&gt;
        &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'this signature has been consumed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Release the lock after the request is processed.
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;releaseSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;forget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Generate a unique prefix for the request based on URL and other parameters.
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'global'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// You can add custom logic here to modify the suffix, such as user IDs or other identifiers&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Breaking Down the Middleware
&lt;/h2&gt;

&lt;p&gt;Let's dive into each part of the code to understand how this middleware works:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Handling Requests
&lt;/h3&gt;

&lt;p&gt;The handle method is the core of this middleware. It ensures that duplicate requests are blocked until the original one is processed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ensureSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;releaseSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, it calls the &lt;strong&gt;&lt;code&gt;ensureSingleRequest&lt;/code&gt;&lt;/strong&gt; method to check whether the request is already locked. If not, it processes the request normally. After the request is processed, it calls &lt;strong&gt;&lt;code&gt;releaseSingleRequest&lt;/code&gt;&lt;/strong&gt; to remove the lock from the cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Locking the Request
&lt;/h3&gt;

&lt;p&gt;In the &lt;strong&gt;&lt;code&gt;ensureSingleRequest&lt;/code&gt;&lt;/strong&gt; method, we create a unique lock key for each request. If the request is already locked, the middleware throws an exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;ensureSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Too Many Requests status code&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Multiple duplicate request. Wait until the previous request is completed.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'this signature has been consumed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lock is set to expire after 60 seconds, meaning if a request takes longer than that to process, the lock will automatically expire and allow new requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Generating Unique Keys
&lt;/h3&gt;

&lt;p&gt;To ensure that each request gets its own lock, we generate a unique key using the request URL and a prefix / suffix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'atomic_lock_middleware_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to lock requests based on their URL, so two different URLs won't interfere with each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Releasing the Lock
&lt;/h3&gt;

&lt;p&gt;Once the request is fully processed, we remove the lock from the cache, allowing new requests to be made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;releaseSingleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;forget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;requestKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the system clean and ensures that the lock isn't held unnecessarily after the request is done.&lt;/p&gt;




&lt;h3&gt;
  
  
  How to Use the Middleware
&lt;/h3&gt;

&lt;p&gt;To use this middleware, first, register it in your Kernel.php file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Laravel 10&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$routeMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Other middleware...&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
    &lt;span class="s1"&gt;'atomic.lock'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Http\Middleware\AtomicLockMiddleware&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Laravel 11&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePath&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="mf"&gt;....&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mf"&gt;.....&lt;/span&gt;

        &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="mf"&gt;...&lt;/span&gt;
            &lt;span class="s1"&gt;'atomic.lock'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Http\Middleware\AtomicLockMiddleware&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="mf"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="mf"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then apply it to specific routes or controllers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/approve-order'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;OrderApprovalController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'submit'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'atomic.lock'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ensure that only one request is processed at a time for the form submission.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why You Should Use This Middleware
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;AtomicLockMiddleware&lt;/strong&gt; provides several benefits for your Laravel application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prevents double submissions&lt;/strong&gt;: Avoid issues like duplicated form submissions, which can create duplicate records or even duplicate payments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensures atomicity&lt;/strong&gt;: Actions are guaranteed to only happen once at a time, ensuring data consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improves user experience&lt;/strong&gt;: Users are prevented from accidentally submitting forms multiple times, which could otherwise cause confusion or errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This simple middleware adds a layer of protection against unwanted duplicate requests, helping your application run more smoothly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Customising the Middleware
&lt;/h3&gt;

&lt;p&gt;You can easily extend this middleware to fit your needs. Here are a few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add user-specific locks&lt;/strong&gt;: You could modify the prefix method to include the user's ID in the request key. This would allow each user to have their own lock for requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom lock expiration times&lt;/strong&gt;: Depending on the type of request, you may want to adjust the expiration time of the lock. For example, you might set longer expiration times for actions that typically take more time to complete.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Basically, anything significant or sensitive that any Laravel application does needs to be handled by duplicated requests. In this case, the atomic lock middleware keeps it off so that every request would go ahead accordingly in an orderly, atomic fashion.&lt;/p&gt;

&lt;p&gt;This little middleware can save you from duplicte form submissions, or any actions which written into your database, and so on, with just some lines of code. Put this lock on your routes for everything from the most simple app up to the most complex API.&lt;/p&gt;

&lt;p&gt;Feel free to play around with it in your Laravel projects and understand how it simplifies the parallel requests handling for you!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This middleware offers a straightforward solution to a common problem, helping you ensure that no request gets processed twice unintentionally. By keeping things simple and atomic, your app will provide a more seamless experience for users and help you avoid messy data issues.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;Thank you for reading! Don’t forget to subscribe to stay informed about the latest updates in system design. Happy designing!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you found this article insightful and want to stay updated with more content on system design and technology trends, be sure to follow me on :-&lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/hafiqdotcom" rel="noopener noreferrer"&gt;https://twitter.com/hafiqdotcom&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/in/hafiq93" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hafiq93&lt;/a&gt;&lt;br&gt;
Buy Me Coffee: &lt;a href="https://paypal.me/mhi9388" rel="noopener noreferrer"&gt;https://paypal.me/mhi9388&lt;/a&gt; /&lt;br&gt;
&lt;a href="https://buymeacoffee.com/mhitech" rel="noopener noreferrer"&gt;https://buymeacoffee.com/mhitech&lt;/a&gt;&lt;br&gt;
Medium: &lt;a href="https://medium.com/@hafiqiqmal93" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>webdev</category>
      <category>database</category>
      <category>redis</category>
    </item>
    <item>
      <title>How to Install WKHTMLTOPDF with Patched QT on Ubuntu and CentOS 8</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Mon, 07 Oct 2024 01:58:40 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/how-to-install-wkhtmltopdf-with-patched-qt-on-ubuntu-and-centos-8-4j12</link>
      <guid>https://forem.com/afiqiqmal/how-to-install-wkhtmltopdf-with-patched-qt-on-ubuntu-and-centos-8-4j12</guid>
      <description>&lt;p&gt;Ever needed to convert HTML to PDF and found yourself tangled in a web of outdated software? You’re in the right place. In this guide, I’ll show you how to install wkhtmltopdf with the patched QT on Ubuntu and CentOS 8. Let’s make this painless and straightforward!&lt;/p&gt;




&lt;h2&gt;
  
  
  Why WKHTMLTOPDF with Patched QT?
&lt;/h2&gt;

&lt;p&gt;First things first, why are we even bothering with this patched QT version? Well, the patched QT version of wkhtmltopdf offers better compatibility and more features compared to the stock version that you usually get from default repositories. If you need reliable PDF generation with advanced CSS and JavaScript support, this is the way to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing on Ubuntu LTS
&lt;/h2&gt;

&lt;p&gt;Let’s start with Ubuntu. Here’s how you can get wkhtmltopdf up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step-by-Step Guide:
&lt;/h3&gt;

&lt;p&gt;Update your package list and install &lt;strong&gt;xfonts-75dpi&lt;/strong&gt; package since it is required by wkhtmltopdf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# sudo apt-get update 
# sudo apt-get install -y wget xfonts-75dpi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the wkhtmltopdf package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# cd ~
# wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the downloaded package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# sudo dpkg -i wkhtmltox_0.12.6.1-2.jammy_amd64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# wkhtmltopdf --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went smoothly, you should see the version of WKHTMLTOPDF printed out. Now, you’re all set on Ubuntu!&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing on CentOS 8
&lt;/h2&gt;

&lt;p&gt;Now, let’s move on to CentOS 8. The steps are a bit different, but just as simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step-by-Step Guide:
&lt;/h3&gt;

&lt;p&gt;Install the WKHTMLTOPDF package directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# sudo dnf install -y https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox-0.12.6.1-3.almalinux9.$(uname -m).rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# wkhtmltopdf --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Your CentOS 8 system should now have wkhtmltopdf installed and ready to go.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up!
&lt;/h2&gt;

&lt;p&gt;There you have it — a quick and easy way to install wkhtmltopdf with the patched QT on both Ubuntu and CentOS 8. Whether you’re converting reports, invoices or any other HTML content to PDF, this setup will have you covered.&lt;/p&gt;

&lt;p&gt;Remember, keeping your tools up-to-date and using the right versions can save you a lot of headaches down the line. Happy converting!&lt;/p&gt;

&lt;p&gt;If you found this guide helpful, share it with your friends and colleagues. Let’s make the web to PDF conversion process a breeze for everyone!&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Tips
&lt;/h2&gt;

&lt;p&gt;Always check for the latest releases on the &lt;a href="https://github.com/wkhtmltopdf/packaging/releases" rel="noopener noreferrer"&gt;official WKHTMLTOPDF GitHub page&lt;/a&gt;.&lt;br&gt;
For large scale deployments, consider automating the installation process with scripts or configuration management tools.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks for reading! If you have any questions or run into any issues, drop a comment below. Let’s help each other out!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>centos</category>
      <category>ubuntu</category>
      <category>devops</category>
      <category>development</category>
    </item>
    <item>
      <title>AWS Session Manager vs SSH</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Mon, 07 Oct 2024 01:39:36 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/aws-session-manager-vs-ssh-n3f</link>
      <guid>https://forem.com/afiqiqmal/aws-session-manager-vs-ssh-n3f</guid>
      <description>&lt;h2&gt;
  
  
  A Comparison between SSM vs SSH
&lt;/h2&gt;

&lt;p&gt;When dealing with the management of server access, there are 2 tools that frequently pop up: &lt;strong&gt;AWS Session Manager and SSH&lt;/strong&gt;. Both have unique benefits and a few downsides. Understanding these should help you decide which is best for your needs.&lt;/p&gt;

&lt;p&gt;In this post, we are going to explore the features, benefits, and limitations of each method and dive deep into their security aspects. Also, I'll share why I lean towards SSH over Session Manager.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is AWS Session Manager?
&lt;/h2&gt;

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

&lt;p&gt;AWS Session Manager, part of AWS Systems Manager, provides a way to access EC2 instances without needing to open inbound ports or manage SSH keys. It’s integrated into the AWS ecosystem, offering a way to connect directly via the AWS Management Console, CLI or SDKs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of AWS Session Manager
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Need for Open Ports:&lt;/strong&gt; You don’t need to expose inbound ports to your instances, which reduces the risk of external attacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrated with IAM:&lt;/strong&gt; Uses AWS Identity and Access Management (IAM) to control access, simplifying permission management and providing detailed access logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging:&lt;/strong&gt; Integration with AWS CloudTrail and Amazon CloudWatch offers detailed session logs, useful for auditing and troubleshooting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No SSH Keys:&lt;/strong&gt; Eliminates the need to manage and rotate SSH keys, which can be a security concern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform:&lt;/strong&gt; Works with both Linux and Windows instances, making it a flexible choice for diverse environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages of AWS Session Manager
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Dependency:&lt;/strong&gt; You need to be within the AWS ecosystem to use Session Manager. It’s not an option if you’re not using AWS services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning Curve:&lt;/strong&gt; Transitioning from SSH to Session Manager involves learning new tools and processes, which might be a bit challenging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connectivity Required:&lt;/strong&gt; Session Manager requires network access to AWS, which can be a limitation if connectivity is unreliable.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is SSH?
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;SSH (Secure Shell)&lt;/strong&gt; is a protocol that has been used for years to securely access remote machines. It’s a staple tool for many system administrators, providing a secure way to run commands and manage systems remotely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of SSH
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Widely Used and Tested:&lt;/strong&gt; SSH is a mature tool with broad support. It’s a reliable choice with extensive documentation and community support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Control:&lt;/strong&gt; SSH provides a straightforward, direct connection to your server, which many find intuitive and easy to use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline Access:&lt;/strong&gt; As long as you have network access, you can use SSH without relying on a specific cloud provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customisable:&lt;/strong&gt; SSH offers extensive configuration options, making it adaptable to various environments and security requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages of SSH
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Port Exposure:&lt;/strong&gt; SSH typically requires opening port 22, which can be a security risk if not properly managed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Management:&lt;/strong&gt; Managing SSH keys can be a hassle and lost or stolen keys can lead to security breaches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging and Monitoring:&lt;/strong&gt; While possible, setting up logging and monitoring for SSH sessions can be more complex than with AWS Session Manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall Rules:&lt;/strong&gt; SSH access might be restricted by firewall rules, potentially complicating connectivity.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Security Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Session Manager Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Inbound Ports:&lt;/strong&gt; Session Manager doesn’t require open inbound ports, reducing potential attack vectors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Integration:&lt;/strong&gt; Access control is managed through IAM policies, making permissions easier to handle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Logging:&lt;/strong&gt; Detailed logs are automatically recorded, which helps with auditing and troubleshooting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-to-End Encryption:&lt;/strong&gt; Communication is encrypted, safeguarding data during transmission.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SSH Security
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Port Exposure:&lt;/strong&gt; SSH’s need for an open port can be a vulnerability. It’s crucial to use best practices like changing default ports and implementing fail2ban to mitigate risks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Management:&lt;/strong&gt; Effective key management is essential to avoid security issues. Compromised keys can lead to unauthorized access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Additional Tools Required:&lt;/strong&gt; To enhance security, additional measures like multi-factor authentication (MFA) or using a bastion host may be necessary.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why I Prefer SSH Over AWS Session Manager
&lt;/h2&gt;

&lt;p&gt;While AWS Session Manager offers several benefits, particularly in environments where AWS is the primary platform, &lt;strong&gt;I personally prefer SSH for a few reasons related to my setup and preferences&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Site-to-Site VPN
&lt;/h3&gt;

&lt;p&gt;We can use a &lt;strong&gt;site-to-site VPN&lt;/strong&gt; to securely connect on-premises network with remote networks. This setup allows me to manage servers across different environments, not limited to AWS. SSH fits seamlessly into this architecture because it can operate independently of the cloud provider, giving me more flexibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bastion Host
&lt;/h3&gt;

&lt;p&gt;I also use a &lt;strong&gt;bastion host&lt;/strong&gt; to manage access to my internal servers. SSH works well with this approach, allowing me to securely tunnel connections through the bastion host. This adds an extra layer of security and control, which I find valuable. With SSH, I can easily configure and manage these tunnels to meet my specific needs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BUT i would say that you need to carefully configure your security group to open only required port and specific sources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Portability and Third-Party Tools 🚀
&lt;/h3&gt;

&lt;p&gt;One of the big advantages of SSH is the ease of using third-party tools like &lt;a href="https://termius.com/" rel="noopener noreferrer"&gt;Termius&lt;/a&gt;. These tools provide a user-friendly interface for managing SSH connections and can be used on various devices, including mobile phones and laptops.&lt;/p&gt;

&lt;p&gt;The portability is crucial for me, as it allows me to access and manage my servers from almost anywhere, whether I’m on the go or working from a different location. Tool like &lt;strong&gt;Termius&lt;/strong&gt; make SSH connections more accessible and convenient, offering a seamless experience across different devices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0l8b9dls2mtpeh7fir2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0l8b9dls2mtpeh7fir2.png" alt="Example of screenshot using Termius SSH management" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;On the other hand, AWS Session Manager requires a direct connection to AWS and is not integrated as seamlessly as in my current VPN and bastion host configuration. Again, while Session Manager has great functionality within the AWS ecosystem, portability and integration with third-party tools I use in managing my SSHs are not as effective compared to this solution. And that makes SSH far more versatile and accessible when it comes to meeting my needs.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Summaries
&lt;/h2&gt;

&lt;p&gt;Both AWS Session Manager and SSH is actually has their own pros and cons based on your needs. AWS Session Manager simplifies access management within the AWS environment and enhances security with its integrated logging and IAM features. However, it requires a reliance on AWS and may not fit every network setup. SSH, on the other hand, offers flexibility and is well-suited for environments.&lt;/p&gt;

&lt;p&gt;For me, the best tool for you, its depend on your specific setup, security requirements and personal preferences. 🤘&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Thank you for reading! Don't forget to subscribe to stay informed about the latest updates in system design and technology. Happy Crafting!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you found this article insightful and want to stay updated with more content on system design and technology trends, be sure to follow me on :-&lt;br&gt;
Twitter: &lt;a href="https://twitter.com/hafiqdotcom" rel="noopener noreferrer"&gt;https://twitter.com/hafiqdotcom&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/in/hafiq93" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hafiq93&lt;/a&gt;&lt;br&gt;
Buy Me Coffee: &lt;a href="https://paypal.me/mhi9388" rel="noopener noreferrer"&gt;https://paypal.me/mhi9388&lt;/a&gt; / &lt;br&gt;
&lt;a href="https://buymeacoffee.com/mhitech" rel="noopener noreferrer"&gt;https://buymeacoffee.com/mhitech&lt;/a&gt;&lt;br&gt;
Medium: &lt;a href="https://medium.com/@hafiqiqmal93" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloud</category>
      <category>security</category>
    </item>
    <item>
      <title>Choosing the Right Primary Key for the Database</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Mon, 07 Oct 2024 01:15:51 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/choosing-the-right-primary-key-for-the-database-3efd</link>
      <guid>https://forem.com/afiqiqmal/choosing-the-right-primary-key-for-the-database-3efd</guid>
      <description>&lt;h2&gt;
  
  
  ULID vs UUID vs Auto Increment??
&lt;/h2&gt;

&lt;p&gt;Primary keys play a critical role in database management systems, serving as a unique identifier for each record in a table. They enable efficient retrieval, updating and deletion of data and help maintain data integrity by ensuring that no duplicate records are present. When designing a database schema, one of the most important decisions is selecting the right primary key type, which can significantly impact performance, scalability and ease of use.&lt;/p&gt;

&lt;p&gt;This article will explore the pros and cons of three popular primary key types:- Universally Unique Identifier (UUID), Universally Unique Lexicographically Sortable Identifier (ULID) and auto-incrementing integers. We will discuss the properties and characteristics of each, along with examples to help you make an informed decision when choosing the right primary key for your database.&lt;/p&gt;




&lt;h2&gt;
  
  
  Universally Unique Identifier (UUID)
&lt;/h2&gt;

&lt;p&gt;A UUID is a 128-bit number that is designed to be globally unique, meaning that the probability of generating the same UUID twice is astronomically low. They are represented as a string of 36 characters, including dashes and can be generated independently without the need for a central authority. There are various versions of UUIDs, but Version 4, which relies on random numbers, is the most commonly used. The format of a UUID is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;XXXXXXXX-XXXX-MXXX-NXXX-XXXXXXXXXXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Where x is a hexadecimal digit (0-9, a-f) and M and N represent specific bits with predefined meanings. For example, a UUID might look like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;123e4567-e89b-12d3-a456–426614174000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In a database, a UUID primary key might appear in a table like this:&lt;/p&gt;


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



&lt;h3&gt;
  
  
  Benefit of UUIDs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Global uniqueness: UUIDs provide an extremely low risk of collision, making them suitable for distributed systems or databases where multiple clients may be generating IDs simultaneously.&lt;/li&gt;
&lt;li&gt;No central authority needed: UUIDs can be generated independently on each client without the need for coordination, making them suitable for decentralized systems.&lt;/li&gt;
&lt;li&gt;Easy to merge data: When combining data from different databases, UUIDs eliminate the need to worry about conflicting primary key values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawback of UUIDs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Size: UUIDs are larger than auto-incrementing integers, occupying 16 bytes of storage as opposed to 4 bytes for a typical integer. This can lead to increased storage and indexing costs, as well as decreased performance when querying or joining tables.&lt;/li&gt;
&lt;li&gt;Not human-readable: UUIDs are difficult to read, remember and communicate verbally, making them less user-friendly for developers and support teams.&lt;/li&gt;
&lt;li&gt;Unordered: UUIDs are not generated in a sequential manner, which can lead to fragmentation and decreased performance when inserting data into a table with a clustered index.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Universally Unique Lexicographically Sortable Identifier (ULID)
&lt;/h2&gt;

&lt;p&gt;ULIDs are another type of unique identifier that combines the advantages of UUIDs with the added benefit of being sortable. They are 128-bit numbers, represented as a 26-character string composed of upper-case letters and digits. The first half of the ULID represents a timestamp, while the second half is a randomly generated value. The format of a ULID is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;01ARZ3NDEKTSV4RRFFQ69G5FAV
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In a database, a ULID primary key might appear in a table like this:&lt;/p&gt;


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



&lt;h3&gt;
  
  
  Benefit of ULIDs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Global uniqueness: Like UUIDs, ULIDs provide a very low risk of collision, making them suitable for distributed systems.&lt;/li&gt;
&lt;li&gt;Lexicographically sortable: ULIDs are generated in a way that ensures they are sortable by their creation time, making them more efficient for querying and inserting into tables with clustered indexes.&lt;/li&gt;
&lt;li&gt;No central authority needed: ULIDs can be generated independently on each client without the need for coordination, making them suitable for decentralized systems.&lt;/li&gt;
&lt;li&gt;Human-readable: While not as easy to read as auto-incrementing integers, ULIDs are more human-readable than UUIDs due to their shorter length and character set.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawback of ULIDs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Size: ULIDs occupy 16 bytes of storage, similar to UUIDs, which can lead to increased storage and indexing costs, as well as decreased performance when querying or joining tables.&lt;/li&gt;
&lt;li&gt;Not as human-readable as integers: Although more readable than UUIDs, ULIDs are still not as user-friendly as auto-incrementing integers, which can pose challenges for developers and support teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Auto-Incrementing Integers
&lt;/h2&gt;

&lt;p&gt;Auto-incrementing integers are the most common type of primary key used in databases. As the name suggests, auto-incrementing integers are sequential numbers that automatically increase by a specified increment (usually 1) for each new record added to the table. An example of an auto-incrementing primary key sequence might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1, 2, 3, 4, 5, ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In a database, an auto-incrementing integer primary key might appear in a table like this:&lt;/p&gt;


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



&lt;h3&gt;
  
  
  Benefit of Auto Increments:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Easy to understand: Auto-incrementing integers are human-readable and easy to communicate verbally, making them user-friendly for developers and support teams.&lt;/li&gt;
&lt;li&gt;Smaller size: Auto-incrementing integers typically occupy 4 bytes of storage, which can lead to lower storage and indexing costs, as well as improved performance when querying or joining tables.&lt;/li&gt;
&lt;li&gt;Ordered: Auto-incrementing integers are generated sequentially, which can improve performance when inserting data into tables with clustered indexes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawback of Auto Increments:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Risk of collisions: In distributed systems or databases where multiple clients may be generating IDs simultaneously, there is a risk of conflicting primary key values.&lt;/li&gt;
&lt;li&gt;Central authority needed: Auto-incrementing integers require coordination between clients or a central authority to ensure unique ID generation, which can be a challenge in decentralized systems.&lt;/li&gt;
&lt;li&gt;Difficult to merge data: When combining data from different databases, auto-incrementing integers can lead to conflicting primary key values, making the merge process more complex.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the Right Primary Key
&lt;/h2&gt;

&lt;p&gt;When deciding on the type of primary key to use for your database, it is essential to consider the specific requirements and constraints of your system. Here are some guidelines to help you choose the most suitable primary key based on your situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized Systems: If you have a centralized system where a single authority manages ID generation, auto-incrementing integers are an excellent choice due to their simplicity, smaller size and human-readable format. They also provide better performance when working with clustered indexes.&lt;/li&gt;
&lt;li&gt;Distributed Systems: For distributed systems, where multiple clients generate IDs simultaneously and there is no central authority, UUIDs or ULIDs are more appropriate. Both provide global uniqueness and can be generated independently by each client. ULIDs have the added advantage of being lexicographically sortable, which can improve query performance.&lt;/li&gt;
&lt;li&gt;Data Merging: If your system requires frequent merging of data from different databases, UUIDs or ULIDs are the better choice, as they eliminate the need to resolve conflicting primary key values.&lt;/li&gt;
&lt;li&gt;Performance: If performance is a top priority, consider using auto-incrementing integers or ULIDs. Auto-incrementing integers offer better storage and indexing efficiency, while ULIDs provide better performance when working with clustered indexes due to their sortable nature.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling Primary Keys in Data Analytics
&lt;/h2&gt;

&lt;p&gt;When working with primary keys in data analytics, it is crucial to understand the characteristics of each primary key type and how they might impact your analyses. Here are some tips for handling different primary keys in data analytics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-Incrementing Integers: When using auto-incrementing integers as primary keys, ensure that your analysis takes into account the ordered nature of these keys. For instance, when analyzing trends or patterns over time, ensure that the data is correctly sorted based on the auto-incrementing integer.&lt;/li&gt;
&lt;li&gt;UUIDs and ULIDs: In data analytics, UUIDs and ULIDs can be more challenging to work with due to their complexity and larger size. To facilitate analysis, consider creating additional indexes or using derived columns to sort or filter the data based on relevant attributes.&lt;/li&gt;
&lt;li&gt;Data Aggregation: When aggregating data from multiple sources with different primary key types, consider standardizing the primary keys by converting them to a common type, such as UUIDs or ULIDs. This can simplify the data merging process and ensure consistent analysis across all sources.&lt;/li&gt;
&lt;li&gt;Human Readability: When presenting data analytics results to stakeholders, consider using more human-readable identifiers, such as usernames or email addresses, instead of complex primary keys like UUIDs or ULIDs. This can make the results more accessible and understandable for non-technical audiences.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;In conclusion, choosing the right primary key for your database is a critical decision that can have a lasting impact on the performance, scalability and overall success of your system. By carefully considering the specific requirements and constraints of your situation and engaging in thoughtful discussions with your team, you can make informed choices that will lay a strong foundation for your database design. Remember that the primary key type you choose will not only affect your system’s technical aspects but also the ease of use for developers, support teams and even the stakeholders who rely on the data for decision-making. So, take the time to understand the trade-offs and select the primary key that best meets the unique needs of your project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Good database design is like a well-organized library, and primary keys are the Dewey Decimal System that keeps everything in order.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;Article orginated from &lt;a href="https://medium.com/geekculture/choosing-the-right-primary-key-for-the-database-326136eff4f4" rel="noopener noreferrer"&gt;https://medium.com/geekculture/choosing-the-right-primary-key-for-the-database-326136eff4f4&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you found this article insightful and want to stay updated on technology trends, be sure to follow me on :-&lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/hafiqdotcom" rel="noopener noreferrer"&gt;https://twitter.com/hafiqdotcom&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/in/hafiq93" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hafiq93&lt;/a&gt;&lt;br&gt;
BuyMeCoffee: &lt;a href="https://paypal.me/mhi9388" rel="noopener noreferrer"&gt;https://paypal.me/mhi9388&lt;/a&gt; / &lt;a href="https://buymeacoffee.com/mhitech" rel="noopener noreferrer"&gt;https://buymeacoffee.com/mhitech&lt;/a&gt;&lt;br&gt;
Medium: &lt;a href="https://medium.com/@hafiqiqmal93" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>webdev</category>
      <category>database</category>
      <category>backend</category>
    </item>
    <item>
      <title>Normalizing Fancy Text to Normal Text in Laravel</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Sun, 06 Oct 2024 09:22:24 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/normalizing-fancy-text-to-normal-text-in-laravel-5805</link>
      <guid>https://forem.com/afiqiqmal/normalizing-fancy-text-to-normal-text-in-laravel-5805</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Article originated from &lt;a href="https://medium.com/@hafiqiqmal93/normalizing-fancy-text-to-normal-text-in-laravel-7d9ed56d5a78" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93/normalizing-fancy-text-to-normal-text-in-laravel-7d9ed56d5a78&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Text input from users are not at all interesting. With the advent of Unicode in the smartphones, users now have the luxury (and sometimes the whimsy) to input text in a variety of styles and formats. From emojis to diacritics, ligatures to full-width characters, the range of “fancy text” can be extremely confusing or difficult to understand by the system. While visually appealing, these text variations pose a significant challenge for the system particularly in terms of data consistency, searchability, and user experience.&lt;/p&gt;

&lt;p&gt;Here are the example of fancy text:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;𝘕𝘦𝘪𝘨𝘩𝘣𝘰𝘳 𝘮𝘢𝘬𝘦 𝘢 𝘯𝘦𝘸 𝘤𝘰𝘯𝘯𝘦𝘤𝘵𝘪𝘰𝘯 𝘶𝘯𝘥𝘦𝘳 𝘵𝘰 𝘰𝘶𝘳 𝘮𝘦𝘵𝘦𝘳 𝘢𝘯𝘥 𝘸𝘦 𝘥𝘪𝘴𝘤𝘰𝘷𝘦𝘳𝘦𝘥 𝘪𝘵 𝘣𝘦𝘤𝘢𝘶𝘴𝘦 𝘵𝘩𝘦𝘺 𝘴𝘸𝘪𝘵𝘤𝘩 𝘰𝘧𝘧 𝘵𝘩𝘦 𝘮𝘢𝘪𝘯 𝘮𝘦𝘵𝘦𝘳 𝘢𝘯𝘥 𝘪 𝘨𝘰 𝘥𝘰𝘸𝘯 𝘵𝘰 𝘤𝘩𝘦𝘤𝘬 𝘢𝘯𝘥 𝘴𝘰𝘮𝘦𝘰𝘯𝘦 𝘨𝘰 𝘥𝘰𝘸𝘯 𝘢𝘭𝘴𝘰 𝘵𝘰 𝘰𝘧𝘧 𝘪𝘵 𝘢𝘨𝘢𝘪𝘯 𝘢𝘯𝘥 𝘤𝘭𝘢𝘪𝘮𝘪𝘯𝘨 𝘵𝘩𝘢𝘵𝘪𝘴 𝘵𝘩𝘦𝘪𝘳 𝘮𝘦𝘵𝘦𝘳, 𝘪𝘵 𝘰𝘯𝘭𝘺 𝘩𝘢𝘱𝘱𝘦𝘯𝘴 𝘵𝘩𝘪𝘴 𝘸𝘦𝘦𝘬..𝘯𝘦𝘷𝘸𝘳 𝘪𝘯 𝘵𝘩𝘦 𝘱𝘢𝘴𝘵. 𝘛𝘩𝘦 𝘺𝘦𝘭𝘭𝘰𝘸 𝘩𝘰𝘴𝘦 𝘪𝘴 𝘫𝘶𝘴𝘵 𝘯𝘦𝘸𝘭𝘺 𝘤𝘰𝘯𝘯𝘦𝘤𝘵𝘦𝘥
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like italic character but its not italic. Its actually belongs to Mathematical Alphanumeric Symbols.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem in PHP 💥
&lt;/h2&gt;

&lt;p&gt;Well, a very obvious problem is that PHP can't JSON encode deformed UTF-8 characters upon receipt. In the modern way of doing web development, where APIs and frontend frameworks use JSON to transport data, this is a problem. If treated wrong, such deformed characters will result in data corruption, crash, or angry users.&lt;/p&gt;

&lt;p&gt;Our goal is simple :- came out with the solution that will convert every fancy text into normal readable text.&lt;/p&gt;

&lt;h2&gt;
  
  
  PHP Normalizer
&lt;/h2&gt;

&lt;p&gt;Normalization forms are pivotal to understanding the normalization process. They cater to different linguistic and technical needs. For instance, the NFC form combines characters into their composed forms, whereas NFD does the opposite, decomposing composed characters into their constituent parts. NFKC and NFKD forms go further, considering compatibility characters - folding variations of characters into a canonical form. These forms ensure that text comparison, searching, and storage are consistent and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution 🚀
&lt;/h2&gt;

&lt;p&gt;The code snippet provided is a sterling example of PHP approach to solving complex problems with simplicity and efficiency. Let's dissect this solution, understand its components, and see how it seamlessly integrates :-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;normalizeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;$intl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FORM_C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FORM_D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NFD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FORM_KC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NFKC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FORM_KC_CF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FORM_KD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NFKD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NFC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NFKC_CF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$intl&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;isNormalized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$form&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;\Normalizer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$form&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The usage is simple:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$normalText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Utils&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;normalizeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$YOUR_FANCY_STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may register inside helper function to make it easier to use. For example:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;function_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'normalize_text'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;normalize_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Utils&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;normalizeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// USAGE&lt;/span&gt;
&lt;span class="nv"&gt;$normalText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$YOUR_FANCY_STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At its core, this function leverages PHP's &lt;code&gt;**Normalizer**&lt;/code&gt; class-a part of the Internationalization (intl) extension-to address the normalization. The &lt;code&gt;**Normalizer**&lt;/code&gt; class offers several normalization forms, each tailored to different normalization needs. This function iterates through these forms, checking if the text is already normalized in a given form using &lt;code&gt;**isNormalized**&lt;/code&gt; function. If not, it normalizes the text to that form and returns the normalized string.&lt;/p&gt;




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

&lt;p&gt;While fancy text may add visual appeal to user input, it poses significant challenges for data processing and system interoperability. However, with the adoption of PHP's Normalizer class and the implementation of normalization forms, developers can overcome these challenges and ensure that their applications maintain data consistency and reliability in the face of diverse text inputs.&lt;/p&gt;




&lt;p&gt;Do you have any experiences or challenges related to handling fancy text in your projects? How do you currently address such issues, and do you find PHP's Normalizer class useful in your workflow? Let's continue the conversation and share our insights to help each other navigate the complexities of modern web development. 🤜🏼&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>normalization</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Blurry Image Detection in Laravel</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Sun, 06 Oct 2024 04:21:22 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/blurry-image-detection-in-laravel-4d8d</link>
      <guid>https://forem.com/afiqiqmal/blurry-image-detection-in-laravel-4d8d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Article originated from &lt;a href="https://medium.com/@hafiqiqmal93/blurry-image-detection-in-laravel-4c91168e00f1" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93/blurry-image-detection-in-laravel-4c91168e00f1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Crucial aspect of user experience, storing blurry images is significantly detract from the quality of a website or application. This article delves into how you can detect and manage blurry images using Laravel with help of Python and OpenCV, ensuring the application’s media remains sharp and engaging.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge of Blurry Images
&lt;/h2&gt;

&lt;p&gt;Blurry images are more than just a visual nuisance; they can undermine the professionalism of your website or app. In e-commerce, real estate listings, online galleries or any platform where image quality is paramount, ensuring clarity is essential. The challenge lies in detecting blurriness programmatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Laravel to the Rescue
&lt;/h2&gt;

&lt;p&gt;Laravel can be paired with Python to create an effective solution for this problem. By leveraging Laravel’s file validation alongside a Python script utilizing OpenCV, developers can seamlessly integrate blur detection into their file upload processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blurriness Detection Concept
&lt;/h2&gt;

&lt;p&gt;The detection of blurry images involves analyzing the image’s sharpness. This is typically done using the Laplacian operator, a mathematical tool used in image processing. The Laplacian operator measures the rate at which pixel intensity changes, and a lower variance of the Laplacian indicates a blurrier image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing in Laravel
&lt;/h2&gt;

&lt;p&gt;In Laravel, we can create a custom validation rule to check for image blurriness. This rule executes a Python script that uses the Laplacian operator to determine the sharpness of the image. Let’s break down the process:&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation OpenCV Python:
&lt;/h3&gt;

&lt;p&gt;Install PIP (Ubuntu) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install python3-pip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install OpenCV using PIP&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install opencv-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might want to consider to install under &lt;code&gt;**www-data**&lt;/code&gt; user if your application runs under  &lt;code&gt;**www-data**&lt;/code&gt;. If Yes, follow below commands to install&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /var/www/.local
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /var/www/.cache
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;www-data.www-data /var/www/.local
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;www-data.www-data /var/www/.cache
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; www-data pip3 &lt;span class="nb"&gt;install &lt;/span&gt;opencv-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Python Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_image_laplacian_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gray_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_BGR2GRAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Laplacian&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gray_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CV_64F&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;laplacian_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_image_laplacian_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;laplacian_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Laravel Rule:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImageBlurDetectionRule&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ValidationRule&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$fail&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;UploadedFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// ignore if not image&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPath&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;guessExtension&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'jpeg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gif'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bmp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'svg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'webp'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// get real path for the file&lt;/span&gt;
        &lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRealPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;escapeshellcmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'image.python_path'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" blur_detection.py '&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;base_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'scripts'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;successful&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Blur image are not accepted. Please make sure your :attribute image is clearly visible.'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;The integration of Laravel with a Python script for blur detection works in a seamless manner, offering a sophisticated yet straightforward approach to ensuring image quality. Here’s how the process unfolds:&lt;/p&gt;

&lt;h4&gt;
  
  
  Image Upload
&lt;/h4&gt;

&lt;p&gt;When a user uploads an image to the Laravel application, the custom validation rule (ImageBlurDetectionRule) is triggered.&lt;/p&gt;

&lt;h4&gt;
  
  
  Validation Rule Execution
&lt;/h4&gt;

&lt;p&gt;This rule first checks if the uploaded file is indeed an image by verifying its extension. If the file is not an image, the process stops here.&lt;/p&gt;

&lt;h4&gt;
  
  
  Python Script Invocation
&lt;/h4&gt;

&lt;p&gt;If the file is an image, the rule then calls a Python script, blur_detection.py. The image's path is passed to this script as a command-line argument.&lt;/p&gt;

&lt;h4&gt;
  
  
  Image Processing in Python:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The Python script uses OpenCV to handle the image analysis.&lt;/li&gt;
&lt;li&gt;The script reads the image and converts it into grayscale. This simplification allows for more straightforward analysis without the complexity of color.&lt;/li&gt;
&lt;li&gt;It then applies the Laplacian operator to the grayscale image. The Laplacian operator is a mathematical tool that highlights areas of rapid intensity change, which are typically edges in an image. Blurry images have fewer and less defined edges, resulting in a lower variance of the Laplacian.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Blurriness Measurement
&lt;/h4&gt;

&lt;p&gt;The script calculates the variance of the Laplacian, which serves as a measure of the image’s sharpness. A lower variance indicates a blurrier image.&lt;/p&gt;

&lt;h4&gt;
  
  
  Result Evaluation:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The script outputs the Laplacian variance as a numerical value.&lt;/li&gt;
&lt;li&gt;Back in Laravel, the validation rule captures this output and checks if the value falls below a predefined threshold. This threshold determines whether an image is considered sharp enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Validation Feedback
&lt;/h4&gt;

&lt;p&gt;If the image is too blurry (ex: the Laplacian variance is below the threshold), the validation rule fails and the user receives a message indicating that the image is blurry and should be checked.&lt;/p&gt;

&lt;h4&gt;
  
  
  User Experience Enhancement
&lt;/h4&gt;

&lt;p&gt;By preventing the upload of low-quality, blurry images, this solution enhances the overall user experience. Users are prompted to only upload clear, high-quality images, which maintains the visual standard of the application.&lt;/p&gt;




&lt;p&gt;This process is highly customizable. Developers can adjust the threshold for blurriness according to the specific needs of their application. Note that, the threshold is based on your observation. For advance usage, may need ML to determine the threshold. Moreover, the integration of Python within Laravel allows for further expansion into more advanced image processing techniques, offering a flexible and robust solution for managing image quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Application
&lt;/h2&gt;

&lt;p&gt;Incorporating this functionality in your Laravel application enhances the user experience by preventing the upload of low-quality images. This is particularly useful in scenarios where image clarity is critical, such as online portfolios, product catalogs, or user profile pictures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization and Flexibility
&lt;/h2&gt;

&lt;p&gt;The threshold for blurriness can be adjusted according to specific needs. Additionally, the integration of Python within Laravel offers flexibility to incorporate more advanced image processing techniques if required.&lt;/p&gt;

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

&lt;p&gt;The combination of Laravel and Python for detecting blurry images is a powerful solution. It not only ensures the visual quality of your application but also enhances the overall user experience. With this approach, developers can maintain high standards for media content, contributing to a more polished and professional online presence.&lt;/p&gt;




&lt;p&gt;Have you tried implementing this solution in your Laravel project? Share your experiences and any insights you’ve gained in the comments below. Let’s continue to elevate the standards of web development together!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>python</category>
      <category>opencv</category>
    </item>
    <item>
      <title>Laravel SoftDelete: Avoiding the Unique Constraint Problem</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Sun, 06 Oct 2024 04:01:22 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/laravel-softdelete-avoiding-the-unique-constraint-problem-8k2</link>
      <guid>https://forem.com/afiqiqmal/laravel-softdelete-avoiding-the-unique-constraint-problem-8k2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article originated from &lt;a href="https://medium.com/@hafiqiqmal93/laravel-softdelete-avoiding-the-unique-constraint-problem-45381d9745a0" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93/laravel-softdelete-avoiding-the-unique-constraint-problem-45381d9745a0&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In case you’ve been using Laravel for a while, especially when projects involve data integrity, most likely you have already encountered the SoftDelete feature. Pretty useful because you can “delete” records without really taking them out of the database. What Laravel does is just add a deleted_at timestamp so it marks it as deleted, but remains in the system. That’s all well and good to retain historical data, but it does introduce one potentially sticky problem — what happens to unique constraints when you restore soft-deleted records?&lt;/p&gt;




&lt;p&gt;This becomes a problem when you want to restore a record that already has, for instance, a unique email or username, in the database. Laravel will just throw an error and stop the proceedings. Fortunately, there’s an easy way of avoiding this problem in a very clean manner.&lt;/p&gt;

&lt;p&gt;Let’s walk through a solution using a trait that will help you bypass the unique constraints when using SoftDelete in Laravel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Problem
&lt;/h2&gt;

&lt;p&gt;Let’s take a basic example. Imagine you have a users table with an email field that must be unique:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;softDeletes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you soft delete a user with the email &lt;a href="mailto:johncena@wwe.com"&gt;johncena@wwe.com&lt;/a&gt; and then later create a new user with the same email, Laravel will complain about the unique constraint on the email field, throwing an error. In the same situation, when you try to restore the deleted user, Laravel also will complain about the unique constraint on the email field and throwing an same error.&lt;/p&gt;

&lt;p&gt;This becomes a headache, especially when dealing with large systems where record restoration is a common task.&lt;/p&gt;

&lt;p&gt;To prevent this, we can temporarily alter the values of unique fields when a record is soft deleted and restore the original values when the record is brought back. This way, the database doesn’t trip over the unique constraint during soft deletes or restores.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Using a Trait to Avoid Duplicate Constraints
&lt;/h2&gt;

&lt;p&gt;A Laravel trait is a great way to encapsulate this functionality. Here’s a trait we can use to handle the problem:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;AvoidDuplicateConstraintSoftDelete&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getDuplicateAvoidColumns&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;bootAvoidDuplicateConstraintSoftDelete&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;restoring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;trashed&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getDuplicateAvoidColumns&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// handle for Spatie Translatable library&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'getTranslatableAttributes'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nv"&gt;$translates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTranslatableAttributes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$translates&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$translates&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$translate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$translate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTranslations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$translation&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                        &lt;span class="nv"&gt;$values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$translation&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'--'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                    &lt;span class="p"&gt;}&lt;/span&gt;
                                    &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setTranslations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                &lt;span class="p"&gt;}&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'--'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;})[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getDuplicateAvoidColumns&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// handle for Spatie Translatable library&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'getTranslatableAttributes'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;$translates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTranslatableAttributes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$translates&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$translates&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$translate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$translate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTranslations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$translation&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="nv"&gt;$values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$translation&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'--'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                &lt;span class="p"&gt;}&lt;/span&gt;
                                &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setTranslations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'--'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$column&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This trait does a couple of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On deletion&lt;/strong&gt;, it appends a timestamp to the unique field, essentially making the field unique again without affecting the original value. This trick is useful for keeping unique constraints satisfied while the record is soft-deleted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On restoration&lt;/strong&gt;, it strips away the timestamp, restoring the original value of the unique field.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unique Field Handling&lt;/strong&gt;: Whenever a model with this trait is deleted, the trait takes care of appending a timestamp to the fields that you want to keep unique (example: email, username). This prevents conflicts if you try to add a new record with the same unique values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling Translatable Fields:&lt;/strong&gt; If your model uses the Spatie Translatable library, this trait is smart enough to handle multilingual fields as well. It looks for the translatable attributes, adjusts their values, and saves them with the timestamp trick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restoration:&lt;/strong&gt; When you restore a soft-deleted record, the trait strips off the timestamp from the unique fields, returning the field to its original value.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Applying the Trait to Your Model
&lt;/h3&gt;

&lt;p&gt;Here’s how you can apply this trait in your Laravel model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;SoftDeletes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AvoidDuplicateConstraintSoftDelete&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Specify which columns should avoid the unique constraint issue&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getDuplicateAvoidColumns&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding the &lt;code&gt;**AvoidDuplicateConstraintSoftDelete**&lt;/code&gt; trait to your model and specifying which columns need to avoid unique constraint conflicts (like &lt;strong&gt;email&lt;/strong&gt; and &lt;strong&gt;username&lt;/strong&gt;), you can easily prevent these issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Approach Works?
&lt;/h3&gt;

&lt;p&gt;What that means is that, in the event of a soft delete record, it would not cause a conflict with further operations because of some unique constraints. Or, in other words, this way you will be able to, by appending the timestamp to unique fields, render the record “hidden” for the database in terms of uniqueness but still recoverable when needed.&lt;/p&gt;

&lt;p&gt;This is quite useful when you’re dealing with a large database and restoration of records is quite common. You won’t have to deal every time with the “duplicate entry” error whenever you bring a soft-deleted user or any other model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The most useful thing in Laravel is SoftDelete, but sometimes it gives headaches while working with unique constraints. Here comes a simple, trait-based solution that will give an elegant way of avoiding the problem, just by temporary changes of unique fields on deletion and restore afterward. This way you will avoid frustrating mistakes and let your application work smoothly not breaking unique constraints in your database.&lt;/p&gt;

&lt;p&gt;If any of your fields have been made multilingual or make use of libraries like Spatie’s Translatable, the above solution will work without problems in each of these cases. The SoftDeletes are meant to give you flexibility, not get in your way. With the above minor fix in place you’ll avoid most the pitfalls and keep your data tidy and your users happy.&lt;/p&gt;




&lt;p&gt;By adding this trait to your models, you’ll be saving yourself time and headaches, especially if you’re dealing with large datasets where soft-deleting and restoring are frequent operations. Give it a try in your Laravel project, and you’ll see how smoothly it handles those tricky unique constraint problems!&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Thank you for reading! Don’t forget to subscribe to stay informed about the latest updates in system design and e-commerce innovations. Happy designing!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you found this article insightful and want to stay updated with more content on system design and technology trends, be sure to follow me on :-&lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/hafiqdotcom" rel="noopener noreferrer"&gt;https://twitter.com/hafiqdotcom&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/in/hafiq93" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hafiq93&lt;/a&gt;&lt;br&gt;
Buy Me Coffee: &lt;a href="https://paypal.me/mhi9388" rel="noopener noreferrer"&gt;https://paypal.me/mhi9388&lt;/a&gt; /&lt;br&gt;
&lt;a href="https://buymeacoffee.com/mhitech" rel="noopener noreferrer"&gt;https://buymeacoffee.com/mhitech&lt;/a&gt;&lt;br&gt;
Medium: &lt;a href="https://medium.com/@hafiqiqmal93" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>mysql</category>
      <category>programming</category>
      <category>php</category>
    </item>
    <item>
      <title>Installing Supervisor On Amazon Linux 2023</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Sun, 06 Oct 2024 03:30:01 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/installing-supervisor-on-amazon-linux-2023-2foc</link>
      <guid>https://forem.com/afiqiqmal/installing-supervisor-on-amazon-linux-2023-2foc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Original Article -&amp;gt; &lt;a href="https://medium.com/aws-in-plain-english/installing-supervisor-on-amazon-linux-2023-697b65901457" rel="noopener noreferrer"&gt;https://medium.com/aws-in-plain-english/installing-supervisor-on-amazon-linux-2023-697b65901457&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Amazon Linux 2023 stands as a reliable choice for developers and system administrators. However, users may face a hiccup: &lt;a href="http://supervisord.org/" rel="noopener noreferrer"&gt;Supervisor&lt;/a&gt;, is absent from the default RPM packages. This omission poses a challenge for efficiently managing and monitoring processes on Amazon Linux instances.&lt;/p&gt;

&lt;p&gt;But fret not! This guide will walk you through the steps to effortlessly install Supervisor on Amazon Linux 2023 using Pip, ensuring smooth sailing for your process management needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Challenge
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;AL2023 is RPM-based and includes components sourced from multiple versions of Fedora and other distributions, such as CentOS 9 Stream. The Amazon Linux kernel is sourced from the long-term support (LTS) releases directly from kernel.org, chosen independently from other distributions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;BUT, the absence of Supervisor (&lt;a href="https://docs.aws.amazon.com/linux/al2023/release-notes/all-packages-AL2023.3.html" rel="noopener noreferrer"&gt;Refer Here&lt;/a&gt;) in the default package repository complicates matters for users reliant on its intuitive process management capabilities. Supervisor streamlines the task of controlling and monitoring processes, making it indispensable for system administrators and developers. Without it, overseeing complex applications and services becomes significantly more cumbersome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Supervisor on Amazon Linux 2023 with Pip:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Prerequisites:
&lt;/h3&gt;

&lt;p&gt;Before proceeding, ensure your instance has Python and Pip installed. You can verify their presence by running the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo dnf install python3 python3-pip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Install Supervisor
&lt;/h3&gt;

&lt;p&gt;Once Python and Pip are available, installing Supervisor becomes a breeze. Execute the following command to install Supervisor using Pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo pip install supervisor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Generate Supervisor Configuration File
&lt;/h3&gt;

&lt;p&gt;After installation, generate the default configuration file for Supervisor using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo_supervisord_conf &amp;gt; /etc/supervisor/supervisord.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Customize Configuration (Optional)
&lt;/h3&gt;

&lt;p&gt;Feel free to customize the generated configuration file according to your specific requirements. You can edit &lt;code&gt;/etc/supervisor/supervisord.conf&lt;/code&gt; using your preferred text editor.&lt;/p&gt;

&lt;p&gt;I would suggest to create the &lt;code&gt;conf.d&lt;/code&gt; folder inside the &lt;code&gt;/etc/supervisor/&lt;/code&gt; and modify the &lt;code&gt;supervisord.conf&lt;/code&gt; to include the folders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[include]
files = /etc/supervisor/conf.d/*.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Start Supervisor Service
&lt;/h3&gt;

&lt;p&gt;With Supervisor installed and configured, start the Supervisor service using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo supervisord -c /etc/supervisor/supervisord.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We might want to auto start this supervisor service once reboot or on start server. Create a service called “supervisord.service”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim /usr/lib/systemd/system/supervisord.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and paste this script inside the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Process Monitoring and Control Daemon
After=rc-local.service nss-user-lookup.target

[Service]
Type=forking
ExecStart=/usr/local/bin/supervisord -c /etc/supervisor/supervisord.conf

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might need to reload the daemon as it new script is created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Verify Installation
&lt;/h3&gt;

&lt;p&gt;Confirm that Supervisor is up and running by checking its status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl enable supervisord
sudo systemctl start supervisord
sudo systemctl status supervisord
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F1%2AZxaSQq5YJ2UZKqkWy7ZoVg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1400%2Fformat%3Awebp%2F1%2AZxaSQq5YJ2UZKqkWy7ZoVg.png" alt="Sample"&gt;&lt;/a&gt;&lt;br&gt;
Now you are good to go to create your own application supervisor config.&lt;/p&gt;

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

&lt;p&gt;While Supervisor may not come pre-packaged with Amazon Linux 2023, installing it via Pip is a simple process that enhances your ability to manage processes effectively. By following the steps outlined in this guide, you can equip your Amazon Linux instances with Supervisor, empowering you to streamline process control and monitoring. Whether you’re overseeing intricate applications or managing critical services, Supervisor on Amazon Linux 2023 ensures smooth operation in the ever-evolving landscape of cloud computing.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Thank you for reading! Don’t forget to subscribe to stay informed about the latest updates in system design and e-commerce innovations. Happy designing!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you found this article insightful and want to stay updated with more content on system design and technology trends, be sure to follow me on :-&lt;/p&gt;

&lt;p&gt;Twitter: &lt;a href="https://twitter.com/hafiqdotcom" rel="noopener noreferrer"&gt;https://twitter.com/hafiqdotcom&lt;/a&gt;&lt;br&gt;
LinkedIn: &lt;a href="https://www.linkedin.com/in/hafiq93" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/hafiq93&lt;/a&gt;&lt;br&gt;
Buy Me Coffee: &lt;a href="https://paypal.me/mhi9388" rel="noopener noreferrer"&gt;https://paypal.me/mhi9388&lt;/a&gt; /&lt;br&gt;
&lt;a href="https://buymeacoffee.com/mhitech" rel="noopener noreferrer"&gt;https://buymeacoffee.com/mhitech&lt;/a&gt;&lt;br&gt;
Medium: &lt;a href="https://medium.com/@hafiqiqmal93" rel="noopener noreferrer"&gt;https://medium.com/@hafiqiqmal93&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Laravel Jetstream database session with multiple user table</title>
      <dc:creator>Hafiq Iqmal</dc:creator>
      <pubDate>Mon, 11 Oct 2021 07:10:19 +0000</pubDate>
      <link>https://forem.com/afiqiqmal/laravel-jetstream-database-session-with-multiple-user-table-47ia</link>
      <guid>https://forem.com/afiqiqmal/laravel-jetstream-database-session-with-multiple-user-table-47ia</guid>
      <description>&lt;p&gt;If you are using Jetsream, you might notice that there is a feature called &lt;a href="https://jetstream.laravel.com/2.x/features/browser-sessions.html#browser-sessions" rel="noopener noreferrer"&gt;Browser Sessions&lt;/a&gt;. This feature allow the user to view the browser sessions associated with their account. Plus, the user may revoke other browser sessions other than the one being used by the device they are currently using.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, what is the problem?
&lt;/h3&gt;

&lt;p&gt;The problem is that, when multiple guard authentication happen, session is stored only based on user primary key in &lt;code&gt;user_id&lt;/code&gt; column. Based on component Jetstream &lt;strong&gt;&lt;code&gt;LogoutOtherBrowserSessionsForm&lt;/code&gt;&lt;/strong&gt; , the logic is where if there are 2 guard with same id is stored and one of the guard user click revoke session in Jetstream, both of the session would be deleted. It would be nice if the session table accept polymorphic relationship&lt;/p&gt;

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



&lt;h3&gt;
  
  
  How about the solution?
&lt;/h3&gt;

&lt;p&gt;So, i decide to came out a solution&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a custom session driver to override database session used by Laravel default database session manager&lt;/li&gt;
&lt;li&gt;Alter current session to accept polymorphic relation&lt;/li&gt;
&lt;li&gt;Implement polymorphic relation to &lt;code&gt;LogoutOtherBrowserSessionsForm&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lets get started
&lt;/h2&gt;

&lt;p&gt;If you are not using database driver for session, this article might not for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a custom session driver
&lt;/h3&gt;

&lt;p&gt;So, let’s start by looking at &lt;code&gt;\Illuminate\Session\DatabaseSessionHandler&lt;/code&gt;. You will notice that there is method &lt;code&gt;addUserInformation&lt;/code&gt; to add &lt;code&gt;user_id&lt;/code&gt; to the payload of session table. This is where we can extend this class and override this method to add our polymorphic relation.&lt;/p&gt;

&lt;p&gt;Create a class name as &lt;code&gt;DatabaseSessionHandler&lt;/code&gt; extend from &lt;code&gt;\Illuminate\Session\DatabaseSessionHandler&lt;/code&gt;. Override &lt;code&gt;addUserInformation&lt;/code&gt; and add to the payload with morph column. We might want to keep the parent method to keep the old session driver. Here the full snippet :-&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?php

class DatabaseSessionHandler extends \Illuminate\Session\DatabaseSessionHandler
{
    protected function addUserInformation(&amp;amp;$payload)
    {
        if ($this-&amp;gt;container-&amp;gt;bound(Guard::class)) {
            $payload['authenticable_id'] = $this-&amp;gt;userId();
            $payload['authenticable_type'] = $this-&amp;gt;container-&amp;gt;make(Guard::class)-&amp;gt;user()?-&amp;gt;getMorphClass();
        }

        return parent::addUserInformation($payload);
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Done extending the &lt;code&gt;DatabaseSessionHandler&lt;/code&gt;. Now, registering the &lt;code&gt;DatabaseSessionHandler&lt;/code&gt; is done through a provider, which is set up the same way as the built-in &lt;code&gt;\Illuminate\Session\DatabaseSessionHandler&lt;/code&gt;. Im using name “database2” as a driver name. You may freely change the driver name as you wish&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?php


class SessionServiceProvider extends ServiceProvider
{
    public function boot()
    {
        \Session::extend('database2', function ($app) {
            return new DatabaseSessionHandler(
                $app['db']-&amp;gt;connection($app['config']['session.connection']), 
                $app['config']['session.table'], 
                $app['config']['session.lifetime'], 
                $app
            );
        });
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, the custom session database driver is now registered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create/Alter Session Migration Table
&lt;/h3&gt;

&lt;p&gt;Lets start with by publishing a migration for session table if not exist&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;gt; php artisan session:table


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The content of the session would look like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Schema::create('sessions', function (Blueprint $table) {
    $table-&amp;gt;string('id')-&amp;gt;primary();
    $table-&amp;gt;foreignId('user_id')-&amp;gt;nullable()-&amp;gt;index();
    $table-&amp;gt;string('ip_address', 45)-&amp;gt;nullable();
    $table-&amp;gt;text('user_agent')-&amp;gt;nullable();
    $table-&amp;gt;text('payload');
    $table-&amp;gt;integer('last_activity')-&amp;gt;index();
});


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You would notice this migration doesn’t come with polymorphic relationship. This is where we need to alter the table. Add morphs relation named it as &lt;code&gt;authenticable&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Schema::create('sessions', function (Blueprint $table) {
    $table-&amp;gt;string('id')-&amp;gt;primary();
    $table-&amp;gt;nullableMorphs('authenticable'); // add this

    $table-&amp;gt;foreignId('user_id')-&amp;gt;nullable()-&amp;gt;index();

    $table-&amp;gt;string('ip_address', 45)-&amp;gt;nullable();
    $table-&amp;gt;text('user_agent')-&amp;gt;nullable();
    $table-&amp;gt;text('payload');
    $table-&amp;gt;integer('last_activity')-&amp;gt;index();
});


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I would suggest not to remove the user_id column because we shared session table. In case, you want to revert back to original session database driver, it wont be a problem. Unless you specify another table for new session driver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In case you already have session table&lt;/strong&gt;, you might want to alter the table. Just create another migration to alter the table&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;gt; php artisan make:migration alter_session_table --table=sessions


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You might need to delete all the existing session. Just add DB truncate before migration happen. You may follow like below :-&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?php

class AlterSessionTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        DB::table('sessions')-&amp;gt;truncate();

        Schema::table('sessions', function(Blueprint $table) {
            $table-&amp;gt;after('id', function (Blueprint $table ){
                $table-&amp;gt;nullableMorphs('authenticable');
            });
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('sessions', function(Blueprint $table) {
            $table-&amp;gt;dropMorphs('authenticable');
        });
    }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Change session driver in env file
&lt;/h3&gt;

&lt;p&gt;At this point, you can change session default driver to your new custom session driver.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
QUEUE_CONNECTION=
SESSION_DRIVER=database2
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You may try to login and you will notice in your session driver table, the morph column started to filled in.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Customize LogoutOtherBrowserSessionsForm (Jetstream)
&lt;/h3&gt;

&lt;p&gt;Let’s take a look at Jetstream &lt;code&gt;LogoutOtherBrowserSessionsForm class&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;The red line shows the logic used to display and revoke session in the browser session feature. As mention, the query is not handling multi table or multi guard which cause see session of others. Let’s create new livewire component and extend this class.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;gt; php artisan make:livewire LogoutOtherBrowserSessionsForm


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now alter the file and extend to Jetstream class&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?php

use Laravel\Jetstream\Http\Livewire\LogoutOtherBrowserSessionsForm as BaseLogoutOtherBrowserSessionsForm;

class LogoutOtherBrowserSessionsForm extends BaseLogoutOtherBrowserSessionsForm
{
   //
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Override both method &lt;code&gt;deleteOtherSessionRecords&lt;/code&gt; and &lt;code&gt;getSessionsProperty&lt;/code&gt; to meet the requirement. Adding extra query and fix some logic and it will look like this :-&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?php

namespace App\Http\Livewire;

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Laravel\Jetstream\Http\Livewire\LogoutOtherBrowserSessionsForm as BaseLogoutOtherBrowserSessionsForm;

class LogoutOtherBrowserSessionsForm extends BaseLogoutOtherBrowserSessionsForm
{
    /**
     * Delete the other browser session records from storage.
     *
     * @return void
     */
     protected function deleteOtherSessionRecords()
     {
         if (!Str::contains(config('session.driver'), 'database')) {
             return;
         }

         DB::connection(config('session.connection'))
             -&amp;gt;table(config('session.table', 'sessions'))
             -&amp;gt;where('authenticable_id', Auth::user()-&amp;gt;getAuthIdentifier())
             -&amp;gt;where('authenticable_type', Auth::user()-&amp;gt;getMorphClass())
             -&amp;gt;where('id', '!=', request()-&amp;gt;session()-&amp;gt;getId())
             -&amp;gt;delete();
     }

    /**
     * Get the current sessions.
     *
     * @return \Illuminate\Support\Collection
     */
    public function getSessionsProperty(): \Illuminate\Support\Collection
    {
        if (!Str::contains(config('session.driver'), 'database')) {
            return collect();
        }

        return collect(
            DB::connection(config('session.connection'))
                -&amp;gt;table(config('session.table', 'sessions'))
                -&amp;gt;where('authenticable_id', Auth::user()-&amp;gt;getAuthIdentifier())
                -&amp;gt;where('authenticable_type', Auth::user()-&amp;gt;getMorphClass())
                -&amp;gt;orderBy('last_activity', 'desc')
                -&amp;gt;get()
        )-&amp;gt;map(function ($session) {
            return (object) [
                'agent' =&amp;gt; $this-&amp;gt;createAgent($session),
                'ip_address' =&amp;gt; $session-&amp;gt;ip_address,
                'is_current_device' =&amp;gt; $session-&amp;gt;id === request()-&amp;gt;session()-&amp;gt;getId(),
                'last_active' =&amp;gt; Carbon::createFromTimestamp($session-&amp;gt;last_activity)-&amp;gt;diffForHumans(),
            ];
        });
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Ok now everything is set up.&lt;/p&gt;

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

&lt;p&gt;Once verified, the browser session shows only the one i’m using logged in based on guard. Means that our custom session are working fine. 🤘&lt;/p&gt;

&lt;p&gt;That’s it. Hope its help 😁. Thanks for your time.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://jetstream.laravel.com/2.x/introduction.html" rel="noopener noreferrer"&gt;https://jetstream.laravel.com/2.x/introduction.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>jetstream</category>
      <category>php</category>
    </item>
  </channel>
</rss>
