Laravel Import, Export File ด้วย laravel-excel

Poolsawat Apin
4 min readFeb 5, 2024

Laravel-Excel คืออะไร

Laravel Excel มีวัตถุประสงค์เพื่อเป็น PhpSpreadsheet ที่ปรุงแต่งโดย Laravel: wrapper ที่เรียบง่าย แต่สวยงาม PhpSpreadsheet โดยมีเป้าหมายเพื่อทำให้การส่งออก (export) และนำเข้า (import) ง่ายขึ้น

🔥 PhpSpreadsheet เป็นไลบรารีที่เขียนด้วย PHP ล้วนๆ และมีชุดคลาสที่ให้คุณอ่านและเขียนไปยังรูปแบบไฟล์สเปรดชีตต่างๆ เช่น Excel และ LibreOffice Calc

คุณสมบัติ Laravel Excel

  • ส่งออกคอลเลกชันไปยัง Excel ได้อย่างง่ายดาย
  • ส่งออกคำค้นหาด้วยการแบ่งส่วนอัตโนมัติเพื่อประสิทธิภาพที่ดีขึ้น
  • คิวการส่งออกเพื่อประสิทธิภาพที่ดีขึ้น
  • ส่งออกมุมมอง Blade ไปยัง Excel ได้อย่างง่ายดาย
  • นำเข้าไปยังคอลเลกชันได้อย่างง่ายดาย
  • อ่านไฟล์ Excel เป็นกลุ่มๆ
  • จัดการส่วนแทรกนำเข้าเป็นชุด

การติดตั้ง

ก่อนที่จะเริ่มทำการติดตั้ง มาตรวจสอบ php extensions ก่อน ว่ามีความพร้อมตามนี้หรื

  • PHP: ^7.2\|^8.0
  • Laravel: ^5.8
  • PhpSpreadsheet: ^1.21
  • PHP extension php_zip enabled
  • PHP extension php_xml enabled
  • PHP extension php_gd2 enabled
  • PHP extension php_iconv enabled
  • PHP extension php_simplexml enabled
  • PHP extension php_xmlreader enabled
  • PHP extension php_zlib enabled

เมื่อทุกอย่างพร้อม ก็มาเริ่มติดตั้งกันเลย

ทำการเริ่ม dependency package เข้า ด้วย composer

// ติดตั้งแบบกำหนดเวอร์ชั่น
composer require maatwebsite/excel:^3.1

// ติดตั้งแบบเวอร์ชั่น ล่าสุด
composer require maatwebsite/excel

ทำการเพิ่ม new provider ใน config/

'providers' => [
/*
* Package Service Providers...
*/
Maatwebsite\Excel\ExcelServiceProvider::class,
]

'aliases' => [
...
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
]

php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config

เริ่มการส่งออก (export)

ใช้คำสั่งเพื่อสร้างคลาส export

php artisan make:export UsersExport --model=User
.
├── app
│ ├── Exports
│ │ ├── UsersExport.php

└── composer.json
<?php

namespace App\Exports;

use App\Models\User;
use Maatwebsite\Excel\Concerns\FromCollection;

class UsersExport implements FromCollection
{
public function collection()
{
return User::all();
}
}

สร้าง controller กรณีต้องการเพื่อทำดาวน์โหลด

<?php

namespace App\Http\Controllers;

use App\Exports\UsersExport;
use Maatwebsite\Excel\Facades\Excel;

class UsersController extends Controller
{
public function export()
{
return Excel::download(new UsersExport, 'users.xlsx');
}
}

Route::get('users/export/', [UsersController::class, 'export']);

คุณสมบัติเพิ่มเติมของการส่งออก (export)

เริ่มการนำเข้า (importing)

php artisan make:import UsersImport --model=User
.
├── app
│ ├── Imports
│ │ ├── UsersImport.php

└── composer.json
<?php

namespace App\Imports;

use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\ToModel;

class UsersImport implements ToModel
{
/**
* @param array $row
*
* @return User|null
*/
public function model(array $row)
{
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => Hash::make($row[2]),
]);
}
}

สร้าง controller เพื่อนำเข้าข้อมูล

use App\Imports\UsersImport;
use Maatwebsite\Excel\Facades\Excel;
use App\Http\Controllers\Controller;

class UsersController extends Controller
{
public function import()
{
Excel::import(new UsersImport, 'users.xlsx');

return redirect('/')->with('success', 'All good!');
}
}

คุณสมบัติเพิ่มเติมของการนำเข้า (importing)

ตัวอย่างโค๊ดของผม

  • การนำเข้า (importing)
<?php

namespace App\Exports;

use App\Models\ShippingOrder;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithColumnWidths;
use CommonHelper;

