<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[My bits on dev]]></title><description><![CDATA[PHP/Laravel developer at Code Runway B.V. Proud father of two kids. Tech optimist. Apple and Laravel enthusiast.]]></description><link>https://stefrouschop.nl</link><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 01:46:29 GMT</lastBuildDate><atom:link href="https://stefrouschop.nl/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[When to use Laravel global scopes]]></title><description><![CDATA[Laravel global scopes are great, but I don't see them used a lot. Instead, I see a lot of local scopes being used to achieve the same thing. With proper implementation of global scopes, the code and security would be greatly improved. Let me illustra...]]></description><link>https://stefrouschop.nl/when-to-use-laravel-global-scopes</link><guid isPermaLink="true">https://stefrouschop.nl/when-to-use-laravel-global-scopes</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Stef Rouschop]]></dc:creator><pubDate>Mon, 20 Feb 2023 15:31:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1676906895380/31dc1713-a56a-4afb-8dd3-c5f2279227fe.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Laravel global scopes are great, but I don't see them used a lot. Instead, I see a lot of local scopes being used to achieve the same thing. With proper implementation of global scopes, the code and security would be greatly improved. Let me illustrate this with a simple example.</p>
<h2 id="heading-the-local-scope-way">The local scope way</h2>
<p>In our codebase, we have a model <code>Transaction</code> that stores transactions for our users. If we want to get the transaction for a logged-in user from the database, we could do it like this:</p>
<pre><code class="lang-php">$transactions = Transaction::where(<span class="hljs-string">'user_id'</span>, auth()-&gt;id())-&gt;get();
</code></pre>
<p>Since we would use this a lot throughout the codebase, it would make sense to create a local scope in the <code>Transaction</code> model like this:</p>
<pre><code class="lang-php">
<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Relations</span>\<span class="hljs-title">BelongsTo</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Transaction</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">scopeForLoggedInUser</span>(<span class="hljs-params">$query</span>): <span class="hljs-title">void</span>
    </span>{
        $query-&gt;where(<span class="hljs-string">'user_id'</span>, auth()-&gt;id());
    }
}
</code></pre>
<p>With this local scope, we can make the query like this:</p>
<pre><code class="lang-php">$transactions = Transaction::forLoggedInUser()-&gt;get();
</code></pre>
<p>This is a nice DRY (<strong>D</strong>on't <strong>R</strong>epeat <strong>Y</strong>ourself) refactor that cleans it up a little.</p>
<h2 id="heading-a-question-you-should-ask-yourself">A question you should ask yourself</h2>
<p>Local scopes are awesome, I use them where I can to keep the code DRY. But I learned to ask myself this question when I create a local scope: <strong><em>“Will the majority of the queries for this model use this local scope”.</em></strong></p>
<p>If the answer is no, keep the local scope. It makes a lot of sense to use it.</p>
<h2 id="heading-when-the-answer-is-yes">When the answer is yes</h2>
<p>This would be the point when considering a global scope. A global scope is always applied to all queries of a given model. You can create a global scope simply by creating a class that implements <code>Illuminate\Database\Eloquent\Scope</code>.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Scopes</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Builder</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Scope</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TransactionsForLoggedInUserScope</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Scope</span>
</span>{

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">apply</span>(<span class="hljs-params">Builder $builder, Model $model</span>)
    </span>{
        $builder-&gt;where(<span class="hljs-string">'user_id'</span>, auth()-&gt;id());
    }
}
</code></pre>
<p>After that, you should let your model be aware of the global scope:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Scopes</span>\<span class="hljs-title">TransactionsForLoggedInUserScope</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Transaction</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">protected</span> <span class="hljs-built_in">static</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">booted</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-built_in">static</span>::addGlobalScope(<span class="hljs-keyword">new</span> TransactionsForLoggedInUserScope());
    }
}
</code></pre>
<p>If you would now get all the transactions, it will only return the transactions of the logged-in user.</p>
<pre><code class="lang-php">Transaction::all();
</code></pre>
<h2 id="heading-removing-a-global-scope">Removing a global scope</h2>
<p>There are some scenarios thinkable where you want to create a <code>Transaction</code> query without the global scope applied. For example, in an admin overview or for some global statistic calculations. This can be done by using the <code>withoutGlobalScope</code> method:</p>
<pre><code class="lang-php">Transaction::withoutGlobalScope(TransactionsForLoggedInUserScope::class)-&gt;get();
</code></pre>
<h2 id="heading-anonymous-global-scopes">Anonymous Global Scopes</h2>
<p>There is also a way to create a global scope without the use of an extra file. Instead of pointing to the <code>TransactionsForLoggedInUserScope</code> class, you can include the query like this:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Model</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Transaction</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Model</span>
</span>{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">protected</span> <span class="hljs-built_in">static</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">booted</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-built_in">static</span>::addGlobalScope(<span class="hljs-string">'for_logged_in_users'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Builder $builder</span>) </span>{
            $builder-&gt;where(<span class="hljs-string">'user_id'</span>, auth()-&gt;id());
        });
    }
}
</code></pre>
<p>Personally, I don't like anonymous global scopes since it can bloat your model if the query is a bit complex. To be consistent throughout the entire codebase, I always tend to use the external file global scopes, even if the query is as simple as in our example.</p>
<h2 id="heading-the-downside-of-global-scopes">The downside of global scopes</h2>
<p>I won't lie to you, if you're working on a codebase with global scopes and are not aware of them, you might fall into situations where nothing makes sense anymore. It made me question my career choices one (very bad) day 😂. You will get totally unexpected results when tinkering. If this has happened once, you've learned that it is good to have the <code>withoutGlobalScopes</code> method in your tool belt while working in a codebase you're not quite familiar with.</p>
<h2 id="heading-security">Security</h2>
<p>If the security of your app relies on a global scope, it's dangerous when you or a fellow developer will make changes to it in the future. Imagine in our example that someone would remove or change the global scope. Then all results for a user's transactions would be completely faulty! This is why it's really important to implement tests for global scopes, so this can't happen. A typical (PEST) test for our example would look like this:</p>
<pre><code class="lang-php">it(<span class="hljs-string">"should only get the transactions for the logged-in user"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>)</span>{
    $user = User::factory()-&gt;create();
    $otherUser = User::factory()-&gt;create();

    $transaction_1 = Transaction::factory()-&gt;create([<span class="hljs-string">'user_id'</span> =&gt; $user-&gt;id]);
    $transaction_2 = Transaction::factory()-&gt;create([<span class="hljs-string">'user_id'</span> =&gt; $otherUser-&gt;id]);

    <span class="hljs-keyword">$this</span>-&gt;actingAs($user);
    expect(Transaction::all())-&gt;toHaveCount(<span class="hljs-number">1</span>)
        -&gt;and($transaction_1-&gt;is(Transaction::first()));
});
</code></pre>
]]></content:encoded></item><item><title><![CDATA[How to secure model ID’s in Livewire and why this is important]]></title><description><![CDATA[TL;DR
There is a way to protect your public properties in Livewire 2 in the way it will work in the upcoming Livewire 3. There is a Trait at the end of the article you can use for this.
Why should you protect model ID's in Livewire
Livewire is great!...]]></description><link>https://stefrouschop.nl/how-to-secure-model-ids-in-livewire-and-why-this-is-important</link><guid isPermaLink="true">https://stefrouschop.nl/how-to-secure-model-ids-in-livewire-and-why-this-is-important</guid><category><![CDATA[Livewire]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Stef Rouschop]]></dc:creator><pubDate>Sun, 05 Feb 2023 15:27:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675610462638/143b9372-086d-471e-af4b-d4cd0f22ad52.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-tldr">TL;DR</h3>
<p>There is a way to protect your public properties in Livewire 2 in the way it will work in the upcoming Livewire 3. There is a Trait at the end of the article you can use for this.</p>
<h3 id="heading-why-should-you-protect-model-ids-in-livewire">Why should you protect model ID's in Livewire</h3>
<p>Livewire is great! It provides superpowers to your backend code to also create a reactive frontend without relying heavily on JavaScript. But as with everything, with great power comes great responsibility. I’ve been working with Livewire since version 1 with much enthusiasm. In the years of using it, I’ve developed some best practices for performance and security. In this article, I will focus on what I think is the most important thing in Livewire security.</p>
<p>One caveat in Livewire (version 1 and 2) is that only public properties remain state between Livewire interactions. This shouldn’t be a problem, at least not if you’re aware of how a potential hacker would abuse this. Let’s go over this by a simplified example. We’ll create a very simple Livewire component that will publish a draft blog post.</p>
<p>A different best practice that I need to mention is to only store the model ID and not the entire model in a public property. I know we are allowed to do this, but this will impact performance, but that is out of the scope of this article.</p>
<p>Let’s start by creating this component and see how this can be exploited.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Livewire</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Post</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Livewire</span>\<span class="hljs-title">Component</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Contracts</span>\<span class="hljs-title">View</span>\<span class="hljs-title">View</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">Access</span>\<span class="hljs-title">AuthorizationException</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">Access</span>\<span class="hljs-title">AuthorizesRequests</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EditPostComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">AuthorizesRequests</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> $postId;

    <span class="hljs-keyword">public</span> $state = [
        <span class="hljs-string">'title'</span> =&gt; <span class="hljs-string">''</span>,
        <span class="hljs-string">'body'</span> =&gt; <span class="hljs-string">''</span>,
        <span class="hljs-string">'published'</span> =&gt; <span class="hljs-literal">false</span>,
    ];

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rules</span>(<span class="hljs-params"></span>): <span class="hljs-title">array</span>
    </span>{
        <span class="hljs-keyword">return</span> [
            <span class="hljs-string">'state.title'</span> =&gt; <span class="hljs-string">'required|string|max:255'</span>,
            <span class="hljs-string">'state.body'</span> =&gt; <span class="hljs-string">'required|string|max:5000'</span>,
            <span class="hljs-string">'state.published'</span> =&gt; <span class="hljs-string">'boolean'</span>,
        ];
    }

    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@throws</span> AuthorizationException
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mount</span>(<span class="hljs-params">Post $post</span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;authorize(<span class="hljs-string">'update'</span>, $post);

        <span class="hljs-keyword">$this</span>-&gt;postId = $post-&gt;id;
        <span class="hljs-keyword">$this</span>-&gt;state = $post-&gt;only(<span class="hljs-string">'title'</span>, <span class="hljs-string">'body'</span>, <span class="hljs-string">'published'</span>);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params"></span>): <span class="hljs-title">View</span>
    </span>{
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'livewire.edit-post-component'</span>);
    }

    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@throws</span> AuthorizationException
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">save</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;validate();
        $post = Post::findOrFail(<span class="hljs-keyword">$this</span>-&gt;postId);
        <span class="hljs-keyword">$this</span>-&gt;authorize(<span class="hljs-string">'update'</span>, $post);

        $post-&gt;title = <span class="hljs-keyword">$this</span>-&gt;state[<span class="hljs-string">'title'</span>];
        $post-&gt;body = <span class="hljs-keyword">$this</span>-&gt;state[<span class="hljs-string">'body'</span>];
        $post-&gt;save();
    }
}
</code></pre>
<p>Let's see what the component does. When the component is loaded:</p>
<ul>
<li><p>We will check if the user is allowed to update this post with <code>$this-&gt;authorize('update', $post);</code> (the policy to check this is out of the scope of this article).</p>
</li>
<li><p>After all checks out, we will hydrate the state and fill the <code>$postId</code>.</p>
</li>
</ul>
<p>When a user makes changes and calls <code>save</code>:</p>
<ul>
<li><p>We'll get the Post model from the database.</p>
</li>
<li><p>Check again if the user is allowed to edit this Post and save the changes.</p>
</li>
</ul>
<p>For clarity, I've left out all the code for the frontend in this article.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675601108515/7709044c-83c7-475b-8d72-288b6fdc54fe.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-lets-hack-this-component">Let's hack this component!</h3>
<p>There are a couple of ways how you can change a public Livewire property. Here is a simple one. Firstly, go into the Chrome devtools and search in the source code where you can find <code>wire:id=xxxxxxx</code> and select it. After that, you can go to the console and type <code>$0.__livewire.data</code>, you can see that the result provides you all the public properties. When you type <code>$0.__livewire.$wire.postId = 4</code> you've successfully changed the <code>$postId</code>. If you would now make changes to any of the fields, it will be saved to the Post id of 4. It's that easy!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675603359622/9eb138b1-04ed-4899-9b13-59a1ebb7335d.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-how-to-protect-your-component">How to protect your component</h3>
<p>There are multiple ways how we can protect our component. The first one is really easy: Wait until Livewire 3 is released, since we will then get the possibility to protect public properties by adding the <code>/** @locked */</code> annotation. But at this point, we're unaware when Livewire 3 will be released.</p>
<p>I have to be honest, I used to protect my components by encrypting and decrypting the ID within the component. When someone would try to change the value and the new value was not decryptable, it would throw an error that I would catch. But later on I found a great different approach by Mark Snape in this video: <a target="_blank" href="https://www.youtube.com/watch?v=bA1dMbUiwuA">https://www.youtube.com/watch?v=bA1dMbUiwuA</a>. Mark listens in the Livewire component to see if someone would update the <code>PostId</code> and then throw an error. I think this is the best approach possible!</p>
<p>I tried to take this a little further and created a Trait, so we can use it easily in all our components and also use the <code>/** @locked */</code> annotation, so it will also be compatible with Livewire 3 💪🏻. After upgrading to Livewire 3, we only need to remove the Trait, and we're done.</p>
<p>In our component, we'll just need to add the annotation and import our Trait.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Livewire</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Post</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Livewire</span>\<span class="hljs-title">Component</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Contracts</span>\<span class="hljs-title">View</span>\<span class="hljs-title">View</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Traits</span>\<span class="hljs-title">WithLockedPublicPropertiesTrait</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">Access</span>\<span class="hljs-title">AuthorizationException</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Foundation</span>\<span class="hljs-title">Auth</span>\<span class="hljs-title">Access</span>\<span class="hljs-title">AuthorizesRequests</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EditPostComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">AuthorizesRequests</span>, <span class="hljs-title">WithLockedPublicPropertiesTrait</span>;

    <span class="hljs-comment">/** <span class="hljs-doctag">@locked</span>  */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> $postId;

    ...
}
</code></pre>
<p>And here is the trait that we can use for this.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Traits</span>\<span class="hljs-title">Livewire</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Str</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">ReflectionException</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Exceptions</span>\<span class="hljs-title">LockedPublicPropertyTamperException</span>;

