<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Adithya Srinivasan's blog on PHP, Laravel and more]]></title><description><![CDATA[I'm a programmer and I write about solutions to problems that I face on a daily basis | PHP, Laravel developer in Berlin, Germany]]></description><link>https://adithya.dev/</link><image><url>https://adithya.dev/favicon.png</url><title>Adithya Srinivasan&apos;s blog on PHP, Laravel and more</title><link>https://adithya.dev/</link></image><generator>Ghost 5.33</generator><lastBuildDate>Fri, 23 Feb 2024 07:38:24 GMT</lastBuildDate><atom:link href="https://adithya.dev/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Importing data faster with MySQL]]></title><description><![CDATA[Using LOAD DATA LOCAL to speed up data imports in MySQL instead of using MySQL workbench]]></description><link>https://adithya.dev/importing-data-faster-with-mysql/</link><guid isPermaLink="false">6460ae20afd7f22e4e66edb8</guid><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sun, 14 May 2023 09:53:58 GMT</pubDate><content:encoded><![CDATA[<p>I use <a href="https://www.mysql.com/products/workbench/">MySQL Workbench</a> as GUI for connecting to my MySQL servers. It is notoriously slow when you want to import data. I recently tried to do this for a <a href="https://www.arbeitnow.com/blog/schengen-visa-best-country-to-apply">Schengen Visa data set</a>. Often takes minutes to load data of even several 100 KBs.</p><p>To counter this, I use <code>LOAD DATA</code> directly via the command line. I logged in with the command line using <code>mysql -u root</code> and selected the database. I ran:</p><p><code>mysql&gt; LOAD DATA INFILE &apos;/data.csv&apos; INTO TABLE data;</code></p><p>I got an error: </p><p><code>ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement</code></p><p>Then I ran it with LOCAL and series of errors &amp; solutions I tried.</p><pre><code>mysql&gt; LOAD DATA LOCAL INFILE &apos;/data.csv&apos; INTO TABLE data;
ERROR 3948 (42000): Loading local data is disabled; this must be enabled on both the client and server sides
mysql&gt; set global local_infile=true;
mysql&gt; LOAD DATA LOCAL INFILE &apos;/data.csv&apos; INTO TABLE data;
ERROR 2068 (HY000): LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.
</code></pre><p>This meant that I had to change how I logged in to the shell.</p><p><code>mysql -u root &#xA0;--local_infile=1</code></p><p>Then the data import worked successfully (and FAST!)</p><pre><code>mysql&gt; LOAD DATA LOCAL INFILE &apos;/data.csv&apos; INTO TABLE data;
Query OK, 1767 rows affected, 8835 warnings (0.07 sec)</code></pre><p>Now the CSV format was not being considered, so I had to split up the columns.</p><pre><code>LOAD DATA LOCAL INFILE &apos;/data.csv&apos; INTO TABLE data FIELDS TERMINATED BY &apos;,&apos; ENCLOSED BY &apos;&quot;&apos; LINES TERMINATED BY &apos;\r\n&apos; IGNORE 1 LINES;</code></pre>]]></content:encoded></item><item><title><![CDATA[PHP & Docker: Address already in use]]></title><description><![CDATA[How to fix PHP & Docker: Address already in use error]]></description><link>https://adithya.dev/php-docker-address-already-in-use/</link><guid isPermaLink="false">642fde84afd7f22e4e66ed98</guid><category><![CDATA[php]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Fri, 07 Apr 2023 09:16:22 GMT</pubDate><content:encoded><![CDATA[<p>I use Docker and Docker compose to run my Laravel setup locally on Windows using WSL2. One fine day, I got this error below and my docker containers would not work.</p><pre><code>2023-04-07 10:48:49 [07-Apr-2023 08:48:49] NOTICE: Failed implicitly binding to ::, retrying with 0.0.0.0
2023-04-07 10:48:49 [07-Apr-2023 08:48:49] NOTICE: Failed implicitly binding to ::, retrying with 0.0.0.0
2023-04-07 10:48:49 [07-Apr-2023 08:48:49] ERROR: unable to bind listening socket for address &apos;9000&apos;: Address already in use (98)
2023-04-07 10:48:49 [07-Apr-2023 08:48:49] ERROR: unable to bind listening socket for address &apos;9000&apos;: Address already in use (98)
2023-04-07 10:48:49 [07-Apr-2023 08:48:49] ERROR: FPM initialization failed
2023-04-07 10:48:49 [07-Apr-2023 08:48:49] ERROR: FPM initialization failed</code></pre><p>To identify the cause, I ran a command on Powershell to find which process is running on this port.</p><pre><code> Get-Process -Id (Get-NetTCPConnection -LocalPort 9000).OwningProcess</code></pre><p>Which gave me this result</p><pre><code>
Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
   1926     168  3334352    1776924     821.08  14252   1 phpstorm64</code></pre><p>On looking up online, turns out PHPStorm runs its own debugging server on port 9000 by default. I went to settings, searched for <code>built in server</code> and changed the port number. This issue could have been caused by the latest upgrade to PHPStorm 2023.1.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adithya.dev/content/images/2023/04/image.png" class="kg-image" alt loading="lazy" width="1359" height="788" srcset="https://adithya.dev/content/images/size/w600/2023/04/image.png 600w, https://adithya.dev/content/images/size/w1000/2023/04/image.png 1000w, https://adithya.dev/content/images/2023/04/image.png 1359w" sizes="(min-width: 720px) 720px"><figcaption>How to change the default port for PHP Built-in Server in PHPStorm</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[How to format code with Laravel Pint automatically on Git Commit]]></title><description><![CDATA[Automatic code format with Laravel Pint]]></description><link>https://adithya.dev/format-code-with-laravel-pint-on-commiting/</link><guid isPermaLink="false">64073b5aafd7f22e4e66ed54</guid><category><![CDATA[laravel]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Wed, 01 Mar 2023 13:36:00 GMT</pubDate><content:encoded><![CDATA[<p>I have been using <a href="https://laravel.com/docs/pint">Laravel Pint</a> for a while now. Initially, I ran <code>pint</code> to format my code manually and across all files. Recently, I have been making use of <a href="https://www.atlassian.com/git/tutorials/git-hooks">Git pre-commit hooks</a> to apply code format changes to only files that have been modified. I use this both for Arbeitnow with Laravel and the blog with Statamic.</p><p>To do this, you will need to install Laravel pint:</p><pre><code>composer require laravel/pint --dev</code></pre><p>Then, go to your root directory, find <code>.git/hooks</code> and create a copy of the <code>pre-commit.sample</code> hook file. I named mine as <code>pre-commit</code> and made it executable using <code>chmod +x pre-commit</code></p><p>Open that <code>pre-commit</code> file on your editor and paste this:</p><pre><code>#!/bin/sh
