+3

Backup dữ liệu hàng ngày với Laravel Excel

Xem thêm : Top website rèn luyện thuật toán tốt nhất cho sinh viên

Dữ liệu là một phần quan trọng cũng như là trái tim của một hệ thống vì vậy việc sao lưu dữ liệu để sử dụng lại khi cần thiết là việc rất quan trọng. Trong bài viết này mình hướng dẫn tạo một project đơn giản để backup dữ liệu sử dụng Laravel Excel.

Tại sao lại phải là excel, sao mình không backup file .sql hay backup thư mục data, vì đây là yêu cầu backup dữ liệu từ khách hàng "Tự động backup dữ liệu hàng ngày (file csv)" vì vậy phải backup ra file .csv hoặc .xlsx thì khách hàng mới có thể sử dụng được.

Cài đặt Laravel Exel

Cài đặt Laravel Excel thông qua command:

composer require maatwebsite/excel:^3.1

Xem chi tiết docs cài đặt của Laravel Excel tại https://docs.laravel-excel.com/3.1/getting-started/installation.html

Tạo Exporting collections

Tạo class Export với câu lệnh:

php artisan make:export AccountExport

Chỉnh sửa hàm AccountExport phù hợp với nghiệp vụ, dữ liệu muốn đồng bộ:

<?php

namespace App\Exports;

use App\Http\Helpers;
use App\Models\Account;
use DateTime;
use Exception;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;

class AccountExport implements FromCollection, WithHeadings, ShouldAutoSize, WithMapping
{
    use Exportable;
    private $stt;

    public function __construct()
    {
        $this->stt = 0;
    }

    /**
    * @return Collection
    */
    public function collection(): Collection
    {
        return Account::query()->select([
            'name',
            'name_org',
            'phone_number',
            'email',
            'class',
            'status',
            'created_at'
        ])->where('deleted_at', null)
            ->get();
    }

    public function headings(): array
    {
        return [
            'STT',
            'Tên',
            'Tên tổ chức',
            'Số điện thoại',
            'Email',
            'Lớp',
            'Trạng thái',
            'Tạo ngày'
        ];
    }

    /**
     * @throws Exception
     */
    public function map($row): array
    {
        return [
            $this->stt++,
            $row->name,
            $row->name_org,
            $row->phone_number,
            $row->email,
            $row->class,
            $row->status == 1? 'Hoạt động' : 'Khóa',
            $row->created_at->format('d/m/Y H:i')
        ];
    }
}
  • $this->stt++ thêm vào để đánh số thứ tự cho từng dòng

Mua Cloud VPS tại iNET, nhấn vào banner ở phía trên. Nhập mã TRANNGUYENHAN để được giảm giá thêm 10% khi mua dịch vụ Cloud VPS tại iNET.

Tạo job backup data

Tạo một bảng lưu trữ account_backups để lưu lại tên file cũng như thời gian sao lưu của từng file để làm chức năng trên giao diện, tạo bảng bằng migrate với hàm up như sau:

Schema::create('account_backups', function (Blueprint $table) {
        $table->id();
        $table->string('file_name');
        $table->timestamps();
    });

Tạo job BackupAccountDataJob

Tạo job bằng câu lệnh artisan:

php artisan make:job BackupAccountDataJob 

Chỉnh sửa hàm handle trong job để sao lưu dữ liệu và lưu kết quả sau khi sao lưu vào bảng account_backups đã tạo ở trên:

public function handle()
{
    $folder = 'backup/';
    $name = 'account-' . now()->format('Y-m-d') . '.csv';

    $path = $folder . $name;

    Excel::store(new AccountExport(), $path, 'public', \Maatwebsite\Excel\Excel::CSV);

    $backupFile = new AccountBackup();
    $backupFile->fill([
        'file_name' => $name
    ]);
    $backupFile->save();
}

