پرش به مطلب اصلی

راهنمای ساخت مدیریت پایه

در این راهنما، به صورت مرحله ‌به ‌مرحله یاد می‌گیریم که چگونه با استفاده از Doravel و ساختار ماژولار آن، یک مدیریت ساده و کامل (CRUD) بسازیم.
هدف این است که حتی اگر برای اولین بار با Doravel کار می‌کنید، بتوانید به راحتی یک ماژول عملی راه‌اندازی کنید.


۱. ساخت ماژول

ابتدا یک ماژول جدید برای بخش مدیریت ایجاد کنید:

php artisan module:make Bank

۲. تنظیم مسیرها (Routes)

پاک کردن مسیرهای پیش‌فرض

داخل فایل‌های زیر، Route های پیش‌فرض برای ماژول را پاک کنید.

  • MODULE_NAME/routes/web.php
  • MODULE_NAME/routes/api.php
نکته

به جای MODULE_NAME نام ماژول خود را قرار دهید.


پیکربندی RouteServiceProvider

در Service Provider ماژول، مسیرها تعریف شده است، prefix و as و middleware را تنظیم کنید.

مسیرهای وب

RouteServiceProvider.php
Route::middleware(['web', 'authorized'])
->prefix('admin/basic')
->as('admin.basic.')
->group(module_path('Bank', '/routes/web.php'));

مسیرهای API

در صورتی که در ماژول خود به API نیاز دارید، مسیرهای API را نیز به صورت زیر تنظیم کنید.

RouteServiceProvider.php
Route::middleware(['web', 'authorized'])
->prefix('admin/api/basic')
->as('admin.api.basic.')
->group(module_path('Bank', '/routes/api.php'));
نکته مهم

حتما middleware با نام authorized را اضافه کنید تا فقط کاربران وارد شده یا احرازهویت شده در پروژه بتوانند به این مسیرها دسترسی داشته باشند.

اطلاع

در نظر داشته باشید که middleware با نام authorized در پکیج Doravel وجود دارد و نیاز به تعریف مجدد در پروژه نیست.


۳. ساخت کنترلر

یک کنترلر جدید شامل صفحات و عملیات های یک مدیریت کامل که با ساختار Resource است، ایجاد کنید:

BankController.php
class BankController extends Controller
{
public function index() {}
public function create() {}
public function store(Request $request) {}
public function show(Bank $bank) {}
public function edit(Bank $bank) {}
public function update(Request $request, Bank $bank) {}
public function destroy(Bank $bank) {}
}

۴. تعریف مسیرهای CRUD

تمامی Route های مربوط به CRUD را در فایل web.php تعریف کنید.

web.php
Route::prefix('banks')->as('banks.')->controller(BankController::class)->group(function () {

Route::get('/', 'index')
->name('index')
->title('لیست بانک ها')
->showInSidebar();

Route::get('create', 'create')
->name('create')
->title('درج بانک')
->parentRoute('admin.basic.banks.index');

Route::post('store', 'store')->name('store');

Route::prefix('{bank}')->group(function () {

Route::get('show', 'show')
->name('show')
->title('جزئیات بانک')
->parentRoute('admin.basic.banks.index');

Route::get('edit', 'edit')
->name('edit')
->title('ویرایش بانک')
->parentRoute('admin.basic.banks.index');

Route::put('update', 'update')->name('update');

Route::delete('destroy', 'destroy')->name('destroy');

});

});
نکته

بهتر است برای title مربوط به Route ها، از ترجمه استفاده کنید تا در صورت نیاز به چندزبانه شدن، راحت‌ تر بتوانید این کار را انجام دهید (نیاز به استفاده از تابع کمکی ترجمه ()__ نیست و می‌توانید به صورت مستقیم کلید ترجمه را قرار دهید).

توجه

چون برای این مدیریت API تعریف نداریم، فایل api.php خالی می‌ماند.


۵. تعریف منو با Doravel

در سایدبار پنل مدیریت، منوها به صورت خودکار از روی Route های تعریف شده ساخته می‌شوند.
اما اگر بخواهید سطح های بالاتر از Route ها را نیز در منو داشته باشید، باید آن‌ها را به صورت دستی تعریف کنید.
برای اینکار باید یک متد تعریف کنید و این متد را داخل boot در ServiceProvider اصلی ماژول صدا بزنید.