files=$(git diff --cached --name-only --diff-filter=ACM -- &apos;*.php&apos;);
vendor/bin/pint $files</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adithya.dev/content/images/2023/03/image.png" class="kg-image" alt="Laravel Pint formatting the files" loading="lazy" width="712" height="85" srcset="https://adithya.dev/content/images/size/w600/2023/03/image.png 600w, https://adithya.dev/content/images/2023/03/image.png 712w"><figcaption>Laravel Pint formatting the files</figcaption></figure><p>This fetches the list of files that have been Added, Copied or Modified (ACM in diff-filter) and passes that as the argument for <code>pint</code> to format only those files.</p><p>If you want to include files that were renamed, you can use the additional flag <code>R</code> in <code>diff-filter</code>. Additionally, if you want to disable the output of <code>pint</code>, you can pass in the flag <code>-q</code> for quiet output like this:</p><p><code>vendor/bin/pint $files -q</code></p>]]></content:encoded></item><item><title><![CDATA[Fixing Laravel 10 return type error in Controller]]></title><description><![CDATA[How to fix return type mismatch issue in Laravel 10]]></description><link>https://adithya.dev/fixing-laravel-return-type-error-in-controller/</link><guid isPermaLink="false">63f0a39dafd7f22e4e66ed05</guid><category><![CDATA[laravel]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sat, 18 Feb 2023 10:17:45 GMT</pubDate><content:encoded><![CDATA[<p>Laravel 10 added <a href="https://laravel.com/docs/10.x/releases#types">support for types</a> and it&apos;s great. In some cases, you might see errors if the defined types and actual return types do not match.</p><p>When you create a new Controller using <code>php artisan make:controller IndexController</code> , you get this default stub.</p><pre><code>&lt;?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
 
class IndexController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(): Response
    {
        //
    }
 
    /**
     * Display the specified resource.
     */
    public function show(): Response
    {
        //
    }
 
    // ...
 
}</code></pre><p> &#xA0;In this case, if you try to return a view inside of either index or show methods using the <code>view</code> helper, you will see an error.</p><pre><code>TypeError:

Return value is expected to be Illuminate\Http\Response, Illuminate\View\View returned</code></pre><p>The fix is to update the return type of the method from <code>Response</code> to <code>View</code> from the Illuminate\View\View namespace.</p><pre><code>use Illuminate\View\View;
public function index(): View
    {
        //
    }
    </code></pre><p>Alternatively, you could also use <code>response()-&gt;view(...)</code>, <code>Response::view</code> &#xA0;or update the default return type to <code>Response | View</code></p>]]></content:encoded></item><item><title><![CDATA[Statamic: Find & Replace in Bard]]></title><description><![CDATA[How to find and replace text in Statamic Bard]]></description><link>https://adithya.dev/statamic-find-replace-in-bard/</link><guid isPermaLink="false">63e21bf8afd7f22e4e66ecad</guid><category><![CDATA[statamic]]></category><category><![CDATA[laravel]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Tue, 07 Feb 2023 10:27:51 GMT</pubDate><content:encoded><![CDATA[<p>When working with <a href="https://statamic.dev/fieldtypes/bard">Statamic Bard</a>, I wanted to find and replace certain occurrence of a word - say find &quot;Lorem ipsum&quot; and replace with &quot;LOREM IPSUM&quot;. I used this great addon called <a href="https://jacksleight.dev/docs/bard-mutator">Bard Mutator</a> (v1 and v2). I&apos;m using Statamic v3.4 with Tiptap v2 and Bard Mutator v2.</p><p>To get started:</p><ul><li>Make sure you have a bard field in your statamic blueprint</li><li>Install Bard Mutator from the link above</li><li>Add the following snippet that lets you perform advanced operations to a service provider. Either <code>src/Providers/AppServiceProvider</code> or a separate Service Provider will do.<br></li></ul><pre><code class="language-PHP">$this-&gt;app-&gt;bind(
    \Tiptap\Editor::class,
    \JackSleight\StatamicBardMutator\Editor::class
);</code></pre><p>This is the content I have in my bard field (repeated multiple times) so we can see how to replace just once and all occurrences if we want to.</p><h3 id="to-replace-only-once-in-each-paragraph">To replace only once in each paragraph:</h3><pre><code class="language-PHP">Mutator::data(&apos;paragraph&apos;, function ($data, $meta){
            if (empty($data-&gt;content)) {
                return;
            }

            foreach ($data-&gt;content as $i =&gt; $node) {
                if (isset($node-&gt;text)) {
                    $regex = &apos;/^(.*?)\bLorem Ipsum\b(.*)$/&apos;;
                    if (!preg_match($regex, $node-&gt;text, $match)) {
                        continue;
                    }

                    [, $before, $after] = $match;
                    array_splice($data-&gt;content, $i, 1, [
                        (object)[&apos;type&apos; =&gt; &apos;text&apos;, &apos;text&apos; =&gt; $before],
                        (object)[&apos;type&apos; =&gt; &apos;text&apos;, &apos;text&apos; =&gt; &apos;LOREM IPSUM&apos;],
                        (object)[&apos;type&apos; =&gt; &apos;text&apos;, &apos;text&apos; =&gt; $after],
                    ]);
                    break;
                }
                
            }
});</code></pre><h3 id="to-replace-only-once-in-the-entire-article">To replace only once in the entire article:</h3><p>I used <code>$meta</code> which gives us access to the root tag. Bard data mutator for paragraph is triggered once for each paragraph in the bard field. So you need access to <code>$meta[&apos;root&apos;]</code> which is great to store shared variables.</p><pre><code class="language-PHP"> Mutator::data(&apos;paragraph&apos;, function ($data, $meta){
            if (empty($data-&gt;content)) {
                return;
            }

            if (isset($meta[&apos;root&apos;]-&gt;replaced) &amp;&amp; $meta[&apos;root&apos;]-&gt;replaced){
                return;
            }

            foreach ($data-&gt;content as $i =&gt; $node) {
                if (isset($node-&gt;text)) {
                    $regex = &apos;/^(.*?)\bLorem Ipsum\b(.*)$/&apos;;
                    if (!preg_match($regex, $node-&gt;text, $match)) {
                        continue;
                    }

                    [, $before, $after] = $match;
                    array_splice($data-&gt;content, $i, 1, [
                        (object)[&apos;type&apos; =&gt; &apos;text&apos;, &apos;text&apos; =&gt; $before],
                        (object)[&apos;type&apos; =&gt; &apos;text&apos;, &apos;text&apos; =&gt; &apos;DUMMY TEXT&apos;],
                        (object)[&apos;type&apos; =&gt; &apos;text&apos;, &apos;text&apos; =&gt; $after],
                    ]);
                    $meta[&apos;root&apos;]-&gt;replaced = true;
                    break;
                }
                
            }
});</code></pre><hr><p>Read how to<a href="https://adithya.dev/format-code-with-laravel-pint-on-commiting/"> format Statamic code automatically with Laravel Pint</a></p>]]></content:encoded></item><item><title><![CDATA[Statamic: Disk [assets] does not have a configured driver]]></title><description><![CDATA[How to fix Disk does not have a configured driver issue in Statamic / Laravel]]></description><link>https://adithya.dev/statamic-disk-not-configured/</link><guid isPermaLink="false">63de352b68cc324a3443ffae</guid><category><![CDATA[statamic]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sat, 04 Feb 2023 10:38:54 GMT</pubDate><content:encoded><![CDATA[<p>On a fresh install of Statamic 3.4 and Peak Studio starter kit, I got this error. </p><pre><code> Disk [assets] does not have a configured driver.

  at vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemManager.php:137
    133&#x2595;     {
    134&#x2595;         $config ??= $this-&gt;getConfig($name);
    135&#x2595;
    136&#x2595;         if (empty($config[&apos;driver&apos;])) {
  &#x279C; 137&#x2595;             throw new InvalidArgumentException(&quot;Disk [{$name}] does not have a configured driver.&quot;);
    138&#x2595;         }
    139&#x2595;
    140&#x2595;         $name = $config[&apos;driver&apos;];
    141&#x2595;</code></pre><p>All I had to do was go to <code>config/filesystems.php</code> and add this new block under <code>disks</code></p><pre><code>&apos;assets&apos; =&gt; [
            &apos;driver&apos; =&gt; &apos;local&apos;,
            &apos;root&apos; =&gt; public_path(&apos;assets&apos;),
            &apos;url&apos; =&gt; &apos;/assets&apos;,
            &apos;visibility&apos; =&gt; &apos;public&apos;,
        ],
