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 😉