BankServiceProvider.php
use Dornica\Foundation\Doravel\Facade\Doravel;
use Dornica\Foundation\RoutePropertyCollector\MenuCollector\Builders\MenuGroup;
use Dornica\Foundation\RoutePropertyCollector\MenuCollector\Builders\MenuSubgroup;

public function registerMenuGroup(): void
{
Doravel::menu(function () {
return [
MenuGroup::make()
->name('admin.basic')
->title("اطلاعات پایه")
->icon('fa-regular fa-memo-circle-info')
->subMenu([
MenuSubgroup::make()
->name('admin.basic.banks')
->title('لیست بانک ها'),
]),
];
});
}

۶. ساخت و مدیریت مدل‌ها

در پروژه‌های مبتنی بر Doravel، معمولاً در ابتدای پروژه، تمامی مدل‌ها یک‌بار و به‌صورت کامل ساخته می‌شوند. بنابراین در اغلب موارد فایل مدل از قبل وجود دارد و فقط باید به داخل ماژول منتقل شود.

حالت اول: مدل از قبل در پروژه وجود دارد (سناریوی رایج)

در ابتدای پروژه همه مدل‌ها ساخته شده‌اند و در مسیر زیر قرار دارند:

app/Models

برای مثال مدل بانک در مسیر زیر قرار دارد:

app/Models/Bank.php

از آنجا که ساختار پروژه ما ماژولار است، نباید مدل‌ها در ریشه پروژه باقی بمانند. مدل مربوطه را به داخل ماژول منتقل کنید:

MODULE_PATH/app/Models/Bank.php
نکته مهم

بعد از انتقال، حتماً namespace مدل را با ساختار ماژول اصلاح کنید.

مثال Namespace
namespace Modules\Bank\Models;

حالت دوم: مدل وجود ندارد (سناریوی استثنایی)

اگر به هر دلیلی مدل مورد نظر در پروژه وجود نداشت، فقط همان مدل را بسازید.

مثلاً برای جدول banks:

php artisan code:models --table=banks

یا مثال دیگر:

php artisan code:models --table=users

پس از ساخته شدن مدل در مسیر:

app/Models

دقیقاً مانند حالت اول، مدل را به داخل ماژول منتقل کنید:

MODULE_PATH/app/Models

و namespace آن را اصلاح نمایید.

نکات مهم

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

منبع پکیج

برای اطلاعات بیشتر در مورد پکیج ساخت مدل:

Reliese Laravel


۷. ایجاد فایل‌های Blade

چهار فایل اصلی برای CRUD بسازید:

MODULE_PATH/resources/views/index.blade.php
MODULE_PATH/resources/views/show.blade.php
MODULE_PATH/resources/views/create.blade.php
MODULE_PATH/resources/views/edit.blade.php

۸. صفحه لیست (Index)

Table Generator

ساخت Table Generator با استفاده از کامند زیر:

php artisan dornica:make-table BankTable --module=Bank
نکته

برای مشاهده جزئیات بیشتر در مورد Table Generator می‌توانید به مستندات مربوطه مراجعه کنید: مستندات Table Generator

کنترلر

استفاده از Table Generator و نمایش در ویو:

BankController.php
use Dornica\PanelKit\BladeLayout\Facade\BladeLayout;
use Modules\Bank\Generators\Tables\BankTable;

public function index()
{
BladeLayout::table(BankTable::class);

return view('bank::index');
}

ویو

نمایش یا Render کردن Table Generator در ویو:

index.blade.php
<x-default-layout>

{!! bladeLayout()->table()->render() !!}

</x-default-layout>

۹. صفحه درج (Create)

کنترلر

نمایش فرم درج:

BankController.php
public function create()
{
return view('bank::create');
}

اعتبارسنجی ورودی‌ها

استفاده از Form Request برای اعتبارسنجی ورودی‌ها:

StoreBankRequest.php
class StoreBankRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'code' => ['required', 'string', 'max:50', 'unique:banks,code'],
];
}
}

برای استفاده در Form Validation باید در انتهای Blade ویو، اسکریپت اعتبارسنجی را اضافه کنید.

@push("scripts")
@canAccess('admin.basic.banks.store')
{!! FormValidator::formRequest(Modules\Bank\Http\Requests\StoreBankRequest::class, "#create-bank") !!}
@endcanAccess
@endpush
هشدار

توجه کنید که create-bank# باید همان id فرم و StoreBankRequest باید همان نام کلاس Form Request باشد.

ویو

پیاده سازی فرم درج با استفاده از کامپوننت‌های Blade:

نکته

برای ساخت فرم‌ها می‌توانید از مجموعه کامپوننت‌های فرم مثل text-input و select و radio-group و سایر موارد مشابه استفاده کنید: مستندات کامپوننت‌های فرم

create.blade.php
<x-default-layout>

<x-card :title="getPageTitle()">

<form
id="create-bank"
action="{{ route("admin.basic.banks.store") }}"
method="post"
>
@csrf

<div class="row g-3">

<x-text-input
containerClass="col-md-6"
name="name"
:label="__('base::general.name')"
:value="old('name')"
/>

<x-text-input
containerClass="col-md-6"
name="code"
:label="__('base::general.code')"
:value="old('code')"
direction="ltr"
/>

</div>

@canAccess('admin.basic.banks.store')
<div class="card-footer pb-0 px-0 d-flex gap-4 pt-5 justify-content-end mt-4">
<x-reset-button
:title="__('base::general.reset')"
variant="light"
appearance="outline"
/>

<x-button
buttonType="submit"
:title="__('base::general.submit')"
/>
</div>
@endcanAccess

</form>

</x-card>

@push("scripts")
@canAccess('admin.basic.banks.store')
{!! FormValidator::formRequest(Modules\Bank\Http\Requests\StoreBankRequest::class, "#create-bank") !!}
@endcanAccess
@endpush

</x-default-layout>

ذخیره اطلاعات

پیاده سازی منطق ذخیره اطلاعات در کنترلر:

BankController.php
public function store(StoreBankRequest $request)
{
$inputs = $request->validated();
$inputs['sort'] = getNextSortValue(Bank::class);

try {
Bank::create($inputs);

return redirect()
->route('admin.basic.banks.index')
->withFlash(
message: __("base::message.create_successfully"),
type: 'success',
);
} catch (Exception $exception) {
Log::error($exception);

return back()
->withFlash(
message: __("base::message.error_occurred"),
type: 'error',
);
}
}

۱۰. صفحه جزئیات (Show)

پیاده سازی Banner Generator برای نمایش اطلاعات کلی:

BankBanner.php
namespace Modules\Bank\Generators\Banners;

use Dornica\PanelKit\Generator\Banner\BaseBanner;
use Dornica\PanelKit\Generator\Banner\Builders\Banner;

class BankBanner extends BaseBanner
{
public function items(): array
{
return [
Banner::make()
->label(__("bank::general.bank"))
->value($this->bank->name)
->icon("fa-solid fa-building-columns"),
];
}
}

کنترلر

استفاده از Banner Generator و نمایش در ویو:

BankController.php
use Modules\Bank\Models\Bank;
use Dornica\PanelKit\BladeLayout\Facade\BladeLayout;
use Modules\Bank\Generators\Banners\BankBanner;

public function show(Bank $bank)
{
BladeLayout::data(compact('bank'));

BladeLayout::banner(BankBanner::class);

return view('bank::show', compact('bank'));
}

ویو

نمایش اطلاعات با استفاده از کامپوننت‌های Blade:

نکته

برای صفحهات جزئیات می‌توانید از کامپوننت‌های Design (رابط کاربری) مثل data-display و data-item و card و موارد مشابه برای نمایش بهتر جزئیات استفاده کنید: مستندات کامپوننت‌های Design

show.blade.php
<x-default-layout>

<x-data-display cols="3" :alignValues="true">

<x-data-item
:label="__('base::general.name')"
:value=" $bank->name"
/>

<x-data-item
:label="__('base::general.code')"
:value=" $bank->code"
dir="ltr"
/>

<x-data-item
:label="__('base::general.sort')"
:value=" $bank->sort"
/>

<x-data-item
:label="__('base::general.status')"
:value="$bank->is_active == Dornica\Foundation\Core\Enums\IsActive::YES ? __('base::enum.is_active.yes') : __('base::enum.is_active.no')"
/>

<x-data-item
:label="__('base::general.created_at')"
:value=" verta($bank->created_at)->format('Y/m/d H:i:s')"
dir="ltr"
valueClass="text-right"
/>

<x-data-item
:label="__('base::general.created_by')"
:value="$bank->createdBy?->full_name"
/>

<x-data-item
:label="__('base::general.updated_at')"
:value="verta($bank->updated_at)->format('Y/m/d H:i:s')"
dir="ltr"
valueClass="text-right"
/>

<x-data-item
:label="__('base::general.updated_by')"
:value=" $bank->updatedBy?->full_name"
/>

</x-data-display>

</x-default-layout>

۱۱. صفحه ویرایش (Edit)

کنترلر

استفاده از Banner Generator و نمایش در ویو:

BankController.php
use Modules\Bank\Models\Bank;
use Dornica\PanelKit\BladeLayout\Facade\BladeLayout;
use Modules\Bank\Generators\Banners\BankBanner;

public function edit(Bank $bank)
{
BladeLayout::data(compact('bank'));

BladeLayout::banner(BankBanner::class);

return view('bank::edit', compact('bank'));
}

اعتبارسنجی ورودی‌ها

استفاده از Form Request برای اعتبارسنجی ورودی‌ها:

UpdateBankRequest.php
class UpdateBankRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'code' => [
'required',
'string',
'max:50',
Rule::unique('banks', 'code')->ignore($this->bank->id)
],
];
}
}

برای استفاده در Form Validation باید در انتهای Blade ویو، اسکریپت اعتبارسنجی را اضافه کنید.

@push("scripts")
@canAccess('admin.basic.banks.store')
{!! FormValidator::formRequest(Modules\Bank\Http\Requests\UpdateBankRequest::class, "#update-bank") !!}
@endcanAccess
@endpush
هشدار

توجه کنید که update-bank# باید همان id فرم و UpdateBankRequest باید همان نام کلاس Form Request باشد.

ویو

پیاده‌سازی فرم ویرایش با استفاده از کامپوننت‌های Blade:

نکته

در فرم ها می‌توانید از کامپوننت‌های فرم برای ساخت فرم‌های استاندارد و قابل توسعه استفاده کنید: مستندات کامپوننت‌های فرم

edit.blade.php
<x-default-layout>

<x-card :title="getPageTitle()">

<form
id="update-bank"
action="{{ route('admin.basic.banks.update', encryptValue($bank->id)) }}"
method="post"
>
@csrf
@method('PUT')

<div class="row g-3">
<x-text-input
containerClass="col-md-6"
name="name"
:label="__('base::general.name')"
:value="old('name', $bank->name)"
/>

<x-text-input
containerClass="col-md-6"
name="code"
:label="__('base::general.code')"
:value="old('code', $bank->code)"
direction="ltr"
/>

<x-number-input
containerClass="col-md-6"
name="sort"
min="1"
:label="__('base::general.sort')"
:value="old('sort', $bank->sort)"
/>

<x-radio-group
containerClass="col-md-6"
id="is_active"
:label="__('base::general.status')"
name="is_active"
:options="Dornica\Foundation\Core\Enums\IsActive::componentOptions('base')"
:checked="old('is_active', $bank->is_active->value)"
/>
</div>

@canAccess('admin.basic.banks.update')
<div class="card-footer pb-0 px-0 d-flex gap-4 pt-5 justify-content-end mt-4">
<x-reset-button
:title="__('base::general.reset')"
variant="light"
appearance="outline"
/>

<x-button
:title="__('base::general.update')"
buttonType="submit"
/>
</div>
@endcanAccess

</form>

</x-card>

@push("scripts")
@canAccess('admin.basic.banks.update')
{!! FormValidator::formRequest(Modules\Bank\Http\Requests\UpdateBankRequest::class, "#update-bank") !!}
@endcanAccess
@endpush

</x-default-layout>

ذخیره تغییرات

پیاده سازی منطق ذخیره تغییرات در کنترلر:

BankController.php
public function update(UpdateBankRequest $request, Bank $bank)
{
try {
$bank->update($request->validated());

return redirect()
->route('admin.basic.banks.index')
->withFlash(
message: __("base::message.update_successfully"),
type: 'success',
);
} catch (Exception $exception) {
Log::error($exception);

return back()
->withFlash(
message: __("base::message.error_occurred"),
type: 'error',
);
}
}

۱۲. عملیات حذف (Delete)

پیاده سازی منطق حذف در کنترلر:

public function destroy(Bank $bank)
{
try {
$bank->delete();

return back()
->withFlash(
message: __("base::message.delete_successfully"),
type: 'success',
);
} catch (Exception $exception) {
Log::error($exception);

return back()
->withFlash(
message: __("base::message.error_occurred"),
type: 'error',
);
}
}

نتیجه‌گیری

تا اینجا یک CRUD کامل برای بانک‌ها ساختید که شامل:

  • تعریف ماژول
  • تنظیم مسیرها و منو
  • ساخت مدل، کنترلر و ویوها
  • پیاده‌سازی لیست، درج، ویرایش، نمایش و حذف