</code></pre><p>If you use a driver other than <code>local</code>, take a look at <a href="https://laravel.com/docs/9.x/filesystem#configuration">the documentation</a> on how to configure it.</p>]]></content:encoded></item><item><title><![CDATA[Cloudflare purging page cache with Laravel & Statamic]]></title><description><![CDATA[How to clear cloudflare page cache using Laravel and for Statamic CMS]]></description><link>https://adithya.dev/cloudflare-purging-page-cache-with-laravel/</link><guid isPermaLink="false">63cbf3ad68cc324a3443ff63</guid><category><![CDATA[laravel]]></category><category><![CDATA[statamic]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sat, 21 Jan 2023 14:27:48 GMT</pubDate><content:encoded><![CDATA[<p>I use Cloudflare to cache pages for a day. Recently I built the <a href="https://www.arbeitnow.com/blog/">Arbeitnow blog</a> using <a href="https://statamic.com/">Statamic</a>. When I update the content, I want the cache to be updated and the latest changes to be shown to the visitor.</p><p>When I deploy changes to content, I added a new Laravel command as part of the deployment flow. Important thing to note is that the Purge by single-file (by URL) allows only 30 entries at a time. <br><br>With statamic, I just grab the URLs from the <code>Entry</code> repository for a single collection. You could filter more, perhaps grab only records that have been updated in the last day, or since the last deployment.</p><p>This is what I have in the <code>handle</code> method. For this, you need two things:</p><ul><li>Zone ID (it&apos;s on your site page in Cloudflare portal)</li><li>API Key (Generate one <a href="https://dash.cloudflare.com/profile/api-tokens">here</a> with permissions for clearing / purging cache in a zone) <br></li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adithya.dev/content/images/2023/01/image.png" class="kg-image" alt="permissions for clearing / purging cache" loading="lazy" width="903" height="121" srcset="https://adithya.dev/content/images/size/w600/2023/01/image.png 600w, https://adithya.dev/content/images/2023/01/image.png 903w" sizes="(min-width: 720px) 720px"><figcaption>permissions for clearing / purging cache</figcaption></figure><pre><code class="language-PHP">// use config() to grab it from your environment
$apiKey = &apos;&apos;; 
$zoneId = &apos;&apos;;

$all = Entry::query()
            -&gt;where(&apos;collection&apos;, &apos;collection_name&apos;)
            -&gt;where(&apos;published&apos;, true)
            -&gt;get();

        foreach ($all-&gt;chunk(30) as $entries){
            $files = [];
            /** @var Entry $entry */
            foreach ($entries as $entry){
                $files[] = url($entry-&gt;uri);
            }

            $urls = [
                &apos;files&apos; =&gt; $files
            ];

            $http = Http::withBody(json_encode($urls),&apos;application/json&apos;)
                -&gt;withToken($apiKey)
                -&gt;post(&apos;https://api.cloudflare.com/client/v4/zones/&apos; . $zoneId . &apos;/purge_cache&apos;);

            $this-&gt;info(&apos;Submitted to Cloudflare with status: &apos; . $http-&gt;status());

            if (!$http-&gt;ok()){
                dump($http-&gt;body());
            }

        }</code></pre>]]></content:encoded></item><item><title><![CDATA[Elasticsearch API key & PHP]]></title><description><![CDATA[How to setup API key for Elasticsearch and use it with PHP]]></description><link>https://adithya.dev/elasticsearch-api-key-php/</link><guid isPermaLink="false">638cb67768cc324a3443fec2</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[laravel]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sun, 04 Dec 2022 15:23:34 GMT</pubDate><content:encoded><![CDATA[<p>When you enable <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-minimal-setup.html">XPACK security</a> in Elasticsearch, your clients (my website in this case) requires authentication to be setup. If you do not setup proper authentication, you will get similar errors.<br></p><pre><code class="language-JSON">Elastic\Elasticsearch\Exception\ClientResponseException
