<?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[PrestaShop, E-commerce, Web - Krystian Podemski]]></title><description><![CDATA[PrestaShop, E-commerce, Web - Krystian Podemski]]></description><link>https://podemski.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 22 Apr 2026 08:27:31 GMT</lastBuildDate><atom:link href="https://podemski.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Smarty Cache in PrestaShop — Don’t Make This Mistake!]]></title><description><![CDATA[For some reason, Smarty cache in PrestaShop is still a subject of debate among developers working with this system. Over many years of working with PrestaShop, I’ve come across various opinions on how it should be configured.
As a reminder, in Advanc...]]></description><link>https://podemski.dev/smarty-cache-in-prestashop-dont-make-this-mistake</link><guid isPermaLink="true">https://podemski.dev/smarty-cache-in-prestashop-dont-make-this-mistake</guid><category><![CDATA[PrestaShop]]></category><category><![CDATA[cache]]></category><category><![CDATA[Smarty]]></category><dc:creator><![CDATA[Krystian Podemski]]></dc:creator><pubDate>Mon, 25 Aug 2025 06:00:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-Cmz06-0btw/upload/65366546f1a2536f579a610b565e1ba7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For some reason, Smarty cache in PrestaShop is still a subject of debate among developers working with this system. Over many years of working with PrestaShop, I’ve come across various opinions on how it should be configured.</p>
<p>As a reminder, in <strong>Advanced → Performance → Smarty settings → Template compilation</strong> (for PrestaShop 8.2), we have three options:</p>
<ol>
<li><p>Never recompile template files.</p>
</li>
<li><p>Recompile templates if the files have been updated.</p>
</li>
<li><p>Force compilation.</p>
</li>
</ol>
<p>Out of these three, the second option — recompiling templates when their files have changed — is often recommended. Why? Honestly, no one knows for sure. I’ve heard arguments that when changes are made in the back office, product prices or information might not refresh properly on the storefront. But that’s simply not true. Any modification event, such as editing a product or category, should trigger cache clearing for the relevant modules — for example, the <strong>Featured Products</strong> module on the homepage.</p>
<p>The purpose of this article is to clearly state that on a production site, there is absolutely no reason to use any option other than <strong>“Never recompile template files.”</strong> Any other setting will only make your store less efficient. Of course, things are different in a test or development environment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755639801773/a26e91fd-3a24-4e64-a1dd-70c20834d5f6.png" alt class="image--center mx-auto" /></p>
<p>I’d also add that in some specific situations, monitoring template files for modifications can drastically slow down the front office loading time. I’ve seen this happen while consulting on module implementations from certain companies working with PrestaShop.</p>
<p>Of course, also make sure that caching itself is enabled. There, it’s worth setting the option to <strong>“Clear cache every time something has been modified”</strong>, because otherwise, changes made in the back office won’t actually be visible to visitors. (Yes, I’m aware that sometimes it makes sense to clear the cache only on demand — especially in high-traffic stores where cache management is handled differently. :-))</p>
]]></content:encoded></item><item><title><![CDATA[Cleaning PrestaShop database]]></title><description><![CDATA[The PrestaShop database can grow significantly over time, and not all the data collected there is needed throughout the entire operation of the store. In this article, I’ll share a few SQL queries that will help slim down the database.
Database prefi...]]></description><link>https://podemski.dev/cleaning-prestashop-database</link><guid isPermaLink="true">https://podemski.dev/cleaning-prestashop-database</guid><category><![CDATA[PrestaShop]]></category><category><![CDATA[ecommerce]]></category><dc:creator><![CDATA[Krystian Podemski]]></dc:creator><pubDate>Fri, 22 Aug 2025 06:00:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-9gPKrsbGmc/upload/08f5c2f4723f6b91c920519a4c433384.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The PrestaShop database can grow significantly over time, and not all the data collected there is needed throughout the entire operation of the store. In this article, I’ll share a few SQL queries that will help slim down the database.</p>
<h2 id="heading-database-prefix">Database prefix</h2>
<p>In this article, table names are intentionally written as <code>PREFIX_table_name</code>, because the prefix is something you should adapt to your own setup — I trust you’re not using the default <code>ps_</code>, and if you are, I suggest changing it. After copying the SQL query, remember to replace all instances of <code>PREFIX</code>. I also recommend making a backup before running any of the queries below.</p>
<h2 id="heading-cleaning-the-prefixguest-and-prefixcart-tables">Cleaning the <code>PREFIX_guest</code> and <code>PREFIX_cart</code> tables</h2>
<p>The <code>PREFIX_guest</code> table stores data about visitors, but over time it also accumulates records that are no longer useful. One way to clean this table is to remove all records that didn’t result in a purchase, customer registration, or cart creation.  </p>
<p>The following query will delete abandoned carts older than one month:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> PREFIX_cart
<span class="hljs-keyword">WHERE</span> id_cart <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">IN</span> (<span class="hljs-keyword">SELECT</span> id_cart <span class="hljs-keyword">FROM</span> PREFIX_orders)
<span class="hljs-keyword">AND</span> <span class="hljs-keyword">date_add</span> &lt; <span class="hljs-keyword">NOW</span>() - <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">1</span> <span class="hljs-keyword">MONTH</span>;
</code></pre>
<h2 id="heading-the-prefixconnections-and-prefixconnectionssource-tables">The <code>PREFIX_connections</code> and <code>PREFIX_connections_source</code> tables</h2>
<p>These tables store information about customer visits, provided you’re using the <strong>statsdata</strong> module and have data collection enabled. If your store relies on Google Analytics, Matomo, or another analytics solution, you likely don’t need the data from these tables.  </p>
<p>That said, it’s worth noting that sometimes third-party modules use this data — for example, some Google Tag Manager integration modules rely on these tables to determine the traffic source for conversions. Before changing the configuration of the <strong>statsdata</strong> module or cleaning these tables, it’s a good idea to check with a specialist to make sure you don’t actually need this data.  </p>
<p>If you’re using Google Tag Manager modules that may depend on this information, it’s better to only remove records older than, say, 6 months. Below are two queries that will delete records older than 3 months.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> PREFIX_connections
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">date_add</span> &lt; <span class="hljs-keyword">NOW</span>() - <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">3</span> <span class="hljs-keyword">MONTH</span>;
</code></pre>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> PREFIX_connections_source
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">date_add</span> &lt; <span class="hljs-keyword">NOW</span>() - <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">3</span> <span class="hljs-keyword">MONTH</span>;
</code></pre>
<h2 id="heading-the-prefixlog-and-prefixmail-tables">The <code>PREFIX_log</code> and <code>PREFIX_mail</code> tables</h2>
<p>These tables store data that may turn out to be important, but you certainly don’t need their entire history. I recommend archiving them from time to time. Here, you have two options: you can either clean out records older than a few months or completely clear them after archiving once in a while.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> PREFIX_log
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">date_add</span> &lt; <span class="hljs-keyword">NOW</span>() - <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">3</span> <span class="hljs-keyword">MONTH</span>;
</code></pre>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> PREFIX_mail
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">date_add</span> &lt; <span class="hljs-keyword">NOW</span>() - <span class="hljs-built_in">INTERVAL</span> <span class="hljs-number">3</span> <span class="hljs-keyword">MONTH</span>;
</code></pre>
<h2 id="heading-optimizing-tables-after-cleaning">Optimizing tables after cleaning</h2>
<p>After cleaning up individual tables, it’s a good idea to run a command that will optimize them, for example:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">OPTIMIZE</span> <span class="hljs-keyword">TABLE</span> PREFIX_connections, PREFIX_connections_source;
</code></pre>
<h2 id="heading-whats-next">What’s next?</h2>
<p>There are many modules available on the market that allow you to perform such cleanups in a simpler way. Remember to always run such modules on a store copy first, as they may cause data integrity issues.  </p>
<p>One of the free modules that can be useful for cleaning up a PrestaShop database is <a target="_blank" href="https://github.com/SpiriitLabs/prestaclean"><strong>PrestaClean</strong></a>, available on GitHub.  </p>
<p>The queries mentioned in this article are worth running regularly — you might even consider creating a script that runs via a cron job on your server. Of course, these are just a few examples of tables that can be safely cleaned; this doesn’t cover fixing data integrity issues, removing ghost records in some places, outdated discounts, etc. Perhaps one day there will be an opportunity to write about that as well.</p>
]]></content:encoded></item><item><title><![CDATA[The PrestaShopBackup Class. How to use it in your solutions?]]></title><description><![CDATA[PrestaShop is a robust e-commerce platform designed not only for merchants but also — and perhaps above all — for developers who want to build custom solutions. It offers plenty of built-in features that can be extended and adapted to specific needs,...]]></description><link>https://podemski.dev/the-prestashopbackup-class-how-to-use-it-in-your-solutions</link><guid isPermaLink="true">https://podemski.dev/the-prestashopbackup-class-how-to-use-it-in-your-solutions</guid><category><![CDATA[PrestaShop]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[ecommerce]]></category><dc:creator><![CDATA[Krystian Podemski]]></dc:creator><pubDate>Wed, 20 Aug 2025 06:30:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/hL8slYnc-bM/upload/2123f2ed50c6b052aa998f65cdbbe95b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PrestaShop is a robust e-commerce platform designed not only for merchants but also — and perhaps above all — for developers who want to build custom solutions. It offers plenty of built-in features that can be extended and adapted to specific needs, minimizing the necessity of developing functions from scratch. One of the less-known but useful tools is the <code>PrestaShopBackup</code> class, which simplifies the implementation of backup mechanisms.</p>
<h2 id="heading-classesprestashopbackupphp">classes/PrestaShopBackup.php</h2>
<p>The <code>PrestaShopBackup</code> class exists to make it easier to create database backups. It contains all the necessary logic for gathering database tables, generating a backup file name, and saving the backup data into a file. By default, it is used in the <strong>Advanced → Database → DB Backup</strong> section, but to illustrate a practical use of the <code>PrestaShopBackup</code> class in your own solution, let’s consider a scenario where you need a backup mechanism for specific tables related to product and category languages.<br />We’ll assume we need these tables because we created a mechanism that generates, for example, SEO-friendly URLs and other product SEO data based on given criteria, but we want to ensure the user always has a backup.</p>
<h2 id="heading-dont-reinvent-the-wheel">Don’t reinvent the wheel</h2>
<p>Instead of building this from scratch, you can extend the <code>PrestaShopBackup</code> class by creating a new class, for example <code>ProductLangTablesBackup</code>.<br />The method responsible for creating and saving a new backup is <code>PrestaShopBackupCore::add()</code>. By default, it creates a backup of all the data in your store (except for some tables, such as statistics, if backups were configured that way). Obviously, we are not interested in a full backup, so in our code we need to add a condition that simply skips all the tables we don’t care about.</p>
<h2 id="heading-example">Example</h2>
<p>Below you’ll find code I used in one of my projects. It’s based on the class from PrestaShop 1.7.8. If you want to follow this article and build something for your own solution, <strong>make sure to use the <a target="_blank" href="https://github.com/PrestaShop/PrestaShop/blob/8.2.x/classes/PrestaShopBackup.php">PrestaShop 8.2 codebase</a></strong>.<br />In the code, I’ve highlighted the lines that were modified compared to the original file. We changed:</p>
<ul>
<li>the file name  </li>
<li>reversed the backup condition — instead of ignoring selected tables, we allow only selected tables  </li>
</ul>
<p>The most important changes are highlighted in the code below.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductLangTablesBackup</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PrestaShopBackupCore</span>
</span>{
    <span class="hljs-comment">/**
     * Creates a new backup file.
     *
     * <span class="hljs-doctag">@return</span> bool true on successful backup
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params"></span>)
    </span>{
        $tablesToBackup = [_DB_PREFIX_ . <span class="hljs-string">'product_lang'</span>];
        <span class="hljs-comment">// Generate some random number, to make it extra hard to guess backup file names</span>
        $rand = dechex(mt_rand(<span class="hljs-number">0</span>, min(<span class="hljs-number">0xffffffff</span>, mt_getrandmax())));
        $date = time();
        $backupfile = <span class="hljs-keyword">$this</span>-&gt;getRealBackupPath() . $date . <span class="hljs-string">'-'</span> . $rand . <span class="hljs-string">'-ProductLanguageData.sql'</span>;

        <span class="hljs-comment">// Figure out what compression is available and open the file</span>
        <span class="hljs-keyword">if</span> (function_exists(<span class="hljs-string">'bzopen'</span>)) {
            $backupfile .= <span class="hljs-string">'.bz2'</span>;
            $fp = @bzopen($backupfile, <span class="hljs-string">'w'</span>);
        } <span class="hljs-keyword">elseif</span> (function_exists(<span class="hljs-string">'gzopen'</span>)) {
            $backupfile .= <span class="hljs-string">'.gz'</span>;
            $fp = @gzopen($backupfile, <span class="hljs-string">'w'</span>);
        } <span class="hljs-keyword">else</span> {
            $fp = @fopen($backupfile, <span class="hljs-string">'wb'</span>);
        }

        <span class="hljs-keyword">if</span> ($fp === <span class="hljs-literal">false</span>) {
            <span class="hljs-keyword">echo</span> Context::getContext()-&gt;getTranslator()-&gt;trans(<span class="hljs-string">'Unable to create backup file'</span>, <span class="hljs-keyword">array</span>(), <span class="hljs-string">'Admin.Advparameters.Notification'</span>) . <span class="hljs-string">' "'</span> . addslashes($backupfile) . <span class="hljs-string">'"'</span>;

            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }

        <span class="hljs-keyword">$this</span>-&gt;id = realpath($backupfile);

        fwrite($fp, <span class="hljs-string">'/* Backup for '</span> . Tools::getHttpHost(<span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>) . __PS_BASE_URI__ . <span class="hljs-string">"\n *  at "</span> . date($date) . <span class="hljs-string">"\n */\n"</span>);
        fwrite($fp, <span class="hljs-string">"\n"</span> . <span class="hljs-string">'SET NAMES \'utf8\';'</span>);
        fwrite($fp, <span class="hljs-string">"\n"</span> . <span class="hljs-string">'SET FOREIGN_KEY_CHECKS = 0;'</span>);
        fwrite($fp, <span class="hljs-string">"\n"</span> . <span class="hljs-string">'SET SESSION sql_mode = \'\';'</span> . <span class="hljs-string">"\n\n"</span>);

        <span class="hljs-comment">// Find all tables</span>
        $tables = Db::getInstance()-&gt;executeS(<span class="hljs-string">'SHOW TABLES'</span>);
        $found = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">foreach</span> ($tables <span class="hljs-keyword">as</span> $table) {
            $table = current($table);

            <span class="hljs-comment">// Skip tables which do not start with _DB_PREFIX_</span>
            <span class="hljs-keyword">if</span> (strlen($table) &lt; strlen(_DB_PREFIX_) || strncmp($table, _DB_PREFIX_, strlen(_DB_PREFIX_)) != <span class="hljs-number">0</span>) {
                <span class="hljs-keyword">continue</span>;
            }

            <span class="hljs-keyword">if</span> (!in_array($table, $tablesToBackup)) {
                <span class="hljs-keyword">continue</span>;
            }

            <span class="hljs-comment">// Export the table schema</span>
            $schema = Db::getInstance()-&gt;executeS(<span class="hljs-string">'SHOW CREATE TABLE `'</span> . $table . <span class="hljs-string">'`'</span>);

            <span class="hljs-keyword">if</span> (count($schema) != <span class="hljs-number">1</span> || !<span class="hljs-keyword">isset</span>($schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Table'</span>]) || !<span class="hljs-keyword">isset</span>($schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Create Table'</span>])) {
                fclose($fp);
                <span class="hljs-keyword">$this</span>-&gt;delete();
                <span class="hljs-keyword">echo</span> Context::getContext()-&gt;getTranslator()-&gt;trans(<span class="hljs-string">'An error occurred while backing up. Unable to obtain the schema of %s'</span>, <span class="hljs-keyword">array</span>($table), <span class="hljs-string">'Admin.Advparameters.Notification'</span>);

                <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
            }

            fwrite($fp, <span class="hljs-string">'/* Scheme for table '</span> . $schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Table'</span>] . <span class="hljs-string">" */\n"</span>);

            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;psBackupDropTable) {
                fwrite($fp, <span class="hljs-string">'DROP TABLE IF EXISTS `'</span> . $schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Table'</span>] . <span class="hljs-string">'`;'</span> . <span class="hljs-string">"\n"</span>);
            }

            fwrite($fp, $schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Create Table'</span>] . <span class="hljs-string">";\n\n"</span>);

            $data = Db::getInstance()-&gt;query(<span class="hljs-string">'SELECT * FROM `'</span> . $schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Table'</span>] . <span class="hljs-string">'`'</span>, <span class="hljs-literal">false</span>);
            $sizeof = Db::getInstance()-&gt;numRows();
            $lines = explode(<span class="hljs-string">"\n"</span>, $schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Create Table'</span>]);

            <span class="hljs-keyword">if</span> ($data &amp;&amp; $sizeof &gt; <span class="hljs-number">0</span>) {
                <span class="hljs-comment">// Export the table data</span>
                fwrite($fp, <span class="hljs-string">'INSERT INTO `'</span> . $schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Table'</span>] . <span class="hljs-string">"` VALUES\n"</span>);
                $i = <span class="hljs-number">1</span>;
                <span class="hljs-keyword">while</span> ($row = Db::getInstance()-&gt;nextRow($data)) {
                    $s = <span class="hljs-string">'('</span>;

                    <span class="hljs-keyword">foreach</span> ($row <span class="hljs-keyword">as</span> $field =&gt; $value) {
                        $tmp = <span class="hljs-string">"'"</span> . pSQL($value, <span class="hljs-literal">true</span>) . <span class="hljs-string">"',"</span>;
                        <span class="hljs-keyword">if</span> ($tmp != <span class="hljs-string">"'',"</span>) {
                            $s .= $tmp;
                        } <span class="hljs-keyword">else</span> {
                            <span class="hljs-keyword">foreach</span> ($lines <span class="hljs-keyword">as</span> $line) {
                                <span class="hljs-keyword">if</span> (strpos($line, <span class="hljs-string">'`'</span> . $field . <span class="hljs-string">'`'</span>) !== <span class="hljs-literal">false</span>) {
                                    <span class="hljs-keyword">if</span> (preg_match(<span class="hljs-string">'/(.*NOT NULL.*)/Ui'</span>, $line)) {
                                        $s .= <span class="hljs-string">"'',"</span>;
                                    } <span class="hljs-keyword">else</span> {
                                        $s .= <span class="hljs-string">'NULL,'</span>;
                                    }

                                    <span class="hljs-keyword">break</span>;
                                }
                            }
                        }
                    }
                    $s = rtrim($s, <span class="hljs-string">','</span>);

                    <span class="hljs-keyword">if</span> ($i % <span class="hljs-number">200</span> == <span class="hljs-number">0</span> &amp;&amp; $i &lt; $sizeof) {
                        $s .= <span class="hljs-string">");\nINSERT INTO `"</span> . $schema[<span class="hljs-number">0</span>][<span class="hljs-string">'Table'</span>] . <span class="hljs-string">"` VALUES\n"</span>;
                    } <span class="hljs-keyword">elseif</span> ($i &lt; $sizeof) {
                        $s .= <span class="hljs-string">"),\n"</span>;
                    } <span class="hljs-keyword">else</span> {
                        $s .= <span class="hljs-string">");\n"</span>;
                    }

                    fwrite($fp, $s);
                    ++$i;
                }
            }
            ++$found;
        }

        fclose($fp);
        <span class="hljs-keyword">if</span> ($found == <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">$this</span>-&gt;delete();
            <span class="hljs-keyword">echo</span> Context::getContext()-&gt;getTranslator()-&gt;trans(<span class="hljs-string">'No valid tables were found to backup.'</span>, <span class="hljs-keyword">array</span>(), <span class="hljs-string">'Admin.Advparameters.Notification'</span>);

            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
}
</code></pre>
<p>Executing a backup of only the tables we care about is as simple as running:</p>
<pre><code class="lang-php"><span class="hljs-comment">// The following code will create a new backup based on your file</span>
$backup = <span class="hljs-keyword">new</span> ProductLangTablesBackup();
$backup-&gt;add();
</code></pre>
<p>This is, of course, a very simple example of how you can make your work easier. If you need something more advanced, there are <a target="_blank" href="https://github.com/ramazancetinkaya/mysql-backup">ready-made libraries</a> you can use in your modules.
And finally, I’d like to recommend that module developers consider including such solutions in their products. More than once, it can help your users — and as you can see, the implementation is not complicated. 😉</p>
]]></content:encoded></item><item><title><![CDATA[Are the modules in your PrestaShop store safe?]]></title><description><![CDATA[The PrestaShop ecosystem is full of great modules, but it is also full of modules that are either years past their prime or that developers should have retired years ago. The topic of attacks on online stores based on open-source software is nothing ...]]></description><link>https://podemski.dev/are-the-modules-in-your-prestashop-store-safe</link><guid isPermaLink="true">https://podemski.dev/are-the-modules-in-your-prestashop-store-safe</guid><category><![CDATA[PrestaShop]]></category><category><![CDATA[Security]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Krystian Podemski]]></dc:creator><pubDate>Sun, 17 Aug 2025 18:38:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/yekGLpc3vro/upload/978d90395aa3b9b32e4a8fe8acca36dc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The PrestaShop ecosystem is full of great modules, but it is also full of modules that are either years past their prime or that developers should have retired years ago. The topic of attacks on online stores based on open-source software is nothing new; every platform struggles with this problem, including PrestaShop. However, there are initiatives in our ecosystem aimed at helping the community in this uneven fight. Meet Friends of Presta Security Advisories.</p>
<h2 id="heading-friends-of-presta">Friends of Presta</h2>
<p>Let’s start with what Friends of Presta actually is. As they write on their website:</p>
<blockquote>
<p>“Friends of Presta brings together the PrestaShop community consisting of programmers, integrators, agencies, and software publishers. We are the first network of technology experts around PrestaShop CMS.”</p>
</blockquote>
<p>This is an independent association of members of the PrestaShop community founded in France, which runs a series of initiatives in our ecosystem. One of them is “Security Advisories,” aimed at improving security in the ecosystem.</p>
<h2 id="heading-friends-of-presta-security-advisories">Friends of Presta Security Advisories</h2>
<p>“Friends of Presta” launched an initiative that consists of creating a catalog with information about vulnerabilities in modules available for PrestaShop. They respond to reports from the community, clients of the association’s members, investigate them, and make sure the whole process ends with patching the vulnerabilities.</p>
<p>They follow best practices for reporting such bugs, investigate the issue, contact the authors of modules where vulnerabilities have been found, and then share detailed information about the potential attack and the version containing the security patch. However, it sometimes happens that the author completely ignores communications from their side. Unfortunately, some popular marketplaces (not from Poland and not the official one) ignore such calls to react. In this case, the Friends of Presta maintainers recommend finding an alternative to the affected module.</p>
<p>The list of module security advisories on <a target="_blank" href="https://security.friendsofpresta.org/">this site</a> can undoubtedly make it easier for agencies and store owners to react even before an attack, which is the most important thing — to respond before it happens.</p>
<h2 id="heading-automatic-module-checking-in-your-store">Automatic module checking in your store</h2>
<p>Now that you know such an initiative exists, let’s move to the main point of this article. One of the community members has created a module that allows you to scan your stores in search of add-ons that have known security vulnerabilities.</p>
<p><a target="_blank" href="https://www.linkedin.com/in/walbacal/">Wilson Alba Cal</a> has shared a <a target="_blank" href="https://github.com/prestaalba/fop_publishedvulnerabilityscan">module</a> that fetches vulnerability information from the Friends of Presta site and creates a ready report available in your store’s back office. The module also includes an option for scheduling periodic tasks that will check the store, e.g., once a day, and if any modules requiring your attention are detected, an email will be sent to you.</p>
<p><img src="https://www.podemski.info/wp-content/uploads/2024/08/fop_securityadvisories-2048x634.jpeg" alt="Module screenshot" /></p>
<h2 id="heading-prestashop-store-security">PrestaShop store security</h2>
<p>It’s worth installing the described module, adding a recurring CRON task on your server, and helping yourself keep your stores secure. Of course, <strong>this is not a solution to every security problem</strong>. The security of any software is a complex matter. You should, above all, make sure to apply regular updates. If your store runs on an older version of PrestaShop, nothing prevents the agency that manages it from implementing only and exclusively security patches released for newer versions. Very often this is possible, sometimes it requires more work, but it’s certainly not as much as a full update.</p>
<p>It’s also worth remembering to properly care for the storage of your back office passwords — the access to your back office should be unique, maybe even cataloged in a <strong>secured password manager</strong>, and you might also consider using <strong>two-factor authentication modules</strong>. It’s equally important to update modules regularly. However, all of this does not mean that your store will never fall victim to an attack — unfortunately, quite the opposite. This does not only concern PrestaShop. Every month, similar cases occur in the WordPress or Magento ecosystems. Therefore, following the above recommendations or downloading and installing the module described in this article is just the first step.</p>
<h2 id="heading-the-foppublishedvulnerabilityscan-module">The <code>fop_publishedvulnerabilityscan</code> module</h2>
<p>The latest version of the module can be <a target="_blank" href="https://github.com/prestaalba/fop_publishedvulnerabilityscan">downloaded from GitHub</a> in the repository where its development is ongoing.  </p>
<p>If you are a developer and want to expand this module with new features, that’s of course possible too.</p>
<p>The module mentioned in this article is not the only community product designed to help you fight vulnerabilities in the PrestaShop ecosystem. In upcoming articles, we will cover more advanced solutions.</p>
]]></content:encoded></item><item><title><![CDATA[PrestaShop: adding new fields to the customer form]]></title><description><![CDATA[In this article, I will demonstrate how to edit, add, and modify fields available in the customer form, in PrestaShop.

One of the users of a popular Polish PrestaShop group on Facebook did not manage to modify the form fields available during the re...]]></description><link>https://podemski.dev/prestashop-adding-new-fields-to-the-customer-form</link><guid isPermaLink="true">https://podemski.dev/prestashop-adding-new-fields-to-the-customer-form</guid><category><![CDATA[PrestaShop]]></category><category><![CDATA[ecommerce]]></category><dc:creator><![CDATA[Krystian Podemski]]></dc:creator><pubDate>Fri, 14 Jan 2022 22:32:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1645391979841/iUzpi7gSN.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, I will demonstrate how to edit, add, and modify fields available in the customer form, in PrestaShop.</p>
<hr />
<p>One of the users of a popular Polish PrestaShop group on Facebook did not manage to modify the form fields available during the registration process. His task was to alter the "optin" field. He wanted to change both the content and style of the field.</p>
<blockquote>
<p>Code from this article will work with PrestaShop 1.7.7 and above versions.</p>
</blockquote>
<p>With help comes the "additionalCustomerFormFields" hook. Since PrestaShop version 1.7.7, it got the <code>&amp;$fields</code> parameter, which contains a whole array of fields used to create a registration form.</p>
<p>This article describes how we can use it.</p>
<h2 id="heading-how-does-it-work">How does it work?</h2>
<p>As in many other places in the system, we have a parameter that is available as a reference (<a target="_blank" href="https://www.php.net/manual/en/language.references.php">here's</a> a more detailed explanation of what references are), thanks to that, we can operate with the data freely, and we will apply our modifications after calling this Hook.</p>
<p>To get started, we need, of course, to hook to the appropriate place in the code in our module.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">install</span>(<span class="hljs-params"></span>)
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">parent</span>::install() &amp;&amp; <span class="hljs-keyword">$this</span>-&gt;registerHook(<span class="hljs-string">'additionalCustomerFormFields'</span>);
}
</code></pre>
<p>In our module, we have access to the entire array of fields in the method that handles the hook. You can <code>dump</code> them, like this:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hookAdditionalCustomerFormFields</span>(<span class="hljs-params">$params</span>)
</span>{
    $format = $params[<span class="hljs-string">'fields'</span>];
    dump($format);
}
</code></pre>
<p>If we go to the registration page at this point, we will see all the fields:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645391979841/iUzpi7gSN.jpeg" alt="form fields dump PrestaShop" /></p>
<p>The rest is just a formality. Let's move on to the real-world use of <code>hookAdditionalCustomerFormFields</code>.</p>
<h2 id="heading-adding-fields">Adding fields</h2>
<p>Each field is an instance of <code>FormField</code>. This is a class available globally in the system. We can add a new field like this:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hookAdditionalCustomerFormFields</span>(<span class="hljs-params">$params</span>)
</span>{
    $format = $params[<span class="hljs-string">'fields'</span>];
    $format[<span class="hljs-string">'confirmation_email'</span>] = (<span class="hljs-keyword">new</span> FormField())
        -&gt;setName(<span class="hljs-string">'confirmation_email'</span>)
        -&gt;setType(<span class="hljs-string">'email'</span>)
        -&gt;setLabel(<span class="hljs-keyword">$this</span>-&gt;trans(<span class="hljs-string">'Confirm your e-mail address'</span>, [], <span class="hljs-string">'Modules.Demooverridecustomerformatter.Front'</span>))
        -&gt;setRequired(<span class="hljs-literal">true</span>);
    $params[<span class="hljs-string">'fields'</span>] = $format;
}
</code></pre>
<p>This way, we added a new field to the form. This field called "confirmation_email" is a typical example of adding an element to force the buyer to confirm their email address.</p>
<h2 id="heading-deleting-fields">Deleting fields</h2>
<p>If we want to make any of the fields no longer available in the form, we can remove them using PHP's <code>unset</code>. Example:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hookAdditionalCustomerFormFields</span>(<span class="hljs-params">$params</span>)
</span>{
    $format = $params[<span class="hljs-string">'fields'</span>];
    <span class="hljs-keyword">unset</span>($format[<span class="hljs-string">'id_gender'</span>]);
    $params[<span class="hljs-string">'fields'</span>] = $format;
}
</code></pre>
<h2 id="heading-modifying-existing-fields">Modifying existing fields</h2>
<p>Let's see how we can modify fields, which the author of the thread on Facebook cared about. How to change the fields? Let's focus on the <code>optin</code> field. Let's modify its label and add a style (although this is not something we should do in this application layer).</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hookAdditionalCustomerFormFields</span>(<span class="hljs-params">$params</span>)
</span>{
    $format = $params[<span class="hljs-string">'fields'</span>];
    $newLabel = <span class="hljs-string">'&lt;em class="text-warning"&gt;'</span>;
    $newLabel .=    <span class="hljs-keyword">$this</span>-&gt;trans(
        <span class="hljs-string">'I want to receive free gift from your partners'</span>,
        [],
        <span class="hljs-string">'Modules.Demooverridecustomerformatter.Front'</span>
    );
    $newLabel .= <span class="hljs-string">'&lt;/em&gt;'</span>;
    $format[<span class="hljs-string">'optin'</span>]-&gt;setLabel(
        $newLabel
    );
    $params[<span class="hljs-string">'fields'</span>] = $format;
}
</code></pre>
<p>As we can see, by having access to an instance of a field, we can use methods available in the <code>FormField</code> class, one of them is <code>setLabel</code>.</p>
<p>We added <code>em</code>, color and changed the text, the effect is as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645391981673/mcwJt21tQ.jpeg" alt="example of modified customer form in PrestaShop" /></p>
<h2 id="heading-summary">Summary</h2>
<p>As we can see, modifying the registration form fields using a dedicated <code>Hook</code> is quite accessible and allows developers to have complete control over it. As the fields are an array, we can freely modify them, change their order, delete and add new ones.</p>
<p>This article did not cover all the issues related to the registration form and its fields, I did not show how to receive the values sent from the additional fields, verify and possibly save them in the database. There will be time for that at another time.</p>
<p>If you have any questions, let me know :-)</p>
]]></content:encoded></item><item><title><![CDATA[Better PrestaShop Profiler in version 1.7.8. How to use it in the older versions?]]></title><description><![CDATA[In version 1.7.8 of the PrestaShop returns the ability to collect more precise information about the modules that are displayed on the front office of your store.
Enabling the PrestaShop front office profiler is a great place to start if you need to ...]]></description><link>https://podemski.dev/better-prestashop-profiler-in-version-178-how-to-use-it-in-the-older-versions</link><guid isPermaLink="true">https://podemski.dev/better-prestashop-profiler-in-version-178-how-to-use-it-in-the-older-versions</guid><dc:creator><![CDATA[Krystian Podemski]]></dc:creator><pubDate>Sat, 08 Jan 2022 18:06:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1645391966074/g6z0EdCvb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In version 1.7.8 of the PrestaShop returns the ability to collect more precise information about the modules that are displayed on the front office of your store.</p>
<p>Enabling the PrestaShop front office profiler is a great place to start if you need to know more about the performance of your PrestaShop.</p>
<p>The data is collected both for hooks and modules attached to them, but also for widgets. If you don't have PrestaShop 1.7.8, don't worry. The profiler in the new version is very easy to use also with older versions of PrestaShop. In this post I will show you how to use it in the older versions.</p>
<p>I've tested it in PrestaShop 1.7.7, and 1.7.6.</p>
<h2 id="heading-using-the-profiler-from-178-in-lower-versions-of-prestashop">Using the profiler from 1.7.8 in lower versions of PrestaShop</h2>
<p>To use profiler in lower versions of PrestaShop, only 4 steps are needed.</p>
<ol>
<li>First download the PrestaShop 1.7.8 package, you can do it from <a target="_blank" href="https://github.com/PrestaShop/PrestaShop/tree/1.7.8.x">here</a>.</li>
<li>After unpacking it, copy the <code>tools/profiling</code> folder and move it to the same path in the older PrestaShop version.</li>
<li>Next step is to edit the <code>config/config.inc.php</code> file, just in the place where you currently have this code:</li>
</ol>
<pre><code class="lang-php"><span class="hljs-keyword">if</span> (_PS_DEBUG_PROFILING_) {
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Controller.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/ObjectModel.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Db.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Tools.php'</span>;
}
</code></pre>
<p>replace it with:</p>
<pre><code class="lang-php"><span class="hljs-keyword">if</span> (_PS_DEBUG_PROFILING_) {
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Profiler.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Controller.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/ObjectModel.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Db.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Hook.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Module.php'</span>;
    <span class="hljs-keyword">include_once</span> _PS_TOOL_DIR_ . <span class="hljs-string">'profiling/Tools.php'</span>;
}
</code></pre>
<p>The last step you need to do, is to edit the <code>controllers/admin/AdminLegacyLayoutController.php</code> file and delete:</p>
<p><code>$this-&gt;outPutHtml;</code></p>
<p>from the end of the <code>display</code> method.</p>
<p>Now if you set <code>_PS_DEBUG_PROFILING_</code> to <code>true</code> in the <code>config/defines.inc.php</code>, you can use a better version of the profiler in your PrestaShop store.</p>
<h2 id="heading-summary">Summary</h2>
<p>All this should give you more information about the health of your front office. You can expect more features available in the front office profiler in the next major version of PrestaShop.</p>
<p>Do you want to know more about the PrestaShop 8, and future of the PrestaShop? Read an article: <a target="_blank" href="https://build.prestashop.com/news/prestashop-beyond-1-7/">PrestaShop Beyond 1.7</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645391966074/g6z0EdCvb.jpeg" alt="PrestaShop front office profiler" /></p>
]]></content:encoded></item><item><title><![CDATA[Display extra column in the PrestaShop Grid]]></title><description><![CDATA[This article is about PrestaShop version 1.7.7 and above.
The order list in PrestaShop has quite a bit of information about them. One piece of information missing, however, is the name of the selected carrier.
In this article I will walk you through ...]]></description><link>https://podemski.dev/display-extra-column-in-the-prestashop-grid</link><guid isPermaLink="true">https://podemski.dev/display-extra-column-in-the-prestashop-grid</guid><dc:creator><![CDATA[Krystian Podemski]]></dc:creator><pubDate>Thu, 23 Dec 2021 08:14:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1645391938177/PP4JTXmtZ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This article is about PrestaShop version 1.7.7 and above.</em></p>
<p>The order list in <a target="_blank" href="https://github.com/PrestaShop/PrestaShop">PrestaShop</a> has quite a bit of information about them. One piece of information missing, however, is the name of the selected carrier.</p>
<p>In this article I will walk you through the module that I published here:</p>
<p><a target="_blank" href="https://github.com/kpodemski/kporderlistcarrier">https://github.com/kpodemski/kporderlistcarrier</a></p>
<p>This module allows you to add carrier name to the order list and filter by it. The whole thing is based on <a target="_blank" href="https://devdocs.prestashop.com/1.7/modules/concepts/hooks/#hooks">Hook</a>, which is available in PrestaShop standard but it works only with the Symfony version of this page. That is why the module will work only for PrestaShop 1.7.7+</p>
<h2 id="heading-how-it-works-grid-component">How it works. Grid component</h2>
<p>At this moment, any list of elements taken from the database, on pages that have been migrated to Symfony in PrestaShop, uses a new component: <a target="_blank" href="https://devdocs.prestashop.com/1.7/development/components/grid/">Grid</a>.</p>
<p>This component is similar to <em>HelperList</em>, which you may know from older versions of PrestaShop. <em>HelperList</em> is still present in the core, but you might want to use a modern alternative, which is Grid.</p>
<p>With this component, we will display a list of elements e.g. from the database. We will add options to search through it, sorting, we can also enable various operations on records, deleting, editing, or other. We can also define them ourselves.</p>
<p>Detailed information about the component is available in PrestaShop developers' documentation, you can find there examples of usage, default data types and much more about this component.</p>
<h2 id="heading-example-module">Example module</h2>
<p>The solution I linked above uses the ability to modify the lists created by this component, directly from within the module. As you can see, I have attached to two Hooks:</p>
<pre><code class="lang-php"><span class="hljs-comment">/** <span class="hljs-doctag">@var</span> array */</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> MODULE_HOOKS = [
    <span class="hljs-string">'actionOrderGridDefinitionModifier'</span>,
    <span class="hljs-string">'actionOrderGridQueryBuilderModifier'</span>,
];
</code></pre>
<p>The names of these Hooks are dynamic. You may want to hook to the customer list instead of the order list by replacing <em>Order</em> with <em>Customer</em>. You can find yet another example that executes hooks like these in the file: <code>/src/Core/Grid/GridFactory.php</code>.</p>
<pre><code class="lang-php"><span class="hljs-comment">/**
 * {<span class="hljs-doctag">@inheritdoc</span>}
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGrid</span>(<span class="hljs-params">SearchCriteriaInterface $searchCriteria</span>)
</span>{
    $definition = <span class="hljs-keyword">$this</span>-&gt;definitionFactory-&gt;getDefinition();
    $data = <span class="hljs-keyword">$this</span>-&gt;dataFactory-&gt;getData($searchCriteria);

    <span class="hljs-comment">// Here it is, hook that will modify the Grid data</span>
    <span class="hljs-keyword">$this</span>-&gt;hookDispatcher-&gt;dispatchWithParameters(<span class="hljs-string">'action'</span> . Container::camelize($definition-&gt;getId()) . <span class="hljs-string">'GridDataModifier'</span>, [
        <span class="hljs-string">'data'</span> =&gt; &amp;$data,
    ]);

    $filterForm = <span class="hljs-keyword">$this</span>-&gt;filterFormFactory-&gt;create($definition);
    $filterForm-&gt;setData($searchCriteria-&gt;getFilters());
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Grid(
        $definition,
        $data,
        $searchCriteria,
        $filterForm
    );
}
</code></pre>
<p>The Hook name is created as follows:
<code>action&lt;Identifier of a given component&gt;GridDataModifier</code>.</p>
<p>The identifier is for example:
<code>const GRID_ID = 'customer';</code></p>
<p>This <code>const</code> is defined here: <code>/src/Core/Grid/Definition/Factory/CustomerGridDefinitionFactory.php</code></p>
<p>Once we are hooked to the appropriate places in the core, we can execute our code. As it usually happens, in the implementation of a given Hook we get in the <code>$params</code> array, the appropriate "tools" to be able to modify the selected component.</p>
<p>In the case of <code>hookActionOrderGridDefinitionModifier</code>, we have access to the Grid component definition, which is hidden behind the <code>GridDefinitionInterface</code> implementation.</p>
<p>Using the <code>$definition</code> variable in the code, we can manipulate the columns in the listing. In the case of the sample module, we add the <code>carrier_name</code> column, which we display after the payment column. How do we know that the payment name column is payment? We can use, for example, the developer tools in the Chrome browser:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645391938177/PP4JTXmtZ.png" alt="Example dev tools PrestaShop" /></p>
<p>Next, we assign a filter to the column so that we can look for orders that should be shipped with the carrier we are looking for:</p>
<pre><code class="lang-php">$filters = $definition-&gt;getFilters();
$filters-&gt;add((<span class="hljs-keyword">new</span> Filter(<span class="hljs-built_in">static</span>::CARRIER_FIELD_NAME, TextType::class))
    -&gt;setTypeOptions([
        <span class="hljs-string">'required'</span> =&gt; <span class="hljs-literal">false</span>,
    ])
    -&gt;setAssociatedColumn(<span class="hljs-built_in">static</span>::CARRIER_FIELD_NAME)
);
</code></pre>
<p>The code that allows us to retrieve the carrier name is a bit more complicated, but the principle is the same. We get the <code>$params</code> argument to modify the information retrieval for a given list and we do what we need with it.</p>
<p>The Grid component does not use the database legacy code, but the Doctrine one, <code>$params['search_query_builder']</code> is a Doctrine instance of <code>QueryBuilder</code>.</p>
<p>In the <code>addSelect</code> method we can... yes, add the fields we want to retrieve. This works very similar to the DbQuery class that you may now from PrestaShop core.</p>
<p>It may puzzle you that at this point in the code:</p>
<pre><code class="lang-php">$queryBuilder-&gt;addSelect(
    <span class="hljs-string">'IF(carrier.name = "0", "'</span>.Configuration::get(<span class="hljs-string">'PS_SHOP_NAME'</span>).<span class="hljs-string">'", carrier.name) carrier_name'</span>
);
</code></pre>
<p>We check that the record is not "0". We do this because PrestaShop will set "0" when we select a carrier, which is added by default in the installer as "In-Store Pickup". This is a relic of the past, which should be straightened out in version 8.0 🙂</p>
<p>In the following code snippets, we already rely on search criteria. With this code, we control how we sort the listing and possibly filter it.</p>
<p>The whole process may seem very similar for developers who have already worked with Hooks like <code>actionSomethingListingFieldsModifier</code>. And, of course, it is.</p>
<h2 id="heading-advanced-grid-usage">Advanced Grid usage</h2>
<p>Of course, the sample module that I give you is the simplest example of how to modify the Grid component. Grid is a very flexible mechanism that will allow you to add your own implementation of various mass actions that can be performed on records. There are also many possibilities of formatting the output data, etc.</p>
<p>I encourage you to read the <a target="_blank" href="https://devdocs.prestashop.com/">documentation for developers</a> and check the repository with more sample PrestaShop modules <a target="_blank" href="https://github.com/PrestaShop/example-modules/">here</a>.</p>
<p>btw. this is my first article here, your feedback will be a gift 🙏🏻</p>
]]></content:encoded></item></channel></rss>