<span class="hljs-keyword">trait</span> WithLockedPublicPropertiesTrait
{
    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@throws</span> LockedPublicPropertyTamperException|ReflectionException
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatingWithLockedPublicPropertiesTrait</span>(<span class="hljs-params">$name</span>): <span class="hljs-title">void</span>
    </span>{
        $propertyName = Str::of($name)-&gt;explode(<span class="hljs-string">'.'</span>)-&gt;first();
        $reflectionProperty = <span class="hljs-keyword">new</span> \ReflectionProperty(<span class="hljs-keyword">$this</span>, $propertyName);
        <span class="hljs-keyword">if</span> (Str::of($reflectionProperty-&gt;getDocComment())-&gt;contains(<span class="hljs-string">'@locked'</span>)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> LockedPublicPropertyTamperException(<span class="hljs-string">"You are not allowed to tamper with the protected property <span class="hljs-subst">{$propertyName}</span>"</span>);
        }
    }
}
</code></pre>
<p>The Trait uses <code>ReflectionProperty</code> to see if <code>@locked</code> is available on the property that is about to be changed. If so, it will throw an <code>LockedPublicPropertyTamperException</code> exception. You need to create this exception first, of course. Since I always store all the state in a public <code>$state</code> array, the updated Livewire model name will be something like <code>state.title</code>, since we only need the first part of this dot-notation, we strip the unneeded part in the first line of the method.</p>
<p>You can follow me on twitter to stay up to date with Laravel and Livewire articles <a target="_blank" href="https://twitter.com/stef_r">https://twitter.com/stef_r</a></p>
]]></content:encoded></item><item><title><![CDATA[Make changes to the database during a Livewire Pest chain]]></title><description><![CDATA[Tests
Tests are great! They're probably the reason why you're still able to sleep at night after someone decided you had to make some last minute changes. Also, they really speed up your development. Imagine working on a piece of code that has a lot ...]]></description><link>https://stefrouschop.nl/make-changes-to-the-database-during-a-livewire-pest-chain</link><guid isPermaLink="true">https://stefrouschop.nl/make-changes-to-the-database-during-a-livewire-pest-chain</guid><category><![CDATA[Livewire]]></category><category><![CDATA[pestphp]]></category><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Stef Rouschop]]></dc:creator><pubDate>Sat, 05 Nov 2022 16:16:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667664834149/88jVTs1jj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-tests">Tests</h2>
<p>Tests are great! They're probably the reason why you're still able to sleep at night after someone decided you had to make some last minute changes. Also, they really speed up your development. Imagine working on a piece of code that has a lot of dynamics based on variables, if we didn't have tests it would be really hard to work on it by having to manually click around in the browser in to check if the code still works after every change. We can do that in milliseconds with tests! Anyway, tests have made me a better and a more confident developer.</p>
<h2 id="heading-pest">Pest</h2>
<p><a target="_blank" href="https://pestphp.com">Pest</a> is awesome! It makes the tests so much more readable. It allows you to still use PHPUnit and Pest syntax at the same time, so you can slightly shift over to Pest at your own pace. Pest makes your tests almost read like a nice story. That comes in handy when get back to a codebase after a few months or have to work on tests that someone else created.</p>
<h2 id="heading-make-changes-in-the-background-during-the-pest-chain">Make changes in the background during the pest chain</h2>
<p>Livewire Pest tests can look like a nice chain of actions and assertions on the Livewire component, which I fancy a lot. However, sometimes you need to make changes to the database or assert on a model (that is not on the Livewire component) during the chain at some point. There are a few ways how we can handle that.</p>
<p>I'll illustrate this with a very simple example. Imagine if we have an <code>Order</code> model in Laravel and an <code>OrderComponent</code> Livewire component. We have a refresh button that would call our <code>refreshOrderCount()</code> public method, and this would update the <code>$orderCount</code> public property. Simple stuff!</p>
<p>Here is the component:</p>
<pre><code>&lt;?php