401 Unauthorized: 
{
	&quot;error&quot;: {
		&quot;root_cause&quot;: [
			{
				&quot;type&quot;: &quot;security_exception&quot;,
				&quot;reason&quot;: &quot;missing authentication credentials for REST request [/index/_search]&quot;,
				&quot;header&quot;: { &quot;WWW-Authenticate&quot;: [&quot;Basic realm=\&quot;security\&quot; charset=\&quot;UTF-8\&quot;&quot;, &quot;ApiKey&quot;] }
			}
		],
		&quot;type&quot;: &quot;security_exception&quot;,
		&quot;reason&quot;: &quot;missing authentication credentials for REST request [/index/_search]&quot;,
		&quot;header&quot;: { &quot;WWW-Authenticate&quot;: [&quot;Basic realm=\&quot;security\&quot; charset=\&quot;UTF-8\&quot;&quot;, &quot;ApiKey&quot;] }
	},
	&quot;status&quot;: 401
}
</code></pre><p>I found two ways to solve this, I could setup either one of these:</p><ul><li>basic authentication</li><li>API Key</li></ul><h3 id="basic-authentication">Basic Authentication</h3><p>Use this <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html#set-built-in-user-passwords">guide</a> to generate an username / password combination, default username is <code>elastic</code></p><h4 id="using-elastic-search-client">Using Elastic Search client:</h4><p></p><pre><code class="language-PHP">$client = ClientBuilder::create()
-&gt;setBasicAuthentication(&apos;&lt;username&gt;&apos;, &apos;&lt;password&gt;&apos;)
-&gt;build();</code></pre><p>If you use configuration files, you can set up the following key <code>basicAuthentication</code> in your<a href="https://www.elastic.co/guide/en/elasticsearch/client/php-api/8.5/node_pool.html"> node pool</a></p><pre><code class="language-JSON">&apos;hosts&apos; =&gt; [
	localhost:9200&apos;
],
&apos;basicAuthentication&apos; =&gt; [
              &apos;&lt;username&gt;&apos;,
              &apos;&lt;password&gt;&apos;          
]</code></pre><h3 id="using-babenkoivanelastic-client">Using babenkoivan/elastic-client</h3><p>If you use <a href="https://github.com/babenkoivan/elastic-client">babenkoivan/elastic-client</a> (I use it for Laravel), publish the config file and go to <code>elastic.client.php</code> and add it under your connections</p><pre><code>&lt;?php declare(strict_types=1);

return [
    &apos;default&apos; =&gt; env(&apos;ELASTIC_CONNECTION&apos;, &apos;default&apos;),
    &apos;connections&apos; =&gt; [
        &apos;default&apos; =&gt; [
            &apos;hosts&apos; =&gt; [
                env(&apos;SCOUT_ELASTIC_HOST&apos;, &apos;localhost:9200&apos;),
            ],
           &apos;basicAuthentication&apos; =&gt; [
              &apos;&lt;username&gt;&apos;,
              &apos;&lt;password&gt;&apos;          
			]
        ],
    ],
];
</code></pre><h3 id="api-key">API Key</h3><p>To generate an API key, you will still require the username &amp; password from basic authentication. Then you can generate an API key first using either cURL or Kibana. I used this query:</p><pre><code>curl --location --request POST &apos;http://&lt;username&gt;:&lt;password&gt;@localhost:9200/_security/api_key&apos; \
--header &apos;Content-Type: application/json&apos; \
--data-raw &apos;{
    &quot;name&quot;: &quot;php-client&quot;
}&apos;</code></pre><p>This returned a response with the <code>api_key</code> and <code>id</code> key</p><pre><code class="language-JSON">{
    &quot;id&quot;: &quot;&lt;id&gt;&quot;,
    &quot;name&quot;: &quot;php-client&quot;,
    &quot;api_key&quot;: &quot;&lt;api-key&gt;&quot;,
    &quot;encoded&quot;: &quot;&lt;encoded&gt;&quot;
}</code></pre><h4 id="using-elastic-search">Using Elastic Search</h4><pre><code>$client = ClientBuilder::create()
-&gt;setApiKey(&apos;&lt;id&gt;&apos;, &apos;&lt;api_key&gt;&apos;)
-&gt;build();</code></pre><h3 id="using-babenkoivanelastic-client-1">Using babenkoivan/elastic-client</h3><p>If you use <a href="https://github.com/babenkoivan/elastic-client">babenkoivan/elastic-client</a>, publish the config file and go to <code>elastic.client.php</code> and add it under your connections to use the <code>encoded</code> key - it includes both id and api key.</p><pre><code>&lt;?php declare(strict_types=1);

return [
    &apos;default&apos; =&gt; env(&apos;ELASTIC_CONNECTION&apos;, &apos;default&apos;),
    &apos;connections&apos; =&gt; [
        &apos;default&apos; =&gt; [
            &apos;hosts&apos; =&gt; [
                env(&apos;SCOUT_ELASTIC_HOST&apos;, &apos;localhost:9200&apos;),
            ],
           &apos;apiKey&apos; =&gt; [
              &apos;&lt;encoded&gt;
              ]
        ],
    ],
];
</code></pre>]]></content:encoded></item><item><title><![CDATA[Data Providers in PHPUnit]]></title><description><![CDATA[Data Providers in PHPUnit and how to use them]]></description><link>https://adithya.dev/data-providers-in-phpunit/</link><guid isPermaLink="false">620ec6292f184404b04b0851</guid><category><![CDATA[php]]></category><category><![CDATA[laravel]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Wed, 30 Mar 2022 12:53:47 GMT</pubDate><content:encoded><![CDATA[<p>When I built a <a href="https://www.arbeitnow.com/tools/salary-calculator/germany">Salary Calculator for Germany</a> using Laravel, I started covering the code with Unit Tests using PHPUnit. This particularly came in handy when I had to cover more edge cases as I was able to trust the calculation. One of the features that I didn&apos;t know until that point was <a href="https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html#data-providers">Data Providers in PHPUnit</a>.</p><h3 id="what-is-a-data-provider">What is a Data Provider?</h3><p>When I write a test for a method, or a class, I write multiple assertions with the values for the same code. Data Providers are useful here when all that changes is the values passed to the method.</p><blockquote>DataProviders are a framework for easily controlling how data can be provided from a source </blockquote><p>Let&apos;s take an example of how you would write a test <strong>without</strong> data providers. Assume you have a method <code>add($a, $b)</code> in a class <code>Operations</code> that returns a value, summing up the numbers.</p><pre><code class="language-PHP">    public function test_add()
    {
    	$actual = (new Operations)-&gt;add(1, 2);
        $this-&gt;assertEquals(3, $actual);
    }
