Návrh databázové třídy - díl IV: Export databáze

 |  PHP pro pokročilé  |  4 374x
Návrh databázové třídy - díl IV: Export databáze

Dnešní pokračování pohádky o databázové třídě si vezme pod lupu pár konkrétních metod. Půjde o funkce na export celé databáze s možností force download nebo postupného ukládání do souboru. O možnostech exportu databáze moc článků napsáno nebylo, navíc se nejedná o úplně triviální algoritmus s pár vnořenými cykly. V následujícím článku vám předvedu, jak problém vyřešit s elegancí.

Kostra řešení bude poměrně jednoduchá:

SHOW TABLES

foreach ($tables) {
	SHOW CREATE TABLE
	SELECT * FROM $table
	while (FETCH_ROW) {
		foreach ($row) {
			mysql_real_escape_string($row);					
		}
		$export .= "INSERT INTO $table VALUES implode(, $row)"
	}
}

Nejdříve zjistíme, jaké tabulky exportujeme, následně od každé z nich získáme strukturu přes SHOW CREATE TABLE, vybereme hvězdičku, provedeme escapování a uložíme znění SQL dotazu na insert.

Metody na export databáze budou 2. Budou si podobné, protože v rámci zachování čitelnosti kódu není dobry nápad cpát do už tak velké funkce hromadu podmínek. První metoda uloží celý export do proměnné a nabídne soubor k uložení, druhá bude data postupně ukládat do souboru, který si pak můžeme stáhnout přes FTP klienta.

Před uvedením výsledné funkce si ale nejprve ukážeme pár podpůrných metod, jenž se nám budou hodit. O prováděném exportu chceme samozřejmě mít nějaké zběžné informace, které nám poskytnou následující funkce.

private function prepareBackupInfo(){
	$this->backupInfo['date_pattern'] =  @date('Y-m-d_H.i.s');
	$this->backupInfo['timestamp'] = strtr($this->backupInfo['date_pattern'], array('_' => ' ', '.' => ':'));
	$this->backupInfo['start_time'] = $this->startTime();
	$this->backupInfo['queries'] = 0;
	$this->backupInfo['written'] = 0;
}

private function appendBackupInfo(){
	$output = "";
	$output .= "\n";
	$output .= "--\n";
	$output .= "-- Queries contained: ".$this->backupInfo['queries']."\n";
	$output .= "-- Shots to file: ".$this->backupInfo['written']."\n";
	$output .= "-- Total time: ".$this->stopTime($this->backupInfo['start_time'])."\n";
	$output .= "-- Timestamp start: ".$this->backupInfo['timestamp']."\n";
	$output .= "--\n";
	return $output;
}

Metoda DB::downloadHeaders() zašle potřebné hlavičky pro force download, naproti tomu Db::writeToFile() bude obsluhovat průběžný zápis do souboru. Nakonec uvedu ještě funkci Db::addExportCondition(), kterou využijeme v případě, když budeme chtít exportovat pouze některé tabulky nebo jejich části.

private function downloadHeaders($file_size, $file_type, $file_name){
	header("Pragma: public");
	header("Expires: 0");
	header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
	header("Cache-Control: private", false);
	header("Content-Transfer-Encoding: binary");
	header("Content-Type: ".$file_type);
	header("Content-Length: ".$file_size);
	header("Content-Disposition: attachment; filename=".$file_name);
}

// pouzivano na nekolika mistech v zaloze
// zapis do souboru
// @param string $file_name
// @param string $content
// @return null abych si automaticky vynulloval promennou
public function writeToFile($file_name, $content){
	$file = fopen($file_name, 'a');
	fwrite($file, $content);
	fclose($file);
	return null;
}

public function addExportCondition($table, $where = null){
	$this->exportConditions[$table] = $where; 
}

Nyní už se můžeme pustit do exportu. Funkci uvedu pouze jednu, metodu Db::dumpAndSave() - tu složitější. Tělo pouze kopíruje úvodní vzor, přidaná je podmínka na kontrolu nastavení exportu (konkrétní tabulky a vnitřní podmínky).

