10 β Query Builder va ilg'or Eloquent¶
β¬ οΈ Oldingi: 09 β Eloquent munosabatlari Β· π README Β· Keyingi: 11 β Seeder, Factory va Tinker β‘οΈ
Bu bobda: sayt sekinlashganda yoki Eloquent murakkab so'rovga "yetmay" qolganda nima qilishni o'rganamiz. Query Builder (
DB::table) bilan join/groupBy/having yozamiz, eng mashhur sahna ortidagi sekinlik β N+1 muammosini tanib,with()(eager loading) bilan tuzatamiz, qayta ishlatiladigan filtrlarni scopega aylantiramiz, accessor/mutator va$castsorqali bazadagi xom ma'lumotni model darajasida chiroyli shaklga keltiramiz,withCount/whereHasbilan munosabatlar ustidan so'rov yozamiz va million qatorli jadvalnichunk/cursorbilan xotirani portlatmasdan qayta ishlaymiz.
Muammo¶
8- va 9-boblarda Eloquent bilan yozish bir zavq edi: Post::all(), $post->user, $user->posts. Hammasi sodda. Lekin loyiha o'sgani sayin ikki xil og'riq paydo bo'ladi.
Birinchi og'riq β sekinlik. Blog bosh sahifasini ochasiz, 50 ta post ro'yxati va har birining yonida muallif ismi. Kod chiroyli ko'rinadi:
$posts = Post::all(); // 1 ta so'rov
foreach ($posts as $post) {
echo $post->sarlavha;
echo $post->user->ism; // ... va bu yerda har safar yangi so'rov!
}
Sahifa "ishlaydi", lekin sekin. Sababini ko'rsangiz hayron qolasiz: bitta sahifa ochilishida bazaga 51 marta so'rov ketgan. Bu β N+1 muammosi, va u Laravel loyihalaridagi sekinlikning eng ko'p uchraydigan sababi.
Ikkinchi og'riq β Eloquent yetmay qoladi. "Har kategoriyada nechta sotilgan mahsulot bor, eng ko'pidan kamiga" yoki "narxni bazada tiyinda saqlab, ekranda so'mda ko'rsat" kabi savollar oddiy where bilan hal bo'lmaydi. Mana shu ikki og'riqni β sekinlikni va "yetmaslikni" β bu bobda bartaraf qilamiz. Eloquent ostida nima yotganidan boshlaymiz.
Query Builder β Eloquent ostidagi dvigatel¶
Eloquent β bu Query Builder ustiga qurilgan qulay qatlam. Post::where('holat', 'chop')->get() yozsangiz, ostida aynan Query Builder ishlaydi. Ba'zan modelsiz, to'g'ridan-to'g'ri jadval bilan ishlash qulayroq β masalan tez hisobot, yoki modeli yo'q jadval. Buning uchun DB::table() bor:
use Illuminate\Support\Facades\DB;
// Eng oddiy: butun jadval
$users = DB::table('users')->get();
// where zanjiri + tartiblash + cheklash
$faollar = DB::table('users')
->where('holat', 'faol')
->where('yosh', '>=', 18)
->orderBy('created_at', 'desc')
->limit(10)
->get();
get() β barcha qatorlarni qaytaradi (kollektsiya sifatida). Bitta natija kerak bo'lsa:
$user = DB::table('users')->where('id', 1)->first(); // bitta qator (yoki null)
$soni = DB::table('users')->where('holat', 'faol')->count(); // faqat son
$ism = DB::table('users')->where('id', 1)->value('ism'); // faqat bitta ustun qiymati
π Muhim farq: DB::table() sizga oddiy stdClass obyektlar qaytaradi β model emas. Ya'ni $user->user->posts kabi munosabatlar, accessor, cast β hech biri ishlamaydi. Bularning hammasi faqat Eloquent modelda bor. Shuning uchun qoida: odatda Eloquent (Post::...) ishlating; faqat juda sodda hisobotda yoki tezlik kritik bo'lganda DB::table() ga tushing.
join, groupBy, having¶
SQL bilan tanish bo'lsangiz (agar yo'q bo'lsa β SQL kitobi ga qarang), bular tanish tuyuladi, faqat sintaksis PHP zanjiri ko'rinishida:
// posts + users ni ulash
$natija = DB::table('posts')
->join('users', 'posts.user_id', '=', 'users.id')
->select('posts.sarlavha', 'users.ism')
->get();
// har user nechta post yozgan (groupBy + aggregate)
$hisobot = DB::table('posts')
->select('user_id', DB::raw('COUNT(*) as soni'))
->groupBy('user_id')
->having('soni', '>', 5)
->orderByDesc('soni')
->get();
join() ning argumentlari aynan SQL'dagi ON posts.user_id = users.id ning qismlari. DB::raw() β Laravel'ga "bu matnni xom SQL sifatida qo'y" deyish (masalan COUNT(*)). having() β groupBydan keyingi filtr (where guruhlashdan oldin, having keyin ishlaydi).
π‘ DB::raw() ichiga hech qachon foydalanuvchidan kelgan matnni qo'ymang β bu SQL injection eshigi. Oddiy where('ustun', $qiymat) esa avtomatik xavfsiz: qiymat alohida "bog'lanadi" (parametr binding).
O'zgartirish amallari ham bor:
DB::table('users')->insert(['ism' => 'Ali', 'email' => 'ali@example.com']);
DB::table('users')->where('id', 1)->update(['holat' => 'nofaol']);
DB::table('users')->where('id', 99)->delete();
N+1 muammosi va eager loading¶
Endi bobning yuragiga keldik. Yuqoridagi "51 so'rov" qayerdan chiqdi?
$posts = Post::all(); // 1) SELECT * FROM posts β 1 ta so'rov
foreach ($posts as $post) {
echo $post->user->ism; // 2,3,4...) har post uchun SELECT * FROM users WHERE id=?
}
Eloquent munosabatlar dangasa (lazy): $post->user ga birinchi murojaat qilganda, Laravel o'sha lahzada borib bazadan so'raydi. 50 ta postni aylanib chiqsangiz β 50 ta qo'shimcha so'rov, ustiga boshlang'ich 1 ta. Jami 1 + N. Shuning uchun nomi β N+1.
Yechim β eager loading: munosabatni siklda ishlatishingizni oldindan aytib qo'ying. with() Laravel'ga "postlarni olishdan oldin ularning hamma mualliflarini ham bitta so'rovda tortib kel" deydi:
$posts = Post::with('user')->get(); // endi atigi 2 ta so'rov:
// SELECT * FROM posts
// SELECT * FROM users WHERE id IN (5, 8, 2, ...)
foreach ($posts as $post) {
echo $post->user->ism; // bazaga bormaydi β allaqachon xotirada
}
50 post bo'ladimi, 5000 post bo'ladimi β doim 2 ta so'rov. Mana shu farq sahifani sekundlardan millisekundlarga tushiradi.
with() ning ko'p qirralari bor:
// bir nechta munosabat birga
$posts = Post::with(['user', 'comments'])->get();
// ichma-ich (nested) β izohlar va ularning mualliflari
$posts = Post::with('comments.user')->get();
// faqat kerakli ustunlar (id ni doim qo'shing β munosabat shu bilan bog'lanadi)
$posts = Post::with('user:id,ism')->get();
π Tuzoq: with('user:id,ism') da munosabat kalitini (id) tushirib qoldirmang. Foreign key (masalan posts.user_id) ham selectda bo'lishi shart β bo'lmasa Laravel postlarni mualliflarga ulay olmaydi va user null bo'lib chiqadi.
load() β keyin yuklash (lazy eager)¶
Ba'zan modellar allaqachon olingan, lekin keyin "voy, munosabat ham kerak ekan" deysiz. Qaytadan so'rov yozmang β load() bilan keyin yuklang:
$users = User::all();
// ... shartga qarab qaror qilindi ...
$users->load('posts'); // endi bitta qo'shimcha so'rov bilan hammasiga posts yuklandi
$users->loadMissing('posts'); // faqat hali yuklanmaganlarni yuklaydi (ikki marta ishlamaydi)
π‘ with() β so'rovdan oldin (model olinmasidan). load() β model olingandan keyin. Ikkalasi ham N+1 ni bir xil hal qiladi; qaysi biri kodingizga qulay bo'lsa, shuni tanlang.
π‘ N+1 ni "ko'z bilan" sezmaslik uchun Laravel Debugbar yoki DB::listen() so'rovlarni sanab beradi. Bundan tashqari Model::preventLazyLoading() (odatda AppServiceProviderda, faqat lokal muhitda) β dangasa yuklash sodir bo'lsa darrov xato otadi, shunda N+1 ni unutib qo'ymaysiz.
Local scope β qayta ishlatiladigan filtr¶
Loyihada bitta shart qayta-qayta takrorlanadi. "Chop etilgan post" β bu holat = 'chop' va chop_sana <= hozir. Bu shart bosh sahifada ham, RSS'da ham, qidiruv'da ham kerak. Har joyda qo'lda yozsangiz, ertaga ta'rif o'zgarsa β hamma joyni tuzatishingiz kerak.
Local scope β shu shartni modelga "ot berib" biriktirish. Laravel 12+ da zamonaviy uslub β #[Scope] atributi:
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\Scope;
class Post extends Model
{
#[Scope]
protected function chopEtilgan(Builder $query): void
{
$query->where('holat', 'chop')
->where('chop_sana', '<=', now());
}
// parametr ham qabul qiladi
#[Scope]
protected function holatBoyicha(Builder $query, string $holat): void
{
$query->where('holat', $holat);
}
}
Endi hamma joyda qisqa va aniq:
Post::chopEtilgan()->get();
Post::chopEtilgan()->count();
Post::chopEtilgan()->latest()->paginate(15);
Post::holatBoyicha('qoralama')->get();
π Eski (lekin hozir ham to'liq ishlaydigan va eng ko'p uchraydigan) konvensiya β metod nomini scope bilan boshlash. Atribut yo'q, lekin metod nomi scope prefiksli bo'lishi shart, chaqirilganda esa prefikssiz va kichik harf bilan yoziladi:
public function scopeChopEtilgan(Builder $query): Builder
{
return $query->where('holat', 'chop')
->where('chop_sana', '<=', now());
}
// Chaqirilishi bir xil: Post::chopEtilgan()->get();
π‘ Eski loyihada scopeXxx ni ko'rsangiz hayron bo'lmang β bu o'sha narsa. Yangi kodda #[Scope] atributi tavsiya etiladi, chunki metod nomini chiroyli (scope prefiksisiz) qoldiradi.
Global scope β avtomatik qo'shiladigan filtr¶
Local scope'ni siz chaqirasiz. Global scope esa o'zi avtomatik har bir so'rovga qo'shiladi β siz hech narsa yozmaysiz. Klassik misol: faqat "faol" foydalanuvchilar bilan ishlash, o'chirilganini hech qachon ko'rsatmaslik.
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class FaolScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where('holat', 'faol');
}
}
Modelga #[ScopedBy] atributi bilan biriktiramiz:
namespace App\Models;
use App\Models\Scopes\FaolScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
use Illuminate\Database\Eloquent\Model;
#[ScopedBy([FaolScope::class])]
class User extends Model
{
//
}
Endi User::all() avtomatik WHERE holat = 'faol' qo'shadi β siz so'ramasangiz ham. Kerak bo'lganda chetlab o'tish mumkin:
π Ehtiyot bo'ling: global scope kuchli, lekin "sehrli" β yangi dasturchi nega ba'zi qatorlar chiqmayotganini tushunmay qolishi mumkin. Eng mashhur global scope β Laravel'ning o'z SoftDeletes ('o'chirilganni yashirish') mexanizmi; uni qo'lda yozish o'rniga tayyor trait'dan foydalaning. O'zingiznikini faqat juda asosli holatda yozing.
Accessor va Mutator β model darajasidagi o'zgartirish¶
Bazada ma'lumot "xom" yotadi: narx tiyinda (1500000), ism ALI deb yozib yuborilgan. Ekranda esa boshqacha kerak: narx so'mda (15000), ism Ali. Har joyda qo'lda o'zgartirish β takror va xatoga yo'l. Accessor (o'qishda o'zgartirish) va mutator (yozishda o'zgartirish) buni bir joyda hal qiladi.
Laravel 9+ da zamonaviy uslub β bitta metodda Attribute::make(get: ..., set: ...):
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Product extends Model
{
// Accessor: faqat o'qishda. narx_tiyin bazada, narx ko'rsatishda so'mda
protected function narx(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => $attributes['narx_tiyin'] / 100,
set: fn (float $value) => ['narx_tiyin' => (int) round($value * 100)],
);
}
// Faqat o'qish uchun hosilaviy xossa (bazada ustuni yo'q)
protected function toliqNom(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) =>
$attributes['nomi'] . ' (' . $attributes['kod'] . ')',
);
}
}
Ishlatish β oddiy xossa kabi, lekin sahna ortida o'zgartirish ketadi:
$p = Product::find(1);
echo $p->narx; // 15000 (bazada 1500000, accessor /100 qildi)
$p->narx = 20000; // mutator *100 qiladi
$p->save(); // bazaga 2000000 yoziladi
echo $p->toliqNom; // "Daftar (DFT-01)" β bazada bunday ustun yo'q
π Metod nomi camelCase (toliqNom), xossa esa snake_case ($p->toliq_nom) β ikkalasi ham ishlaydi, Laravel avtomatik moslaydi. Odat: metodni narx() deb yozsangiz, $p->narx deb o'qiysiz.
π‘ Attribute β bu Laravel 8 va undan oldingi getNarxAttribute() / setNarxAttribute() metod juftligining zamonaviy o'rnini bosadigani. Eski kodda o'sha juftlikni ko'rishingiz mumkin; mantiq bir xil, faqat sintaksis ixchamlashgan.
$casts β turni avtomatik o'zgartirish¶
Accessor β qo'lda yozadigan o'zgartirish. Lekin eng ko'p uchraydigan o'zgartirishlar (JSONβmassiv, matnβsana, matnβenum) uchun qo'lda yozish shart emas β Laravel'da tayyor castlar bor. casts() metodida e'lon qilasiz (Laravel 11+ uslubi):
namespace App\Models;
use App\Enums\Holat;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected function casts(): array
{
return [
'sozlamalar' => 'array', // JSON ustun <-> PHP massiv
'chop_sana' => 'datetime', // matn <-> Carbon obyekt
'narxi' => 'decimal:2', // doim 2 xona aniqlik
'faolmi' => 'boolean', // 0/1 <-> true/false
'holat' => Holat::class, // matn <-> enum
'maxfiy_kod' => 'encrypted', // bazada shifrlangan saqlanadi
];
}
}
Endi turlar avtomatik to'g'ri keladi:
$p = Product::find(1);
$p->sozlamalar['til']; // massiv β JSON o'zi parse bo'ldi
$p->sozlamalar = ['til' => 'uz', 'tema' => 'tungi']; // saqlashda o'zi JSON bo'ladi
$p->chop_sana->format('d.m.Y'); // Carbon obyekt β sana metodlari ishlaydi
$p->chop_sana->isPast();
$p->faolmi; // bazada 1 bo'lsa ham β bu true (bool), "1" emas
enum cast¶
PHP'dagi enum (sizga 8.1+ dan tanish) Eloquent bilan ajoyib ishlaydi. Avval enum:
namespace App\Enums;
enum Holat: string
{
case Faol = 'faol';
case Nofaol = 'nofaol';
case Kutilmoqda = 'kutilmoqda';
public function label(): string
{
return match ($this) {
Holat::Faol => 'Faol',
Holat::Nofaol => 'Nofaol',
Holat::Kutilmoqda => 'Kutilmoqda',
};
}
}
'holat' => Holat::class cast tufayli bazadagi 'faol' matni avtomatik Holat::Faol obyektiga aylanadi:
$p = Product::find(1);
$p->holat; // Holat::Faol (enum, matn emas!)
$p->holat === Holat::Faol; // true β xavfsiz, terilgan solishtirish
$p->holat->label(); // "Faol"
$p->holat = Holat::Nofaol; // saqlashda bazaga 'nofaol' yoziladi
$p->save();
π Enum cast β eng yoqimli yangiliklardan biri. Endi if ($p->holat == 'fol') kabi imlo xatosi bilmasdan o'tib ketmaydi: Holat::Fol umuman mavjud emas, PHP darrov xato beradi. Matn o'rniga terilgan tip β kamroq xato degani.
β
'sozlamalar' => 'array' β JSON ustunlar uchun deyarli har doim to'g'ri tanlov.
β JSON ustunni array cast'siz qoldirib, keyin json_decode($p->sozlamalar, true) ni qo'lda yozish β eskirgan, takror va xatoga moyil.
Aggregate va withCount¶
Sonlar bilan ishlash uchun Eloquent'da tayyor metodlar bor:
$jami = Order::count();
$summa = Order::sum('narxi');
$ortacha = Order::avg('narxi');
$engKatta = Order::max('narxi');
// shart bilan
$iyunSummasi = Order::where('sana', '>=', '2026-06-01')->sum('narxi');
Eng foydalisi β withCount(): "har userda nechta post bor" degan savolga N+1'siz javob beradi. U posts ni yuklamaydi β faqat sonini qo'shimcha ustun qilib qaytaradi:
$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->ism . ': ' . $user->posts_count . ' ta post';
// ustun nomi: {munosabat}_count
}
π Agar User::all() qilib, keyin har userda $user->posts->count() desangiz β bu N+1! Har user uchun butun postlar to'plami yuklanadi, faqat sanash uchun. withCount('posts') esa bitta qo'shimcha so'rov bilan faqat sonlarni oladi β ancha arzon.
Shartli sanash ham mumkin:
$users = User::withCount([
'posts', // posts_count
'posts as chop_posts_count' => fn ($q) => $q->where('holat', 'chop'), // chop_posts_count
])->get();
echo $users->first()->chop_posts_count;
π‘ withCount ning oilaviy a'zolari: withSum('orders', 'narxi'), withAvg, withMax, withMin, withExists. Hammasi bir xil ishlaydi β {munosabat}_{amal}_{ustun} nomli qo'shimcha xossa qo'shadi.
whereHas, has va whereRelation β munosabat ustidan so'rov¶
"Kamida bitta posti bor userlar" yoki "chop etilgan posti bor userlar" β bu munosabat ustidan filtr. Eloquent uchun maxsus metodlar bor:
// has β kamida bitta posti bor
$users = User::has('posts')->get();
// kamida 3 ta posti bor
$users = User::has('posts', '>=', 3)->get();
// doesntHave β umuman posti yo'qlar (anti-pattern bilan tanish bo'lsangiz β o'sha)
$users = User::doesntHave('posts')->get();
// whereHas β munosabat ichidagi shart bilan
$users = User::whereHas('posts', function ($query) {
$query->where('holat', 'chop');
})->get();
// whereRelation β sodda whereHas uchun qisqartma
$users = User::whereRelation('posts', 'holat', 'chop')->get();
π with() va whereHas() ni adashtirmang β ikki xil ish:
- with('posts') β postlarni yuklaydi (xotiraga oladi), userlar sonini o'zgartirmaydi.
- whereHas('posts', ...) β userlarni filtrlaydi (shartga mos posti bor userni qoldiradi), postlarni yuklamaydi.
Ikkisi bir so'rovda birga kelishi mumkin va ko'pincha shunday yoziladi:
// chop etilgan posti bor userlarni ol VA ularning chop postlarini yukla
$users = User::whereHas('posts', fn ($q) => $q->where('holat', 'chop'))
->with(['posts' => fn ($q) => $q->where('holat', 'chop')])
->get();
Katta ma'lumot: chunk, lazy, cursor¶
Post::all() β 1000 postda zo'r. Lekin 5 million qatorli jadvalga all() qilsangiz, hammasini xotiraga tortib, dasturni "o'ldirasiz" (Allowed memory size exhausted). Katta hajm uchun ma'lumotni bo'lib qayta ishlash kerak.
// chunk β 500 tadan bo'lib o'qiydi, har bo'lakni qayta ishlab tashlaydi
Post::chunk(500, function ($posts) {
foreach ($posts as $post) {
// ... qayta ishlash ...
}
});
π Agar chunk ichida shu jadvalni yangilab (update/delete) borsangiz, oddiy chunk qatorlar tartibini buzib, ba'zilarini o'tkazib yuborishi mumkin. Bunday holda chunkById ishlating β u id bo'yicha xavfsiz harakatlanadi:
Post::where('holat', 'eski')->chunkById(500, function ($posts) {
foreach ($posts as $post) {
$post->update(['arxivlandi' => true]);
}
});
lazy() va cursor() β bir xil maqsad, boshqa qulaylik:
// lazy β kollektsiya kabi yozasiz, orqada o'zi chunk qiladi
foreach (Post::lazy() as $post) {
// ...
}
// cursor β bitta so'rov, bittadan model (eng kam xotira, lekin bitta so'rov uzoq ochiq turadi)
foreach (Post::cursor() as $post) {
// ...
}
π‘ Qaysisini tanlash? Oddiy qoida: chunkById β ma'lumotni o'zgartirsangiz; lazy β faqat o'qib, kod chiroyli bo'lsin desangiz; cursor β xotira juda taqchil, bir martalik o'qish bo'lsa. all() esa β faqat jadval kichikligiga ishonchingiz komil bo'lganda.
Eloquent Collection β natija ustidan ishlash¶
get() qaytargan narsa oddiy massiv emas β bu Eloquent Collection, juda boy metodlar to'plami bilan (PHP'dagi massiv funksiyalarining chiroyli, zanjirlanadigan varianti). Ma'lumot xotiraga olingandan keyin uni qayta ishlashga juda qulay:
$posts = Post::all();
$sarlavhalar = $posts->pluck('sarlavha'); // faqat sarlavhalar ro'yxati
$guruh = $posts->groupBy('user_id'); // user_id bo'yicha guruhlangan
$chop = $posts->where('holat', 'chop'); // xotirada filtr (so'rov emas!)
$jami = $posts->sum('korishlar'); // umumiy ko'rishlar
$birinchi = $posts->firstWhere('holat', 'chop'); // birinchi mos
$nomlar = $posts->map(fn ($p) => $p->sarlavha); // har birini o'zgartirib yangi to'plam
π Muhim farq. Post::where('holat', 'chop')->get() β bazada filtr (so'rov darajasida). Post::all()->where('holat', 'chop') β avval HAMMA postni xotiraga oladi, keyin xotirada filtrlaydi. Birinchisi deyarli har doim to'g'ri (faqat keraklini bazadan oladi). Collection metodlari β ma'lumot allaqachon olingach, qo'shimcha so'rovsiz qayta ishlash uchun.
Hammasini birlashtiramiz¶
Real misol β "blog bosh sahifasi" so'rovi, bobning hamma g'oyasini bir joyga yig'adi:
$postlar = Post::query()
->chopEtilgan() // local scope
->with('user:id,ism') // N+1 ni oldini olamiz
->withCount('comments') // har postda nechta izoh
->whereHas('user', fn ($q) => $q->where('holat', 'faol')) // faol muallifniki
->latest('chop_sana')
->paginate(15);
Blade'da esa hammasi tayyor β qo'shimcha so'rov yo'q:
@foreach ($postlar as $post)
<article>
<h2>{{ $post->sarlavha }}</h2>
<p>Muallif: {{ $post->user->ism }}</p> {{-- with() tufayli so'rov yo'q --}}
<p>Izohlar: {{ $post->comments_count }} ta</p> {{-- withCount tufayli --}}
<p>Holat: {{ $post->holat->label() }}</p> {{-- enum cast tufayli --}}
</article>
@endforeach
{{ $postlar->links() }}
Bitta sahifa, ozgina so'rov, toza kod. Mana shu β ilg'or Eloquent'ning maqsadi: tez va o'qiladigan.
10-bob mashqlari¶
Quyidagi mashqlarni o'z loyihangizda (blog yoki do'kon) bajaring. Yechimlarini o'zingiz yozing β har biri oldingisidan bir qadam murakkabroq.
DB::table('users')bilan barcha foydalanuvchilarniismbo'yicha alifbo tartibida oling.DB::table()vawherezanjiri bilan: holati "faol" va yoshi 18 dan katta foydalanuvchilarni, eng yangisidan boshlab, faqat 10 tasini chiqaring.DB::table()dajoinishlatibpostsvausersni ulang, natijada post sarlavhasi va muallif ismini ko'rsating.DB::table(),groupByvaDB::raw('COUNT(*)')bilan: har biruser_idnechta post yozganini sanang;havingbilan faqat 3 tadan ko'p yozganlarni qoldiring.- Bilib turib N+1 yarating:
Post::all()qilib, siklda$post->user->ismchiqaring. Debugbar yokiDB::listen()bilan necha so'rov ketganini sanang. - Xuddi shu kodni
Post::with('user')->get()ga o'zgartiring va so'rovlar soni nechtaga tushganini tasdiqlang. with('user:id,ism')ishlating va faqat kerakli ustunlar yuklanganiga ishonch hosil qiling; foreign key (user_id) niselectdan tushirib qoldirsangiz nima bo'lishini ko'ring.comments.userichma-ich eager loading bilan postlar, ularning izohlari va izoh mualliflarini bitta zanjirda yuklang.- Postlarni avval
all()bilan oling, keyinload('user')bilan keyin yuklang;with()bilan farqini tushuntiring (komment sifatida). Postmodeliga#[Scope]atributi bilanchopEtilganlocal scope yozing (holat = 'chop'vachop_sana <= now). Uniget(),count()vapaginate()bilan chaqiring.- Parametr qabul qiladigan
holatBoyicha($q, $holat)scope yozing va uni turli holatlar bilan chaqiring. - Xuddi shu
chopEtilganscope'ni eskiscopeChopEtilgankonvensiyasida ham yozib ko'ring; chaqirilishi o'zgarmasligiga ishonch hosil qiling. FaolScopeglobal scope yozib, uni#[ScopedBy]bilan modelga biriktiring; oddiy so'rov avtomatik filtrlanishini,withoutGlobalScopeesa hammasini qaytarishini tekshiring.Attribute::make(get: ...)bilantoliqNomaccessor yozing (ikkita ustunni birlashtirib qaytaradigan, bazada ustuni yo'q xossa).narxuchun accessor + mutator yozing: bazada tiyinda saqlansin (narx_tiyin), xossada so'mda ko'rinsin va so'mda yozilsin.- Modelga
casts()qo'shing: bitta ustunniarray, bittasinidatetime, bittasinibooleanga cast qiling va har birini sinab ko'ring. Holatnomli string enum yarating (label()metodi bilan) va unicasts()daHolat::classsifatida cast qiling;$model->holat === Holat::Faolsolishtiruvini sinang.withCount('posts')bilan har foydalanuvchining post sonini oling; keyinwithCount(['posts as chop_posts_count' => ...])bilan faqat chop postlar sonini alohida ustun qiling.whereHas('posts', ...)bilan kamida bitta chop etilgan posti bor foydalanuvchilarni filtrlang;whereRelationbilan xuddi shuni qisqaroq yozing;doesntHave('posts')bilan posti yo'qlarni toping.chunkById(500, ...)bilan eski postlarni bo'lib-bo'libarxivlandi = trueqilib yangilang; nega oddiychunkemas,chunkByIdkerakligini komment bilan izohlang.