</code></pre><p>When you want to add multiple assertions, you would be tempted to add another assertion like this:</p><pre><code class="language-PHP">    public function test_add()
    {
    	$actual = (new Operations)-&gt;add(1, 2);
        $this-&gt;assertEquals(3, $actual);
        
        $actual = (new Operations)-&gt;add(3, 4);
        $this-&gt;assertEquals(7, $actual);
    }
    
    // Or a test per method
    public function test_add_first()
    {
    	$actual = (new Operations)-&gt;add(1, 2);
        $this-&gt;assertEquals(3, $actual);
    }
    
    public function test_add_second()
    {
    	$actual = (new Operations)-&gt;add(3, 4);
        $this-&gt;assertEquals(7, $actual);
    }

    
</code></pre><p>These add overhead if the signature of the method changes, or when you want to cover more cases. </p><h3 id="how-to-use-a-data-provider-in-phpunit">How to use a Data Provider in PHPUnit</h3><p>A Data Provider can simplify this, allowing you to pass multiple sets of values for the same test. From the official documentation:</p><!--kg-card-begin: markdown--><p>A data provider method</p>
<ul>
<li>must be <code>public </code></li>
<li>and either return an array of arrays or an object that implements the <code>Iterator</code> interface and yields an array for each iteration step.</li>
</ul>
<!--kg-card-end: markdown--><p>Let&apos;s convert the existing test case using a Data Provider. We will use a <code>dataProvider</code> annotation following the name of the data provider. </p><pre><code class="language-PHP">/**
* @dataProvider addProvider
*/
public function test_add($a, $b, $expected)
{
	$actual = (new Operations)-&gt;add($a, $b);
    	$this-&gt;assertEquals($expected, $actual);
}

public function addProvider()
{
 	return array(
        	array(1, 2, 3),
        	array(4, 5, 9),
    );
}

</code></pre><p>In this case, PHPUnit would pass the take the array, loop through the items (array again) and pass the arguments based on the <strong>position </strong>of the method parameters. </p><p>For the first item, it will take <code>array(1, 2, 3)</code>, call <code>test_add(1, 2, 3)</code> &#xA0;and validate the assertions. Then the next item and so on.</p><h3 id="inline-data-providers-in-phpunit">Inline Data Providers in PHPUnit</h3><p>In the test cases above with a Data Provider, we can simplify it even further by inlining the data provider. The annotation to use is <code><a href="https://phpunit.readthedocs.io/en/9.5/annotations.html?highlight=testWith#testwith">testWith</a></code> </p><pre><code class="language-PHP">/**
* @testWith array(
        	array(1, 2, 3),
        	array(4, 5, 9),
    );
*/
public function test_add($a, $b, $expected)
{
	$actual = (new Operations)-&gt;add($a, $b);
    	$this-&gt;assertEquals($expected, $actual);
}</code></pre>]]></content:encoded></item><item><title><![CDATA[Laravel - Remove slashes on JSON Response]]></title><description><![CDATA[How to escape slashes on JSON response in Laravel]]></description><link>https://adithya.dev/laravel-remove-slashes-on-json-response/</link><guid isPermaLink="false">623333662f184404b04b08a7</guid><category><![CDATA[laravel]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sun, 27 Mar 2022 11:13:26 GMT</pubDate><content:encoded><![CDATA[<p>I built an API endpoint in Laravel - a JSON response with a body. When I looked at the formatted response using a JSON formatter, it looked good when I was working on <a href="https://www.arbeitnow.com/blog/salary-calculation-in-germany/">Salary Calculation in Germany</a>.</p><pre><code class="language-PHP">return response()-&gt;json([&apos;url&apos; =&gt; &apos;https://www.domain.com/api/test&apos;]);
</code></pre><p>I expected a response like this and was what I saw in a JSON formatter.</p><pre><code>https://www.domain.com/api/test</code></pre><p>But the raw response looked like this, with unescaped back slashes on the JSON body.</p><pre><code>https:\/\/www.domain.com\/api\/test</code></pre><p>I used Laravel&apos;s default <a href="https://laravel.com/docs/9.x/responses#json-responses">JSON response helper</a> and upon looking into it, there are options that you can set on the response to escape the slashes - <code>JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT</code>. response()-&gt;json() takes the data as the first parameter, HTTP status code as the next, HTTP headers as the next and then options.</p><pre><code class="language-PHP">return response()-&gt;json(
[&apos;url&apos; =&gt; &apos;https://www.domain.com/api/test&apos;],
200,
[],
JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT
);
</code></pre>]]></content:encoded></item><item><title><![CDATA[PHP substr vs mb_substr]]></title><description><![CDATA[substr vs mb_substr in printing special characters in Laravel Blade.]]></description><link>https://adithya.dev/php-substr-vs-mb_substr/</link><guid isPermaLink="false">6213973b2f184404b04b0858</guid><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Mon, 21 Feb 2022 14:22:21 GMT</pubDate><content:encoded><![CDATA[<p>Recently, I ran into an issue on my <a href="https://www.arbeitnow.com/">job board Arbeitnow</a>. For a couple of job postings, the HTML title tag was being empty, and that&apos;s an issue for users &amp; from SEO perspective.</p><p>The title tag is rendered via Blade templates, and the value is passed from the controller.</p><pre><code class="language-HTML">&lt;html&gt;
&lt;head&gt;
&lt;title&gt; {{ $title }} &lt;/title&gt;
&lt;/head&gt;
...

