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 дээр үргэлжлүүл — дараагийн курсүүд хүлээж байна!