زمانی که بر روی پروژههای بزرگ تجارت آنلاین یا پروژههای مشابه کار میکنید، نیاز دارید تا قابلیت Export و Import اطلاعات دیتابیس در قالب فایلهای CSV یا اکسل را در پروژهی خود داشته باشید. در لاراول، شما میتوانید این کار را با استفاده از پکیج maatwebsite/excel انجام دهید. این پکیج متدهای فراوانی را برای نیازمندیهای مختلف، در اختیار توسعهدهندگان لاراول قرار میدهد. در این مقاله قصد داریم نحوهی نصب و استفاده از این پکیج را به شما آموزش دهیم تا بتوانید با استفاده از آن، هم فایلهای اکسل یا CSV را به دیتابیس خود اضافه کنید و هم از دیتابیس خود Backup بگیرید. پس تا انتهای این مقاله با ما همراه باشید.
برای شروع آموزش ورودی و خروجی اکسل و CSV در لاراول، به دانش کافی در زمینهی لاراول نیاز دارید.
ابتدا یک پروژهی جدید لاراول را ایجاد میکنیم. برای این منظور دستور زیر را در مسیر دلخواه خود اجرا کنید:
composer create-project --prefer-dist laravel/laravel laravel-excel-csv
سپس با دستور زیر وارد پوشهی پروژهای که ایجاد کردهاید، شوید:
cd laravel-excel-csv
سپس دستور زیر را اجرا کنید:
php artisan serve
پس از اجرای این دستور، پروژه بر روی پورت 8000 لوکال هاست در دسترس است. با وارد کردن آدرس http://localhost:8000 در نوار آدرس مرورگر، با این صفحه مواجه میشوید:
قدم بعدی، برقراری ارتباط با دیتابیس است. ابتدا یک دیتابیس دلخواه را ایجاد کنید و سپس مقادیر متغیرهای زیر را در فایل env. پروژه بهروز کنید:
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
با استفاده از دستور زیر، جداول پیشفرض لاراول را ایجاد کنید:
php artisan migrate
اکنون وقت آن رسیده که پکیج maatwebsite/excel را در پروژهی لاراول خود نصب کنیم. برای نصب این پکیج از دستور زیر استفاده کنید:
composer require maatwebsite/excel
برای Publish کردن فایل پیکربندی پکیج، دستور زیر را اجرا کنید:
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config
پس از اجرای این دستور، میتوانید با مراجعه به پوشهی config و فایل excel.php، تنظیمات مربوط به پیکربندی پکیج را مشاهده کنید یا در صورت لزوم، تغییر دهید.
اکنون میخواهیم تعدادی کاربر تصادفی را به جدول Users اضافه کنیم. برای این منظور، با استفاده از دستور زیر به محیط Tinker وارد شوید:
php artisan tinker
سپس دستور زیر را برای ایجاد کاربران تصادفی اجرا کنید:
User::factory()->count(50)->create();
اکنون میتوانید با دستور زیر از محیط Tinker خارج شوید:
exit
نوبت آن رسیده که مسیرهای مورد نظر خود را ایجاد کنیم. به پوشهی routes و فایل web.php مراجعه کنید و سه مسیر زیر را برای Import و Export فایلهای اکسل و CSV تعریف کنید:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('file-import-export', [UserController::class, 'fileImportExport']);
Route::post('file-import', [UserController::class, 'fileImport'])->name('file-import');
Route::get('file-export', [UserController::class, 'fileExport'])->name('file-export');
پکیج maatwebsite یک دستور را برای ایجاد کلاس Import در نظر گرفته است. از کلاس ایجاد شده در کنترلر دلخواه برای Import کردن فایل اکسل یا CSV به جدول موردنظرمان در دیتابیس استفاده میکنیم. حالا یک کلاس Import با نام UsersImport را برای جدول Users ایجاد کنید:
php artisan make:import UsersImport --model=User
با اجرای دستور فوق، کلاس UsersImport در مسیر app/Imports ایجاد میشود. به این فایل مراجعه کرده و از کدهای زیر در آن استفاده کنید:
<?php
namespace App\Imports;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class UsersImport implements ToModel
{
/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => Hash::make($row[2])
]);
}
}
اگر بخواهیم ردیفهایی با ایمیل تکراری وارد جدول کاربران نشوند، باید Interface با نام WithUpserts را در کلاس Import فوق Implement کرده و از متد زیر استفاده کنیم:
class UsersImport implements ToModel, WithUpserts
{
/**
* @return string|array
*/
public function uniqueBy()
{
return 'email';
}
}
اگر برای مثال بخواهیم از ردیفهایی که email ندارند، صرف نظر شود، میتوانیم مقدار null را در متد model برگردانیم:
public function model(array $row)
{
if (!isset($row[1])) {
return null;
}
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => Hash::make($row[2])
]);
}
زمانی که فایل اکسل موردنظر، حاوی تعداد زیادی ردیف است، فرایند Import کردن میتواند زمانبر شود؛ چرا که برای هر ردیف یک کوئری Insert باید اجرا شود. شما میتوانید مشخص کنید که حداکثر چه تعداد ردیف در هر بار، میتواند به دیتابیس وارد شود. برای این کار لازم است که کلاس Import ایجاد شده، قرارداد WithBatchInserts را Implement کند:
<?php
namespace App\Imports;
use App\Models\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
class UsersImport implements ToModel, WithBatchInserts
{
/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => Hash::make($row[2])
]);
}
public function batchSize(): int
{
return 1000;
}
}
همانطور که میبینید، در متد batchSize، تعداد حداکثر 1000 ردیف را برای هر بار Import کردن، مشخص کردهایم.
هنگامی که حجم فایل اکسل بالا باشد، ممکن است فرایند Import کردن، تاثیر زیادی بر روی Memory Usage داشته باشد. برای رفع این مشکل، میتوانیم با Implement کردن قرارداد WithChunkReading در کلاس Import، فایل را به صورت تکه تکه، Import کنیم:
<?php
namespace App\Imports;
use App\Models\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithChunkReading;
class UsersImport implements ToModel, WithBatchInserts, WithChunkReading
{
/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new User([
'name' => $row[0],
'email' => $row[1],
'password' => Hash::make($row[2])
]);
}
public function batchSize(): int
{
return 1000;
}
public function chunkSize(): int
{
return 1000;
}
}
میبینید که در متد chunkSize، تعداد 1000 ردیف را به هر تکه اختصاص دادهایم. بهترین حالت برای فایلهای با حجم بالا، استفادهی همزمان از هر دو قرارداد WithBatchInserts و WithChunkReading میباشد.
میتوانید فرایند Import کردن را در صف یا Queue قرار دهید. برای این کار نیاز است که کلاس Import ایجاد شده، قرارداد ShouldQueue را Implement کند. این قرارداد باید با قرارداد WithChunkReading استفاده شود:
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\WithChunkReading;
class UsersImport implements ToModel, WithChunkReading, ShouldQueue
{
...
}
پکیج maatwebsite یک دستور دیگر را نیز برای ایجاد کلاس Export در نظر گرفته است. از کلاس ایجاد شده برای Export کردن اطلاعات دیتابیس، در قالب فایل اکسل یا CSV استفاده میکنیم. حالا یک کلاس Export با نام UsersExport را برای جدول Users ایجاد کنید:
php artisan make:export UsersExport --model=User
با اجرای دستور فوق کلاس UsersExport در مسیر app/Exports ایجاد میشود. به این فایل مراجعه کرده و از کدهای زیر در آن استفاده کنید:
<?php
namespace App\Exports;
use App\Models\User;
use Maatwebsite\Excel\Concerns\FromCollection;
class UsersExport implements FromCollection
{
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return User::all();
}
}
برای کوئریهای بزرگ، بهتر است که قرارداد FromQuery را به جای قرارداد FromCollection در کلاس فوق، Implement کنیم تا کوئری به صورت تکه تکه اجرا شود:
<?php
namespace App\Exports;
use App\Models\User;
use Maatwebsite\Excel\Concerns\FromQuery;
class UsersExport implements FromQuery
{
public function query()
{
return User::query();
}
}
اگر حجم داده بالا باشد، بهتر است فرایند Export، در صف یا Queue قرار گیرد. برای این منظور، باید کلاس Export، قرارداد ShouldQueue را Implement کند.
با Implement کردن قرارداد WithMapping، میتوانید فقط ستونهای دلخواه را در فایل خروجی داشته باشید یا مقادیر آنها را Customize کنید:
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\WithMapping;
use App\Models\User;
use Maatwebsite\Excel\Concerns\FromQuery;
class UsersExport implements FromQuery, WithMapping
{
public function query()
{
return User::query();
}
/**
* @return array
* @var User $users
*/
public function map($users): array
{
return [
ucwords($users->name),
$users->email,
$users->created_at
];
}
}
اگر بخواهید که هر ستون در فایل اکسل یا CSV خروجی، عنوان داشته باشد، باید قرارداد WithHeadings را Implement کنید:
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithHeadings;
use App\Models\User;
use Maatwebsite\Excel\Concerns\FromQuery;
class UsersExport implements FromQuery, WithMapping, WithHeadings
{
public function query()
{
return User::query();
}
/**
* @return array
* @var User $users
*/
public function map($users): array
{
return [
$users->id,
ucwords($users->name),
$users->email,
$users->created_at
];
}
public function headings(): array
{
return [
'#',
'Name',
'Email',
'Date Registered',
];
}
}
اکنون باید یک کنترلر را برای اجرای عملیات موردنظرمان ایجاد کنیم. با استفاده از دستور زیر، یک کنترلر با نام UsersController ایجاد کنید:
php artisan make:controller UserController
اکنون متدهای موردنظرمان را که در مسیرها تعریف کردیم، پیادهسازی میکنیم:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use App\Imports\UsersImport;
use App\Exports\UsersExport;
class UserController extends Controller
{
/**
* @return \Illuminate\Support\Collection
*/
public function fileImportExport()
{
return view('file-import');
}
/**
* @return \Illuminate\Support\Collection
*/
public function fileImport(Request $request)
{
Excel::import(new UsersImport, $request->file('file')->store('temp'));
return back();
}
/**
* @return \Illuminate\Support\Collection
*/
public function fileExport()
{
return Excel::download(new UsersExport, 'users-collection.xlsx');
}
}
همانطور که میبینید، سه متد fileImportExport، fileImport و fileExport را ایجاد کردهایم.
متد fileImportExport برای نمایش صفحهی مربوط به عملیات Import و Export کاربرد دارد.
این متد پس از ذخیرهی موقت فایل، عملیات Import را انجام میدهد. این عملیات با استفاده از Excel Facade و متد import صورت میگیرد. اگر کلاس Import ایجاد شده، از Trait با نام Maatwebsite\Excel\Concerns\Importable استفاده کند، میتوان بدون استفاده از Facade و به شکل زیر عملیات Import را انجام داد:
(new UsersImport)->import($request->file('file')->store('temp'));
میتوانید ابتدا فایل آپلود شده توسط کاربر را ذخیره کرده و سپس فایل ذخیره شده را به شکل زیر Import کنید:
Excel::import(new UsersImport, storage_path('users.xlsx'));
به طور پیشفرض، فرمت فایل از Extention آن خوانده میشود. اگر میخواهید که خودتان فرمت فایل را مشخص کنید، میتوانید آن را به عنوان پارامتر سوم، به متد import پاس دهید. مثلا برای فرمت اکسل:
(new UsersImport)->import($request->file('file')->store('temp'), null, \Maatwebsite\Excel\Excel::XLSX);
یا برای فرمت CSV:
(new UsersImport)->import($request->file('file')->store('temp'), null, \Maatwebsite\Excel\Excel::CSV);
این متد نیز با استفاده از Excel Facade و متد download، فایل اکسل خروجی را دانلود میکند. اگر کلاس Export ایجاد شده، از Trait با نام Maatwebsite\Excel\Concerns\Exportable استفاده کند، میتوان بدون استفاده از Facade و به شکل زیر عملیات Export را انجام داد:
return (new UsersExport)->download('users-collection.xlsx');
میتوانید در پارامتر دوم متد فوق، نوع فایل خروجی را نیز مشخص کنید. به صورت پیشفرض، نوع فایل از Extension موجود در پارامتر اول مشخص میشود:
return (new UsersExport)->download('users-collection.csv', \Maatwebsite\Excel\Excel::CSV);
حالا قالب Blade موردنظرمان را برای انجام عملیات Import و Export، در مسیر resources/views ایجاد میکنیم:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Import Export Excel & CSV to Database in Laravel 7</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-5 text-center">
<h2 class="mb-4">
Laravel Import and Export CSV & Excel to Database Example
</h2>
<form action="{{ route('file-import') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="form-group mb-4" style="max-width: 500px; margin: 0 auto;">
<div class="custom-file text-left">
<input type="file" name="file" class="custom-file-input" id="customFile">
<label class="custom-file-label" for="customFile">Choose file</label>
</div>
</div>
<button class="btn btn-primary">Import data</button>
<a class="btn btn-success" href="{{ route('file-export') }}">Export data</a>
</form>
</div>
</body>
</html>
با مراجعه به آدرس http://localhost:8000/file-import-export با صفحهی زیر مواجه میشوید:
جمع بندی
در مقالهی خروجی اکسل و CSV در لاراول از مجموعه مقالات آموزش لاراول، با نحوهی Import و Export کردن فایلهای اکسل و CSV از دیتابیس در لاراول آشنا شدیم و برای این منظور از بهترین پکیج لاراول در این زمینه، یعنی پکیج maatwebsite/excel استفاده کردیم. برای مشاهدهی مستندات کامل خروجی اکسل و CSV در لاراول، میتوانید به وب سایت پکیج maatwebsite/excel مراجعه کنید.
امیدوار هستیم که این مقاله برای شما مفید بوده باشد. خوشحال میشویم نظرات، تجربیات و سوالات خود را با ما و سایر کاربران سون لرن به اشتراک بگذارید.
اگر به یادگیری بیشتر لاراول علاقه داری میتوانی در دوره آموزش لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژهها به صورت کامل برنامه نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.
ما یک یوزر داریم ک تعداد زیادی پست ساخته
با روابط چند به چند دریافت کردیم پست های هر کاربر رو
هدف اینه وقتی به ستون پست رسیدیم row های جدیدی زیر row اطلاعات کاربر ایجاد شه که تنها و تنها ستون پست ان پر شود تا به اخرین پست میرسیم
در نهایت هم که row کاربر جدید
شاید کتابخانه ها هم توابعی در این خصوص داشته باشند که چون من خیلی تسلط ندارم در این مورد دوستان دیگر بهتر میتونند کمک کنند،
اما به طور کلی، شما باید قبل از هرکاری دیتا رو براساس آیدی کاربر مرتب کنید، بعد یک حلقه بنویسید که توش بیاد آیدی کاربر ردیف فعلی رو با قبلی مقایسه کنه. بعد یک شرط اینجا بنویسید که اگر یکی بود بیاد فقط ستون پست رو مقداردهی کنه و اگر نبود داده ردیف به طور کامل بشینه.