namespace App\Http\Livewire;

use App\Models\Order;
use Livewire\Component;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderDashboardComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span>
</span>{
    public int $orderCount = <span class="hljs-number">0</span>;

    public <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mount</span>(<span class="hljs-params"></span>)
    </span>{
        $this-&gt;orderCount = Order::count();
    }

    public <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'livewire.order-dashboard-component'</span>);
    }

    public <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">refreshOrderCount</span>(<span class="hljs-params"></span>)
    </span>{
        $this-&gt;orderCount = Order::count();
    }
}
</code></pre><p>If we want to write a test for this behaviour, we would have to add a new order during the Pest Livewire test chain. </p>
<pre><code>it(<span class="hljs-string">'should update the orderCount'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    livewire(OrderDashboardComponent::<span class="hljs-class"><span class="hljs-keyword">class</span>)
        -&gt;<span class="hljs-title">assertSet</span>('<span class="hljs-title">orderCount</span>', 0)
        -&gt;<span class="hljs-title">call</span>('<span class="hljs-title">refreshOrderCount</span>')
                // <span class="hljs-title">Here</span> <span class="hljs-title">we</span> <span class="hljs-title">should</span> <span class="hljs-title">add</span> <span class="hljs-title">an</span> <span class="hljs-title">order</span> <span class="hljs-title">to</span> <span class="hljs-title">the</span> <span class="hljs-title">database</span>
        -&gt;<span class="hljs-title">assertSet</span>('<span class="hljs-title">orderCount</span>', 1)</span>;
}); <span class="hljs-comment">// (This test would fail)</span>
</code></pre><p>If we started with the test above, we should add the new order right after the <code>-&gt;assertSet('orderCount', 0)</code> assertion. Unfortunately, we can't do this in this Pest chain. So we would have to do something like this:</p>
<pre><code>it(<span class="hljs-string">'should update the orderCount'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    $component = livewire(OrderDashboardComponent::<span class="hljs-class"><span class="hljs-keyword">class</span>)</span>;

    $component-&gt;assertSet(<span class="hljs-string">'orderCount'</span>, <span class="hljs-number">0</span>);

    Order::factory()-&gt;create();

    $component-&gt;call(<span class="hljs-string">'refreshOrderCount'</span>)
        -&gt;assertSet(<span class="hljs-string">'orderCount'</span>, <span class="hljs-number">1</span>);
});
</code></pre><p>The above code will work, but it looks kind of messy to me, so I came up with the idea to use <code>tap()</code> to make this work from within the chain.</p>
<pre><code>it(<span class="hljs-string">'should update the orderCount'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    livewire(OrderDashboardComponent::<span class="hljs-class"><span class="hljs-keyword">class</span>)
        -&gt;<span class="hljs-title">assertSet</span>('<span class="hljs-title">orderCount</span>', 0)
        -&gt;<span class="hljs-title">tap</span>(<span class="hljs-title">function</span>()</span>{
            <span class="hljs-attr">Order</span>::factory()-&gt;create();
        })
        -&gt;call(<span class="hljs-string">'refreshOrderCount'</span>)
        -&gt;assertSet(<span class="hljs-string">'orderCount'</span>, <span class="hljs-number">1</span>);
});
</code></pre><p>Or even better:</p>
<pre><code>it(<span class="hljs-string">'should update the orderCount'</span>)
    -&gt;livewire(OrderDashboardComponent::<span class="hljs-class"><span class="hljs-keyword">class</span>)
    -&gt;<span class="hljs-title">assertSet</span>('<span class="hljs-title">orderCount</span>', 0)
    -&gt;<span class="hljs-title">tap</span>(<span class="hljs-title">function</span>()</span>{
        <span class="hljs-attr">Order</span>::factory()-&gt;create();
    })
    -&gt;call(<span class="hljs-string">'refreshOrderCount'</span>)
    -&gt;assertSet(<span class="hljs-string">'orderCount'</span>, <span class="hljs-number">1</span>);
