taha20 / laravel-installer
A web-based step-by-step installer wizard for Laravel applications
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
یک نصبکننده وببیس (Web-based Installer) به شکل ویزارد چند مرحلهای برای پروژههای لاراول.
مراحل ویزارد
- خوشآمدگویی
- بررسی پیشنیازهای سرور (نسخه PHP و اکستنشنها)
- بررسی پرمیشن پوشهها و فایلها
- دریافت و تست اتصال اطلاعات دیتابیس
- نوشتن
.env، ساختAPP_KEY(در صورت نیاز) و اجرای مایگریشن - ساخت حساب مدیر سیستم
- قفل شدن نصبکننده و اتمام نصب
دستور نصب پکیج
composer require taha20/laravel-installer
انتشار فایلهای قابل سفارشیسازی
php artisan vendor:publish --tag=installer-config php artisan vendor:publish --tag=installer-views php artisan vendor:publish --tag=installer-lang
فعالسازی اجبار نصب در کل سایت (اختیاری)
اگر میخواهید تا وقتی نصب کامل نشده، هیچ صفحهای از سایت باز نشود، میدلور را در
bootstrap/app.php (لاراول ۱۱ به بعد) یا app/Http/Kernel.php (لاراول ۱۰) به گروه web اضافه کنید:
// bootstrap/app.php ->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [ \Taha20\LaravelInstaller\Http\Middleware\RedirectIfNotInstalled::class, ]); })
استفاده
بعد از نصب پکیج، آدرس زیر را در مرورگر باز کنید:
https://your-domain.com/installer
اجرای Seeder در پروژههای ماژولار
برخلاف مایگریشنها که فایلمحورند و مسیرشان با الگوهای glob قابل کشف خودکار است،
Seeder ها کلاس PHP هستند و namespace هر ماژول از روی مسیر فایلش قابلاعتماد قابل حدس زدن نیست.
به همین دلیل باید هر Seeder را صریحاً در config/installer.php معرفی کنی:
'seeder_classes' => [ \Database\Seeders\DatabaseSeeder::class, \Modules\Blog\Database\Seeders\BlogDatabaseSeeder::class, \Modules\Shop\Database\Seeders\ShopDatabaseSeeder::class, ],
هر کلاس در این لیست، دقیقاً مثل مایگریشنها، در یک درخواست AJAX جداگانه اجرا میشود
(/installer/migrate/seed/prepare یکبار برای ساخت لیست، و /installer/migrate/seed/step
بهصورت حلقهای برای اجرای هر کلاس) تا اگر Seeder یک ماژول دادهی زیادی وارد کند، تایماوت رخ ندهد.
اگر کلاسی در لیست وجود نداشته باشد (class_exists() false برگرداند — مثلاً ماژول حذف شده)،
آن کلاس نادیده گرفته میشود و نصب متوقف نمیشود.
برای غیرفعالکردن کامل اجرای seeder ها (مثلاً برای محیطهایی که فقط ساختار جدول لازم است،
نه دادهی نمونه)، کافیست 'run_seeders' => false را در کانفیگ تنظیم کنی.
اجرای تکهتکه (Chunked) مایگریشنها برای جلوگیری از Timeout
روی سرورهای اشتراکی معمولاً max_execution_time بین ۳۰ تا ۶۰ ثانیه است. اگه پروژه مایگریشنهای
زیادی داشته باشه، اجرای یکجای php artisan migrate ممکنه باعث خطای ۵۰۴ یا سفید شدن صفحه بشه.
برای حل این مشکل، مرحله مایگریشن به ۳ درخواست AJAX جدا تقسیم شده:
/installer/migrate/prepare— یک بار صدا زده میشود: فایل.envرا مینویسد،config:clearرا اجرا میکند و لیست مایگریشنهای اجرانشده را در Session ذخیره کرده و تعداد کل را برمیگرداند./installer/migrate/step— به صورت حلقهای (یکی پس از دیگری) توسط جاوااسکریپت صدا زده میشود. هر بار فقط یک فایل مایگریشن را اجرا و از صف Session حذف میکند. چون هر درخواست HTTP فقط یک فایل را اجرا میکند، مدت زمان هر درخواست کوتاه میماند و ریسک تایماوت عملاً از بین میرود./installer/migrate/seed— بعد از پایان همه مایگریشنها یک بار صدا زده میشود و seeder ها را اجرا میکند.
نکته فنی: تابع Migrator::run($paths) در لاراول اگر مسیر دادهشده به .php ختم شود (بهجای مسیر یک پوشه)،
فقط همان فایل را (در صورتی که قبلاً اجرا نشده باشد) اجرا میکند. از همین ویژگی برای اجرای تکفایلی استفاده شده.
محدودیت این روش
اگر داخل یک فایل مایگریشن عملیات بسیار سنگینی انجام شود (مثلاً میلیونها رکورد insert در یک حلقه)، همان یک درخواست AJAX مربوط به آن فایل هنوز میتواند تایماوت بدهد، چون تقسیمبندی در سطح «فایل» است نه داخل خود فایل. راهحلهای تکمیلی برای این حالت:
- مایگریشن سنگین را به چند فایل مایگریشن کوچکتر بشکنید.
- منطق پرکردن دادههای حجیم را به یک Seeder جدا منتقل کنید و آن Seeder را هم به همین روش (چند درخواست AJAX، هر بار یک بخش از داده) تقسیم کنید.
- برای پروژههایی که واقعاً حجم داده بالا دارند، بهجای اجرای همزمان در درخواست HTTP، از یک صف (queue) با
درایور
databaseیاredisاستفاده کنید و مرحله مایگریشن را به یک Job پسزمینه بسپارید؛ صفحه نصبکننده فقط وضعیت Job را هر چند ثانیه یکبار poll میکند. این حالت پیچیدهتر است و در این نسخه پیادهسازی نشده. - در
php.iniیا تنظیمات Nginx/PHP-FPM مقدارmax_execution_timeوfastcgi_read_timeoutرا برای مسیر/installer/*کمی بالاتر ببرید (مثلاً ۱۲۰ ثانیه) تا حاشیه امنیت بیشتری برای فایلهای نسبتاً سنگین وجود داشته باشد. در کد کنترلر هم@set_time_limit(120)قبل از اجرای هر مایگریشن صدا زده میشود (در صورتی که هاست اجازه دهد).
پشتیبانی از ستونهای سفارشی جدول users (پروژههای مختلف)
جدول users در پروژههای مختلف ممکن است ستونهای سفارشی و NOT NULL داشته باشد که در فرم
ساخت ادمین نصبکننده وجود ندارند (مثلاً role_id, company_id, status). اگر این ستونها
مقداردهی نشوند، insert با خطای دیتابیس (Integrity constraint violation) مواجه میشود.
راهحل: قبل از تحویل/نصب پروژه، مقدار پیشفرض این ستونها را در config/installer.php
داخل کلید admin_extra_fields مشخص کنید:
// config/installer.php 'admin_extra_fields' => [ 'role_id' => 1, 'is_active' => true, ],
مقدار میتواند یک closure هم باشد، برای زمانی که مقدار باید در لحظه محاسبه شود
(مثلاً بعد از اجرای seeder ها، از جدولی که تازه پر شده):
'admin_extra_fields' => fn () => [ 'company_id' => \App\Models\Company::first()?->id, ],
⚠️ این مرحله را برای هر پروژهی جدید جداگانه چک کنید — چون ساختار جدول users معمولاً
بین پروژهها فرق میکند، فراموشکردن این تنظیم شایعترین علت شکست مرحلهی ساخت ادمین است.
شبکهی ایمنی: حتی اگر admin_extra_fields را تنظیم نکنید یا ستونی را جا بیندازید،
نصبکننده بهجای نمایش خطای خام SQL، پیام قابلفهمی نشان میدهد که دقیقاً نام ستون مشکلدار
و راه رفعش را مشخص میکند (برای MySQL و PostgreSQL). با این حال بهتر است این خطا اصلاً رخ ندهد
و از قبل کانفیگ را تنظیم کرده باشید.
نکات امنیتی مهم
- بعد از اتمام نصب، حتماً دسترسی به مسیر
/installerاز طریق وبسرور (Nginx/Apache) هم مسدود شود؛ میدلورRedirectIfInstalledاین کار را در سطح لاراول انجام میدهد اما یک لایه امنیتی اضافه در وبسرور توصیه میشود.
ساختار پوشهها
laravel-installer/
├── composer.json
├── config/installer.php
├── routes/web.php
├── src/
│ ├── InstallerServiceProvider.php
│ ├── Console/InstallerResetCommand.php
│ └── Http/
│ ├── Controllers/InstallerController.php
│ └── Middleware/
│ ├── RedirectIfInstalled.php
│ └── RedirectIfNotInstalled.php
└── resources/
├── views/installer/*.blade.php
└── lang/{fa,en}/installer.php