public function dumpAndSave(){
	$limit = 5000000;
	
	$this->prepareBackupInfo();
	$file_name = $this->backupFolder.$this->database.'_'.$this->backupInfo['date_pattern'].'.sql';
	
	if($this->backupFolder && !is_dir($this->backupFolder)){
		mkdir($this->backupFolder, '0777');
	}
	
	$_tables = array();
	$_select = $this->query('SHOW TABLES');
	
	while($t = $_select->fetch(FETCH_ROW)){
		$_tables[] = $t[0];
	}
	
	unset($_select);

	$_output = null;
	$_output = "--\n-- Database: `".$this->database."`\n--\n\n-- --------------------------------------------------------\n\n";
	$_output = $this->writeToFile($file_name, $_output);
	
	// zaloha po jedne tabulce
	foreach ($_tables as $value){
		if(!count($this->exportConditions) || (count($this->exportConditions) && isset($this->exportConditions[$value]))){
		
			// show create table
			$_select_ct = $this->query("SHOW CREATE TABLE `{$value}`");
			$_row = $_select_ct->fetch(FETCH_ROW);
			
			// ukladame do souboru show create table
			$_output = null;
			$_output .= "--\n";
			$_output .= "-- `".$_row[0]."`\n";
			$_output .= "--\n\n";
			$_output .= $_row[1]." ;\n\n";
			$_output = $this->writeToFile($file_name, $_output);
			
			unset($_select_ct);
			
			++$this->backupInfo['queries'];

			if(isset($this->exportConditions[$value])){
				$_select = $this->query("SELECT// FROM `{$value}` {$this->exportConditions[$value]}");
			}else{
				$_select = $this->query("SELECT// FROM `{$value}`");
			}
			
			while($t = $_select->fetch(FETCH_ROW)){
				foreach ($t as $k => $i){
					$t[$k] = "'".$this->escape($i)."'";
				}
				
				$_output .= "INSERT INTO `{$value}` VALUES (".implode(',', $t).");\n";
				++$this->backupInfo['queries'];
				
				// co 5 mega, to zapis do souboru a vynulovani promenne. abych neshazoval server
				if(strlen($_output) > $limit){
					$_output = $this->writeToFile($file_name, $_output);
					++$this->backupInfo['written'];
				}
			}
			
			unset($_select);
			
			// pokud tu jsou nejake resty. zbavime se jich. tady uz tech 5 mega fakt testovat nebudu a rovnou to zapisu
			if($_output){
				$_output = $this->writeToFile($file_name, $_output);
				++$this->backupInfo['written'];
			}
		}
	}
	
	$_output .= $this->appendBackupInfo();
	$_output = $this->writeToFile($file_name, $_output);
}

Aplikace metod bude opět naprosto triviální. Zakomentované řádky nastaví, že se exportuje celá tabulka s kategoriemi a tabulka s produkty zařazenými do kategorie 1, 2 nebo 3.

$db = new Db;
// $db->addExportCondition('shop_categories', '');
// $db->addExportCondition('shop_products', 'WHERE category_id IN (1,2,3)');
$db->dumpAndSave();

Znění druhé funkce si většina z vás jistě domyslí. Ostatní si budou muset počkat na zveřejnění zdrojového kódu, které je naplánováno na šestý díl. Příště si povíme něco o logování změn.

Facebook Twitter Google+

Komentáře k článku "Návrh databázové třídy - díl IV: Export databáze"

Gravatar
Filip 12. 11 2010, 19:59
1/5 Pátek 12. Listopadu 2010, 19:59  |  Chrome, Windows XP

Ahoj
tvé články se mi moc líbí a často se zde inspiruji při psaní kódu, ale je škoda, že články nevydáváš nijak pravidelně (třeba 1 x měsíčně). Já a jistě i spousta tvých čtenářů, se těšíme na pokračování tohoto článku.

S pozdravem Filda

Gravatar
Mike 13. 11 2010, 18:27
2/5 Sobota 13. Listopadu 2010, 18:27  |  Opera, Windows XP

@: Ahoj, v prvé řadě díky tvůj zájem. Bohužel mohu říct jediné, a to, že články píšu pouze když na ně mám čas. A posledních pár měsíců čas vůbec nebyl... Žádnou pravidelnost slíbit nemohu, ale udělám vše pro to, aby to napravil :-)

Gravatar
Vladimir Kasik 7. 1 2011, 15:11
3/5 Pátek 7. Ledna 2011, 15:11  |  Firefox, Windows Vista

Fascinuji me talentovani lide,a ty jsi jednoznacne jeden z nich.

Gravatar
Mike 10. 1 2011, 23:38
4/5 Pondělí 10. Ledna 2011, 23:38  |  Opera, Windows XP

@: Díky ;-)

Gravatar
Marek Šloser 26. 4 2012, 02:25
5/5 Čtvrtek 26. Dubna 2012, 02:25  |  Firefox, Windows 7

Ahoj,

jsi první člověk kterej napsal serial článku tak kvalitně že se fakt naučim OOP. Tohle je to co jsem potřeboval vědet.

Moc díky a makej makej tešim se na další článek :)

Přidat komentář







Nevím, kolik to je
Parak simati, Muballit mitte, Nergal allatu mellamu mesaru, La tapallah Annuaki, Kettu Puluthu qillatua