Schnäppchen

Kategorien

Datenbank Backup in CakePHP

Eigentlich finde ich es günstiger, die Sicherungskopien der Datenbank von Cron erledigen zu lassen, aber der Kunde ist König. Wenn er unbedingt eine Funktion zum Backup der Datenbank will, dann bekommt er sie.

Dieses ist eine frühe aber durchaus funktionierende Version meines CakePHP Datenbank Backup Controllers. Sicherheitshalber der Hinweis, dass für eine Produktionsversion noch ein paar Punkte bedacht werden müssen:

  • Autentifizierung
  • Zugriffsschutz für den Download-Ordner (z.B. über htaccess)
  • Bessere Fehlerbehandlung
  • Ein sinnvolles Verfahren, um die erstellten SQL-Dumps wieder zu löschen.

Mein BackupsController hat übrigens keine eigene Datenbanktabelle “backups” und auch kein Model. Er benutzt daher pro forma das Model der Tabelle “users”.

Zur Installation wird der PHP-Code für den Controller nach …/app/controllers/backups_controller.php kopiert. Der Code für die View kommt nach …/app/views/backups/index.ctp

PHP-Code für den BackupsController
(…/app/controllers/backups_controller.php):

<?php

/**
 * Very simple database backup in CakePHP
 *
 * CakePHP controller class to create a database backup (SQL-dump) of a CakePHP project Database.
 *
 * (Little) tested with CakePHP 1.3.4, PHP 5.2.6. MySQL 5.0.51a
 *
 * @author        Heiner Otterstedt <h.otterstedt@gmail.com>
 * @copyright     Copyright 2010, Heiner Otterstedt
 * @version       0.1.12
 * @link          http://maheo.eu
 * @license       GPL 3.0 (http://www.gnu.org/licenses/gpl-3.0.html)
 * $sorry         German comments only
 */

class BackupsController extends AppController
{ var $name = 'Backups';
  var $uses = array('User');

  function index()
  { // Ein paar projekt-abhängige Variablen
    $database = 'katalog';
    $filename = 'sqldump-' . $database . '-' . date('Ymd-His') . '.sql';
    $limit    = 120; // zwei Minuten

    // vielleicht brauche ich die Variablen noch im View
    $this->set('database', $database);
    $this->set('filename', $filename);
    $this->set('limit', $limit);

    // bei großen Datenbanken ...
    set_time_limit($limit);

    // open...
    $fh = fopen($filename, 'w');
    if(!$fh)
    { $this->Session->setFlash('Die Datei ' . $filename . ' konnte nicht geöffnte werden.');
      $this->redirect('/');
    }

    $tables = $this->User->query('SHOW TABLES FROM katalog');

    foreach($tables as $table)
    { $tableName = $table['TABLE_NAMES']['Tables_in_katalog'];

      $fields = array();
      $types  = array();

      $description = $this->User->query('DESCRIBE ' . $tableName);
     
      foreach($description as $field)
      { //debug($field);
        $fields[] = $field['COLUMNS']['Field'];
        $types[]  = $field['COLUMNS']['Type'];

        // die folgenden werden nur für createCreate benötigt
        //$nix[]     = $field['COLUMNS']['Null'];
        //$key[]     = $field['COLUMNS']['Key'];
        //$default[] = $field['COLUMNS']['Default'];
        //$extra[]   = $field['COLUMNS']['Extra'];
      }

      /*
       * Falls die Tables auch created werden sollen ...
       * Bei mir ist das nicht nötig, dafür habe ich immer ein
       * passendes Shell-Script. Deshalb ist die Funktion hier
       * nicht implementiert.
       */
      //$create = $this->__createCreate($tableName, $fields, $types, $nix, $key, $default, $extra);

      // Select zusammen basteln
      $select = $this->__createSelect($tableName, $fields);

      // Select ausführen
      $query = $this->User->query($select);

      // Ergeblis in die Backupdatei schreiben
      fwrite($fh, $this->__createInsert($tableName, $fields, $query) . "\n");
    }
    fclose($fh);
    $this->Session->setFlash('Alles klar, der SQL-Dump wurde gespeichert.');
  }

  function __createSelect($tableName, $fields)
  { $select = 'SELECT ';
    for($i = 0; $i < count($fields); $i++)
    { $field = $fields[$i];
      $select .= $field;
      if($i < count($fields) - 1) { $select .= ', '; }
    }
    $select .= ' FROM ' . $tableName . ';';
    return $select;
  }

  function __createInsert($tableName, $fields, $query)
  { if(empty($query))
    { return '';
    }
    $insert = 'INSERT INTO ' . $tableName . ' (';
    for($i = 0; $i < count($fields); $i++)
    { $field = $fields[$i];
      $insert .= $field;
      if($i < count($fields) - 1) { $insert .= ', '; }
    }
    $insert .= ') VALUES ';
    for($j = 0; $j < count($query); $j++)
    { $insert .= '(';
      for($i = 0; $i < count($fields); $i++)
      { $field = $fields[$i];
        $insert .= "'" . $query[$j][$tableName][$field] . "'";
        if($i < count($fields) - 1) { $insert .= ', '; }
      }
      $insert .= ')';
      if($j < count($query) - 1) { $insert .= ', '; }
    }
    // Hier im dump brauchen wir auch das ';'
    return $insert . ';';
  }
} /* /class BackupsController */
?>

Und Hier der Code für eine sehr einfache View
(…/app/views/backups/index.ctp):

<?php
echo '<p><strong>' . $session->flash() . "</strong></p><br/>";
echo '<p><strong>Wenn keine Fehler aufgetreten sind, dann findest Du den SQL-Dump der Datenbank "' . $database . '" <a href="' . $filename . '">hier</a>.</strong></p>';
?>

Mein Dank für die gedankliche Vorarbeit geht an den Benutzers theOracle auf cakephp-forum.com.

2 Kommentare zu Datenbank Backup in CakePHP

  • mark

    i am afraid that this could cause memory problems with a lot of records (> 50000 etc?)
    maybe command line commands for sql backups could be the the solution – if you have access to it.

    • IT

      Yes, you’re right. This is definitely a solution for small databases only. In very big databases I would also expect long execution-times (and timeouts).

      As you suggested as well, I would always recommend “mysqldump -opt” in a cron job.

Hinterlassen Sie eine Antwort

 

 

 

Sie können diese HTML Tags verwenden

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

eMail-Benachrichtigung bei weiteren Kommentaren.
Auch möglich: Abo ohne Kommentar.