Vậy là cứ mỗi lần job này được chạy thì sẽ sinh ra 1 file csv ở trong thư mục storage/app/public/backup/* và job sẽ tự đồng chèn một bản ghi là tên file vào trong bảng account_backups.

Tạo command để chạy job

Tạo command chạy job bằng câu lệnh artisan:

php artisan make:command BackupAccountDataCommand

Tạo signature cho command và viết hàm handle như dưới:

<?php

namespace App\Console\Commands;

use App\Jobs\BackupAccountDataJob;
use Illuminate\Console\Command;

class BackupAccountDataCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'account:backup';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        BackupAccountDataJob::dispatch();
    }
}

Lập lịch để định kỳ Laravel sẽ gọi tới command chạy job đồng bộ dữ liệu, trong file App\Console\Kernel.php thêm vào trong hàm schedule dòng sau:

$schedule->command('account:backup')->daily();

Update crontab để crontab gọi tới Job trong project, chạy crontab -e và dán câu lệnh sau vào:

* * * * * php /home/trannguyenhan/CodeFolder/demoproject/artisan schedule:run

Lưu ý đổi /home/trannguyenhan/CodeFolder/demoproject thành đường dẫn project của bạn.

Hiển thị dữ liệu backup cho phép người dùng tải về dữ liệu backup

Tạo 2 route, một là route để liệt kê danh sách file đã backup, một route là tải file backup tương ứng về, trong routes/web.php thêm vào 2 routes:

Route::group(['namespace' => 'App\Http\Controllers', 'prefix' => 'account-backup'], function (){
    Route::get('/', 'AccountBackupController@index')->name('account_backup');
    Route::get('/download/{id}', 'AccountBackupController@download')->name('account_backup.download');
});

Tạo AccountBackupController với 2 function tương ứng đã được định nghĩa trong web.php:

<?php

namespace App\Http\Controllers;

use App\Models\AccountBackup;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

class AccountBackupController extends Controller
{
    public function index()
    {
        $files = AccountBackup::query()
            ->orderBy('created_at', 'desc')
            ->paginate(15);

        return view('account_backup.index',compact('files'));
    }

    public function download($id){
        $file = AccountBackup::query()->where('id',$id)->first();

        if($file == null){
            $this->flashMessage('warning', __('messages.alert.add.not_found'), 'danger');
            return redirect()->route('account_backup');
        }

        $fileName = $file->file_name;
        $path = storage_path('app/public') . '/' . AccountBackup::BACKUP_FOLDER . '/' . $fileName;
        $response = new BinaryFileResponse($path);
        $response->setContentDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            basename($path)
        );

        return $response;
    }
}

Tạo file resources/views/account_backup/index.blade.php:

@extends('layouts.admin.index')
@section('title', 'Danh sách sao lưu')
@section('content')

    <div class="box box-primary">
		<div class="box-body">
			<div class="row">
				<div class="col-md-12">
					<div class="table-responsive">
						<table id="tabelapadrao" class="table table-condensed table-bordered table-hover">
							<thead>
								<tr>
									<th>Tên file</th>
									<th>Tạo ngày</th>
                                    <th class="text-center">Hành động</th>
								</tr>
							</thead>
							<tbody>
								@foreach($files as $file)
                                    <tr>
                                        <td>{{ $file->file_name }}</td>
                                        <td>{{ $file->created_at->format('d/m/Y H:i') }}</td>
                                        <td class="text-center">
                                            <a class="btn btn-warning  btn-xs" href="{{ route('account_backup.download', $file->id) }}" title="Edit {{ $file->file_name }}"><i class="fa fa-download"></i></a>
                                        </td>
                                    </tr>
								@endforeach
							</tbody>
							<tfoot>
								<tr>
									<th>Tên</th>
									<th>Tạo ngày</th>
									<th class="text-center">Hành động</th>
								</tr>
							</tfoot>
						</table>
					</div>
				</div>
				<div class="col-md-12 text-center">
					{{ $files->links() }}
				</div>
			</div>
		</div>
	</div>

@endsection
  • với layouts.admin.index là layout tương ứng của từng website phát triển, phần này mình chỉ đề cập tới nội dung được liệt kê trong trang là phần @section('content').

Dưới đây là kết quả sau khi hoàn thành:

image.png

Xem thêm : Top website rèn luyện thuật toán tốt nhất cho sinh viên


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí