Nowy Dla programisty, PrestaShop

Klasa PrestaShopBackup. Jak jej użyć w Twoich rozwiązaniach?

PrestaShop to rozbudowana platforma e-commerce, zaprojektowana nie tylko dla merchantów, ale też – a może nawet przede wszystkim – dla developerów, którzy pragną tworzyć spersonalizowane rozwiązania. Oferuje mnóstwo wbudowanych funkcji, które można rozszerzać i dostosowywać do konkretnych potrzeb, minimalizując konieczność tworzenia funkcji od podstaw. Jednym z mniej znanych, ale użytecznych narzędzi jest klasa PrestaShopBackup, która upraszcza implementację mechanizmów tworzenia kopii zapasowych.

classes/PrestaShopBackup.php

Klasa PrestaShopBackup istnieje aby ułatwić tworzenie kopii zapasowych bazy danych. Zawiera całą niezbędną logikę do zbierania tabel bazy danych, generowania nazwy pliku kopii zapasowej oraz zapisywania danych kopii zapasowej do pliku. Domyślnie jest ona używana w zakładce Zaawansowane -> Baza danych -> Kopia zapasowa DB, jednak aby zilustrować praktyczne zastosowanie klasy PrestaShopBackup we własnym rozwiązaniu, rozważmy scenariusz, w którym potrzebujesz mechanizmu tworzenia kopii zapasowej dla konkretnych tabel związanych z językami produktów i kategorii. Umownie potrzebujemy właśnie tych tabel ponieważ stworzyliśmy mechanizm, który na przykład generuje przyjazne odnośniki i inne informacje dla SEO produktów na bazie podanych kryteriów jednak chcemy mieć pewność, że zawsze użytkownik będzie posiadać kopię zapasową.

Nie wynajduj koła na nowo

Zamiast budować to od podstaw, możesz rozszerzyć klasę PrestaShopBackup, tworząc klasę nazwaną np. ProductLangTablesBackup. Za utworzenie i zapisanie nowej kopii zapasowej odpowiada metoda PrestaShopBackupCore::add(), domyślnie tworzy ona kopie zapasową wszystkich danych w Twoim sklepie (poza kilkoma tabelami z np. statystykami jeżeli kopie zostały tak skonfigurowane). Nas oczywiście nie interesuje pełna kopia zapasowa, dlatego w naszym kodzie musimy dodać warunek, który po prostu pominie wszystkie tabele, które nas nie interesują.

Przykład

Poniżej znajdziesz kod, którego używałem w jednym z projektów, bazuje on na klasie z PrestaShop 1.7.8, jeżeli chcesz podążyć za tym artykułem i stworzyć coś dla swojego rozwiązania, koniecznie użyj jako bazy kodu z PrestaShop 8.1. W kodzie zaznaczone są linie, które zostały zmodyfikowane w stosunku do oryginalnego pliku. Zmieniliśmy:
– nazwę pliku
– odwróciliśmy warunek tworzenia kopii, zamiast ignorować wybrane tabele, zezwalamy na wybrane tabele

Najważniejsze zmiany są podświetlone w kodzie poniżej.

<?php

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

        // Figure out what compression is available and open the file
        if (function_exists('bzopen')) {
            $backupfile .= '.bz2';
            $fp = @bzopen($backupfile, 'w');
        } elseif (function_exists('gzopen')) {
            $backupfile .= '.gz';
            $fp = @gzopen($backupfile, 'w');
        } else {
            $fp = @fopen($backupfile, 'wb');
        }

        if ($fp === false) {
            echo Context::getContext()->getTranslator()->trans('Unable to create backup file', array(), 'Admin.Advparameters.Notification') . ' "' . addslashes($backupfile) . '"';

            return false;
        }

        $this->id = realpath($backupfile);

        fwrite($fp, '/* Backup for ' . Tools::getHttpHost(false, false) . __PS_BASE_URI__ . "\n *  at " . date($date) . "\n */\n");
        fwrite($fp, "\n" . 'SET NAMES \'utf8\';');
        fwrite($fp, "\n" . 'SET FOREIGN_KEY_CHECKS = 0;');
        fwrite($fp, "\n" . 'SET SESSION sql_mode = \'\';' . "\n\n");

        // Find all tables
        $tables = Db::getInstance()->executeS('SHOW TABLES');
        $found = 0;
        foreach ($tables as $table) {
            $table = current($table);

            // Skip tables which do not start with _DB_PREFIX_
            if (strlen($table) < strlen(_DB_PREFIX_) || strncmp($table, _DB_PREFIX_, strlen(_DB_PREFIX_)) != 0) {
                continue;
            }

            if (!in_array($table, $tablesToBackup)) {
                continue;
            }

            // Export the table schema
            $schema = Db::getInstance()->executeS('SHOW CREATE TABLE `' . $table . '`');

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

                return false;
            }

            fwrite($fp, '/* Scheme for table ' . $schema[0]['Table'] . " */\n");

            if ($this->psBackupDropTable) {
                fwrite($fp, 'DROP TABLE IF EXISTS `' . $schema[0]['Table'] . '`;' . "\n");
            }

            fwrite($fp, $schema[0]['Create Table'] . ";\n\n");

            $data = Db::getInstance()->query('SELECT * FROM `' . $schema[0]['Table'] . '`', false);
            $sizeof = Db::getInstance()->numRows();
            $lines = explode("\n", $schema[0]['Create Table']);

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

                    foreach ($row as $field => $value) {
                        $tmp = "'" . pSQL($value, true) . "',";
                        if ($tmp != "'',") {
                            $s .= $tmp;
                        } else {
                            foreach ($lines as $line) {
                                if (strpos($line, '`' . $field . '`') !== false) {
                                    if (preg_match('/(.*NOT NULL.*)/Ui', $line)) {
                                        $s .= "'',";
                                    } else {
                                        $s .= 'NULL,';
                                    }

                                    break;
                                }
                            }
                        }
                    }
                    $s = rtrim($s, ',');

                    if ($i % 200 == 0 && $i < $sizeof) {
                        $s .= ");\nINSERT INTO `" . $schema[0]['Table'] . "` VALUES\n";
                    } elseif ($i < $sizeof) {
                        $s .= "),\n";
                    } else {
                        $s .= ");\n";
                    }

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

        fclose($fp);
        if ($found == 0) {
            $this->delete();
            echo Context::getContext()->getTranslator()->trans('No valid tables were found to backup.', array(), 'Admin.Advparameters.Notification');

            return false;
        }

        return true;
    }
}

Wykonanie kopii zapasowej tabel, które nas interesują to po prostu wykonanie kodu:

// Poniższy kod utworzy nową kopię zapasową bazując na Twoim pliku
$backup = new ProductLangTablesBackup();
$backup->add();

Jest to oczywiście bardzo, bardzo prosty przykład tego jak można ułatwić sobie prace. Jeżeli potrzebujemy czegoś bardziej zaawansowanego, są oczywiście gotowe biblioteki, których możemy używać w naszych modułach. No i na koniec chciałbym zarekomendować developerom modułów aby takie rozwiązania znajdowały się w ich produktach. Nie raz, nie dwa, może pomóc to Waszym użytkownikom, a implementacja – jak widać – nie jest skomplikowana 😉

Udostępnij

Zobacz inne