</code></pre><p>When the <code>$title</code> variable is passed, it is cut down to just 70 characters as this is the recommended title length for Google. </p><pre><code class="language-PHP">$title = substr(&quot;Title here&quot;, 0, 70);</code></pre><p>I started looking at the few job postings that returned an empty string instead of a valid string.</p><pre><code>$title = substr(&quot;SEO Manager at ZAP-Hosting GmbH &amp; Co. KG | North Rhine-Westphalia | M&#xFC;nster | Germany&quot;, 0, 70);</code></pre><p>My expectation was that it would return <code>SEO Manager at ZAP-Hosting GmbH &amp; Co. KG | North Rhine-Westphalia | M&#xFC;</code> as the result. Unfortunately for me, it didn&apos;t. When I dumped the variable, the result was this:</p><pre><code>b&quot;SEO Manager at ZAP-Hosting GmbH &amp; Co. KG | North Rhine-Westphalia | M&#xC3;&quot;</code></pre><p>Notice the weird <code>b</code> before it? Immediately, I knew this was an issue because the special character <code>&#xFC;</code> and therefore needed to be handled differently.</p><p>I found two possible solutions and both work well:</p><ul><li>Adjust the blade template to print as a special character (no output escaping)</li></ul><pre><code>&lt;html&gt;
&lt;head&gt;
&lt;title&gt; {!! $title !!} &lt;/title&gt;
&lt;/head&gt;
...

</code></pre><ul><li>Change <code><a href="https://www.php.net/manual/en/function.substr.php">substr</a>()</code> calls to <code><a href="https://www.php.net/manual/en/function.mb-substr.php">mb_substr</a>()</code></li></ul>]]></content:encoded></item><item><title><![CDATA[Sentry: Unhandled Failed to fetch]]></title><description><![CDATA[Identifying Unhandled Failed to fetch and NetworkError when attempting to fetch resource in Sentry ]]></description><link>https://adithya.dev/sentry-unhandled-failed-to-fetch/</link><guid isPermaLink="false">6202270d2f184404b04b081c</guid><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Tue, 08 Feb 2022 08:47:09 GMT</pubDate><content:encoded><![CDATA[<p>I use <a href="https://sentry.io/">Sentry</a> extensively for backend error tracking. I set it up for my frontend as well, using the CDN which was pretty simple. All I had to do was add the <code>&lt;script&gt;</code> tag.</p><p>A day after that, I started seeing real world errors from browsers. Some of them were from outdated browsers. Most interesting error that was captured was <code>Unhandled Failed to fetch</code> from Chrome and <code>TypeError: NetworkError when attempting to fetch resource</code> from Firefox.</p><p>Initially, I assumed that this might have been a backend error - perhaps no response from my API - so I checked nginx logs, sentry for backend. Everything looked good, which was puzzling. I looked up on Google to find the cause of this issue. </p><p>On Stack Overflow, I found someone experienced <a href="https://stackoverflow.com/questions/55738408/javascript-typeerror-cancelled-error-when-calling-fetch-on-ios">the same issue</a>. The explanation was quite simple:</p><blockquote>When the page loads, the refresh button changes to cross button, now if some api request is in progress during this page loading time and the user click this cross button, then iOS chrome/safari throws this error. </blockquote><p>What this meant was this error being logged was a hindrance since it was useless, prevented me from seeing the actual errors. Luckily, there was a solution to ignore these on setup.</p><pre><code class="language-js">Sentry.init({
  dsn: &quot;sentry_dsn&quot;,
  ignoreErrors: [
    &apos;TypeError: Failed to fetch&apos;,
    &apos;TypeError: NetworkError when attempting to fetch resource.&apos;,
    &apos;TypeError: Cancelled&apos;
  ],
});
</code></pre><p>I wish that the browsers included more information on this error, so it could be captured by Sentry.<br></p>]]></content:encoded></item><item><title><![CDATA[Laravel Translation for Subfolders]]></title><description><![CDATA[How to access translation from sub folders in Laravel]]></description><link>https://adithya.dev/laravel-translation-for-subfolders/</link><guid isPermaLink="false">61ed27b12f184404b04b07c2</guid><category><![CDATA[laravel]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sun, 23 Jan 2022 10:28:04 GMT</pubDate><content:encoded><![CDATA[<p>I organized my language translation folder in Laravel in this way</p><pre><code>- resources	
	- de	
    - en		
    	- app.php		
        - subfolder			
        	- file.php</code></pre><p>To access the translation keys in app.php, it&apos;s straightforward as described in the <a href="https://laravel.com/docs/8.x/localization">Laravel documentation</a>. To access a key from app.php, you can use:</p><pre><code class="language-PHP">trans(&apos;app.key&apos;)</code></pre><p>Once I organized the language translations inside a sub-folder, I tried to access in this way.</p><pre><code class="language-PHP">trans(&apos;subfolder.file.key&apos;)</code></pre><p>And it wouldn&apos;t work. Laravel documentation does not seem to mention how one can access a key from inside a sub folder. I looked up on <a href="https://github.com/laravel/framework/issues/14212">Github Issues</a> on the Laravel Repository and found a way to access the key.</p><pre><code class="language-PHP">trans(&apos;subfolder/.file.key&apos;)</code></pre><p>This worked out well but the point to notice is that Taylor <a href="https://github.com/laravel/docs/pull/3957">confirmed </a>that this is not a way that may be supported in the future releases.</p>]]></content:encoded></item><item><title><![CDATA[XML Sitemap is rendering as plain text]]></title><description><![CDATA[How to fix issues with Sitemap hreflang for a different locale / language]]></description><link>https://adithya.dev/xml-sitemap-is-rendering-as-plain-text/</link><guid isPermaLink="false">61b5c689f98bb31f51cf52eb</guid><category><![CDATA[indexing]]></category><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Sun, 23 Jan 2022 10:02:10 GMT</pubDate><content:encoded><![CDATA[<p>After adding <code>&lt;xhtml:link&gt;</code> to my sitemap file where I included <code>&lt;xhtml:link rel=&quot;alternate&quot; hreflang=&quot;<strong>supported_language-code</strong>&quot;&gt;</code> my XML sitemap file started rendering as a plain text file.</p><p>In the console window of Chrome, I saw an error as well which indicated the structure was off.</p><pre><code>Caught TypeError: Cannot read properties of null (reading &apos;childNodes&apos;)at XMLDocument.ready (content.js:218)ready @ content.js:218</code></pre><p>I had to adjust my <code>&lt;urlset&gt;</code> from this</p><pre><code>&lt;urlset xmlns=&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot; xmlns:xhtml=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
</code></pre><p>to this, thanks to this Stackoverflow answer</p><pre><code>&lt;urlset xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
        xsi:schemaLocation=&quot;http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.w3.org/TR/xhtml11/xhtml11_schema.html http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd&quot;
        xmlns=&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot;
        xmlns:xhtml=&quot;http://www.w3.org/TR/xhtml11/xhtml11_schema.html&quot;&gt;</code></pre><p>Strangely enough that while the XML was rendering properly, Google Search Console was displaying an issue for the sitemap</p><pre><code>Your Sitemap or Sitemap index file does not properly declare the namespace!
</code></pre><p>The schema below fixed the Google issue (that&apos;s all we care about in 2021, right?) while the rendering issue still persists.</p><pre><code>&lt;urlset xmlns=&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
        xmlns:xhtml=&quot;http://www.w3.org/1999/xhtml&quot;
        xsi:schemaLocation=&quot;http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd&quot;&gt;</code></pre><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Fixing a private package error with composer]]></title><description><![CDATA[Fixing an error for "Could not parse version constraint branch"]]></description><link>https://adithya.dev/fixing-a-package-error-with-composer/</link><guid isPermaLink="false">61d313f12f184404b04b07a1</guid><dc:creator><![CDATA[Adithya Srinivasan]]></dc:creator><pubDate>Mon, 03 Jan 2022 15:24:23 GMT</pubDate><content:encoded><![CDATA[<pre><code>[UnexpectedValueException] Could not parse version constraint branch#3de8008: Invalid version string &quot;branch#3de8008&quot;</code></pre><p>The error above popped up when I tried to install a package locally via Composer. On composer.json, I had the following package definition:</p><pre><code class="language-JSON">    &quot;require&quot;: {
        &quot;package_owner/package_name&quot;: &quot;branch#3de8008&quot;,
}
</code></pre><p>This pointed to a particular commit that I wanted to include when I got the error. It turns out that all packages that are <a href="https://getcomposer.org/doc/05-repositories.md">installed locally or via private packages</a>, have to be prepended with <code>dev-</code> so this fixed the issue.</p><pre><code class="language-JSON">    &quot;require&quot;: {
        &quot;package_owner/package_name&quot;: &quot;dev-branch#3de8008&quot;,
        }
</code></pre>]]></content:encoded></item></channel></rss>