۵ دیدگاه نظر رضا زیدی
ورودی و خروجی اکسل و CSV در لاراول
ورودی و خروجی اکسل و CSV در لاراول

زمانی که بر روی پروژه‌های بزرگ تجارت آنلاین یا پروژه‌های مشابه کار می‌کنید، نیاز دارید تا قابلیت 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 در نوار آدرس مرورگر، با این صفحه مواجه می‌شوید:

خروجی و ورودی اکسل و CSV در لاراول

برقراری ارتباط با دیتابیس

قدم بعدی، برقراری ارتباط با دیتابیس است. ابتدا یک دیتابیس دلخواه را ایجاد کنید و سپس مقادیر متغیرهای زیر را در فایل 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');

Import

پکیج 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])
        ]);
    }
}

Upserting

اگر بخواهیم ردیف‌هایی با ایمیل تکراری وارد جدول کاربران نشوند، باید Interface با نام WithUpserts را در کلاس Import فوق Implement کرده و از متد زیر استفاده کنیم:

class UsersImport implements ToModel, WithUpserts
{
    /**
     * @return string|array
     */
    public function uniqueBy()
    {
        return 'email';
    }
}

Skipping

اگر برای مثال بخواهیم از ردیف‌هایی که 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])
    ]);
}

Batch

زمانی که فایل اکسل موردنظر، حاوی تعداد زیادی ردیف است، فرایند 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 کردن، مشخص کرده‌ایم.

Chunking

هنگامی که حجم فایل اکسل بالا باشد، ممکن است فرایند 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 می‌باشد.

Queue

می‌توانید فرایند 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
{
...
}

Export

پکیج 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

برای کوئری‌های بزرگ، بهتر است که قرارداد 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();
    }
}

Queue

اگر حجم داده بالا باشد، بهتر است فرایند Export، در صف یا Queue قرار گیرد. برای این منظور، باید کلاس Export، قرارداد ShouldQueue را Implement کند.

Mapping

با 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
        ];
    }
}

Heading

اگر بخواهید که هر ستون در فایل اکسل یا 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

متد fileImportExport برای نمایش صفحه‌ی مربوط به عملیات Import و Export کاربرد دارد.

متد fileImport

این متد پس از ذخیره‌ی موقت فایل، عملیات 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);

متد fileExport

این متد نیز با استفاده از 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);

ایجاد View

حالا قالب 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 در لاراول بیشتر بخوانید: خروجی pdf در لاراول جمع بندی

در مقاله‌ی خروجی اکسل و CSV در لاراول از مجموعه مقالات آموزش لاراول، با نحوه‌ی Import و Export کردن فایل‌های اکسل و CSV از دیتابیس در لاراول آشنا شدیم و برای این منظور از بهترین پکیج لاراول در این زمینه، یعنی پکیج maatwebsite/excel استفاده کردیم. برای مشاهده‌ی مستندات کامل خروجی اکسل و CSV در لاراول، می‌توانید به وب سایت پکیج maatwebsite/excel مراجعه کنید.

امیدوار هستیم که این مقاله برای شما مفید بوده باشد. خوشحال می‌شویم نظرات، تجربیات و سوالات خود را با ما و سایر کاربران سون لرن به اشتراک بگذارید.

اگر به یادگیری بیشتر لاراول علاقه داری می‌توانی در دوره آموزش لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژه‌ها به صورت کامل برنامه‌ نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.
۵ دیدگاه
ما همه سوالات و دیدگاه‌ها رو می‌خونیم و پاسخ میدیم
۰۸ دی ۱۴۰۰، ۱۱:۱۲

سلام ممنون مفید بود اما یک سوال ما یک یوزر داریم ک تعداد زیادی پست ساخته با روابط چند به چند دریافت کردیم پست‌های هر کاربر رو هدف اینه وقتی به ستون پست رسیدیم row‌های جدیدی زیر row اطلاعات کاربر ایجاد شه که تنها و تنها ستون پست ان پر شود تا به اخرین پست میرسیم در نهایت هم که row کاربر جدید

نازنین کریمی مقدم ۱۱ دی ۱۴۰۰، ۱۴:۳۲

درود شاید کتابخانه‌ها هم توابعی در این خصوص داشته باشند که چون من خیلی تسلط ندارم در این مورد دوستان دیگر بهتر میتونند کمک کنند، اما به طور کلی، شما باید قبل از هرکاری دیتا رو براساس آیدی کاربر مرتب کنید، بعد یک حلقه بنویسید که توش بیاد آیدی کاربر ردیف فعلی رو با قبلی مقایسه کنه. بعد یک شرط اینجا بنویسید که اگر یکی بود بیاد فقط ستون پست رو مقداردهی کنه و اگر نبود داده ردیف به طور کامل بشینه.

علی ۲۱ شهریور ۱۴۰۰، ۱۲:۲۸

سلام یه سوالی داشتم هرچی گشتم سرچ کردم راه حلی پیدا نشد زمان import کردن چطور بگیبم اولین ردیف رو که هدینگ هست نادیده بگیره؟

نازنین کریمی مقدم ۲۱ شهریور ۱۴۰۰، ۲۲:۱۰

درود ببینید <a href="https://stackoverflow.com/questions/56726778/how-to-skip-first-row-when-importing-file" target="_blank" rel="noopener nofollow ugc">این راهکار</a> براتون جواب میده؟

setare ۳۰ آذر ۱۳۹۹، ۰۶:۳۰

بسیار علی و کاربردی بود. من از این پکیج استفاده میکردم اما از قابلیت‌های اصلیش مثلChunking و ... نمیدونستم چطوری استفاده میشه. بنظرم این مقاله خیلی کاربردی هست

  • پیش‌نیاز
  • نصب لاراول
  • برقراری ارتباط با دیتابیس
  • نصب پکیج
  • ایجاد کاربر
  • ایجاد مسیرها
  • Import
  • Export
  • ایجاد کنترلر
  • ایجاد View
اشتراک گذاری مقاله در :