</code></pre><p>Great, this looks so much better to me! What do you think? You can also use tap to make assertions on a model that is not available in the Livewire component as a property.</p>
]]></content:encoded></item><item><title><![CDATA[How to keep line breaks in user input without unescaping it in Laravel]]></title><description><![CDATA[The problem
I recently got a question from a client about disappearing line breaks. It was a valid question since the text came from a textarea input that was echoed out on a page. The type of content was really simple and had no need for markup, so ...]]></description><link>https://stefrouschop.nl/how-to-keep-line-breaks-in-user-input-without-unescaping-it-in-laravel</link><guid isPermaLink="true">https://stefrouschop.nl/how-to-keep-line-breaks-in-user-input-without-unescaping-it-in-laravel</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Stef Rouschop]]></dc:creator><pubDate>Sat, 28 May 2022 10:13:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/1zR6CeWb4uM/upload/v1653731418398/yr0YQc0Ef.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-problem">The problem</h2>
<p>I recently got a question from a client about disappearing line breaks. It was a valid question since the text came from a textarea input that was echoed out on a page. The type of content was really simple and had no need for markup, so no WYSIWYG or Markdown editor was necessary. However, line breaks were really important in this case.</p>
<p>PHP offers the <code>nl2br()</code> function that has been around since PHP 4. This function will return a string with <code>&lt;br /&gt;</code> or <code>&lt;br&gt;</code> inserted before all newlines (\r\n, \n\r, \n and \r). Just what we need! Let's put it to work with this variable for this example:</p>
<pre><code class="lang-php">$description = <span class="hljs-string">'This is line one\nThis is line 2\nThis is line 3'</span>;
</code></pre>
<pre><code><span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
    {{ nl2br($description) }}
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
</code></pre><p>Nope! That will output: <code>This is line one&lt;br /&gt; This is line 2&lt;br /&gt;This is line 3</code> because we've been using the <code>{{ }}</code> statements. Blade <code>{{ }}</code> statements are automatically sent through PHP's <code>htmlspecialchars</code> function to prevent XSS attacks. By using <code>{!! !!}</code> statements, we can completely skip the unescaping and get the result we want, right?
<strong>No, that is a terrible idea!</strong> Our code would then be vulnerable to XSS attacks.</p>
<p>Here is a really simple rule of thumb: if you are in need of using the <code>{!! !!}</code> statement, take a step back and ask yourself: Where does the content come from? 
If the content comes from some kind of form or other user input, you should <strong>NEVER</strong> use it to protect yourself against XSS attacks.
There are plenty of valid reasons for using <code>{!! !!}</code> statements, but not in our case. </p>
<h2 id="heading-the-solution">The solution</h2>
<p>So what can we do? Well, in my knowledge there are two simple solutions here:</p>
<ul>
<li>Make use of CSS</li>
<li>Make use of PHP</li>
</ul>
<h3 id="heading-solve-it-with-css">Solve it with CSS</h3>
<p>The CSS solution is incredibly simple. We can make use of the <code>white-space</code> CSS property. It is very safe to use since it's supported for a very long time by all major browsers.</p>
<pre><code><span class="hljs-operator">&lt;</span>div style<span class="hljs-operator">=</span><span class="hljs-string">"white-space: pre-line"</span><span class="hljs-operator">&gt;</span>
    {{ $description }}
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
</code></pre><h3 id="heading-solve-it-with-php">Solve it with PHP</h3>
<p>If you're not able to use the CSS solution for whatever reason, there’s always the good old PHP to the rescue. Writing PHP makes me happy, so let's get started!
We could explode on the line breaks and create an array of single lines. Then we can iterate over them and output them with the safe <code>{{ }}</code> statements. To make sure we explode on the line breaks, we can make use of the <code>PHP_EOL</code> constant.</p>
<pre><code>@<span class="hljs-keyword">foreach</span>(explode(PHP_EOL, $description) <span class="hljs-keyword">as</span> $line )
    {{ $line }} &lt;br&gt;