class ExportOrder implements FromQuery, WithHeadings, WithColumnWidths
{
use Exportable;

public function __construct($createdDateStart, $createdDateEnd, $orderStatus, $createdBy, $profile, $headings)
{
$this->createdDateStart = $createdDateStart;
$this->createdDateEnd = $createdDateEnd;
$this->orderStatus = $orderStatus;
$this->createdBy = $createdBy;
$this->profile = $profile;
$this->headings = $headings;
}

public function query()
{
$query = ShippingOrder::query()
->addSelect(DB::raw("DATE_FORMAT(shipping_order.created_at,'%d/%m/%Y %H:%i:%s') as created_at_human"))
->addSelect('shipping_order.order_code as order_code')
->addSelect(DB::raw("
CASE
WHEN order_status = 'success' THEN 'สำเร็จ'
WHEN order_status = 'cancel' THEN 'ยกเลิก'
WHEN order_status = 'draft' THEN 'ฉบับร่าง'
ELSE 'อื่นๆ'
END as order_status
"))
->addSelect(DB::raw("(SELECT
COUNT(*)
FROM shipping_order_item i
WHERE i.order_id = shipping_order.order_id) as items_count"))
->addSelect(DB::raw("(SELECT
SUM(IFNULL(i.sale_price_rate,0))
FROM shipping_order_item i
WHERE i.order_id = shipping_order.order_id) as price_rates_sum"))
->addSelect(DB::raw("(SELECT
SUM(IFNULL(i.special_area_fee,0))
FROM shipping_order_item i
WHERE i.order_id = shipping_order.order_id) as special_area_fees_sum"))
->addSelect(DB::raw("(SELECT
SUM(IFNULL(i.sale_price_rate,0)) +
SUM(IFNULL(i.special_area_fee,0))
FROM shipping_order_item i
WHERE i.order_id = shipping_order.order_id) as price_rate_fee_total"))
->addSelect('shipping_order.sender_name as sender_name')
->addSelect('shipping_order.created_by as created_by');
if(!is_null($this->createdDateStart) && !is_null($this->createdDateEnd)) {
$from = $this->createdDateStart;
$to = $this->createdDateEnd;
$query->whereBetween('created_at', [$from, $to]);
}
if(!is_null($this->orderStatus)) {
$query->where('order_status', $this->orderStatus);
}
if(!is_null($this->createdBy)) {
$query->where('created_by', $this->createdBy);
}

if($this->profile->shop != null) {
$query->where('shop_id',$this->profile->shop->shop_id);
}

return $query;
}

public function headings(): array
{
return $this->headings;
}

public function columnWidths(): array
{
return [
'A' => 25,
'B' => 25,
'C' => 10,
'D' => 15,
'E' => 15,
'F' => 15,
'G' => 15,
'H' => 20,
'I' => 20,
];
}
}
  • การส่งออก (export)
<?php

namespace App\Imports;

use App\Models\ShippingImportItem;
use App\Models\ShippingImport;
// use Maatwebsite\Excel\Concerns\ToModel;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Concerns\WithCalculatedFormulas;
// use Maatwebsite\Excel\Concerns\WithValidation;
use Illuminate\Support\Facades\Validator;

class ImportOrderItem implements ToCollection, WithStartRow, SkipsEmptyRows, WithCalculatedFormulas
{
use Importable;

public function __construct($importId, $createdBy)
{
$this->importId = $importId;
$this->createdBy = $createdBy;
}

/**
* @return int
*/
public function startRow(): int
{
return 2;
}

/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function collection(Collection $rows)
{

Validator::make($rows->toArray(), [
'*.0' => 'required', // A
'*.1' => 'required', // B
'*.2' => 'required', // C
'*.3' => 'required', // D
'*.4' => 'required', // E
// '*.5' => 'required', // F
// '*.6' => 'required', // G
'*.7' => 'required', // H
// '*.8' => 'required', // I
'*.9' => 'required', // J
'*.10' => 'required', // K
// '*.11' => 'required', // L
'*.12' => 'required', // M
// '*.13' => 'required', // N
])->validate();

// dd($rows);
foreach ($rows as $idx => $row) {

$senderName = $row[1];
$senderMobile = $row[2];

if($idx == 0) {
$import = ShippingImport::find($this->importId);
$import->sender_name = $senderName;
$import->sender_mobile = $senderMobile;
$import->save();
}

ShippingImportItem::create([
'import_id' => $this->importId,
'delivery_number' => $row[0],
'sender_name' => $senderName,
'sender_mobile' => $senderMobile,
'recipient_name' => $row[3],
'recipient_mobile' => $row[4],
'recipient_province' => $row[5],
'recipient_district' => $row[6],
'recipient_sub_district' => $row[7],
'recipient_address' => $row[8],
'recipient_zipcode' => $row[9],
'volumetric_weight' => $row[10],
'actual_weight' => $row[11],
'parcel_weight' => $row[12],
// 'shipping_cost_received' => $row[13], // cancel on v2.0
'remote_area_fee' => $row[13],
'import_status'=> "active",
'created_by'=> $this->createdBy,
]);
}
}
}

ปัญหาต่าง ๆ ที่พบระหว่างที่ใช้งาน

  • อ่าน คอลัมน์ที่มีสูตร (Formula cell) แก้ไขด้วย `use Maatwebsite\Excel\Concerns\WithCalculatedFormulas;`
  • ข้ามแถวข้อมูลที่ว่าง แก้ไขด้วย `use Maatwebsite\Excel\Concerns\SkipsEmptyRows;`
  • กำหนดแถวเริ่มอ่านข้อมูล แก้ไขด้วย `use Maatwebsite\Excel\Concerns\WithStartRow;`
  • กำหนดชื่อหัวตารางแต่ละ คอลัมน์ แก้ไขด้วย `use Maatwebsite\Excel\Concerns\WithHeadings;`
  • กำหนดขนาดความกว้างของแต่ละคอลัมน์ แก้ไขด้วย `use Maatwebsite\Excel\Concerns\WithColumnWidths;`

สรุปท้ายบทความ

จากที่ได้ทดลองใช้งาน ปัญหาต่าง ๆ ที่พบในระหว่างการใช้งาน ส่วนใหญ่จะหาวิธีการแก้ไขได้ เพราะ laravel-excel เองจะมีวิธีการแก้ไขไว้หมดแล้ว ยังสามารถกำหนด รูปแบบ (sheet style) ได้อีกด้วย ศึกษาเพิ่มเติม สำหรับเพื่อนคนไหน ได้ลองใช้งาน และพบปัญหาการใช้งาน หรือเจอวิธีการใช้งานดี ๆ เม้นมาบอกกันได้เลย ขอบคุณที่ติดตามครับ

--

--

Poolsawat Apin

Senior Engineering, Full Stack Developer [Subscribe Me @poolsawat.com]