16 β To'liq CRUD ilova¶
β¬ οΈ Oldingi: 15 β Computed properties Β· π Kitob boshi Β· Keyingi: 17 β Nested komponentlar β‘οΈ
Bu bobda: 9β15-boblardagi barcha bilimni bitta real ilovaga birlashtiramiz β "Postlar boshqaruvchisi". Postlarni ro'yxat qilish (Read), modal oyna orqali qo'shish (Create), bir xil formada tahrirlash (Update) va tasdiq so'rab o'chirish (Delete) β hammasi bitta sahifada, qayta yuklanmasdan. Form Object, computed property,
wire:key,wire:confirmva flash xabarni jonli ishlatamiz.
CRUD nima va nega muhim?¶
Hayotiy o'xshatish. Telefon kontaktlaringizni tasavvur qiling. Yangi tanishni qo'shasiz, ro'yxatdan kontaktlarni ko'rasiz, raqami o'zgarsa tahrirlaysiz, kerak bo'lmaganini o'chirasiz. Mana shu to'rt ish β qo'shish, ko'rish, tahrirlash, o'chirish β deyarli har bir dasturning yuragida turadi.
Texnik tilda bu to'rt amal CRUD deb ataladi:
- C β Create (yaratish): yangi yozuv qo'shish.
- R β Read (o'qish): yozuvlarni ko'rish/ro'yxat qilish.
- U β Update (yangilash): mavjud yozuvni tahrirlash.
- D β Delete (o'chirish): yozuvni o'chirish.
Blog postlari, mahsulotlar, foydalanuvchilar, vazifalar, izohlar β bularning hammasi orqasida CRUD turadi. Agar siz CRUD ilovani Livewire'da mukammal qura olsangiz, demak Livewire'ning eng muhim 80% qismini egallagansiz. Shuning uchun bu bob β kitobning markaziy amaliy darsi.
An'anaviy (klassik) Laravel'da CRUD odatda bir necha sahifaga bo'linadi: ro'yxat sahifasi, "qo'shish" sahifasi, "tahrirlash" sahifasi. Har biriga alohida route, controller metodi va Blade fayl kerak bo'ladi. Har bir o'tishda sahifa to'liq qayta yuklanadi.
Livewire'da esa hammasini bitta sahifada, bitta komponentda qilamiz. Foydalanuvchi tugma bosadi β modal oynacha ochiladi, to'ldiradi, saqlaydi β sahifa joyidan jilmaydi, ro'yxat o'sha onda yangilanadi. Bu β zamonaviy, silliq tajriba.
Bu bob β birlashtiruvchi bob
Bu yerda yangi atama deyarli yo'q. Aksincha, siz allaqachon o'rgangan qismlarni β formalar (09), validatsiya (10), Form Object (11), ro'yxatlar va wire:key (13), qidiruv (14), computed property (15) β bir butun ilovaga ulaymiz. Agar biror qism notanish tuyulsa, o'sha bobga qaytib o'qing.
Maqsad: nima quramiz?¶
Bitta PostManager komponenti quramiz. U:
- Barcha postlarni ro'yxat qilib ko'rsatadi (yangidan eskiga).
- Yuqorida qidiruv maydoni bo'ladi β sarlavha bo'yicha filtrlash.
- "+ Yangi post" tugmasi modal oynani ochadi, undan post qo'shiladi.
- Har qatorda "Tahrirlash" tugmasi β o'sha postni bir xil modalda ochadi.
- Har qatorda "O'chirish" tugmasi β tasdiq so'raydi, so'ng o'chiradi.
- Har amaldan keyin muvaffaqiyat xabari chiqadi.
Va bularning hammasi sahifa qayta yuklanmasdan ishlaydi. Mana umumiy arxitektura:
Bu bobdagi barcha kod jonli Laravel 12 + Livewire v4.3.1 loyihada yozilib tekshirildi: sahifa brauzerda HTTP 200 bilan render qilindi, Create / Read / Update / Delete amallari va validatsiya testlari ma'lumotlar bazasiga qarshi muvaffaqiyatli o'tdi.
Tayyorgarlik: Post model va migration¶
CRUD ma'lumotlar bazasi bilan ishlaydi, shuning uchun avval jadval kerak. Bu β Laravel'ning vazifasi, Livewire'niki emas, shuning uchun qisqacha ko'rsatamiz (batafsil β Laravel kitobida).
Model va migration'ni bitta buyruq bilan yaratamiz:
-m flagi migration'ni ham yaratadi. Migration'ga ikki ustun qo'shamiz: sarlavha va matn.
// database/migrations/xxxx_create_posts_table.php
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title'); // sarlavha
$table->text('body'); // matn
$table->timestamps();
});
}
So'ng jadvalni yaratamiz:
Model'da $fillable ni belgilab qo'yamiz β bu mass-assignment uchun qaysi ustunlarni to'ldirish mumkinligini aytadi (ya'ni Post::create([...]) qaysi maydonlarni qabul qilishi):
// app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title', 'body'];
}
Sample data (sinov ma'lumoti)
Ilovani sinash uchun bir nechta namuna post qo'shib qo'ying. Eng tez yo'li β php artisan tinker ochib:
READ: postlar ro'yxati¶
Eng tabiiy boshlanish nuqtasi β ro'yxat. Avvalo postlarni ekranga chiqaramiz.
15-bobdan bilamiz: ma'lumotlar bazasidan o'qish kabi "og'ir" ishni render() ichida emas, computed property ichida qilish kerak. Computed property bir so'rov davomida keshlanadi va unset() bilan tozalanmaguncha qayta-qayta bazaga bormaydi.
{{-- resources/views/components/β‘post-manager.blade.php --}}
<?php
use App\Models\Post;
use Livewire\Attributes\Computed;
use Livewire\Component;
new class extends Component
{
#[Computed]
public function posts()
{
return Post::latest()->get(); // yangidan eskiga
}
};
?>
<div>
<ul>
@forelse ($this->posts as $post)
<li wire:key="post-{{ $post->id }}">
<strong>{{ $post->title }}</strong>
</li>
@empty
<li>Hozircha post yo'q.</li>
@endforelse
</ul>
</div>
Bu yerda ikki muhim narsa bor:
$this->posts(qavssiz!) β computed property'ga shunday murojaat qilamiz. 15-bobda o'rgangandek,posts()metod emas, balki xususiyatdek ishlatiladi.wire:key="post-{{ $post->id }}"β har bir qatorga noyob kalit. Bu Livewire'ga qaysi qator qaysi post ekanini aniq aytadi. 13-bobda o'rgangandek, ro'yxat o'zgarganda (post qo'shilganda yoki o'chganda) Livewire to'g'ri qatorni yangilaydi, adashmaydi. CRUD'dawire:keyshart β chunki ro'yxat doim o'zgarib turadi.
@forelse ... @empty ... @endforelse β agar ro'yxat bo'sh bo'lsa, "Hozircha post yo'q." matnini ko'rsatadi. Bu β foydalanuvchiga do'stona xulq.
Qidiruv qo'shamiz¶
Ro'yxatni sarlavha bo'yicha filtrlash uchun bitta search xususiyat va computed ichida shart qo'shamiz. Bu β 14-bobning qisqartirilgan ko'rinishi:
public string $search = '';
#[Computed]
public function posts()
{
return Post::query()
->when($this->search, fn ($q) => $q->where('title', 'like', "%{$this->search}%"))
->latest()
->get();
}
Blade'da:
wire:model.live.debounce.300ms β foydalanuvchi yozayotganda jonli (live) filtrlaydi, lekin .debounce.300ms har bosishda emas, yozish to'xtagandan 0,3 soniya keyin serverga yuboradi. Bu keraksiz so'rovlarni kamaytiradi (batafsil β 14-bob).
Nega when?
->when($shart, $callback) β agar $shart "haqiqiy" (bo'sh bo'lmagan) bo'lsa, callback'ni qo'llaydi. Ya'ni search bo'sh bo'lsa β barcha postlar, to'lgan bo'lsa β faqat mosini qaytaradi. Bitta so'rovda, ortiqcha ifsiz.
CREATE: modal orqali post qo'shish¶
Endi eng qiziq qism β yangi post qo'shish. Buni modal oyna orqali qilamiz: foydalanuvchi "+ Yangi post" tugmasini bosadi, ekranda kichik forma chiqadi, to'ldiradi, saqlaydi β modal yopiladi va yangi post ro'yxatda paydo bo'ladi.
Form Object β formani alohida joyga ajratamiz¶
Sarlavha va matnni to'g'ridan-to'g'ri komponentga public xususiyat qilib qo'yish ham mumkin edi. Lekin biz Form Object (11-bob) ishlatamiz β chunki bu CRUD'da ikki marta foyda beradi: forma maydonlari va validatsiya alohida, tartibli joyda turadi, va eng muhimi β bir xil formani ham yaratish, ham tahrirlash uchun qayta ishlatamiz (DRY β takrorlamaslik tamoyili).
Form Object yaratamiz:
Bu app/Livewire/Forms/PostForm.php faylini yaratadi. Uni quyidagicha to'ldiramiz:
// app/Livewire/Forms/PostForm.php
<?php
namespace App\Livewire\Forms;
use App\Models\Post;
use Livewire\Attributes\Validate;
use Livewire\Form;
class PostForm extends Form
{
public ?Post $post = null; // tahrirda β qaysi post; yaratishda β null
#[Validate('required|min:5')]
public string $title = '';
#[Validate('required|min:10')]
public string $body = '';
// tahrirlash uchun: formani mavjud post bilan to'ldiramiz
public function setPost(Post $post): void
{
$this->post = $post;
$this->title = $post->title;
$this->body = $post->body;
}
// yangi post yaratish
public function store(): void
{
$this->validate();
Post::create($this->only(['title', 'body']));
$this->reset();
}
// mavjud postni yangilash
public function update(): void
{
$this->validate();
$this->post->update($this->only(['title', 'body']));
$this->reset();
}
}
Bu yerda nimalar bor:
#[Validate(...)]β har maydon uchun validatsiya qoidalari (10-bob). Sarlavha kamida 5 belgi, matn kamida 10 belgi bo'lishi shart.public ?Post $post = nullβ bu Form Object'ning "rejimi"ni belgilaydi.nullbo'lsa β yangi yaratamiz, post bo'lsa β o'shani tahrirlaymiz.setPost($post)β tahrirga o'tganda formani mavjud postning qiymatlari bilan to'ldiradi.store()βvalidate()qiladi, so'ngPost::create(...)bilan yangi yozuv qo'shadi.$this->only(['title', 'body'])β Form Object'dan faqat shu ikki maydonni massiv qilib oladi.update()β mavjud postni yangilaydi.$this->reset()β har amaldan keyin forma maydonlarini tozalaydi.
pull() yoki only()?
11-bobda pull() metodini ko'rgan bo'lishingiz mumkin β u qiymatlarni oladi va bir vaqtning o'zida tozalaydi. Bu yaratish uchun juda qulay. Bu yerda biz only() + alohida reset() ishlatdik, chunki bu yaratishda ham, tahrirlashda ham bir xil ishlaydi va kodni bir xil saqlaydi. Ikkalasi ham to'g'ri β pull() ixchamroq, only() aniqroq.
Modal'ni boshqarish: showModal xususiyati¶
Modal ko'rinishini eng oddiy usulda β bitta bool xususiyat bilan boshqaramiz:
false bo'lsa modal yashirin, true bo'lsa ko'rinadi. Blade'da uni @if bilan o'rab qo'yamiz:
Modal'ni ochish uchun create() metodi:
public PostForm $form; // Form Object β tip-belgili public xususiyat
public bool $showModal = false;
public function create(): void
{
$this->form->reset(); // formani tozalaymiz (eski qiymatlar qolmasin)
$this->showModal = true; // modalni ochamiz
}
Tugma:
Foydalanuvchi tugmani bosadi β create() ishlaydi β forma tozalanadi β showModal = true β modal ekranda paydo bo'ladi. Hammasi qayta yuklanmasdan.
Modal'ni yopishning oson yo'li
Modal'ni yopish uchun alohida metod yozish shart emas β Livewire'ning magic action $set dan foydalanish mumkin (07-bob):
$set('showModal', false) to'g'ridan-to'g'ri xususiyatni o'zgartiradi, metod yozmasdan.
Saqlash: save() metodi¶
Modal ichidagi forma yuborilganda save() ishlaydi. U Form Object'ning store() metodini chaqiradi, modalni yopadi va ro'yxatni yangilaydi:
public function save(): void
{
$this->form->store(); // validate + Post::create + reset
$this->showModal = false; // modalni yopamiz
unset($this->posts); // computed keshini tozalaymiz -> ro'yxat yangilanadi
}
Bu yerda eng muhim qator β unset($this->posts). 15-bobdan eslang: computed property bir so'rov davomida keshlanadi. Yangi post qo'shilgach, eski keshlangan ro'yxatni majburan tozalashimiz kerak β shunda Livewire ro'yxatni bazadan qaytadan o'qiydi va yangi post paydo bo'ladi.
Ehtiyot bo'ling β unset esdan chiqmasin
Agar unset($this->posts) ni yozmasangiz, post bazaga saqlanadi, lekin ro'yxat yangilanmaydi (eski keshni ko'rsatib turadi). Bu β boshlovchilar tez-tez tushadigan tuzoq. CRUD'da har o'zgartirishdan keyin computed'ni unset qiling.
Forma'ning Blade qismi:
@if ($showModal)
<div class="modal">
<form wire:submit="save">
<input type="text" wire:model="form.title" placeholder="Sarlavha">
@error('form.title') <span style="color:#dc2626">{{ $message }}</span> @enderror
<textarea wire:model="form.body" placeholder="Matn"></textarea>
@error('form.body') <span style="color:#dc2626">{{ $message }}</span> @enderror
<button type="submit">Saqlash</button>
<button type="button" wire:click="$set('showModal', false)">Bekor qilish</button>
</form>
</div>
@endif
Diqqat qiling:
wire:model="form.title"β Form Object maydoniga bog'lash nuqta bilan yoziladi (form.title,form.body). Bu 11-bobdan tanish.@error('form.title')β xato xabari hamform.prefiksi bilan. Validatsiya o'tmasa, bu yerda qizil matn chiqadi.wire:submit="save"β forma yuborilgandasaveishlaydi va avtomatikpreventDefaultqiladi (09-bob).
Validatsiya o'tmasa nima bo'ladi?
Agar sarlavha 5 belgidan qisqa yoki matn 10 belgidan qisqa bo'lsa, store() ichidagi validate() xato beradi va metod shu yerda to'xtaydi. showModal = false ga yetib bormaydi β ya'ni modal ochiq qoladi va xato xabarlari formada ko'rinadi. Foydalanuvchi xatoni tuzatib, qaytadan yuborishi mumkin. Bu β aynan kerakli xulq.
UPDATE: bir xil formada tahrirlash¶
Endi DRY tamoyilining go'zalligini ko'ramiz. Tahrirlash uchun yangi forma yasamaymiz β aynan o'sha modal va o'sha formani qayta ishlatamiz. Yagona farq: forma bo'sh emas, balki tanlangan postning qiymatlari bilan to'ldirilgan bo'ladi.
Har qatorga "Tahrirlash" tugmasini qo'shamiz:
<li wire:key="post-{{ $post->id }}">
<strong>{{ $post->title }}</strong>
<button wire:click="edit({{ $post->id }})">Tahrirlash</button>
</li>
wire:click="edit({{ $post->id }})" β bosilganda edit metodiga post id'sini uzatadi (07-bob).
edit() metodi:
public function edit(Post $post): void
{
$this->form->setPost($post); // formani mavjud post bilan to'ldiramiz
$this->showModal = true; // o'sha modalni ochamiz
}
Bu yerda chiroyli narsa: metod argumenti Post $post deb yozilgan. Livewire (Laravel'ning route-model binding mexanizmi kabi) id'ni avtomatik to'liq Post modeliga aylantiradi β siz qo'lda Post::find($id) yozishingiz shart emas. Tugmadan id keladi, metodga tayyor model tushadi.
setPost($post) formaning title, body va post maydonlarini to'ldiradi. Endi $this->form->post null emas β bu "tahrirlash rejimi" belgisidir.
save() ni ikki rejimga moslaymiz¶
Tahrirlash uchun alohida saqlash metodi yozmaymiz. O'sha save() ni aqlli qilamiz: agar form->post mavjud bo'lsa β yangilaymiz, bo'lmasa β yaratamiz.
public function save(): void
{
if ($this->form->post) {
$this->form->update(); // tahrirlash rejimi
} else {
$this->form->store(); // yaratish rejimi
}
$this->showModal = false;
unset($this->posts); // ro'yxatni yangilaymiz
}
Mana shu β CRUD'ning eng nafis qismi. Bitta forma, bitta modal, bitta save() β ham yaratadi, ham tahrirlaydi. Kod takrorlanmaydi, ushlab turish oson.
Hayotiy o'xshatish. Bu β universal pult kabi. Bitta pult ham televizorni yoqadi, ham kanal almashtiradi β ichidagi tugma qaysi rejimda ekanini biladi. Bizning
save()ham shunday: forma "yangimi yoki eskimi" ekaniniform->postorqali biladi va to'g'ri ishni qiladi.
DELETE: tasdiq bilan o'chirish¶
O'chirish β eng xavfli amal, chunki ma'lumot butunlay yo'qoladi. Shuning uchun foydalanuvchidan tasdiq so'raymiz. Livewire buni bir atributda hal qiladi: wire:confirm.
Har qatorga "O'chirish" tugmasini qo'shamiz:
<li wire:key="post-{{ $post->id }}">
<strong>{{ $post->title }}</strong>
<button wire:click="edit({{ $post->id }})">Tahrirlash</button>
<button wire:click="delete({{ $post->id }})" wire:confirm="Rostdan o'chirilsinmi?">
O'chirish
</button>
</li>
wire:confirm="Rostdan o'chirilsinmi?" β tugma bosilganda brauzer tasdiq oynachasini ko'rsatadi. Agar foydalanuvchi "OK" bossa β delete ishlaydi; "Bekor qilish" bossa β hech narsa bo'lmaydi. Sodda va kuchli himoya.
delete() metodi:
public function delete(Post $post): void
{
$post->delete();
unset($this->posts); // ro'yxatni yangilaymiz
}
Bu yerda ham edit dagidek β Post $post argumenti id'dan avtomatik to'liq modelga aylanadi. $post->delete() yozuvni bazadan o'chiradi, unset($this->posts) esa ro'yxatni yangilaydi β o'chirilgan post darhol yo'qoladi.
wire:confirm β minimal himoya
wire:confirm brauzerning oddiy tasdiq oynachasini ishlatadi. U yaxshi minimum, lekin uni mijoz tomonida o'tkazib yuborish mumkin. Haqiqiy himoya doim serverda bo'lishi kerak: o'chirishdan oldin foydalanuvchining bu postni o'chirishga huquqi borligini tekshirish (23-bob). Buni pastda "Xavfsizlik" bo'limida ko'ramiz.
Flash xabar: foydalanuvchiga bildirish¶
Har bir muvaffaqiyatli amaldan keyin foydalanuvchiga "ish bajarildi" deb bildirish kerak β aks holda u tugmani bosdimi yo'qmi, tushunmay qoladi. Eng oddiy usul β flash xabar (session()->flash()).
Flash xabar β sessiyaga bir martalik yoziladigan xabar: keyingi render'da ko'rsatiladi va o'chib ketadi. Metodlarga qo'shamiz:
public function save(): void
{
if ($this->form->post) {
$this->form->update();
session()->flash('xabar', 'Post yangilandi!');
} else {
$this->form->store();
session()->flash('xabar', 'Post qo\'shildi!');
}
$this->showModal = false;
unset($this->posts);
}
public function delete(Post $post): void
{
$post->delete();
session()->flash('xabar', 'Post o\'chirildi!');
unset($this->posts);
}
Blade'da, ro'yxatdan yuqorida, ko'rsatamiz:
Endi "Post qo'shildi!", "Post yangilandi!", "Post o'chirildi!" β har amaldan keyin yashil xabar chiqadi.
Yaxshiroq bildirishnoma β event orqali
Flash xabar oddiy, lekin u sahifa yangilanishida ko'rinadi. Zamonaviy ilovalarda ko'pincha "toast" (ekran burchagidagi suzuvchi xabar) ko'rsatiladi. Buni Livewire eventlari bilan qilish mumkin: $this->dispatch('notify', text: 'Saqlandi!') yuborasiz, JS yoki Alpine uni ushlab toast ko'rsatadi. Eventlarni 18-bobda o'rganasiz.
Modal pattern: property yoki Alpine?¶
Biz modal'ni showModal property bilan boshqardik. Bu β eng oddiy va ishonchli usul: bool xususiyat true/false bo'ladi, Blade @if bilan ko'rsatadi yoki yashiradi. Hammasi server tomonida, qo'shimcha JS yozmasdan.
Lekin yana bir variant bor β Alpine.js bilan boshqarish. Bunda modal'ning ochilish-yopilishi mijoz tomonida (brauzerda, serverga so'rovsiz) bo'ladi:
<div x-data="{ open: false }">
<button @click="open = true">+ Yangi post</button>
<div x-show="open">
{{-- forma --}}
<button @click="open = false">Yopish</button>
</div>
</div>
Alpine variantining afzalligi β modal ochilishi darhol (serverga bormasdan) bo'ladi. Kamchiligi β ozgina ko'proq murakkablik va Alpine bilimini talab qiladi.
Qaysi birini tanlash?
Boshlovchi uchun showModal property usuli yetarli va tushunarli β biz shuni ishlatdik. Tajriba ortgach, faqat ko'rinishni boshqaradigan modal (server holatiga bog'liq bo'lmagan) uchun Alpine'ga o'tishingiz mumkin. Alpine va @entangle (Alpine holatini Livewire xususiyati bilan bog'lash) ni 22-bobda batafsil o'rganasiz.
To'liq ishlaydigan kod¶
Endi hamma qismni bitta faylda birlashtiramiz. Mana to'liq, jonli loyihada tekshirilgan PostManager komponenti:
{{-- resources/views/components/β‘post-manager.blade.php --}}
<?php
use App\Livewire\Forms\PostForm;
use App\Models\Post;
use Livewire\Attributes\Computed;
use Livewire\Component;
new class extends Component
{
public PostForm $form; // Form Object (yaratish + tahrirlash)
public bool $showModal = false; // modal ko'rinishi
public string $search = ''; // qidiruv
// READ β ro'yxat manbai (computed, keshlanadi)
#[Computed]
public function posts()
{
return Post::query()
->when($this->search, fn ($q) => $q->where('title', 'like', "%{$this->search}%"))
->latest()
->get();
}
// CREATE β bo'sh modal ochish
public function create(): void
{
$this->form->reset();
$this->showModal = true;
}
// UPDATE β to'la modal ochish
public function edit(Post $post): void
{
$this->form->setPost($post);
$this->showModal = true;
}
// SAVE β yaratish yoki yangilash (bir xil forma)
public function save(): void
{
if ($this->form->post) {
$this->form->update();
session()->flash('xabar', 'Post yangilandi!');
} else {
$this->form->store();
session()->flash('xabar', 'Post qo\'shildi!');
}
$this->showModal = false;
unset($this->posts); // ro'yxatni yangilash
}
// DELETE β tasdiq bilan o'chirish
public function delete(Post $post): void
{
$post->delete();
session()->flash('xabar', 'Post o\'chirildi!');
unset($this->posts);
}
};
?>
<div>
{{-- flash xabar --}}
@if (session('xabar'))
<p style="color:#16a34a">{{ session('xabar') }}</p>
@endif
{{-- qidiruv + yangi post tugmasi --}}
<input type="text" wire:model.live.debounce.300ms="search" placeholder="Qidirish...">
<button wire:click="create">+ Yangi post</button>
{{-- READ: postlar ro'yxati --}}
<ul>
@forelse ($this->posts as $post)
<li wire:key="post-{{ $post->id }}">
<strong>{{ $post->title }}</strong>
<button wire:click="edit({{ $post->id }})">Tahrirlash</button>
<button wire:click="delete({{ $post->id }})" wire:confirm="Rostdan o'chirilsinmi?">
O'chirish
</button>
</li>
@empty
<li>Hozircha post yo'q.</li>
@endforelse
</ul>
{{-- CREATE/UPDATE: modal forma (bitta forma, ikki rejim) --}}
@if ($showModal)
<div class="modal">
<form wire:submit="save">
<input type="text" wire:model="form.title" placeholder="Sarlavha">
@error('form.title') <span style="color:#dc2626">{{ $message }}</span> @enderror
<textarea wire:model="form.body" placeholder="Matn"></textarea>
@error('form.body') <span style="color:#dc2626">{{ $message }}</span> @enderror
<button type="submit">Saqlash</button>
<button type="button" wire:click="$set('showModal', false)">Bekor qilish</button>
</form>
</div>
@endif
</div>
Va uni sahifaga ulash uchun route (04-bob):
// routes/web.php
use Illuminate\Support\Facades\Route;
Route::livewire('/post-manager', 'post-manager');
Endi /post-manager sahifasiga kirib, to'liq ishlaydigan CRUD ilovani ko'rasiz: qidirasiz, qo'shasiz, tahrirlaysiz, o'chirasiz β hammasi qayta yuklanmasdan.
Sinab ko'ring
- "+ Yangi post" bosing β modal ochiladi.
- Sarlavhaga 2 ta harf yozib "Saqlash" bosing β qizil xato chiqadi, modal ochiq qoladi (validatsiya ishladi).
- To'g'ri to'ldirib saqlang β modal yopiladi, yangi post ro'yxat tepasida, yashil xabar chiqadi.
- "Tahrirlash" bosing β o'sha post bilan to'lgan modal ochiladi. O'zgartirib saqlang.
- "O'chirish" bosing β tasdiq so'raydi. "OK" bossangiz, post yo'qoladi.
- Qidiruvga sarlavha bo'lagini yozing β ro'yxat jonli filtrlanadi.
Xavfsizlik eslatmasi¶
CRUD ilova ma'lumotni o'zgartiradi va o'chiradi β shuning uchun xavfsizlik shart. Hozir bizning misolimizda har kim har qanday postni tahrirlashi va o'chirishi mumkin. Real ilovada bu mumkin emas.
Eng muhim qoida: edit, save (update qismi) va delete metodlarida avtorizatsiya tekshiruvi bo'lishi shart. Laravel'ning authorize() metodi bilan:
public function delete(Post $post): void
{
$this->authorize('delete', $post); // huquq bormi? yo'q bo'lsa β to'xtaydi
$post->delete();
unset($this->posts);
}
$this->authorize('delete', $post) β agar joriy foydalanuvchining bu postni o'chirishga huquqi bo'lmasa, Livewire so'rovni to'xtatadi (403 xato). wire:confirm faqat mijoz tomonidagi qulaylik β u himoya emas. Haqiqiy himoya doim serverda.
Xavfsizlik β eslab qoling
wire:click="delete({{ $post->id }})" dagi id mijozdan keladi β uni o'zgartirib yuborish mumkin. Ya'ni foydalanuvchi boshqa birovning post id'sini yuborishga urinishi mumkin. Shuning uchun serverda authorize() bo'lmasa, har kim har qanday postni o'chirishi mumkin bo'lib qoladi. Public metod = ochiq eshik β har doim avtorizatsiya bilan qulflang. To'liqroq β 23-bob.
Foydalanuvchi tajribasini sayqallash¶
Yuqoridagi kod to'liq ishlaydi, lekin haqiqiy ilovada yana bir necha "kichik nafis tegishlar" foydalanuvchi tajribasini sezilarli yaxshilaydi. Ularning hammasini siz oldingi boblardan bilasiz β endi joyiga qo'yamiz.
Saqlash davomida tugmani bloklash¶
Saqlash so'rovi serverga borib qaytguncha bir lahza o'tadi. Bu paytda foydalanuvchi "Saqlash"ni qayta-qayta bosib, bir xil postni ikki marta qo'shib yuborishi mumkin. Buni wire:loading.attr="disabled" bilan oldini olamiz (20-bob):
<button type="submit" wire:loading.attr="disabled">
<span wire:loading.remove>Saqlash</span>
<span wire:loading>Saqlanmoqda...</span>
</button>
So'rov ketayotgan paytda tugma o'chadi va "Saqlanmoqda..." deb ko'rsatadi β javob kelganda yana "Saqlash"ga qaytadi.
Bo'sh holatni chiroyli ko'rsatish¶
Biz @forelse ... @empty bilan "Hozircha post yo'q." matnini ko'rsatdik. Lekin qidiruv natija bermaganda boshqacha matn yaxshiroq:
@empty
@if ($search)
<li>"{{ $search }}" bo'yicha post topilmadi.</li>
@else
<li>Hozircha post yo'q. "+ Yangi post" bilan birinchisini qo'shing!</li>
@endif
@endforelse
Endi foydalanuvchi ro'yxat haqiqatan bo'shmi yoki shunchaki qidiruv mos kelmadimi β aniq tushunadi.
Sarlavhada postlar sonini ko'rsatish¶
Computed property'dan ro'yxat sonini ham olsa bo'ladi β qo'shimcha so'rovsiz, chunki u keshlangan:
$this->posts allaqachon yuklangani uchun ->count() bazaga qayta bormaydi β Collection'ning o'zini sanaydi. Bu β computed'ning yana bir foydasi.
Tez-tez uchraydigan xatolar¶
CRUD qurganda boshlovchilar bir xil tuzoqlarga tushadi. Ularni oldindan biling:
1. unset($this->posts) esdan chiqib qolishi
Eng keng tarqalgan xato. Post saqlanadi/o'chadi, lekin ro'yxat o'zgarmaydi β chunki computed eski keshni ko'rsatib turibdi. Har o'zgartirishdan keyin unset qiling.
2. wire:key qo'ymaslik
Ro'yxat qatorlariga wire:key qo'ymasangiz, post o'chirilganda yoki qo'shilganda Livewire qatorlarni adashtirib yangilashi mumkin (masalan, noto'g'ri qatorda input qoladi). Doim wire:key="post-{{ $post->id }}".
3. form. prefiksini unutish
Form Object ishlatganda Blade'da wire:model="title" emas, wire:model="form.title" yozish kerak. Xato ham @error('form.title') bo'ladi. Prefiks tushib qolsa, bog'lanish ishlamaydi.
4. Tahrir uchun alohida forma yasash
editForm, createForm deb ikki forma yasash β kodni ikki barobar qiladi va xatoga moyil. Bitta forma + form->post rejim belgisi bilan DRY saqlang.
Xulosa¶
- CRUD β Create (yaratish), Read (o'qish), Update (yangilash), Delete (o'chirish) β deyarli har dasturning yuragi. Livewire'da bularning hammasi bitta komponentda, bitta sahifada, qayta yuklanmasdan bo'ladi.
- READ: ro'yxatni
#[Computed]property'da o'qing va har qatorgawire:keyqo'ying. Computed keshlanadi β o'zgartirishdan keyinunset($this->posts)bilan tozalang. - CREATE: Form Object (
PostForm) maydonlar va validatsiyani tartibli ushlaydi. Modal'nishowModalbool xususiyat bilan boshqaring;$set('showModal', false)bilan yoping. - UPDATE: bir xil forma va
save()ni qayta ishlating (DRY).form->postnullbo'lsa β yaratish, mavjud bo'lsa β yangilash.edit($post)formani to'ldiradi. - DELETE:
wire:confirm="..."tasdiq so'raydi, metod$post->delete()qiladi.wire:confirmβ qulaylik, himoya emas. - Argumentdagi
Post $postid'dan avtomatik to'liq modelga aylanadi β qo'ldafind()shart emas. - Har amaldan keyin flash xabar (
session()->flash()) bilan foydalanuvchiga bildiring; toast kerak bo'lsa β eventlar (18-bob). - Xavfsizlik:
edit,update,deletemetodlarida doim$this->authorize(...)β mijozdan kelganid'ga ishonmang (23-bob).
Amaliy mashqlar¶
-
O'z CRUD'ingiz (oson). "Vazifalar" (
Task) uchun CRUD yarating:titlevadone(bool) maydonlari bilan. Ro'yxat, qo'shish (modal), o'chirish (tasdiq bilan) ishlasin. Tahrirni hozircha qoldirib turing. -
Tahrirlashni inline qiling (o'rta). Modal o'rniga, "Tahrirlash" bosilganda o'sha qatorning o'zida sarlavha input'ga aylansin (modal ochilmasin).
editingIdxususiyatini qo'shing va@if ($editingId === $post->id)bilan o'sha qatorda input ko'rsating, qolganlarida β oddiy matn. Saqlangach yana matnga qaytsin. -
Bekor qilinadigan o'chirish (qiyin).
wire:confirmo'rniga "yumshoq o'chirish" qiling: o'chirilgan post darhol yo'qolmasin, balki "O'chirildi β Bekor qilish?" xabari 5 soniya ko'rinsin. Bu vaqt ichida "Bekor qilish" bosilsa, post qaytsin. (Maslahat: postni darholdelete()qilmang β uni "yashirin" deb belgilang yokideleted_atishlating va xabar bilan ko'rsating. Eventlar vawire:loadingyordam beradi.) -
Validatsiya xabarlarini o'zbekchalashtiring (o'rta).
PostFormdagi#[Validate(...)]gamessage:parametri qo'shing, masalan#[Validate('required|min:5', message: 'Sarlavha kamida 5 belgi bo\'lishi kerak')]. Modal'da xato chiqishini tekshiring (10-bob). -
Avtorizatsiya qo'shing (qiyin). Har postga
user_idustun qo'shing vaPostPolicyyarating.edit,savevadeletemetodlarida$this->authorize(...)chaqiring β toki foydalanuvchi faqat o'zining postlarini boshqara olsin. Boshqa foydalanuvchi post'iga urinilganda 403 chiqishini tekshiring (23-bob).
β¬ οΈ Oldingi: 15 β Computed properties Β· π Kitob boshi Β· Keyingi: 17 β Nested komponentlar β‘οΈ