Laravel Database Backup — Backup & Mail

Photo by Kevin Ku on Unsplash

Disclaimer:

This content is developed to give junior-intermediate Laravel developers (or enthusiasts) a general sense of what’s made possible via a handful of batteries shipped out-of-the-box with Spatie’s laravel-backup package. My featured implementation isn’t necessarily ‘perfect/superior/acclaimed’. My intention is to help you find a path to your own ‘perfect’ implementation — one that’ll hopefully suit your use case better.

Before we begin, I’d like to assume a few things — not covered herein:

  • You have a working installation of Laravel based application
  • Your database is setup
  • You’ve already wired-up your mail driver
  • and you have a terminal in place to run some commands

Some technical details about my local setup — just for reference

Versions:

  • Nginx ~ 1.16
  • MariaDB ~ 10.2.3
  • PHP ~ 7.4
  • Laravel ~ 6.13

Spatie who?!—

Spatie is a Belgium based company whose team is comprised of superhero coders exceptionally committed to making very generous contributions to the PHP and Laravel opensource communities: https://docs.spatie.be/

The Implementation

Checkout the list of requirements over at laravel-backup docs page, and be sure to select the version compatible with your setup. You might also need to double-check that you have Guzzle already installed. If you don’t have Guzzle (HTTP client) listed on the ‘require’ object of your composer.json file, you may need to run the following line of code in your terminal first:

composer require guzzlehttp/guzzle

If everything checks out, you can go on to installing the package; V6 in my case:

Step 1 — Install the package via composer:

composer require spatie/laravel-backup

Note: Your config/app.php file should be automatically populated with the reference to the newly registered service provider: Spatie\Backup\BackupServiceProvider::class

Step 2 — publish the package’s configuration file with:

php artisan vendor:publish — provider=”Spatie\Backup\BackupServiceProvider”

This copies /vendor/spatie/laravel-backup/config/laravel-backup.php and publishes it to your app’s /config path, giving you control over a bunch of configuration options.

Let’s make a quick update in our newly published configuration file —

From:

‘mail’ => [
‘to’ => ‘your@example.com’,
],

To:

‘mail’ => [
‘to’ => env(‘DB_BACKUP_EMAIL’),
],

Also:

if you just want to backup your database alone —

Then comment this base_path() out in ‘include’ => []

/*
* The list of directories and files that will be included in the backup.
*/
‘include’ => [
// base_path(),
],

To specify an email address to send our backups, and backup notifications to.

Now, as you may have observed; we’ll need to update our .env file with the value of our newly declared environment variable DB_BACKUP_EMAIL.

Your entry is expected to look something like this: DB_BACKUP_EMAIL=examplemail@mail.com //(and make sure its an actual email addy)

Step 3 — schedule backup operation(s)

Open up your /app/Console/Kernel.php and add these predefined laravel-backup scheduling commands to the list of actions within the schedule method, and update the default times based on your preference:

$schedule->command(‘backup:clean’)->daily()->at(‘01:00’);
$schedule->command(‘backup:run’)->daily()->at(‘02:00’);

You’ll find details of other enabled commands you can plug into on the docs page.

Quick Test

At this point, we can already test out our backup system by running:

php artisan backup:run

— In our terminal, to initiate a backup process. And with any luck, the backup should run itch-free.

You should also get a notification (by mail or depending on whether or not you set up other notification channels)

Step 4 — listen for Spatie\Backup\Events\BackupZipWasCreated

This next step is crucial to the scope of this tutorial — to listen for the aforementioned event and handle it accordingly.

Every time a backup operation succeeds, laravel-backup fires an event defined in the Spatie\Backup\Events\BackupZipWasCreated class, to notify dependent classes that it’s time to be initiated.

Let's create a listener to help us monitor this event propagation, shall we?

php artisan make:listener MailSuccessfulDatabaseBackup -e \Spatie\Backup\Events\BackupZipWasCreated

Then we’ll register our newly created listener with the corresponding event in our EventServiceProvider.php

protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
\Spatie\Backup\Events\BackupZipWasCreated::class => [
MailSuccessfulDatabaseBackup::class
],
];

The handle method on this newly created listener class accepts the instance of the event we’re listening in on — which exposes a property named: $pathToZip that returns (string) the actual path to the zipped backup file.

You can test this out by modifying the boilerplate code inside your MailSuccessfulDatabaseBackup class —

<?php

namespace
App\Listeners;

// use Illuminate\Contracts\Queue\ShouldQueue;
// use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Mail;
use Spatie\Backup\Events\BackupZipWasCreated;

class MailSuccessfulDatabaseBackup
{
/**
* Create the event listener.
*
*
@return void
*/
public function __construct()
{
//
}

/**
* Handle the event.
*
*
@param BackupZipWasCreated $event
*
@return void
*/
public function handle(BackupZipWasCreated $event)
{
$this->mailBackupFile($event->pathToZip);
}

public function mailBackupFile($path)
{
try {
Mail::raw('You have a new database backup file.', function ($message) use ($path) {
$message->to(env('DB_BACKUP_EMAIL'))
->subject('DB Auto-backup Done')
->attach($path);
});
} catch (\Exception $exception) {
throw $exception;
}

}
}

Long story short —

Via your listener, the path to your backup is captured through the $pathToZip property of the instance of the BackupZipWasCreated every time the event is fired; the path is then included as a mail attachment in your event listener’s handler.

Extra Sauce:

You can set up a route in your route files (web.php) to trigger your backup action directly with a callback, or your typical controller method reference, in lieu of running a command in your terminal, all depending on your use-case. See a simple implementation below — it could be triggered with a button click or an HTTP request directly in your browser or from anywhere in your application.

Route::get('/backup', function () {

\Illuminate\Support\Facades\Artisan::call('backup:run');

return 'Successful backup!';

});

Once you’ve run a successful backup, you’ll get two emails

  • a notification of the successful operation
  • and your database backup attachment

Considering the likelihood of wanting to have processes like this running automatically as scheduled tasks on a server, if you’re wondering how to go about that, please see Laravel documentation for some helpful details.

Associated Github branch:

I’ll push the codes in this lesson to a branch on my public Github repo where you can go over my codes for reference:

https://github.com/davealex/experiments/tree/ft-database-backup

I’m David — A Nigerian software engineer, coffee drinker, and innovator. Thanks for tuning in!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
David Abiola

David Abiola

Human & software engineer. Interested in ()=> {[Arts, Education, Music, Science, Tech, AI, co-Humans]};