@<span class="hljs-keyword">endforeach</span>
</code></pre><h3 id="heading-bonus-solve-it-in-an-anonymous-blade-component">Bonus: solve it in an anonymous Blade component</h3>
<p>I love Blade components, they clean up the view files and make it so much better readable and prevent unneeded repetition. </p>
<pre><code>\\ Blade component
@props([<span class="hljs-string">'content'</span>])

@foreach(explode(PHP_EOL, $content) <span class="hljs-keyword">as</span> $line)
    {{ $line }} <span class="hljs-operator">&lt;</span>br<span class="hljs-operator">&gt;</span>
@endforeach


\\ Usage
<span class="hljs-operator">&lt;</span>x<span class="hljs-operator">-</span>safe<span class="hljs-operator">-</span>line<span class="hljs-operator">-</span>breaks :content<span class="hljs-operator">=</span><span class="hljs-string">"$description"</span><span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
</code></pre>]]></content:encoded></item><item><title><![CDATA[On-demand Xdebug with PHPUnit and Laravel in PhpStorm]]></title><description><![CDATA[On-demand Xdebug with PHPUnit and Laravel in PhpStorm
TLDR;
This guide will make you install Xdebug on your Mac without the performance downsides, but still being able to use all the Xdebug goodnesses. 
Most Xdebug setups only allow you to use it thr...]]></description><link>https://stefrouschop.nl/on-demand-xdebug-with-phpunit-and-laravel-in-phpstorm</link><guid isPermaLink="true">https://stefrouschop.nl/on-demand-xdebug-with-phpunit-and-laravel-in-phpstorm</guid><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><category><![CDATA[PhpStorm]]></category><dc:creator><![CDATA[Stef Rouschop]]></dc:creator><pubDate>Wed, 21 Nov 2018 19:02:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1559491317656/aH3hUpG5O.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="on-demand-xdebug-with-phpunit-and-laravel-in-phpstorm">On-demand Xdebug with PHPUnit and Laravel in PhpStorm</h1>
<h2 id="tldr-">TLDR;</h2>
<p>This guide will make you install Xdebug on your Mac without the performance downsides, but still being able to use all the Xdebug goodnesses. 
Most Xdebug setups only allow you to use it through a web browser plugin. This is not helpful when you’re following a TDD approach. So we’ll get it to work right from your tests in PhpStorm, no browser needed!</p>
<h3 id="having-tests-is-awesome-">Having tests is awesome!</h3>
<p>I’m a huge fan of tests in Laravel and try to develop as much as possible using the TDD approach. When a project grows over the years, your tests are becoming like a guard that will tell when you messed up functionality by adding or changing some code later on. This gives a peace of mind and has saved my bacon many times already.</p>
<p>PhpStorm has a great GUI for running PHPUnit tests where you’re able to click directly on the links to the scripts where a test fails. I love this workflow and I’ve never used <code>phpunit</code> on the command line since. However, when your tests fail, it can become somewhat cumbersome to figure out what is going on. The first thing I always do is add <code>-&gt;dump()</code> to the request, that will point me in the right direction most of the time. But there are cases where you really need to dig into the code. This is where I previously placed some <code>dd()</code> in the code and then tried to follow the steps. Raise your hand if you’ve been there 🤚!</p>
<p>Xdebug is a wonderful tool that can help you with that, but I previously used it a long time ago with a browser plugin. Since PHPUnit gets really slow when Xdebug is installed and it also impacts the composer performance, I removed it from my system and never used it since. Until I recently discovered “on-demand Xdebug” in PhpStorm! <strong>This will not enable Xdebug by default, and will only use it when you need to.</strong>
It’s really simple to set up and I love using it! You can also use it directly in your tests, so you don’t have to recreate the situation in your browser. I’ll show you how to install and use it in this article.</p>
<h3 id="install">Install</h3>
<p>In our company we only use Mac’s, I have no idea how to set this up on Windows, so you’ll have to google around if that’s your case. For Linux, it will more or less be the same (except for PHP installation) I think.</p>
<p>I’m assuming you’ve been using <a target='_blank' rel='noopener noreferrer'  href="https://brew.sh/">Homebrew</a> to install the latest version of PHP. It’s as simple as <code>brew install php</code>.</p>
<p>Extensions as Xdebug have recently been removed from Homebrew and need to be installed using PECL. Luckily that is also really easy!
<code>pecl install xdebug</code>
The install will add a reference to Xdebug in your <code>php.ini</code> file so it will be loaded by default. <strong>We don’t want that!</strong> So we’ll remove this reference. Here’s how to do it:
On your command line enter: <code>php --ini</code> this will give you the path of the <code>php.ini</code> file that is used on your system.</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*bpkBjrbdRmUna2VoillPwg.png" alt=""></p>
<p>Open the file in an editor of your choice, or use the default editor for this filetype. In the case of our example, we can do this with the command 
<code>open /usr/local/etc/php/7.2/php.ini</code>. This will open the <code>php.ini</code> file. 
We can see the reference added by the Xdebug installer on line 1: <code>zend_extension=”xdebug.so”</code>. We need to remove this line so Xdebug is not loaded by default anymore. There, all done 🥳!</p>
<h3 id="phpstorm-setup">PhpStorm setup</h3>
<p>I’ll assume you have a setup for your TDD workflow in PhpStorm. The purpose of this guide is only to extend it with Xdebug. If you don’t have it, here is an old but still relevant video from Laracasts.com: <a target='_blank' rel='noopener noreferrer'  href="https://laracasts.com/series/how-to-be-awesome-in-phpstorm/episodes/22">Be Awesome in PHPStorm: Testing in PHPStorm</a>.</p>
<p>We will need to tell PhpStorm where Xdebug is located for using it on demand. In the settings navigate to <strong>Languages and Frameworks</strong> and then <strong>PHP</strong>. On the <strong>CLI Interpreter</strong> click on the <code>…</code> button like in the screenshot.</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*X90cuOhcBAckv7CCN9Uo_Q.png" alt=""></p>
<p>Now we can specify where Xdebug is located on your system. In our example this is in <code>/usr/local/lib/php/pecl/20170718/xdebug.so</code>, but this path depends on the version and can be different on your system. So be sure to click on the folder icon to navigate through your system and find it.</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*cnKJtOhRjZk9slqCUtUooA.png" alt=""></p>
<p>When the path is correct and you hit the refresh button, you will see the Xdebug version that was found by PhpStorm.</p>
<h3 id="using-it">Using it</h3>
<p>For sake of simplicity, we’ll create a simple test where we assert the content of two routes. The routes we’re about to test will just return some variables, nothing fancy going on here, you’ll get the idea. Laravel makes creating tests really easy with the artisan helper <code>php artisan make:test TwoRoutesTest</code>.</p>
<p>In the image below you can see how the tests are run when you’ll hit the green “play” button, nothing new here. Did you also know about the SHIFT + CTRL + R shortcut? Place your cursor in a test and hit the shortcut and only this single test will run. When you place the cursor outside of a test, all the tests of this test-file will run. I really love this shortcut 😍!</p>
<p><img src="https://cdn-images-1.medium.com/max/2400/1*7BnwSNQLE1RWPge3wCm_dQ.png" alt=""></p>
<h3 id="now-let-s-use-xdebug-">Now, let&#39;s use Xdebug!</h3>
<p>Go to your code and place some breakpoints on the left side of the lines. They will be marked with red circles when you click them. This time, don’t click the green “play” button, but the red bug on the right side of it.</p>
<p>The test will stop on the first breakpoint. You’ll get a nice overview of all (global) variables in the debug screen. When you press the green “play / pause” button on the left side of the screen you’ll go to the next breakpoint. You can really dig in the code now to figure out where your bugs happen. I won’t go into detail about all the PhpStorm/Xdebug functionality. That might be a completely different blog post.</p>
<p><img src="https://cdn-images-1.medium.com/max/2400/1*DbDJsz_1VPuM9XoVabgOrw.png" alt=""></p>
<h3 id="happy-debugging">Happy debugging</h3>
<p>I hope this will enhance your debugging workflow as much as it did for me!</p>
]]></content:encoded></item><item><title><![CDATA[Why a locale sometimes is not enough in Laravel.]]></title><description><![CDATA[It’s always important to let your users feel welcome and appreciated. When you‘re creating apps that need to be scaled globally, that can be a challenge sometimes. Lucky for us, Laravel introduced “Using Translation Strings As Keys” a while ago which...]]></description><link>https://stefrouschop.nl/why-a-locale-is-sometimes-not-enough-in-laravel</link><guid isPermaLink="true">https://stefrouschop.nl/why-a-locale-is-sometimes-not-enough-in-laravel</guid><category><![CDATA[Laravel]]></category><category><![CDATA[translation]]></category><category><![CDATA[localization]]></category><dc:creator><![CDATA[Stef Rouschop]]></dc:creator><pubDate>Wed, 02 May 2018 18:03:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1559462651730/RPlMar-19.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It’s always important to let your users feel welcome and appreciated. When you‘re creating apps that need to be scaled globally, that can be a challenge sometimes. Lucky for us, Laravel introduced “Using Translation Strings As Keys” a while ago which makes our code readable again in opposition to the “short key” translations we had to use before.</p>
<p>But still, there are a few challenges when it comes to localization. Translation is not the only concern. In this post, I will try to explain and also give the solution we’re using at Involved Group (we made this package public available). I’ll try to illustrate with some examples of a dashboard app. Typically these kinds of apps don’t use localization preferences in the URL’s and need some other input to set the Laravel <code>locale</code> settings.</p>
<h3 id="challenge-1-country-language-">Challenge 1: ‘Country’ !== ‘Language’</h3>
<p>Let’s tell you our story how this ball got rolling in our development. In Belgium, there are two spoken languages: French and Dutch (Flemish). We were serving many languages in our app, the Belgium users were able to select either the Dutch or the French flag in the language selector. Problem solved, right? 
No! Our Belgium users complained that there was a Dutch flag on their account while they were Belgian users. Remember that our goal as developers should be to make our users feel welcome and appreciated? These clients really had a fair point.
How could we solve this through out-of-the-box Laravel? We could create two more translation .json files like <code>nl-BE.json</code> and <code>fr-BE.json</code> and then set the Laravel Locale to either <code>_nl-BE_</code> or <code>fr-BE</code>. The downside of this approach is that we need to have these .json files created and translated while the content will be exactly the same as the <code>nl.json</code> and <code>fr.json</code> files. This is will result in maintenance mess and is not an option for us.</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*p1y20XjpVOdbZOTNbDTK2Q.png" alt=""></p>
<h3 id="challenge-2-date-formats-are-defined-by-the-country-not-the-language-">Challenge 2: Date formats are defined by the country, not the language.</h3>
<p>We’ve been using the awesome <a target='_blank' rel='noopener noreferrer'  href="https://github.com/jenssegers/date">Date</a> package in our apps. It’s always one of the first dependencies we’re adding for almost every new project. It is like a translation wrapper around Carbon, if you’ve never used it, give it a try! One of the problems we encounter is that we now have the ability to translate the date, but not automatically format the date according to the country’s preferences.</p>
<h4 id="example">Example</h4>
<p>We’ve established the Belgian/Dutch differences already, but let’s take this a step further. In The Netherlands, the date will be typically written like <code>d-m-Y</code>, in Belgium the format <code>d/m/Y</code> is more common and in Germany <code>d.m.Y</code>. Small difference, but it’s just the small things that matter to make a user feel comfortable with the app he or she is using.
A bigger difference is when we would like to spell out the date in full text. In the Netherlands this would default to <code>l j F Y</code> (dinsdag 24 april 2018) while in the US this would be <code>l F jS Y</code> (Tuesday April 24th 2018).</p>
<h3 id="the-solution">The solution</h3>
<p>We’ve created <a target='_blank' rel='noopener noreferrer'  href="https://github.com/InvolvedGroup/laravel-lang-country">a package</a> that can help you. We’ll not go too much in detail, you can read the rest on the <a target='_blank' rel='noopener noreferrer'  href="https://github.com/InvolvedGroup/laravel-lang-country">github page</a> or in the code.</p>
<p>A middleware for all page visits will be set. When no user (with lang_country preference) is detected, we’ll try to make a perfect match between the browser language preferences and the allowed language/countries in the config file. The lang_country variable will be stored in a session.</p>
<p>The user can change the lang_country variable through the language/country selector (the package will provide a nice helper for this).</p>
<p>The lang_country variable will match a .json file in the package which stores all the applicable settings for this lang_country. The middleware will set the right locale for Laravel for the translation and also the right locale for the Date package.</p>
<p>You’ll be responsible for the right language .json files in the /resources/lang directory. But you’re able to create different files for certain language/country combinations if you need this. We’ll make a smart check on that. So if your lang_country variable is <code>es-CO</code> we will check if this file is present: <code>/resources/lang/es-CO.json</code>. If so, we’ll set the Laravel Locale to <code>es-CO</code> so this file is used. If not, we’ll set the Laravel Locale to ‘<strong><em>es’</em></strong> so the <code>/resources/lang/es.json</code> is used.</p>
<p>When we need nice date format string we can use:</p>
<pre><code>\LangCountry::dateWordsWithDay($blog-&gt;post-&gt;created_at);
</code></pre><p>in our code. Now, every user will see a date format which is comfortable for them.</p>
<p>The package does more:</p>
<ul>
<li>We’ll store the lang_locale preference in the User record of the database if you want this.</li>
<li>It provides a helper to make a simple language/country switcher dropdown.
The LangCountry Facade has a lot of helpers, check it out!</li>
</ul>
<p>I’m pretty sure your opinion about this can differ, that’s cool! But if you’re on the same page as us, take a look at our package and save yourself some time!</p>
]]></content:encoded></item></channel></rss>