Laravel'de Excel ve CSV Import/Export İşlemleri
Müşteri diyor ki: "Ürünleri Excel'den toplu yükleyebilmemiz lazım." Veya "Kullanıcı listesini Excel olarak indirmek istiyorum." Hemen hemen her yönetim panelinde bu talep gelir. Maatwebsite/Excel paketi bu işi Laravel'de çok kolay yapıyor.
Kurulum
composer require maatwebsite/excel
Laravel 12'de auto-discovery çalışır, ekstra config gerekmez. Ama config dosyasını publish etmek isterseniz:
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config
Export — Veriyi Excel'e Aktarmak
Basit Export
php artisan make:export UsersExport --model=User
namespace App\Exports;
use App\Models\User;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class UsersExport implements FromQuery, WithHeadings, WithMapping
{
use Exportable;
public function query()
{
return User::query()->where('is_active', true);
}
public function headings(): array
{
return ['ID', 'Ad Soyad', 'E-posta', 'Kayıt Tarihi'];
}
public function map($user): array
{
return [
$user->id,
$user->name,
$user->email,
$user->created_at->format('d.m.Y'),
];
}
}
Neden FromQuery kullanıyorum, FromCollection değil? Çünkü FromQuery chunk'lara bölerek çalışır — 100.000 kullanıcınız varsa hepsini belleğe almaz. FromCollection küçük veri setleri için tamam ama büyük tablolarda memory limit'e çarpar.
Controller'da Kullanım
use App\Exports\UsersExport;
use Maatwebsite\Excel\Facades\Excel;
public function export()
{
return Excel::download(new UsersExport, 'kullanicilar.xlsx');
}
// CSV olarak indirmek için
public function exportCsv()
{
return Excel::download(new UsersExport, 'kullanicilar.csv');
}
Dosya uzantısını .csv yapınca otomatik CSV formatında indirir. Başka bir şey değiştirmenize gerek yok.
Stil Eklemek
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class UsersExport implements FromQuery, WithHeadings, WithMapping, WithStyles
{
// ... diğer metodlar
public function styles(Worksheet $sheet): array
{
return [
1 => [
'font' => ['bold' => true, 'size' => 12],
'fill' => [
'fillType' => 'solid',
'startColor' => ['rgb' => '4472C4'],
],
'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']],
],
];
}
}
İlk satır (başlıklar) mavi arka plan, beyaz kalın yazı. Müşteriler bu tür detayları çok sever.
Import — Excel'den Veri Yüklemek
Basit Import
php artisan make:import UsersImport --model=User
namespace App\Imports;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
class UsersImport implements ToModel, WithHeadingRow, WithValidation, SkipsEmptyRows
{
public function model(array $row): User
{
return new User([
'name' => $row['ad_soyad'],
'email' => $row['eposta'],
'password' => Hash::make('gecici-sifre-123'),
'is_active' => true,
]);
}
public function rules(): array
{
return [
'ad_soyad' => ['required', 'string', 'max:255'],
'eposta' => ['required', 'email', 'unique:users,email'],
];
}
public function customValidationMessages(): array
{
return [
'ad_soyad.required' => ':attribute alanı zorunludur.',
'eposta.required' => ':attribute alanı zorunludur.',
'eposta.unique' => 'Bu e-posta adresi zaten kayıtlı.',
];
}
}
WithHeadingRow — Excel'in ilk satırını sütun adı olarak kullanır. $row['ad_soyad'] şeklinde erişirsiniz. Bu olmazsa $row[0], $row[1] gibi index ile erişirsiniz ki okunabilirlik sıfır.
WithValidation — Her satırı Laravel validation kurallarıyla kontrol eder. Geçersiz satır varsa hata fırlatır.
SkipsEmptyRows — Boş satırları atlar. Excel dosyalarında her zaman beklenmedik boş satırlar olur.
Controller
use App\Imports\UsersImport;
use Maatwebsite\Excel\Facades\Excel;
public function import(Request $request)
{
$request->validate([
'file' => ['required', 'file', 'mimes:xlsx,csv,xls', 'max:10240'],
]);
try {
Excel::import(new UsersImport, $request->file('file'));
return back()->with('success', 'Kullanıcılar başarıyla yüklendi.');
} catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
$failures = $e->failures();
return back()->with('import_errors', $failures);
}
}
Hata Gösterimi (Blade)
@if (session('import_errors'))
<div class="alert alert-danger">
<strong>İçe aktarma hataları:</strong>
<ul class="mb-0 mt-2">
@foreach (session('import_errors') as $failure)
<li>Satır {{ $failure->row() }}: {{ implode(', ', $failure->errors()) }}</li>
@endforeach
</ul>
</div>
@endif
Kullanıcıya "3. satırda e-posta geçersiz" gibi detaylı hata gösteriyoruz — "bir hata oluştu" demekten çok daha faydalı.
Büyük Dosyalar İçin Queue
10.000+ satırlık Excel dosyalarını senkron işlemek HTTP timeout'a neden olur. Queue'ya atın:
// ShouldQueue interface'i ekleyin
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Illuminate\Contracts\Queue\ShouldQueue;
class UsersImport implements ToModel, WithHeadingRow, WithValidation, WithChunkReading, ShouldQueue
{
public function chunkSize(): int
{
return 500;
}
// ... diğer metodlar
}
// Controller'da
Excel::queuedImport(new UsersImport, $request->file('file'));
return back()->with('success', 'Dosya alındı, arka planda işleniyor.');
WithChunkReading dosyayı 500'er satırlık parçalara böler. Her chunk ayrı bir job olarak queue'ya atılır. 10.000 satırlık dosya = 20 job. Bellek kullanımı sabit kalır.
Şablon İndirme
Import için kullanıcılara boş şablon sunmak iyi pratik:
public function template()
{
return Excel::download(new class implements \Maatwebsite\Excel\Concerns\FromArray, \Maatwebsite\Excel\Concerns\WithHeadings {
public function array(): array
{
return [
['ProjeMan', '[email protected]'],
];
}
public function headings(): array
{
return ['Ad Soyad', 'E-posta'];
}
}, 'kullanici-sablonu.xlsx');
}
Anonim class ile tek seferlik export — ayrı dosya oluşturmaya gerek yok. Bir örnek satır koymak kullanıcının formatı anlamasını kolaylaştırır.
Route'lar
Route::get('users/export', [UserController::class, 'export'])->name('users.export');
Route::get('users/export-csv', [UserController::class, 'exportCsv'])->name('users.export-csv');
Route::get('users/template', [UserController::class, 'template'])->name('users.template');
Route::post('users/import', [UserController::class, 'import'])->name('users.import');
Export ve template GET, import POST. Export işlemleri dosya indirme olduğu için GET uygundur — browser'da direkt açabilirsiniz.
Performans İpuçları
- Export:
FromQuerykullanın,FromCollectiondeğil. 10K+ kayıtta fark belirgin. - Import:
WithChunkReading+ShouldQueuebüyük dosyalar için şart. - CSV vs XLSX: CSV import/export çok daha hızlı. Stil gerekmiyorsa CSV tercih edin.
- Geçici dosyalar: Import sonrası upload edilen dosyayı silmeyi unutmayın.
Storage::delete()veya queue job'ındaWithEventsileAfterImportevent'inde temizleyin.
Import/export özelliği müşterilerin çok sevdiği ve sürekli istediği bir özellik. Bir kez doğru kurduğunuzda her projede aynı pattern'i kullanırsınız.