ساخت پروژه با Composer
پروژه را با Composer بسازید، وابستگیها را نصب کنید و پیش از انتشار فایلهای محیط را از مخزن خارج نگه دارید.
composer create-project laravel/laravel example-appcd example-appphp artisan serve
راهنمای Laravel برای شروع پروژه، Artisan، routing، Blade، validation، Eloquent، queue و آزمون. PDF مرجع، نصب Laravel 8 را با Composer و اجرا در محیط توسعه نشان میدهد. نسخهٔ PHP و extensionها را با نسخهٔ هدف پروژه هماهنگ کنید؛ برای پروژهٔ تازه، نیازمندیهای نسخهٔ پشتیبانیشدهٔ Laravel را از مستندات جاری بررسی کنید. Route روش HTTP، مسیر و handler را به هم وصل میکند. نامگذاری route، محدودکردن متدها و استفاده از controllerهای کوچک، مسیریابی را برای تست، لینکسازی و نگهداری شفافتر میکند. از این صفحه بهعنوان مرجع سریع استفاده کنید و مثالها و نکتهها را با نسخهٔ ابزار، نیاز و شرایط واقعی پروژهٔ خود تطبیق دهید.
پروژه را با Composer بسازید، وابستگیها را نصب کنید و پیش از انتشار فایلهای محیط را از مخزن خارج نگه دارید.
composer create-project laravel/laravel example-appcd example-appphp artisan serveSail محیط Docker پروژه را اجرا میکند؛ در Windows به Docker Desktop و WSL2 نیاز دارید و وضعیت سرویسها را پیش از استفاده بررسی کنید.
./vendor/bin/sail up -d./vendor/bin/sail artisan route:list./vendor/bin/sail downArtisan فایلهای متداول را با ساختار پروژه تولید میکند؛ خروجی فرمان را بازبینی و از scaffold غیرضروری پرهیز کنید.
php artisan listphp artisan route:listphp artisan make:controller CourseControllerphp artisan make:model Course -mمقدارهای محیطی را در تنظیمات سرور نگه دارید؛ config cache را پس از تغییر تنظیمات و در فرایند deployment مدیریت کنید.
php artisan config:clearphp artisan config:cachephp artisan optimize:clearنام پایدار route را برای تولید URL و redirect استفاده کنید، نه تکرار رشتهٔ مسیر در بخشهای مختلف برنامه.
use Illuminate\Support\Facades\Route;Route::get("/courses", [CourseController::class, "index"]) ->name("courses.index");پارامتر را در handler دریافت و در صورت نیاز با الگوی صریح محدود کنید؛ ورودی مسیر همچنان باید مجوزدهی شود.
Route::get("/courses/{course}", [CourseController::class, "show"]) ->whereNumber("course") ->name("courses.show");برای عملیات استاندارد CRUD از resource route استفاده کنید و endpointهای غیرضروری را محدود کنید.
Route::resource("courses", CourseController::class) ->only(["index", "show", "store"]);قواعد مشترک را در گروه تعریف کنید و middleware احراز هویت را جایگزین کنترل مجوز روی هر منبع ندانید.
Route::middleware(["auth"]) ->prefix("admin") ->name("admin.") ->group(function () { Route::get("/courses", [CourseController::class, "index"]) ->name("courses.index"); });در کنترلر، دادهٔ ورودی را با rules صریح بررسی کنید و فقط مقدارهای تأییدشده را به منطق دامنه بدهید.
public function store(Request $request){ $validated = $request->validate([ "title" => ["required", "string", "max:160"], "published" => ["sometimes", "boolean"], ]); $course = Course::create($validated); return redirect()->route("courses.show", $course);}برای API ساختار پاسخ پایدار و کد HTTP متناسب برگردانید؛ پیام خطا نباید stack trace یا دادهٔ محرمانه افشا کند.
return response()->json([ "data" => $course,], 201);وابستگیهای concrete را از container دریافت کنید تا جایگزینی، تست و مدیریت چرخهٔ عمر سرویسها روشن باشد.
public function __construct( private CourseRepository $courses,) {}public function index(){ return $this->courses->published();}دادهٔ لازم را صریح به view بدهید و محاسبات دامنه را در template انباشته نکنید.
return view("courses.show", [ "course" => $course,]);نمونهٔ زیر خروجی را escape میکند؛ برای مجموعهٔ خالی هم حالت جایگزین قابلفهم نمایش دهید.
@forelse ($courses as $course) <h2>{{ $course->title }}</h2>@empty <p>هنوز دورهای ثبت نشده است.</p>@endforelselayout مشترک را برای ساختار ثابت صفحه استفاده کنید و محتوای هر صفحه را در section مشخص قرار دهید.
{{-- layouts/app.blade.php --}}<main> @yield("content")</main>{{-- courses/index.blade.php --}}@extends("layouts.app")@section("content") <h1>دورهها</h1>@endsectionقواعد و پیامهای validation را از controller جدا کنید و دادهٔ نمونهٔ زیر را به عملیات بعدی بدهید.
public function rules(): array{ return [ "title" => ["required", "string", "max:160"], "email" => ["required", "email"], ];}مجوز را در سمت سرور و بر اساس کاربر و منبع بررسی کنید؛ پنهانکردن دکمه در رابط کاربری کافی نیست.
if ($request->user()->cannot("update", $course)) { abort(403);}$course->update($validated);middleware را برای دغدغهٔ مشترک درخواست بهکار ببرید؛ قواعد دامنهای مختص منبع را در policy یا سرویس مناسب نگه دارید.
Route::middleware(["auth", "verified"]) ->group(function () { Route::get("/account", AccountController::class); });نوع ستون و constraint را متناسب با دامنه طراحی کنید؛ rollback را نیز پیش از اجرای migration بررسی کنید.
Schema::create("courses", function (Blueprint $table) { $table->id(); $table->string("title", 160); $table->foreignId("author_id")->constrained("users"); $table->timestamps();});فیلتر را در query builder اعمال کنید و مقدارهای کاربر را از طریق binding داخلی عبور دهید.
$courses = Course::query() ->where("published", true) ->orderByDesc("created_at") ->paginate(20);رابطهٔ مدل را تعریف کنید و دادهٔ مرتبط را پیش از پیمایش بارگذاری کنید تا query اضافی برای هر ردیف ایجاد نشود.
public function author(): BelongsTo{ return $this->belongsTo(User::class, "author_id");}$courses = Course::with("author")->get();تغییرهای وابسته را در transaction انجام دهید تا در صورت exception همهٔ تغییرها rollback شوند.
DB::transaction(function () use ($order) { $order->update(["status" => "paid"]); $order->items()->update(["fulfilled" => false]);});پارامترهای route را به helper بدهید تا مسیر و query string از نام تعریفشده تولید شود.
$url = route("courses.show", ["course" => $course]);return redirect()->route("courses.index");از helperهای URL برای تشخیص مسیر استفاده کنید و مقدارهای host یا URL ارسالشده از کلاینت را بیاعتماد بدانید.
$currentUrl = url()->current();$previousUrl = url()->previous();$routeName = request()->route()?->getName();برای URLهای مطلق، host مورداعتماد و تنظیمات proxy را درست پیکربندی کنید.
$assetUrl = asset("images/logo.svg");$applicationUrl = url("/courses");Job مستقل و قابلتکرار طراحی کنید؛ برای retry، side effectها را idempotent نگه دارید.
SendCourseNotification::dispatch($course) ->onQueue("notifications");برای cache انقضا قرار دهید و هنگام تغییر داده، invalidation یا بازسازی مقدار را مشخص کنید.
$courses = Cache::remember( "courses:published", now()->addMinutes(10), fn () => Course::where("published", true)->get());مسیر موفق، ورودی نامعتبر و کاربر بدون مجوز را تست کنید؛ نمونهٔ زیر را در suite مناسب بهکار ببرید.
public function test_guest_cannot_create_course(): void{ $response = $this->postJson("/api/courses", [ "title" => "GraphQL Basics", ]); $response->assertUnauthorized();}routeهای وب و API را در فایل متناسب تعریف و نامگذاری کنید.
اعتبارسنجی ورودی را با Form Request از کنترلر جدا کنید.
منطق مجوز را با policy یا gate در سمت سرور اعمال کنید.
از Eloquent relationها استفاده کنید و مشکل N+1 را با eager loading کاهش دهید.
تغییر schema را با migration انجام دهید و دادهٔ اولیه را با seeder بسازید.
برای عملیات چندمرحلهای پایگاه داده از transaction استفاده کنید.
کار طولانی را به queue بسپارید و retry و failed jobs را پایش کنید.
تنظیمات محرمانه را در environment نگه دارید و فایل env را منتشر نکنید.
cache و session driver را با زیرساخت محیط اجرا هماهنگ کنید.
تست feature برای احراز هویت، validation، مجوز و پاسخ endpoint بنویسید.
تغییر schema را با migration نسخهپذیر انجام دهید و پیش از rollback اثر حذف یا تبدیل داده را ارزیابی کنید.
برای دادههای تکراری، eager loading را بسنجید و queryهای N+1 را با ابزارهای توسعه آشکار کنید.