Import (درج گروهی دادهها)
Import یک ابزار قدرتمند برای مدیریت فرآیند وارد کردن دادهها از فایل و استفاده از پیکربندیهای سفارشی است. استفاده صحیح از این قابلیت میتواند به شما در سادهسازی روند عملیات دادههای وارد شده کمک کند.
ایجاد Import
برای ایجاد یک کلاس import جدید که از BaseImport ارثبری میکند:
php artisan dornica:make-import SampleImport
برای ایجاد import داخل ماژول مشخص:
php artisan dornica:make-import SampleImport --module=MODULE_NAME
مقدار MODULE_NAME باید نام ماژول شما باشد.
این کلاس به شما امکان میدهد تا ساختار import را تعریف کنید و ویژگیهای مختلف آن را سفارشی کنید.
مثال زیر، ساختار کلی یک کلاس import را نشان میدهد:
<?php
namespace App\Generators\Imports;
use Dornica\Foundation\Import\BaseImport;
class BookImport extends BaseImport
{
public function __construct()
{
$this
->setFileFormat(FileFormat::EXCEL)
->setTitle('درج گروهی کتاب ها')
->setIndexRoute('books.index')
->setPerPage(15);
}
public function fields(): array
{
// تعریف فیلدها
}
public function inputs(): array
{
// تعریف ورودی های فرم درج گروهی
}
public function beforeProcessRows(ImportResult $result): void
{
// بررسی شرایط لازم قبل از درج داده ها
}
public function processRows(ImportResult $result): void
{
// پردازش داده ها
}
}
تنظیمات اولیه
میتوانید تنظیمات پایه را داخل constructor انجام دهید:
public function __construct()
{
$this
->setFileFormat(FileFormat::EXCEL) // تنظیم فرمت فایل که میتواند excel یا csv باشد
->setTitle('درج گروهی کتاب ها') // تنظیم عنوان فرم
->setIndexRoute('books.index') // نام route صفحه لیست (اختیاری)
->setPerPage(15); // تعداد ردیف در هر صفحه پیشنمایش (پیشفرض: ۱۵)
}
دکمه بازگشت به لیست (setIndexRoute)
با setIndexRoute میتوانید نام route صفحه لیست را روی کلاس import تعریف کنید تا در فرم درج گروهی دکمه «بازگشت به لیست» نمایش داده شود.
$this->setIndexRoute('books.index');
- مقدار باید نام route باشد (مثل
'books.index')، نه URL؛ ازroute('books.index')استفاده نکنید. - اگر
setIndexRouteتعریف شده باشد، همان اولویت دارد؛ در غیر این صورت ازindex_routeذخیرهشده در session (مثلاً هنگام باز شدن import از جدول) بهعنوان fallback استفاده میشود. - اگر هیچکدام تنظیم نشده باشد، دکمه بازگشت نمایش داده نمیشود.
تعداد ردیف در هر صفحه پیشنمایش (setPerPage)
با setPerPage میتوانید تعداد ردیفهای نمایشدادهشده در هر صفحه جدول پیشنمایش درج گروهی را تنظیم کنید. مقدار پیشفرض 15 است.
$this->setPerPage(25);
- مقدار باید عدد صحیح بزرگتر از صفر باشد؛ مقادیر کوچکتر از
1به1محدود میشوند. - این تنظیم فقط صفحهبندی سمت کلاینت در مرحله پیشنمایش را کنترل میکند و روی پردازش نهایی import تأثیری ندارد.
دریافت تنظیمات (getters)
متدهای setter متناظر getter دارند و در UI، کنترلر یا منطق سفارشی قابل استفاده هستند:
$import = new BookImport();
$import->getFileFormat(); // FileFormat::EXCEL | FileFormat::CSV
$import->getTitle(); // «درج گروهی کتاب ها»
$import->getIndexRoute(); // 'books.index' یا null
$import->getPerPage(); // 15
مرجع متدهای عمومی BaseImport
| متد | توضیح |
|---|---|
setFileFormat / getFileFormat | فرمت فایل (EXCEL یا CSV) |
setTitle / getTitle | عنوان فرم درج گروهی |
setIndexRoute / getIndexRoute | نام route دکمه بازگشت به لیست |
setPerPage / getPerPage | تعداد ردیف در هر صفحه پیشنمایش |
fields() | تعریف فیلدهای فایل (الزامی / abstract) |
inputs() | تعریف ورودیهای فرم (اختیاری) |
beforeProcessRows($result) | بررسی شرایط قبل از درج |
processRows($result) | پردازش ردیفهای معتبر |
getFieldType($name) | نوع فیلد بهصورت enum ImportFieldType |
getFieldTypeValue($name) | نوع فیلد بهصورت رشته (مثل 'text' یا 'link') |
getFieldMappings() | آرایه نگاشت فیلدها پس از setup |
getFormInputs() | آرایه ورودیهای فرم پس از setup |
getValidationRules() | قوانین اعتبارسنجی جمعشده |
getResourceKey() | کلید یکتای منبع import |
getImportDriver() | درایور فعال (ExcelDriver / CSVDriver) |
readFile($path, $previewMode = false) | خواندن و پارس فایل |
validateData($data, $cacheHash = null) | اعتبارسنجی ردیفها |
getPreviewWithValidation($path, $inputValues = []) | پیشنمایش همراه خطاهای اعتبارسنجی |
preProcessRows($path, $inputValues = []) | خواندن + اعتبارسنجی و ساخت ImportResult |
execute($path, $inputValues = [], $selectedRowNumbers = null) | اجرای کامل: preprocess → before → process |
generateSampleFile() | تولید فایل نمونه و بازگرداندن مسیر |
render() | رندر UI آمادهٔ import |
مثال: اجرای مستقیم بدون Facade
$import = new BookImport();
$result = $import->execute(
filePath: $request->file('file')->getRealPath(),
inputValues: ['province_id' => 1],
selectedRowNumbers: [1, 2, 5], // فقط ردیفهای انتخابشده (۱پایه)
);
foreach ($result->getValidRows() as $row) {
// ...
}
مثال: فقط پیشپردازش (بدون درج)
$import = new BookImport();
$result = $import->preProcessRows(
$uploadedFile->getRealPath(),
['book_type' => 'digital']
);
$valid = $result->getValidRows();
$failed = $result->getFailedRows();
مثال: نوع فیلد بهصورت رشته
if ($this->getFieldTypeValue('file') === 'link') {
// معادل ImportFieldType::LINK->value
}
تعریف فیلدها
در متد fields با استفاده از Builder با نام ImportField، می توان مشخص کرد که چه فیلدهایی باید از فایل وارد شوند.
class BookImport extends BaseImport
{
public function __construct()
{
$this
->setFileFormat(FileFormat::EXCEL)
->setTitle('درج گروهی کتاب ها')
->setIndexRoute('books.index')
->setPerPage(15);
}
public function fields(): array
{
return [
ImportField::make('title')
->column(0)
->label('عنوان')
->description('عنوان کتاب را وارد کنید')
->rules(['string', 'max:255'])
->sample([
'نمونه عنوان ۱',
'نمونه عنوان ۲',
'نمونه عنوان ۳',
]),
ImportField::make('author')
->column(1)
->label('نویسنده')
->description('نام نویسنده کتاب')
->rules(['string', 'max:255'])
->sample([
'نمونه نویسنده ۱',
'نمونه نویسنده ۲',
'نمونه نویسنده ۳',
]),
ImportField::make('isbn')
->column(2)
->label('ISBN')
->description('شابک بینالمللی کتاب')
->rules(['string'])
->sample([
'ISBN1',
'ISBN2',
'ISBN3',
]),
ImportField::make('price')
->column(3)
->label('قیمت')
->rules(['numeric', 'min:0'])
->transform(function ($value) {
// Remove currency symbols and convert to float
return (float) preg_replace('/[^0-9.]/', '', $value);
})
->sample([
'1000.00',
'2000.00',
'3000.00',
]),
ImportField::make('published_at')
->column(4)
->label('تاریخ انتشار')
->rules(['nullable', 'date'])
->transform(function ($value) {
// Convert various date formats to Y-m-d
if (empty($value)) {
return null;
}
try {
return date('Y-m-d', strtotime($value));
} catch (Exception $e) {
return $value;
}
})
->sample([
'2024-01-01',
'2024-02-01',
'2024-03-01',
]),
ImportField::make('file')
->column(5)
->label('فایل')
->type(ImportFieldType::LINK)
->description('لینک فایل یا جلد کتاب')
->rules(['nullable', 'url'])
->sample('https://cdn.example.com/book-cover.jpg'),
];
}
}
پارامترهای ImportField Builder
column: ایندکس ستون فایل یا نام هدر (اختیاری)- فقط برای نگاشت خواندن فایل و جایگذاری در فایل نمونه است؛ ترتیب جدول پیشنمایش را تغییر نمیدهد.
- مقدار عددی (۰پایه) موقعیت ستون در Excel/CSV را مشخص میکند؛ مثلاً
0= ستون A،1= ستون B. - مقدار رشتهای نام هدر فایل است و برای match با عنوان ستون استفاده میشود.
- اگر تعریف نشود، معمولاً با
labelبهعنوان نام هدر match میشود.
label: عنوان قابل مشاهده- این عنوان برای نمایش در رابط کاربری استفاده می شود.
description: متن راهنما (اختیاری)- در صورت تعریف، زیر عنوان ستون در جدول پیشنمایش نمایش داده میشود تا کاربر بداند چه مقداری باید وارد کند.
- اگر تعریف نشود، فقط
labelنمایش داده میشود.
rules/validation: قوانین اعتبارسنجی- در هنگام درج گروهی فیلدها براساس این قوانین اعتبارسنجی میشوند.
validation()نام مستعار (alias) برایrules()است.
required: الزامی بودن فیلد- با فراخوانی
required()قانونrequiredبه قوانین اعتبارسنجی اضافه میشود (اگر از قبل نباشد). - میتوانید
required(false)بزنید تا پرچم را خاموش کنید؛ قوانین از قبل تنظیمشده حذف نمیشوند.
- با فراخوانی
transform: تبدیل مقدار- این متد به شما اجازه میدهد تا تغییر دلخواه را بر روی مقداری که از آن فیلد دریافت می شود را اعمال کنید
sample: نمونه مقدار- در ایجاد فایل نمونه برای فیلد از این مقدار استفاده می شود.
- مقدار اسکالر → یک سطر نمونه.
- آرایه از مقادیر → هر عنصر در یک سطر جداگانه در فایل Excel/CSV نوشته میشود.
type: نوع داده ستون- مقدار پیشفرض
textاست. - برای تعیین نوع میتوانید از enum
ImportFieldTypeاستفاده کنید (مثلImportFieldType::LINK). - مقدار این فیلد در پردازش قابل خواندن است و میتوانید براساس آن منطق متفاوت اجرا کنید.
- مقدار پیشفرض
defaultValue: مقدار پیشفرض فیلد- در آرایه نگاشت فیلد ذخیره میشود و برای منطق سفارشی در دسترس است.
placeholder: متن راهنمای ورودی- متن placeholder مرتبط با فیلد در نگاشت ذخیره میشود.
options: گزینههای اضافی- آرایهای از متادیتای سفارشی که با
array_mergeبه گزینههای قبلی اضافه میشود.
- آرایهای از متادیتای سفارشی که با
rulesWhen: قوانین اعتبارسنجی شرطی- یک callback دریافت میکند که آرایه کامل سطر (شامل مقادیر ستونهای فایل و inputهای فرم) را به عنوان ورودی میگیرد.
- باید قوانین Laravel validation را برگرداند.
- در صورت تعریف، برای هر سطر بر
rules()اولویت دارد.
labelWhen: عنوان شرطی- یک callback دریافت میکند که آرایه کامل سطر را به عنوان ورودی میگیرد.
- نام فیلد را در پیامهای خطای اعتبارسنجی و عنوان ستون جدول پیشنمایش بهصورت داینامیک تعیین میکند.
messagesWhen: پیام خطای سفارشی شرطی- یک callback دریافت میکند که آرایه کامل سطر را به عنوان ورودی میگیرد.
- کلیدها میتوانند نام rule (مثل
required) یا ترکیبfield.ruleباشند.
مثال: required، defaultValue، placeholder و options
ImportField::make('isbn')
->column(2)
->label('ISBN')
->required() // معادل rules(['required']) وقتی قوانین خالی باشند
->placeholder('978...')
->defaultValue(null)
->options([
'mask' => '978############',
'help_url' => 'https://example.com/isbn-help',
])
->sample('9786001234567'),
// معادل rules با نام مستعار validation
ImportField::make('title')
->label('عنوان')
->validation(['required', 'string', 'max:255'])
->sample('نمونه عنوان'),
ترتیب ستونها و پیشنمایش
ترتیب نمایش در UI از نگاشت فیزیکی فایل جداست.
۱. نگاشت ستون فایل با column() (فقط خواندن/نمونه)
column() فقط مشخص میکند داده از کدام ستون فایل خوانده شود و در فایل نمونه کجا نوشته شود — روی ترتیب جدول پیشنمایش اثر ندارد.
اولویت هنگام خواندن فایل:
->column(N)عددی → فقط همان ایندکس (۰پایه)->column('Header')رشتهای → match با نام هدر- بدون
column→ match باlabelبهعنوان نام هدر (و در فایل نمونه، جایگاه بهترتیب تعریف درfields())
return [
ImportField::make('title')->column(0)->label('عنوان')->sample('نمونه'),
ImportField::make('author')->column(13)->label('نویسنده')->sample('نمونه'),
ImportField::make('file')->label('فایل')->sample('https://cdn.example.com/cover.jpg'),
];
۲. ترتیب ستونها در جدول پیشنمایش
پیشنمایش فقط ترتیب آرایه را در نظر میگیرد:
- فیلدهای
fields()بهترتیب تعریف - سپس inputهای
inputs()بهترتیب تعریف
پس برای جابهجا کردن ستونهای UI کافی است ترتیب آیتمها را در fields() / inputs() عوض کنید؛ نیازی به تغییر column() نیست.
ولیدیشن شرطی
گاهی قوانین اعتبارسنجی یک فیلد به مقدار ستونهای دیگر یا inputهای فرم وابسته است. برای این حالت از متدهای rulesWhen، labelWhen و messagesWhen استفاده کنید.
callbackها آرایهای دریافت میکنند که شامل:
- مقادیر ستونهای همان سطر از فایل
- مقادیر inputهای تعریفشده در متد
inputs()(مثلbook_typeیاprovince)
مثال ۱: ولیدیشن بر اساس input فرم
public function inputs(): array
{
return [
ImportInput::make('book_type')
->type('select')
->label('نوع کتاب')
->rules(['required'])
->items([
['id' => 'physical', 'name' => 'فیزیکی'],
['id' => 'digital', 'name' => 'دیجیتال'],
]),
];
}
public function fields(): array
{
return [
ImportField::make('isbn')
->label('ISBN')
->rulesWhen(function (array $row) {
return match ($row['book_type'] ?? null) {
'physical' => ['required', 'string', 'regex:/^978\d{10}$/'],
'digital' => ['nullable', 'string', 'max:50'],
default => ['required', 'string'],
};
})
->labelWhen(function (array $row) {
return match ($row['book_type'] ?? null) {
'physical' => 'شابک (کتاب فیزیکی)',
'digital' => 'شناسه دیجیتال',
default => 'ISBN',
};
})
->messagesWhen(function (array $row) {
if (($row['book_type'] ?? null) !== 'physical') {
return [];
}
return [
'required' => 'برای کتاب فیزیکی، وارد کردن شابک الزامی است.',
'regex' => 'شابک کتاب فیزیکی باید ۱۳ رقم و با 978 شروع شود.',
];
}),
ImportField::make('file')
->label('فایل')
->type(ImportFieldType::LINK)
->rulesWhen(function (array $row) {
return ($row['book_type'] ?? null) === 'digital'
? ['required', 'url']
: ['nullable', 'url'];
})
->labelWhen(function (array $row) {
return ($row['book_type'] ?? null) === 'digital'
? 'لینک فایل دیجیتال'
: 'لینک فایل (اختیاری)';
})
->messagesWhen(function (array $row) {
if (($row['book_type'] ?? null) !== 'digital') {
return [];
}
return [
'required' => 'برای کتاب دیجیتال، لینک فایل الزامی است.',
'url' => 'لینک فایل دیجیتال معتبر نیست.',
];
}),
];
}
مثال ۲: ولیدیشن بر اساس مقدار ستون دیگر
ImportField::make('price')
->label('قیمت')
->rulesWhen(function (array $row) {
if (str_contains(mb_strtolower((string) ($row['title'] ?? '')), 'رایگان')) {
return ['nullable', 'numeric', 'min:0'];
}
return ['required', 'numeric', 'min:1'];
})
->labelWhen(function (array $row) {
return str_contains(mb_strtolower((string) ($row['title'] ?? '')), 'رایگان')
? 'قیمت (کتاب رایگان)'
: 'قیمت';
})
->messagesWhen(function (array $row) {
if (str_contains(mb_strtolower((string) ($row['title'] ?? '')), 'رایگان')) {
return [];
}
return [
'required' => 'برای کتاب غیررایگان، وارد کردن قیمت الزامی است.',
'min' => 'قیمت باید بزرگتر از صفر باشد.',
];
}),
- اگر
rulesWhenمقدارnullبرگرداند، ازrules()استاتیک بهعنوان fallback استفاده میشود. - برای فیلدهای کاملاً شرطی، قوانین را فقط داخل
rulesWhenتعریف کنید و ازrequired()استاتیک استفاده نکنید. labelWhenهم روی پیامهای خطای اعتبارسنجی و هم روی عنوان ستون در جدول پیشنمایش اثر میگذارد؛ اگر callback مقدار خالی برگرداند، ازlabel()بهعنوان fallback استفاده میشود.
چند سطر نمونه با آرایه در sample()
اگر برای یک فیلد بهجای مقدار اسکالر، آرایهای به sample() بدهید، در فایل نمونه هر عنصر آرایه در یک سطر جداگانه نوشته میشود (نه همه در یک سلول).
ImportField::make('title')
->label('عنوان')
->sample([
'راهنمای معلم آموزش درس9',
'راهنمای معلم آموزش درس10',
'راهنمای معلم آموزش درس11',
]),
ImportField::make('page_number')
->label('شماره صفحه')
->sample([
'10',
'11',
'12',
]),
// مقدار اسکالر فقط در سطر اول نوشته میشود؛ سطرهای بعدی خالی میمانند
ImportField::make('file')
->label('فایل')
->sample('https://cdn.example.com/cover.jpg'),
قواعد تولید فایل نمونه:
- تعداد سطرها = طول بلندترین آرایه
sampleبین فیلدها. - برای فیلدهایی که آرایه کوتاهتری دارند، ایندکسهای خالی بهصورت سلول خالی نوشته میشوند (میتوانید صریحاً
''بگذارید). - اگر همه فیلدها اسکالر باشند، فقط یک سطر نمونه ساخته میشود.
- مقدار اسکالر در کنار آرایهها فقط در سطر اول نوشته میشود و در سطرهای بعدی خالی میماند.
آرایه را بهصورت PHP array به sample() بدهید؛ رشتهای مثل "['a', 'b']" بهعنوان یک مقدار متنی در یک سلول نوشته میشود و به چند سطر تبدیل نمیشود.
تشخیص نوع ستون در processRows
اگر در fields() برای یک فیلد نوع تعریف کرده باشید، در زمان پردازش میتوانید نوع همان ستون را دریافت کنید:
use Dornica\Foundation\Import\Enums\ImportFieldType;
public function processRows(ImportResult $result): void
{
foreach ($result->getValidRows() as $row) {
// بهصورت enum
if ($this->getFieldType('file') === ImportFieldType::LINK) {
// ...
}
// یا بهصورت رشته
if ($this->getFieldTypeValue('file') === 'link') {
FileManager::diskDriver('local')
->path('bank/logos')
->module('Bank')
->name('logo_' . time())
->fileType('general')
->uploader(authenticator()->user())
->uploadWithLink($row['file']);
}
}
}
تعریف ورودیها
ورودیهای تعریفشده در متد inputs() در فرم درج گروهی نمایش داده میشوند. مقدار انتخابشده در هر input بهصورت خودکار به همه ردیفهای فایل اضافه میشود؛ یعنی اگر input با نام province_id تعریف کنید، مقدار انتخابشده در هر ردیف در دسترس خواهد بود و میتوانید در processRows یا rulesWhen از آن استفاده کنید.
در جدول پیشنمایش، ستونها بهترتیب تعریف در fields() و سپس inputs() نمایش داده میشوند؛ column() فقط نگاشت فایل است و ترتیب UI را عوض نمیکند.
سلکت ساده (با آیتمهای ثابت)
public function inputs(): array
{
return [
ImportInput::make('province_id')
->type('select')
->label('استان')
->required()
->rules(['required'])
->items([
['id' => 1, 'name' => 'تهران'],
['id' => 2, 'name' => 'اصفهان'],
['id' => 3, 'name' => 'شیراز'],
])
->containerClass('col-md-6'),
];
}
سلکت وابسته (Depend On)
برای سلکتهایی که گزینههایشان بر اساس انتخاب والد از API بارگذاری میشود (مثل استان → شهر → منطقه)، از متدهای id()، dependsOn() / parentId() و routeName() استفاده کنید. رفتار مشابه x-select با parentId و routeName در FormComponent است.
مثال کامل: استان → شهر → منطقه
use Dornica\Foundation\Import\Builders\ImportInput;
public function inputs(): array
{
$provinces = Province::query()
->orderBy('name')
->get(['id', 'name'])
->map(fn ($province) => [
'id' => $province->id,
'name' => $province->name,
])
->all();
return [
ImportInput::make('province')
->type('select')
->id('import-province-select')
->label('استان')
->required()
->rules(['required'])
->items($provinces)
->containerClass('col-md-6'),
ImportInput::make('city_id')
->type('select')
->id('import-city-select')
->label('شهر')
->required()
->rules(['required'])
->dependsOn('import-province-select', 'api.admin.api.cities.index')
->displayModel(City::class)
->containerClass('col-md-6'),
ImportInput::make('district_id')
->type('select')
->id('import-district-select')
->label('منطقه')
->dependsOn('import-city-select', 'api.admin.api.districts.index')
->displayModel(District::class)
->containerClass('col-md-6'),
];
}
- هر input باید
id()یکتا داشته باشد. - در
dependsOn()باید id سلکت والد را بدهید، نهnameفیلد. - سلکت والد (مثل استان) معمولاً
itemsدارد؛ سلکتهای وابسته معمولاً فقطrouteNameدارند و گزینهها از API لود میشوند.
معادل تنظیم دستی (بدون dependsOn)
ImportInput::make('city_id')
->type('select')
->id('import-city-select')
->label('شهر')
->parentId('import-province-select')
->routeName('api.admin.api.cities.index')
->displayModel(City::class),
نمایش نام در پیشنمایش (بهجای ID)
در مرحله «نتایج»، مقادیر inputهای فرم در جدول پیشنمایش نمایش داده میشوند. بهصورت پیشفرض بهجای شناسه خام، برچسب (label) گزینه نمایش داده میشود. ترتیب resolve به این صورت است:
| اولویت | روش | توضیح |
|---|---|---|
| ۱ | items | برای سلکتهای با لیست ثابت؛ نام از name / title / label خوانده میشود |
| ۲ | displayUsing | callback سفارشی برای تبدیل مقدار به متن نمایشی |
| ۳ | displayModel | خواندن برچسب از مدل Eloquent |
| ۴ | routeName | استنتاج خودکار مدل از route (مثلاً cities.index → مدل city) |
برای سلکتهای وابسته (dependsOn) معمولاً items خالی است؛ بنابراین بهتر است صریحاً displayModel() یا displayUsing() تعریف کنید تا در پیشنمایش بهجای ID، نام نمایش داده شود.
مثال ۱: resolve از مدل
ImportInput::make('city_id')
->dependsOn('import-province-select', 'api.admin.api.cities.index')
->displayModel(City::class);
// ستون برچسب و کلید مقدار سفارشی
ImportInput::make('book_id')
->dependsOn('import-grade-select', 'admin.api.v1.books.list')
->displayModel(Book::class, 'title', 'id');
مثال ۲: resolve با callback (مثلاً enum / نوع)
ImportInput::make('type')
->dependsOn('import-book-select', 'admin.api.v1.books.subject-categories-types')
->displayUsing(fn ($value) => match ((int) $value) {
1 => 'فصل',
2 => 'عنوان',
3 => 'هفته',
default => (string) $value,
});
نمایش شناسه خام در پیشنمایش
اگر گاهی لازم است در جدول پیشنمایش همان ID نمایش داده شود (نه نام)، از showValueInPreview() استفاده کنید:
ImportInput::make('district_id')
->dependsOn('import-city-select', 'api.admin.api.districts.index')
->displayModel(District::class)
->showValueInPreview(); // پیشنمایش: id خام
مقادیر ذخیرهشده در ردیفها همیشه شناسه (ID) هستند؛ متدهای displayModel / displayUsing فقط نمایش پیشنمایش را تغییر میدهند. در processRows از همان ID استفاده کنید.
پارامترهای ImportInput Builder
| متد | توضیح |
|---|---|
type | نوع ورودی؛ در حال حاضر فقط select |
label | عنوان نمایشی در فرم و پیشنمایش |
rules / validation / required | قوانین اعتبارسنجی Laravel؛ validation() نام مستعار rules() است |
items | گزینههای سلکت ثابت (id, name) |
containerClass | کلاس CSS ستون (پیشفرض: col-md-6) |
placeholder | متن placeholder سلکت |
defaultValue / selected | مقدار پیشفرض / مقدار ازپیشانتخابشده |
description | توضیح کمکی |
id | شناسه HTML سلکت؛ الزامی برای سلکت وابسته |
parentId | id سلکت والد |
routeName | نام route API برای بارگذاری گزینههای وابسته |
dependsOn($parentId, $routeName) | میانبر parentId + routeName |
routeParameters | پارامترهای route |
parameters | پارامترهای اضافی ارسالی به API |
allowSelectAll | انتخاب همه (سلکت چندتایی) |
infiniteScroll | فعالسازی بارگذاری بینهایت برای گزینههای remote |
clearable | امکان پاک کردن مقدار سلکت (پیشفرض: true) |
displayModel($class, $displayColumn = 'name', $displayValueColumn = 'id') | resolve برچسب پیشنمایش از مدل Eloquent |
displayColumn | ستون برچسب هنگام resolve از مدل (پیشفرض: name) |
displayValueColumn | ستونی که با مقدار ارسالی سلکت match میشود (پیشفرض: id) |
displayUsing | callback سفارشی برای تبدیل مقدار به برچسب پیشنمایش |
showValueInPreview | نمایش شناسه خام در پیشنمایش بهجای برچسب |
مثال: placeholder، defaultValue، selected، clearable و infiniteScroll
ImportInput::make('province_id')
->type('select')
->label('استان')
->placeholder('یک استان انتخاب کنید')
->defaultValue(1) // اگر selected ست نشده باشد، همین مقدار انتخاب میشود
->clearable(true) // امکان پاک کردن انتخاب
->required()
->items([
['id' => 1, 'name' => 'تهران'],
['id' => 2, 'name' => 'اصفهان'],
])
->containerClass('col-md-6'),
ImportInput::make('city_id')
->type('select')
->id('import-city-select')
->label('شهر')
->dependsOn('import-province-select', 'api.admin.api.cities.index')
->infiniteScroll() // بارگذاری تدریجی گزینهها از API
->clearable(false) // جلوگیری از خالی شدن مقدار
->routeParameters(['guard' => 'admin'])
->parameters(['active' => 1])
->displayModel(City::class)
->containerClass('col-md-6'),
// مقدار ازپیشانتخابشده صریح
ImportInput::make('book_type')
->type('select')
->label('نوع کتاب')
->selected('physical')
->validation(['required']) // معادل rules(['required'])
->items([
['id' => 'physical', 'name' => 'فیزیکی'],
['id' => 'digital', 'name' => 'دیجیتال'],
]),
selectedنسبت بهdefaultValueدر رندر فرم اولویت دارد (selected ?? default_value).- اگر کاربر هنوز مقداری انتخاب نکرده باشد، در پردازش از
default_valueبهعنوان fallback استفاده میشود.
پردازش داده ها
با استفاده از متد processRows، میتوانید پردازشهای دلخواه خود را بر روی ردیفهای داده انجام دهید.
بررسی شرایط قبل از درج (beforeProcessRows)
گاهی قبل از درج دادهها باید شرایطی در سیستم برقرار باشد؛ برای مثال وجود «وضعیت شروع» در جدول وضعیتها. در این حالت متد beforeProcessRows را override کنید.
این متد بعد از اعتبارسنجی و انتخاب ردیفها و قبل از processRows اجرا میشود. اگر شرط برقرار نباشد، با پرتاب ImportProcessException عملیات متوقف شده و پیام خطا به کاربر نمایش داده میشود.
use Dornica\Foundation\Import\Exceptions\ImportProcessException;
use Dornica\Foundation\Import\ImportResult;
public function beforeProcessRows(ImportResult $result): void
{
$startStatus = BookStatus::query()
->where('is_start', true)
->first();
if (!$startStatus) {
throw new ImportProcessException('وضعیت شروع برای کتابها تعریف نشده است.');
}
}
public function processRows(ImportResult $result): void
{
foreach ($result->getValidRows() as $row) {
Book::create([
'title' => $row['title'],
'author' => $row['author'],
'status_id' => BookStatus::query()->where('is_start', true)->value('id'),
// ...
]);
}
}
beforeProcessRowsاختیاری است؛ اگر override نشود، مستقیماًprocessRowsاجرا میشود.- میتوانید
ImportProcessExceptionرا داخلprocessRowsهم پرتاب کنید تا در میانه پردازش، عملیات متوقف و پیام به کاربر برگردانده شود. - در مسیر Facade نیز متد
apply()ابتداbeforeProcessRowsو سپسprocessRowsرا فراخوانی میکند.
دری افت ردیفهای معتبر
ابتدا متد getValidRows بر روی شیء ImportResult $result فراخوانی میشود. این متد تمام ردیفهایی که موفق به عبور از فرآیند اعتبارسنجی شدهاند را برمیگرداند.
دریافت ردیفهای نامعتبر
برای دسترسی به ردیفهای حاوی خطا، میتوانید از متد getFailedRows استفاده کنید که ردیفهای نامعتبر را برمیگرداند.
class BookImport extends BaseImport
{
public function __construct()
{
$this
->setFileFormat(FileFormat::EXCEL)
->setTitle('درج گروهی کتاب ها');
}
public function processRows(ImportResult $result): void
{
$data = [];
foreach ($result->getValidRows() as $row) {
$data[] = [
'title' => $row['title'],
'author' => $row['author'],
'isbn' => $row['isbn'],
'price' => $row['price'],
'published_at' => $row['published_at'],
];
}
Book::insert($data);
}
}
اگر تعداد ردیفها زیاد است، پردازش همزمان همهٔ آنها داخل processRows میتواند باعث timeout یا مصرف بالای حافظه شود. در این حالت بهتر است بهجای درج مستقیم در همان متد، دادهها را به یک Job (یا چند Job تکهتکه) بسپارید و پردازش سنگین را در صف (queue) انجام دهید.
استفاده از Blade Layout Import
در صورتی که از Blade Layout استفاده میکنید، میتوانید فرم وارد کردن دادهها را به راحتی در یک نما رندر کنید. این مورد برای پیادهسازی سفارشی در پروژه های پنل بسیار مفید است.
use Dornica\PanelKit\BladeLayout\Facade\BladeLayout;
use App\Imports\BookImport;
public function index()
{
BladeLayout::import(BookImport::class);
return view('books.form');
}
<x-default-layout>
{!! bladeLayout()->import()->render() !!}
</x-default-layout>
استفاده از Facade Import
کلاس Import برای مدیریت فرآیند وارد کردن دادهها از فایل طراحی شده است. این Facade با تکیه بر یک کلاس پیکربندی (Import Class)، عملیات خواندن فایل، اعتبارسنجی و پردازش دادهها را بهصورت مرحلهای انجام میدهد.
چه زمانی از Facade استفاده کنیم؟
از Facade Import زمانی استفاده کنید که میخواهید فرایند import را بهصورت برنامهای (programmatic) و خارج از UI استاندارد پنل اجرا کنید؛ یعنی خودتان فایل را دریافت کنید، اعتبارسنجی کنید و در صورت نیاز apply را صدا بزنید.
| سناریو | ابزار مناسب |
|---|---|
| فرم درج گروهی در پنل با پیشنمایش و انتخاب ردیف | BladeLayout::import(...) (یا دکمه import جدول) — نه Facade |
| کنترلر / API / Command / Job سفارشی | Facade Import |
| فقط تولید و دانلود فایل نمونه در کد خودتان | Facade Import (getSampleFile) |
اجرای دستی process سپس apply روی کلاس BaseImport | Facade Import |
- برای UI آمادهٔ پنل →
BladeLayout/ ImportUI - برای کنترل کامل در کد خودتان (بدون وابستگی به فرم و پیشنمایش پنل) → Facade
Import
متدهای اصلی Facade:
| متد | نقش |
|---|---|
config($class) | تعیین کلاس پیکربندی (BaseImport) |
file($uploadedFile) | تعیین فایل آپلودشده |
inputs($array) | ورودیهای اضافی فرم (مثل province_id) |
process() | خواندن فایل و اعتبارسنجی (preProcessRows) |
getRows() / getFailedRows() | دریافت ردیفهای معتبر / نامعتبر پس از process |
apply() | اجرای beforeProcessRows و سپس processRows |
getSampleFile() | تولید فایل نمونه و بازگرداندن مسیر آن |
ایجاد یک نمونه جدید
ابتدا فایل ورودی را دریافت کرده و سپس با استفاده از Facade Import، کلاس پیکربندی و فایل را مشخص میکنیم. پس از آن، فرآیند پردازش اجرا میشود.
use Dornica\Foundation\Import\Facades\Import;
use App\Imports\SampleImport;
$uploadedFile = $request->file('file');
$result = Import::config(SampleImport::class)->file($uploadedFile)->process();
$validRows = $result->getRows();
use Dornica\Foundation\Import\Facades\Import;
use App\Imports\SampleImport;
$sampleFilePath = Import::config(SampleImport::class)->getSampleFile();
return response()->download($sampleFilePath);
پردازش
به کمک این متد، فایل مورد نظر خوانده شده و اعتبارسنجیهای لازم بر اساس تنظیمات تعریفشده در پیکربندی اعمال میگردد.
دریافت ردیفهای معتبر
برای دریافت ردیفهای معتبر پس از پردازش، میتوان از متد getRows استفاده کرد: