PHP / Эцсийн төсөл

Эцсийн төсөл

Баяр хүргье — PHP курсийн эцсийн хичээлд хүрлээ! Одоо сурсан бүхнээ нэгтгэх цаг боллоо. Энэ төсөлд нэвтрэлттэй блог апп бүтээнэ — хэрэглэгч бүртгүүлж, нэвтэрч, нийтлэл бичиж, засаж, устгах боломжтой. Энэ нэг төсөл дотор PHP-ийн цөмийг бүрэн харах болно.

Төслийн бүтэц ба суулгалт

bash
# Laravel апп үүсгэнэ
composer create-project laravel/laravel php-блог
cd php-блог

# Нэвтрэлтийн scaffold суулгана (login, register бэлэн болно)
composer require laravel/breeze --dev
php artisan breeze:install blade
npm install && npm run dev

# Өгөгдлийн сан тохируулна (.env-д)
# DB_DATABASE=php_блог

# Model, migration, controller үүсгэнэ
php artisan make:model Нийтлэл -mc --resource

# Migration ажиллуулна
php artisan migrate

# Апп ажиллуулна
php artisan serve

Файлын бүтэц:

код
php-блог/
├── app/
│   ├── Http/Controllers/НийтлэлController.php
│   └── Models/Нийтлэл.php
├── database/migrations/
│   └── xxxx_create_нийтлэлүүд_table.php
├── resources/views/
│   ├── layouts/app.blade.php
│   └── нийтлэлүүд/
│       ├── жагсаалт.blade.php
│       ├── харуулах.blade.php
│       └── маягт.blade.php
└── routes/web.php

Өгөгдлийн сан ба Model

php
<?php
// database/migrations/xxxx_create_нийтлэлүүд_table.php
public function up(): void
{
    Schema::create('нийтлэлүүд', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')
              ->constrained()
              ->cascadeOnDelete();
        $table->string('гарчиг', 255);
        $table->text('агуулга');
        $table->string('зураг_зам')->nullable();
        $table->boolean('нийтлэгдсэн')->default(false);
        $table->timestamps();
    });
}
?>
php
<?php
// app/Models/Нийтлэл.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Нийтлэл extends Model
{
    protected $table    = 'нийтлэлүүд';
    protected $fillable = ['гарчиг', 'агуулга', 'зураг_зам', 'нийтлэгдсэн'];

    protected $casts = [
        'нийтлэгдсэн' => 'boolean',
    ];

    // Энэ нийтлэлийн эзэн хэрэглэгч
    public function хэрэглэгч()
    {
        return $this->belongsTo(User::class);
    }

    // Зөвхөн нийтлэгдсэн нийтлэлүүдийг авах scope
    public function scopeНийтлэгдсэн($query)
    {
        return $query->where('нийтлэгдсэн', true);
    }

    // Агуулгын хэсгийг авах
    public function хэсэг(int $урт = 200): string
    {
        return mb_substr(strip_tags($this->агуулга), 0, $урт) . '...';
    }
}
?>

Controller — бүрэн CRUD

php
<?php
// app/Http/Controllers/НийтлэлController.php
namespace App\Http\Controllers;

use App\Models\Нийтлэл;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class НийтлэлController extends Controller
{
    public function __construct()
    {
        // Зочид зөвхөн жагсаалт, харуулах-ыг үзнэ
        $this->middleware('auth')->except(['index', 'show']);
    }

    public function index()
    {
        $нийтлэлүүд = Нийтлэл::нийтлэгдсэн()
            ->with('хэрэглэгч')
            ->latest()
            ->paginate(6);

        return view('нийтлэлүүд.жагсаалт', compact('нийтлэлүүд'));
    }

    public function show(Нийтлэл $нийтлэл)
    {
        abort_unless($нийтлэл->нийтлэгдсэн || Auth::id() === $нийтлэл->user_id, 404);
        return view('нийтлэлүүд.харуулах', compact('нийтлэл'));
    }

    public function create()
    {
        return view('нийтлэлүүд.маягт', ['нийтлэл' => new Нийтлэл()]);
    }

    public function store(Request $хүсэлт)
    {
        $өгөгдөл = $хүсэлт->validate([
            'гарчиг'      => 'required|min:3|max:255',
            'агуулга'     => 'required|min:20',
            'нийтлэгдсэн' => 'boolean',
        ]);

        Auth::user()->нийтлэлүүд()->create($өгөгдөл);

        return redirect()->route('нийтлэлүүд.index')
            ->with('амжилт', 'Нийтлэл амжилттай үүслээ!');
    }

    public function edit(Нийтлэл $нийтлэл)
    {
        abort_unless(Auth::id() === $нийтлэл->user_id, 403);
        return view('нийтлэлүүд.маягт', compact('нийтлэл'));
    }

    public function update(Request $хүсэлт, Нийтлэл $нийтлэл)
    {
        abort_unless(Auth::id() === $нийтлэл->user_id, 403);

        $өгөгдөл = $хүсэлт->validate([
            'гарчиг'      => 'required|min:3|max:255',
            'агуулга'     => 'required|min:20',
            'нийтлэгдсэн' => 'boolean',
        ]);

        $нийтлэл->update($өгөгдөл);

        return redirect()->route('нийтлэлүүд.show', $нийтлэл)
            ->with('амжилт', 'Нийтлэл шинэчлэгдлээ!');
    }

    public function destroy(Нийтлэл $нийтлэл)
    {
        abort_unless(Auth::id() === $нийтлэл->user_id, 403);
        $нийтлэл->delete();

        return redirect()->route('нийтлэлүүд.index')
            ->with('амжилт', 'Нийтлэл устгагдлаа.');
    }
}
?>

Route ба Blade view

php
<?php
// routes/web.php
use App\Http\Controllers\НийтлэлController;

Route::get('/', fn() => redirect()->route('нийтлэлүүд.index'));
Route::resource('нийтлэлүүд', НийтлэлController::class);
?>
blade
{{-- resources/views/нийтлэлүүд/жагсаалт.blade.php --}}
@extends('layouts.app')

@section('content')
<div class="max-w-4xl mx-auto py-8 px-4">
    <div class="flex justify-between items-center mb-6">
        <h1 class="text-2xl font-bold">Нийтлэлүүд</h1>
        @auth
            <a href="{{ route('нийтлэлүүд.create') }}"
               class="bg-blue-600 text-white px-4 py-2 rounded">
                + Шинэ нийтлэл
            </a>
        @endauth
    </div>

    @if (session('амжилт'))
        <div class="bg-green-100 border border-green-400 text-green-700 p-3 rounded mb-4">
            {{ session('амжилт') }}
        </div>
    @endif

    @forelse ($нийтлэлүүд as $нийтлэл)
        <article class="border rounded p-5 mb-4 hover:shadow-md transition">
            <h2 class="text-xl font-semibold mb-2">
                <a href="{{ route('нийтлэлүүд.show', $нийтлэл) }}">
                    {{ $нийтлэл->гарчиг }}
                </a>
            </h2>
            <p class="text-gray-600 mb-3">{{ $нийтлэл->хэсэг() }}</p>
            <div class="text-sm text-gray-400 flex justify-between">
                <span>{{ $нийтлэл->хэрэглэгч->name }}</span>
                <span>{{ $нийтлэл->created_at->diffForHumans() }}</span>
            </div>
        </article>
    @empty
        <p class="text-gray-500">Одоогоор нийтлэл байхгүй байна.</p>
    @endforelse

    <div class="mt-6">{{ $нийтлэлүүд->links() }}</div>
</div>
@endsection

Дараагийн хичээлд:

Чамайг энэ курсийг дуусгасанд чин сэтгэлээсээ баяр хүргэж байна! Та PHP-ийн суурь ойлголт, OOP, өгөгдлийн сан, аюулгүй байдал, Laravel хүртэл бүгдийг сурсан. Дараагийн алхам:

  • Өөрийн төсөл бүтээ — сурсан зүйлсийг бодит асуудал шийдвэрлэхэд хэрэглэ
  • Laravel гүнзгийрүүл — Queue, Events, API Resources, Sanctum судал
  • PostgreSQL курс — өгөгдлийн санг гүнзгийрүүл
  • Ulaanbaatar.app дээр үргэлжлүүл — дараагийн курсүүд хүлээж байна!