20-bo‘lim
Amaliy loyiha
Yigirma bo'lim bilimini bitta ishlaydigan ilovada birlashtirish - vazifalar boshqaruvchisi.
Ushbu bo‘lim mundarijasi
Bu bo'limda haqiqiy ilova quramiz: vazifalar boshqaruvchisi. U ma'lumotlar bazasi bilan ishlaydi, kirishni tekshiradi, xatolarni boshqaradi va JSON API beradi.
Har bir qismda qaysi bo'lim ishlatilganini ko'rsatamiz.
Loyiha xaritasi #
// Har bir misol qat'iy turlar rejimida ishlaydi.
$qismlar = [
["Holat", "enum - vazifa holati", "19 zamonaviy PHP"],
["Vazifa", "readonly qiymat obyekti", "12 OOP asoslari"],
["VazifaXatosi", "ilova istisnolari ierarxiyasi", "15 xatolar"],
["Tekshiruv", "kirish ma'lumotini tekshirish", "9 validatsiya"],
["Ombor", "PDO bilan saqlash", "16 PDO"],
["Xizmat", "biznes mantiq", "13 interfeys"],
["ApiJavob", "bir xil shakldagi javob", "18 JSON va API"],
];
printf("%-14s %-34s %s\n", "QISM", "VAZIFASI", "BO'LIM");
echo str_repeat("-", 72) . "\n";
foreach ($qismlar as [$nom, $vazifa, $bolim]) {
printf("%-14s %-34s %s\n", $nom, $vazifa, $bolim);
}
echo "\n";
echo "Fayl tuzilmasi (14-bo'lim):\n";
$tuzilma = [
"public/index.php" => "yagona kirish nuqtasi",
"src/Holat.php" => "App\\Holat",
"src/Vazifa.php" => "App\\Vazifa",
"src/VazifaXatosi.php" => "App\\VazifaXatosi",
"src/Ombor.php" => "App\\Ombor",
"src/VazifaXizmati.php" => "App\\VazifaXizmati",
"config/sozlama.php" => "sirlar - git ga QO'SHILMAYDI",
"composer.json" => "psr-4: App\\ -> src/",
];
foreach ($tuzilma as $yol => $izoh) {
printf(" %-26s # %s\n", $yol, $izoh);
}
QISM VAZIFASI BO'LIM
------------------------------------------------------------------------
Holat enum - vazifa holati 19 zamonaviy PHP
Vazifa readonly qiymat obyekti 12 OOP asoslari
VazifaXatosi ilova istisnolari ierarxiyasi 15 xatolar
Tekshiruv kirish ma'lumotini tekshirish 9 validatsiya
Ombor PDO bilan saqlash 16 PDO
Xizmat biznes mantiq 13 interfeys
ApiJavob bir xil shakldagi javob 18 JSON va API
Fayl tuzilmasi (14-bo'lim):
public/index.php # yagona kirish nuqtasi
src/Holat.php # App\Holat
src/Vazifa.php # App\Vazifa
src/VazifaXatosi.php # App\VazifaXatosi
src/Ombor.php # App\Ombor
src/VazifaXizmati.php # App\VazifaXizmati
config/sozlama.php # sirlar - git ga QO'SHILMAYDI
composer.json # psr-4: App\ -> src/
1-qadam: holat va qiymat obyekti #
enum Holat: string
{
case Yangi = "yangi";
case Jarayonda = "jarayonda";
case Yakunlangan = "yakunlangan";
case Bekor = "bekor";
public function matni(): string
{
return match ($this) {
Holat::Yangi => "Yangi",
Holat::Jarayonda => "Bajarilmoqda",
Holat::Yakunlangan => "Yakunlangan",
Holat::Bekor => "Bekor qilingan",
};
}
public function yopiqmi(): bool
{
return $this === Holat::Yakunlangan || $this === Holat::Bekor;
}
/** Qaysi holatga o'tish mumkin - biznes qoidasi */
public function otishMumkinmi(Holat $yangi): bool
{
return match ($this) {
Holat::Yangi => $yangi !== Holat::Yangi,
Holat::Jarayonda => in_array($yangi, [Holat::Yakunlangan, Holat::Bekor], true),
default => false, // yopiq holatdan chiqib bo'lmaydi
};
}
}
final readonly class Vazifa
{
public function __construct(
public ?int $id,
public string $sarlavha,
public Holat $holat,
public int $muhimlik,
public ?string $muddat = null,
) {}
public function kechikdimi(string $bugun): bool
{
if ($this->muddat === null || $this->holat->yopiqmi()) {
return false;
}
return $this->muddat < $bugun;
}
public function holatBilan(Holat $yangi): self
{
return new self($this->id, $this->sarlavha, $yangi, $this->muhimlik, $this->muddat);
}
}
echo "Holat o'tishlari jadvali:\n";
$sarlavha = sprintf("%-14s", "DAN \\ GA");
foreach (Holat::cases() as $h) {
$sarlavha .= sprintf(" %-12s", $h->value);
}
echo rtrim($sarlavha) . "\n";
echo str_repeat("-", 66) . "\n";
foreach (Holat::cases() as $dan) {
$qator = sprintf("%-14s", $dan->value);
foreach (Holat::cases() as $ga) {
$qator .= sprintf(" %-12s", $dan->otishMumkinmi($ga) ? "mumkin" : "-");
}
echo rtrim($qator) . "\n";
}
echo "\n";
$v = new Vazifa(1, "Hisobotni yozish", Holat::Jarayonda, 2, "2024-08-30");
printf("Vazifa: %s\n", $v->sarlavha);
printf("Holat: %s\n", $v->holat->matni());
printf("Kechikdimi: %s\n", $v->kechikdimi("2024-09-01") ? "ha" : "yo'q");
$yopiq = $v->holatBilan(Holat::Yakunlangan);
printf("Yakunlangandan keyin kechikdimi: %s\n", $yopiq->kechikdimi("2024-09-01") ? "ha" : "yo'q");
Holat o'tishlari jadvali:
DAN \ GA yangi jarayonda yakunlangan bekor
------------------------------------------------------------------
yangi - mumkin mumkin mumkin
jarayonda - - mumkin mumkin
yakunlangan - - - -
bekor - - - -
Vazifa: Hisobotni yozish
Holat: Bajarilmoqda
Kechikdimi: ha
Yakunlangandan keyin kechikdimi: yo'q
enum ichida yashaydiotishMumkinmi() usuli - bu biznes qoidasi, va u
enum ichida turibdi.
Muqobil yechim - bu tekshiruvni kontrollerda yozish bo'lardi. Lekin unda qoida tarqalib ketadi: veb kontroller, API kontroller va konsol buyrug'i - har biri o'z nusxasini yozadi va ular vaqt o'tishi bilan bir-biridan farq qila boshlaydi.
Qoida turning o'zida bo'lsa, uni chetlab o'tish mumkin emas.
Jadvalga e'tibor bering: yopiq holatlardan (yakunlangan,
bekor) hech qayerga o'tib bo'lmaydi. Bu ataylab -
yakunlangan vazifani qayta ochish uchun alohida amal
kerak.
2-qadam: istisnolar va tekshiruv #
enum Holat: string
{
case Yangi = "yangi";
case Jarayonda = "jarayonda";
case Yakunlangan = "yakunlangan";
case Bekor = "bekor";
}
abstract class VazifaXatosi extends RuntimeException
{
abstract public function kod(): int;
abstract public function foydalanuvchiXabari(): string;
}
final class VazifaTopilmadi extends VazifaXatosi
{
public function __construct(public readonly int $id)
{
parent::__construct("Vazifa topilmadi: id={$id}");
}
public function kod(): int { return 404; }
public function foydalanuvchiXabari(): string { return "Bunday vazifa mavjud emas."; }
}
final class MalumotNotogri extends VazifaXatosi
{
public function __construct(public readonly array $maydonlar)
{
parent::__construct("Validatsiya xatosi: " . implode(", ", array_keys($maydonlar)));
}
public function kod(): int { return 422; }
public function foydalanuvchiXabari(): string { return "Kiritilgan ma'lumot noto'g'ri."; }
}
final class OtishTaqiqlangan extends VazifaXatosi
{
public function __construct(public readonly string $dan, public readonly string $ga)
{
parent::__construct("Holat o'tishi taqiqlangan: {$dan} -> {$ga}");
}
public function kod(): int { return 409; }
public function foydalanuvchiXabari(): string
{
return "Bu holatga o'tish mumkin emas.";
}
}
/** Kiruvchi ma'lumotni tekshiradi va TOZA massiv qaytaradi */
function vazifaniTekshir(array $kirish): array
{
$xatolar = [];
$sarlavha = trim((string) ($kirish["sarlavha"] ?? ""));
if (mb_strlen($sarlavha) < 3 || mb_strlen($sarlavha) > 200) {
$xatolar["sarlavha"] = "Sarlavha 3-200 belgi bo'lsin.";
}
$muhimlik = filter_var($kirish["muhimlik"] ?? null, FILTER_VALIDATE_INT, [
"options" => ["min_range" => 1, "max_range" => 5],
]);
if ($muhimlik === false) {
$xatolar["muhimlik"] = "Muhimlik 1 dan 5 gacha bo'lsin.";
}
$holat = Holat::tryFrom((string) ($kirish["holat"] ?? "yangi"));
if ($holat === null) {
$xatolar["holat"] = "Noma'lum holat.";
}
$muddat = $kirish["muddat"] ?? null;
if ($muddat !== null && $muddat !== "") {
$d = DateTimeImmutable::createFromFormat("Y-m-d", (string) $muddat);
$x = DateTimeImmutable::getLastErrors();
if ($d === false || ($x !== false && ($x["warning_count"] + $x["error_count"]) > 0)) {
$xatolar["muddat"] = "Sana YYYY-MM-DD shaklida bo'lsin.";
}
} else {
$muddat = null;
}
if ($xatolar !== []) {
throw new MalumotNotogri($xatolar);
}
return ["sarlavha" => $sarlavha, "muhimlik" => (int) $muhimlik,
"holat" => $holat, "muddat" => $muddat];
}
$sinovlar = [
"to'g'ri" => ["sarlavha" => "Hisobotni yozish", "muhimlik" => "3", "muddat" => "2024-09-30"],
"qisqa sarlavha" => ["sarlavha" => "ab", "muhimlik" => "3"],
"yomon muhimlik" => ["sarlavha" => "Normal sarlavha", "muhimlik" => "99"],
"yomon sana" => ["sarlavha" => "Normal sarlavha", "muhimlik" => "2", "muddat" => "2024-02-31"],
"hammasi yomon" => ["sarlavha" => "", "muhimlik" => "abc", "holat" => "yoq"],
];
foreach ($sinovlar as $nom => $kirish) {
try {
$toza = vazifaniTekshir($kirish);
printf("%-16s OK -> %s (muhimlik %d)\n", $nom, $toza["sarlavha"], $toza["muhimlik"]);
} catch (MalumotNotogri $x) {
printf("%-16s HTTP %d\n", $nom, $x->kod());
foreach ($x->maydonlar as $maydon => $xabar) {
printf("%-16s %-10s %s\n", "", $maydon, $xabar);
}
}
}
to'g'ri OK -> Hisobotni yozish (muhimlik 3)
qisqa sarlavha HTTP 422
sarlavha Sarlavha 3-200 belgi bo'lsin.
yomon muhimlik HTTP 422
muhimlik Muhimlik 1 dan 5 gacha bo'lsin.
yomon sana HTTP 422
muddat Sana YYYY-MM-DD shaklida bo'lsin.
hammasi yomon HTTP 422
sarlavha Sarlavha 3-200 belgi bo'lsin.
muhimlik Muhimlik 1 dan 5 gacha bo'lsin.
holat Noma'lum holat.
3-qadam: ombor va xizmat #
enum Holat: string
{
case Yangi = "yangi";
case Jarayonda = "jarayonda";
case Yakunlangan = "yakunlangan";
case Bekor = "bekor";
public function otishMumkinmi(Holat $yangi): bool
{
return match ($this) {
Holat::Yangi => $yangi !== Holat::Yangi,
Holat::Jarayonda => in_array($yangi, [Holat::Yakunlangan, Holat::Bekor], true),
default => false,
};
}
}
final readonly class Vazifa
{
public function __construct(
public ?int $id,
public string $sarlavha,
public Holat $holat,
public int $muhimlik,
) {}
}
final class VazifaTopilmadi extends RuntimeException {}
final class OtishTaqiqlangan extends RuntimeException {}
/** Saqlash qatlami - FAQAT ma'lumot bilan ishlaydi */
final class Ombor
{
public function __construct(private readonly PDO $pdo)
{
$this->pdo->exec("
CREATE TABLE vazifalar (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sarlavha TEXT NOT NULL,
holat TEXT NOT NULL,
muhimlik INTEGER NOT NULL
)
");
}
public function qosh(Vazifa $v): Vazifa
{
$stmt = $this->pdo->prepare(
"INSERT INTO vazifalar (sarlavha, holat, muhimlik) VALUES (?, ?, ?)"
);
$stmt->execute([$v->sarlavha, $v->holat->value, $v->muhimlik]);
return new Vazifa((int) $this->pdo->lastInsertId(), $v->sarlavha, $v->holat, $v->muhimlik);
}
public function top(int $id): Vazifa
{
$stmt = $this->pdo->prepare("SELECT * FROM vazifalar WHERE id = ?");
$stmt->execute([$id]);
$q = $stmt->fetch();
if ($q === false) {
throw new VazifaTopilmadi("Vazifa topilmadi: {$id}");
}
return $this->qatordan($q);
}
/** @return list<Vazifa> */
public function royxat(?Holat $holat = null): array
{
if ($holat === null) {
$stmt = $this->pdo->query("SELECT * FROM vazifalar ORDER BY muhimlik DESC, id");
} else {
$stmt = $this->pdo->prepare(
"SELECT * FROM vazifalar WHERE holat = ? ORDER BY muhimlik DESC, id"
);
$stmt->execute([$holat->value]);
}
// array_values - 5 va 18-bo'limlardagi qoida
return array_values(array_map($this->qatordan(...), $stmt->fetchAll()));
}
public function holatniYangila(int $id, Holat $yangi): Vazifa
{
$stmt = $this->pdo->prepare("UPDATE vazifalar SET holat = ? WHERE id = ?");
$stmt->execute([$yangi->value, $id]);
return $this->top($id);
}
private function qatordan(array $q): Vazifa
{
return new Vazifa(
(int) $q["id"],
(string) $q["sarlavha"],
Holat::from((string) $q["holat"]),
(int) $q["muhimlik"],
);
}
}
/** Biznes qatlam - qoidalarni biladi, HTTP ni bilmaydi */
final class VazifaXizmati
{
public function __construct(private readonly Ombor $ombor) {}
public function yarat(string $sarlavha, int $muhimlik): Vazifa
{
return $this->ombor->qosh(new Vazifa(null, $sarlavha, Holat::Yangi, $muhimlik));
}
public function holatniOzgartir(int $id, Holat $yangi): Vazifa
{
$v = $this->ombor->top($id);
if (!$v->holat->otishMumkinmi($yangi)) {
throw new OtishTaqiqlangan(
"Holat o'tishi taqiqlangan: {$v->holat->value} -> {$yangi->value}"
);
}
return $this->ombor->holatniYangila($id, $yangi);
}
}
// --- Ishlatish ---
$pdo = new PDO("sqlite::memory:", null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$xizmat = new VazifaXizmati($ombor = new Ombor($pdo));
$xizmat->yarat("Hisobotni yozish", 3);
$xizmat->yarat("Serverni yangilash", 5);
$xizmat->yarat("Xatga javob berish", 1);
echo "Barcha vazifalar:\n";
foreach ($ombor->royxat() as $v) {
printf(" #%d [%d] %-22s %s\n", $v->id, $v->muhimlik, $v->sarlavha, $v->holat->value);
}
echo "\nHolatni o'zgartiramiz:\n";
$xizmat->holatniOzgartir(1, Holat::Jarayonda);
$xizmat->holatniOzgartir(1, Holat::Yakunlangan);
echo " #1 yakunlandi\n";
echo "\nTaqiqlangan o'tishga urinish:\n";
try {
$xizmat->holatniOzgartir(1, Holat::Jarayonda);
} catch (OtishTaqiqlangan $x) {
echo " " . $x->getMessage() . "\n";
}
echo "\nMavjud bo'lmagan vazifa:\n";
try {
$xizmat->holatniOzgartir(999, Holat::Bekor);
} catch (VazifaTopilmadi $x) {
echo " " . $x->getMessage() . "\n";
}
echo "\nFaqat yangi vazifalar:\n";
foreach ($ombor->royxat(Holat::Yangi) as $v) {
printf(" #%d %s\n", $v->id, $v->sarlavha);
}
Barcha vazifalar:
#2 [5] Serverni yangilash yangi
#1 [3] Hisobotni yozish yangi
#3 [1] Xatga javob berish yangi
Holatni o'zgartiramiz:
#1 yakunlandi
Taqiqlangan o'tishga urinish:
Holat o'tishi taqiqlangan: yakunlangan -> jarayonda
Mavjud bo'lmagan vazifa:
Vazifa topilmadi: 999
Faqat yangi vazifalar:
#2 Serverni yangilash
#3 Xatga javob berish
Ombor faqat saqlash bilan shug'ullanadi: SQL,
qatorlarni obyektga aylantirish. U biznes qoidalarini
bilmaydi.
VazifaXizmati esa qoidalarni biladi, lekin SQL
yozmaydi.
Bu ajratish uch foyda beradi:
| Foyda | Izoh |
|---|---|
| Sinash oson | Ombor o'rniga soxta obyekt qo'yish mumkin |
| Baza almashtiriladi | SQLite, MySQL, fayl - Xizmat o'zgarmaydi |
| Qoida bir joyda | Uch kontroller bir xil Xizmat ni chaqiradi |
Ombor ni interfeys qilib, VazifaXizmati ni unga
bog'lash yanada yaxshiroq bo'lardi (13-bo'lim):
interface OmborInterfeysi {
public function top(int $id): Vazifa;
public function qosh(Vazifa $v): Vazifa;
}
Shunda sinovlarda haqiqiy bazasiz ishlash mumkin.
E'tibor bering, royxat() da array_values ishlatilgan -
5 va 18-bo'limlardagi qoida. Usiz JSON javobi massiv
emas, obyekt bo'lib chiqardi.
4-qadam: HTTP qatlami #
<?php
// public/index.php - yagona kirish nuqtasi
declare(strict_types=1);
require __DIR__ . "/../vendor/autoload.php";
use App\{Holat, VazifaXizmati, VazifaXatosi};
// 15-bo'lim: global ishlovchi
set_exception_handler(function (Throwable $x): void {
error_log(sprintf("[%s] %s: %s in %s:%d",
date("c"), $x::class, $x->getMessage(), $x->getFile(), $x->getLine()));
$kod = $x instanceof VazifaXatosi ? $x->kod() : 500;
$xabar = $x instanceof VazifaXatosi
? $x->foydalanuvchiXabari()
: "Ichki xatolik yuz berdi.";
http_response_code($kod);
header("Content-Type: application/json; charset=utf-8");
echo json_encode(["ok" => false, "error" => ["xabar" => $xabar]],
JSON_UNESCAPED_UNICODE);
exit(1);
});
$sozlama = require __DIR__ . "/../config/sozlama.php";
$pdo = new PDO($sozlama["dsn"], $sozlama["user"], $sozlama["parol"], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$xizmat = new VazifaXizmati(new Ombor($pdo));
// 8 va 18-bo'limlar: so'rovni o'qish
$usul = $_SERVER["REQUEST_METHOD"];
$yol = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) ?? "/", "/");
$qism = $yol === "" ? [] : explode("/", $yol);
$tana = [];
if (in_array($usul, ["POST", "PATCH"], true)) {
$xom = file_get_contents("php://input"); // $_POST JSON ni O'QIMAYDI
$tana = $xom === "" ? [] : json_decode($xom, true, 512, JSON_THROW_ON_ERROR);
}
// 6 va 19-bo'limlar: match bilan marshrutlash
[$kod, $javob] = match (true) {
$usul === "GET" && $qism === ["api", "vazifalar"]
=> [200, $xizmat->royxat(Holat::tryFrom($_GET["holat"] ?? ""))],
$usul === "POST" && $qism === ["api", "vazifalar"]
=> [201, $xizmat->yarat(
(string) ($tana["sarlavha"] ?? ""),
(int) ($tana["muhimlik"] ?? 3)
)],
$usul === "PATCH" && count($qism) === 3 && $qism[1] === "vazifalar"
=> [200, $xizmat->holatniOzgartir(
(int) $qism[2],
Holat::from((string) ($tana["holat"] ?? ""))
)],
default => throw new RuntimeException("Bunday manzil yo'q"),
};
http_response_code($kod);
header("Content-Type: application/json; charset=utf-8");
header("X-Content-Type-Options: nosniff"); // 17-bo'lim
echo json_encode(["ok" => true, "data" => $javob],
JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
Yuqoridagi faylda birorta biznes qoidasi yo'q. U faqat uch ish qiladi:
- So'rovni o'qib, kerakli turlarga aylantiradi.
- Tegishli xizmat usulini chaqiradi.
- Natijani JSON ga o'rab qaytaradi.
Barcha qoidalar - holat o'tishlari, validatsiya, saqlash - pastdagi qatlamlarda.
Bu nima beradi: ertaga konsol buyrug'i yoki Telegram
bot qo'shsangiz, ular xuddi shu VazifaXizmati ni
chaqiradi. Qoidalar takrorlanmaydi.
Va e'tibor bering, try/catch ham yo'q - global
ishlovchi barcha VazifaXatosi larni tutib, to'g'ri
HTTP kodini o'zi qo'yadi. Har bir istisno o'z kodini
biladi (kod() usuli).
Bundan keyin nima #
$yol = [
["Sinovlar", "PHPUnit yoki Pest", "Har bir qatlamni alohida sinash"],
["Statik tahlil", "PHPStan yoki Psalm", "Xatolarni ishga tushirmasdan topish"],
["Kod uslubi", "PHP-CS-Fixer", "Jamoada bir xil formatlash"],
["Freymvork", "Laravel yoki Symfony", "Marshrutlash, ORM, DI konteyner"],
["Shablon", "Twig", "HTML ni PHP dan ajratish"],
["Jurnal", "Monolog", "Tuzilgan jurnal yozish"],
["Kesh", "Redis yoki APCu", "Tez-tez so'raladigan ma'lumot"],
["Navbat", "RabbitMQ yoki baza", "Uzoq amallarni fon rejimiga o'tkazish"],
];
printf("%-16s %-24s %s\n", "MAVZU", "VOSITA", "NIMA BERADI");
echo str_repeat("-", 76) . "\n";
foreach ($yol as [$mavzu, $vosita, $nima]) {
printf("%-16s %-24s %s\n", $mavzu, $vosita, $nima);
}
echo "\n";
echo "Loyihani kengaytirish g'oyalari:\n";
$goyalar = [
"Foydalanuvchi va kirish tizimi (10, 17-bo'limlar)",
"Vazifalarni foydalanuvchiga biriktirish",
"Izohlar va fayl biriktirish (11-bo'lim)",
"Muddat yaqinlashganda elektron xat yuborish",
"CSV eksport (11-bo'lim)",
"Har bir qatlam uchun birlik sinovlari",
];
foreach ($goyalar as $i => $g) {
printf(" %d. %s\n", $i + 1, $g);
}
echo "\n";
echo "Eng muhim maslahat:\n";
echo " Kichik loyiha yozing va uni OXIRIGACHA yetkazing.\n";
echo " Tugatilgan kichik loyiha - tugatilmagan kattadan qimmatliroq.\n";
MAVZU VOSITA NIMA BERADI
----------------------------------------------------------------------------
Sinovlar PHPUnit yoki Pest Har bir qatlamni alohida sinash
Statik tahlil PHPStan yoki Psalm Xatolarni ishga tushirmasdan topish
Kod uslubi PHP-CS-Fixer Jamoada bir xil formatlash
Freymvork Laravel yoki Symfony Marshrutlash, ORM, DI konteyner
Shablon Twig HTML ni PHP dan ajratish
Jurnal Monolog Tuzilgan jurnal yozish
Kesh Redis yoki APCu Tez-tez so'raladigan ma'lumot
Navbat RabbitMQ yoki baza Uzoq amallarni fon rejimiga o'tkazish
Loyihani kengaytirish g'oyalari:
1. Foydalanuvchi va kirish tizimi (10, 17-bo'limlar)
2. Vazifalarni foydalanuvchiga biriktirish
3. Izohlar va fayl biriktirish (11-bo'lim)
4. Muddat yaqinlashganda elektron xat yuborish
5. CSV eksport (11-bo'lim)
6. Har bir qatlam uchun birlik sinovlari
Eng muhim maslahat:
Kichik loyiha yozing va uni OXIRIGACHA yetkazing.
Tugatilgan kichik loyiha - tugatilmagan kattadan qimmatliroq.
Holatenum ini yozing va o'tish qoidasini qo'shing.Vazifanireadonlyqiymat obyekti sifatida yozing.- Istisno ierarxiyasini quring - har biri o'z HTTP kodini bilsin.
- Validatsiya funksiyasini yozing va besh xil kirish bilan sinang.
Omborsinfini PDO bilan yozing.VazifaXizmatini yozing - u SQL bilmasin.- Taqiqlangan holat o'tishini sinab ko'ring.
Omboruchun interfeys ajrating va soxta amalga oshirish yozing.public/index.phpkirish nuqtasini yozing.- Loyihani
-.envvaconfig/veb ildizidan tashqarida ekanini tekshiring.
Xulosa #
- Haqiqiy ilova - alohida imkoniyatlar emas, ularning birgalikda ishlashi.
- Biznes qoidasini turning o'zida saqlang - shunda uni chetlab o'tib bo'lmaydi.
- Qiymat obyektlarini
readonlyqiling; "o'zgartirish" yangi obyekt qaytarsin. - Istisno o'z HTTP kodini bilsin - kontrollerda
ifzanjiri kerak bo'lmaydi. - Validatsiya toza va turlangan ma'lumot qaytarsin, xom massiv emas.
Omborfaqat saqlaydi,Xizmatfaqat qoidalarni biladi.- Biznes qatlam HTTP haqida hech narsa bilmasin - shunda uni sinash oson.
- Kontroller yupqa bo'lsin: o'qish, chaqirish, javob berish.
- Ro'yxat qaytarayotganda
array_values()ni unutmang. - Global istisno ishlovchisi barcha
try/catchni kontrollerdan olib tashlaydi. - Keyingi qadam - sinovlar va statik tahlil: ular kodni ishonchli qiladi.
Darslik shu bilan yakunlandi. Endi eng muhim qadam sizniki: o'z loyihangizni yozing va uni oxirigacha yetkazing.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.