Excel
Module Excel tích hợp PhpSpreadsheet vào framework, hỗ trợ export và import file Excel/CSV với API theo kiểu concern-based.
Cài đặt
composer require phpoffice/phpspreadsheet
Đăng ký service provider trong bootstrap/app.php:
use Vietiso\Core\Excel\ExcelServiceProvider;
$app->register(ExcelServiceProvider::class);
Export
Tạo Export class
Tạo một class implement interface nguồn dữ liệu và dùng trait Exportable:
use Vietiso\Core\Excel\Concerns\FromCollection;
use Vietiso\Core\Excel\Concerns\WithHeadings;
use Vietiso\Core\Excel\Concerns\Exportable;
class UsersExport implements FromCollection, WithHeadings
{
use Exportable;
public function collection(): iterable
{
return User::all();
}
public function headings(): array
{
return ['ID', 'Họ tên', 'Email', 'Ngày tạo'];
}
}
Download file
use Vietiso\Core\Excel\Facade\Excel;
// Qua Facade
return Excel::download(new UsersExport, 'users.xlsx');
// Qua trait Exportable
return (new UsersExport)->download('users.xlsx');
Lưu file lên disk
// Lưu vào local filesystem
Excel::store(new UsersExport, 'exports/users.xlsx');
// Lưu lên disk cụ thể (s3, ftp, ...)
Excel::store(new UsersExport, 'exports/users.xlsx', disk: 's3');
// Qua trait
(new UsersExport)->store('exports/users.xlsx', disk: 's3');
Lấy raw content
$content = Excel::raw(new UsersExport, Excel::XLSX);
Định dạng file hỗ trợ
| Hằng số | Định dạng |
|---|---|
Excel::XLSX | Excel 2007+ (.xlsx) — mặc định |
Excel::XLS | Excel 97-2003 (.xls) |
Excel::CSV | CSV (.csv) |
Excel::ODS | OpenDocument (.ods) |
Excel::HTML | HTML (.html) |
Nguồn dữ liệu Export
FromCollection
Trả về bất kỳ iterable nào (array, Collection,...):
class UsersExport implements FromCollection
{
public function collection(): iterable
{
return User::where('active', 1)->get();
}
}
FromArray
Trả về plain PHP array:
class ReportExport implements FromArray
{
public function array(): array
{
return [
['Alice', 'alice@example.com'],
['Bob', 'bob@example.com'],
];
}
}
FromQuery
Trả về query builder — writer tự gọi ->get():
class UsersExport implements FromQuery
{
public function query(): mixed
{
return User::query()->orderBy('name');
}
}
FromGenerator
Tối ưu bộ nhớ cho dataset rất lớn — yield từng dòng:
class BigExport implements FromGenerator
{
public function generator(): \Generator
{
foreach (User::cursor() as $user) {
yield [$user->id, $user->name, $user->email];
}
}
}
Tùy chỉnh Export
WithMapping — biến đổi từng dòng
class UsersExport implements FromCollection, WithMapping
{
public function collection(): iterable
{
return User::all();
}
public function map(mixed $row): array
{
return [
$row->id,
$row->name,
$row->email,
$row->created_at->format('d/m/Y'),
];
}
}
WithStyles — định dạng ô
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class UsersExport implements FromCollection, WithStyles
{
public function styles(Worksheet $sheet): array
{
return [
1 => ['font' => ['bold' => true, 'size' => 12]], // dòng tiêu đề
'A' => ['font' => ['italic' => true]], // cột A
];
}
}
WithColumnWidths — độ rộng cột
class UsersExport implements FromCollection, WithColumnWidths
{
public function columnWidths(): array
{
return ['A' => 10, 'B' => 30, 'C' => 40];
}
}
ShouldAutoSize — tự động co dãn cột
use Vietiso\Core\Excel\Concerns\ShouldAutoSize;
class UsersExport implements FromCollection, ShouldAutoSize
{
// ...
}
WithTitle — đặt tên sheet
use Vietiso\Core\Excel\Concerns\WithTitle;
class UsersExport implements FromCollection, WithTitle
{
public function title(): string
{
return 'Danh sách người dùng';
}
}
WithChunkExport — export theo chunk (tiết kiệm RAM)
Kết hợp với FromQuery để phân trang query tự động:
class UsersExport implements FromQuery, WithChunkExport
{
public function query(): mixed
{
return User::query();
}
public function chunkSize(): int
{
return 1000;
}
}
Export nhiều sheet
use Vietiso\Core\Excel\Concerns\WithMultipleSheets;
class UsersWorkbook implements WithMultipleSheets
{
public function sheets(): array
{
return [
new ActiveUsersSheet,
new InactiveUsersSheet,
];
}
}
Mỗi sheet là một export class thông thường:
class ActiveUsersSheet implements FromQuery, WithTitle, WithHeadings
{
public function title(): string { return 'Active'; }
public function headings(): array { return ['ID', 'Tên', 'Email']; }
public function query(): mixed { return User::where('active', 1); }
}
Import
Tạo Import class
use Vietiso\Core\Excel\Concerns\ToModel;
use Vietiso\Core\Excel\Concerns\WithHeadingRow;
use Vietiso\Core\Excel\Concerns\Importable;
class UsersImport implements ToModel, WithHeadingRow
{
use Importable;
public function model(array $row): mixed
{
return new User([
'name' => $row['ho_ten'],
'email' => $row['email'],
]);
}
}
Chạy import
use Vietiso\Core\Excel\Facade\Excel;
// Qua Facade
Excel::import(new UsersImport, 'uploads/users.xlsx');
// Qua trait Importable
(new UsersImport)->import('uploads/users.xlsx');
// Từ disk
Excel::import(new UsersImport, 'imports/users.xlsx', disk: 's3');
Đọc dữ liệu về array
$rows = Excel::toArray(new UsersImport, 'uploads/users.xlsx');
foreach ($rows as $row) {
// $row là array
}
Đích nhận dữ liệu Import
ToModel — lưu từng dòng thành model
class UsersImport implements ToModel
{
public function model(array $row): mixed
{
if (empty($row[1])) {
return null; // bỏ qua dòng này
}
return new User([
'name' => $row[0],
'email' => $row[1],
]);
}
}
ToCollection — nhận toàn bộ dữ liệu một lần
class UsersImport implements ToCollection
{
public array $users = [];
public function collection(iterable $rows): void
{
foreach ($rows as $row) {
$this->users[] = $row;
}
}
}
ToArray — nhận raw array
class UsersImport implements ToArray
{
public function array(array $array): void
{
// $array là toàn bộ dữ liệu của sheet
}
}
Tùy chỉnh Import
WithHeadingRow — dùng dòng đầu làm key
class UsersImport implements ToModel, WithHeadingRow
{
public function model(array $row): mixed
{
// key là heading đã được normalize (snake_case)
return new User([
'name' => $row['ho_ten'],
'email' => $row['email'],
]);
}
}
WithStartRow — bắt đầu đọc từ dòng chỉ định
use Vietiso\Core\Excel\Concerns\WithStartRow;
class UsersImport implements ToModel, WithStartRow
{
public function startRow(): int
{
return 3; // bỏ qua 2 dòng đầu
}
}
WithChunkReading — đọc theo chunk (tiết kiệm RAM)
class UsersImport implements ToModel, WithChunkReading
{
public function chunkSize(): int
{
return 500;
}
}
WithBatchInserts — lưu DB theo batch
class UsersImport implements ToModel, WithBatchInserts
{
public function batchSize(): int
{
return 200; // insert 200 records/lần
}
}
WithValidation — validate từng dòng
use Vietiso\Core\Excel\Concerns\WithValidation;
class UsersImport implements ToModel, WithHeadingRow, WithValidation
{
public function rules(array $data): array
{
return [
'ho_ten' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
];
}
public function customValidationMessages(): array
{
return [
'email.unique' => 'Email :input đã tồn tại trong hệ thống.',
];
}
}
SkipsOnFailure — bỏ qua dòng lỗi validation
use Vietiso\Core\Excel\Concerns\SkipsOnFailure;
use Vietiso\Core\Excel\Validators\Failure;
class UsersImport implements ToModel, WithValidation, SkipsOnFailure
{
public array $failures = [];
public function onFailure(Failure ...$failures): void
{
$this->failures = array_merge($this->failures, $failures);
}
}
Sau khi import, kiểm tra các dòng bị bỏ qua:
$import = new UsersImport;
$import->import('users.xlsx');
foreach ($import->failures as $failure) {
echo "Dòng {$failure->row()}: " . implode(', ', $failure->errors());
}
SkipsOnError — bỏ qua dòng ném exception
use Vietiso\Core\Excel\Concerns\SkipsOnError;
class UsersImport implements ToModel, SkipsOnError
{
public function onError(\Throwable $e): void
{
logger()->error('Import error: ' . $e->getMessage());
}
}
Events
Đăng ký event trên Export/Import
use Vietiso\Core\Excel\Concerns\WithEvents;
use Vietiso\Core\Excel\Events\AfterSheet;
use Vietiso\Core\Excel\Events\BeforeExport;
class UsersExport implements FromCollection, WithEvents
{
public function registerEvents(): array
{
return [
BeforeExport::class => function (BeforeExport $event) {
// chạy trước khi bắt đầu export
},
AfterSheet::class => function (AfterSheet $event) {
$event->sheet->getDelegate()->setTitle('Users');
},
];
}
}
Danh sách Events
| Event | Thời điểm |
|---|---|
BeforeExport | Trước khi bắt đầu export |
AfterExport | Sau khi export hoàn thành |
BeforeWriting | Trước khi ghi file |
AfterWriting | Sau khi ghi file xong |
BeforeSheet | Trước khi bắt đầu xử lý một sheet |
AfterSheet | Sau khi xử lý xong một sheet |
BeforeImport | Trước khi bắt đầu import |
AfterImport | Sau khi import hoàn thành |
Bảng tổng hợp Concerns
Export
| Interface / Trait | Mô tả |
|---|---|
FromCollection | Dữ liệu từ iterable (array, Collection,...) |
FromArray | Dữ liệu từ plain PHP array |
FromQuery | Dữ liệu từ query builder |
FromGenerator | Dữ liệu từ PHP Generator (ít tốn RAM nhất) |
WithHeadings | Thêm dòng tiêu đề |
WithMapping | Biến đổi từng dòng trước khi ghi |
WithStyles | Định dạng ô/cột/dòng |
WithColumnWidths | Độ rộng cột |
ShouldAutoSize | Tự động co dãn cột |
WithTitle | Đặt tên sheet |
WithMultipleSheets | Export nhiều sheet |
WithChunkExport | Export theo chunk, tiết kiệm RAM |
WithEvents | Đăng ký event listener |
Exportable (trait) | Thêm download(), store(), raw() vào class |
Import
| Interface / Trait | Mô tả |
|---|---|
ToModel | Map từng dòng thành model, tự động save |
ToCollection | Nhận toàn bộ dữ liệu một lần |
ToArray | Nhận raw array |
WithHeadingRow | Dùng dòng đầu làm key |
WithStartRow | Bắt đầu đọc từ dòng chỉ định |
WithMapping | Biến đổi từng dòng sau khi đọc |
WithValidation | Validate từng dòng |
SkipsOnFailure | Bỏ qua dòng lỗi validation, nhận danh sách failures |
SkipsOnError | Bỏ qua dòng ném exception |
WithChunkReading | Đọc file theo chunk, tiết kiệm RAM |
WithBatchInserts | Lưu DB theo batch |
WithMultipleSheets | Import nhiều sheet |
WithEvents | Đăng ký event listener |
Importable (trait) | Thêm import(), toArray(), toCollection() |