Розробка бекенду сайту на PHP (Laravel)
Laravel — найзрілішим PHP-фреймворком з повним стеком інструментів: Eloquent ORM, Artisan CLI, очереди, події, планувальник, сповіщення, сховище, кешування, трансляція подій у реальному часі. Все це в одному пакеті з узгодженою документацією.
Laravel займає розумну нішу: коли потрібно швидко, надійно та з можливістю зростання. Серед PHP-фреймворків це практично стандарт для нових проектів.
Структура додатку
Laravel слідує MVC, але правильна організація вимагає трохи більше структури — сервісний шар для бізнес-логіки:
app/
Http/
Controllers/
Api/V1/
ProductController.php
UserController.php
OrderController.php
Requests/
CreateProductRequest.php
UpdateOrderRequest.php
Resources/
ProductResource.php
ProductCollection.php
Middleware/
EnsureRole.php
Models/
Product.php
User.php
Order.php
Services/
ProductService.php
OrderService.php
PaymentService.php
Repositories/
ProductRepository.php
Jobs/
SendOrderConfirmation.php
ProcessPayment.php
Events/
OrderPlaced.php
Listeners/
NotifyAdminOnOrder.php
routes/
api.php
web.php
Eloquent моделі
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;
class Product extends Model
{
use SoftDeletes;
protected $fillable = ['name', 'slug', 'price', 'category_id', 'attributes', 'is_active'];
protected $casts = [
'price' => 'decimal:2',
'attributes' => 'array',
'is_active' => 'boolean',
];
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function variants(): HasMany
{
return $this->hasMany(ProductVariant::class);
}
public function scopeActive(Builder $query): Builder
{
return $query->where('is_active', true);
}
public function scopeWithCategory(Builder $query): Builder
{
return $query->with('category:id,name,slug');
}
}
Складні запити через Eloquent або Query Builder:
$products = Product::query()
->active()
->withCategory()
->when($request->category_id, fn($q, $id) => $q->where('category_id', $id))
->when($request->search, fn($q, $s) => $q->where('name', 'like', "%{$s}%"))
->orderByDesc('created_at')
->paginate($request->per_page ?? 20);
API Resources
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'price' => (float) $this->price,
'category' => $this->whenLoaded('category', fn() => [
'id' => $this->category->id,
'name' => $this->category->name,
]),
'attributes' => $this->attributes,
'created_at' => $this->created_at->toISOString(),
];
}
}
Form Requests та валідація
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateProductRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->hasRole('admin');
}
public function rules(): array
{
return [
'name' => ['required', 'string', 'min:2', 'max:255'],
'price' => ['required', 'numeric', 'min:0.01'],
'category_id' => ['nullable', 'exists:categories,id'],
'attributes' => ['nullable', 'array'],
'description' => ['nullable', 'string'],
];
}
public function messages(): array
{
return [
'name.required' => 'Назва обов\'язкова',
'price.min' => 'Ціна повинна бути більша за нуль',
'category_id.exists' => 'Категорія не знайдена',
];
}
}
Аутентифікація через Laravel Sanctum
Sanctum обслуговує SPA (cookie-based) та мобільні додатки (token-based) єдиним механізмом:
// routes/api.php
Route::post('/auth/login', [AuthController::class, 'login']);
Route::post('/auth/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn(Request $req) => $req->user());
Route::apiResource('products', ProductController::class);
});
// AuthController
public function login(Request $request): JsonResponse
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required|string',
]);
if (!Auth::attempt($credentials)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$user = Auth::user();
$token = $user->createToken('api-token', $user->getAbilities())->plainTextToken;
return response()->json([
'token' => $token,
'user' => new UserResource($user),
]);
}
Очереди та eventi
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendOrderConfirmation implements ShouldQueue
{
use Queueable, InteractsWithQueue;
public int $tries = 3;
public int $backoff = 60;
public function __construct(private readonly Order $order) {}
public function handle(MailService $mailService): void
{
$mailService->sendOrderConfirmation($this->order);
}
public function failed(\Throwable $exception): void
{
Notification::route('slack', config('services.slack.webhook'))
->notify(new JobFailed($this->order->id, $exception->getMessage()));
}
}
// Диспетчеризація
SendOrderConfirmation::dispatch($order)->delay(now()->addSeconds(5));
// Подія + листенери
event(new OrderPlaced($order));
Кешування
use Illuminate\Support\Facades\Cache;
// Tagged cache (redis)
$products = Cache::tags(['products', "category:{$categoryId}"])
->remember("products:cat:{$categoryId}:page:{$page}", 300, function () use ($categoryId, $page) {
return Product::active()->where('category_id', $categoryId)->paginate(20, ['*'], 'page', $page);
});
// Інвалідація при зміні
Cache::tags(['products', "category:{$product->category_id}"])->flush();
Планувальник
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
$schedule->job(new SyncInventory)->hourly();
$schedule->command('app:send-reminders')->dailyAt('09:00');
$schedule->call(function () {
DB::table('sessions')->where('last_activity', '<', now()->subDays(7))->delete();
})->weekly();
}
Терміни розробки
- Проект + міграції + моделі — 3–5 днів
- API + Resources + Requests — 1–2 тижні
- Auth + Permissions (Spatie) — 3–5 днів
- Очереди + події — 3–5 днів
- Інтеграції — 1–3 тижні
- Тести (Feature + Unit) — 1 тиждень
Корпоративний сайт або веб-додаток середнього масштабу: 5–10 тижнів. Laravel — зрілий вибір з величезною екосистемою пакетів та передбачуваною поведінкою.







