Sari la conținut
← Laravel + MySQL
Progres curs 0 / 6 lecții (0%)

Proiectarea schemei

Lectură estimată: 4 minute

Estimare pentru text, la un ritm de 180 de cuvinte pe minut. Timpul pentru analizarea codului, exerciții și proiecte se adaugă separat și depinde de ritmul tău.

Problema pe care o vei rezolva: Preț istoric și raport fără N+1

Produsul P costă acum 2.500 bani. Comanda veche conține 2 bucăți la 2.000 bani fiecare. Raportul listează 20 de comenzi și produsele lor.

Rezultatul urmărit

Modelează prețul unitar în poziția comenzii. Construiește o listare cu relații încărcate explicit, apoi măsoară numărul de interogări pentru 1 și 20 de comenzi.

Cum recunoști o soluție corectă

Comanda veche rămâne la 4.000 bani, indiferent de prețul actual. Numărul de interogări nu trebuie să crească cu fiecare rând din listare. Măsurarea se face pe aceeași structură de date.

Parcurge lecțiile în ordine și păstrează exercițiile în registrul descărcabil. Ultima lecție include atelierul de 40 de minute în care combini conceptele și testezi o variație a situației inițiale. Exemplele comerciale sunt fictive; nu reprezintă rezultate ale unor clienți.

O schemă de bază de date bine gândită îți economisește luni de suferință. Prost gândită, te obligă la workaround-uri la fiecare funcție nouă. Lecția asta e despre principiile de proiectare: ce e o entitate, cum alegi tipurile de coloane, ce înseamnă normalizare și când o încalci intenționat.

Entități și relații

Pornești de la substantivele din domeniu: „un magazin are produse, produsele aparțin unor categorii, clienții plasează comenzi, o comandă are mai multe produse". Fiecare substantiv „important" devine un tabel; verbele dintre ele devin relații.

Tipuri de coloane

  • Text scurt (nume, titlu, slug): `string` (VARCHAR, implicit 255).
  • Text lung (descriere, conținut): `text`.
  • Numere întregi: `integer`, `unsignedBigInteger` pentru chei străine.
  • Bani: `decimal(10, 2)` — niciodată `float` pentru sume.
  • Adevărat/fals: `boolean`.
  • Momente: `timestamp` sau `datetime`; `date` pentru doar dată.

Normalizare

Regula de bază: fiecare informație trăiește într-un singur loc. Nu pui numele categoriei în tabelul de produse — pui `category_id` și numele stă în tabelul de categorii. Așa, când redenumești o categorie, o faci o dată. Excepția (denormalizarea) e când performanța chiar cere să duplici ceva — dar o faci conștient, nu din lene.

Convenții Laravel

  • Nume de tabel la plural, snake_case: `produse`, `comenzi`.
  • Cheie primară `id`.
  • Cheie străină: `_id`, de ex. `categorie_id`.
  • Tabel pivot: numele celor două tabele la singular, alfabetic: `categorie_produs`.
  • Coloane `created_at` / `updated_at` (adăugate de `$table->timestamps()`).

Schema unui mini-blog

users:      id, name, email, password, timestamps
categorii:  id, nume, slug, timestamps
articole:   id, user_id (FK), categorie_id (FK), titlu, slug, continut, publicat, publicat_la, timestamps
comentarii: id, articol_id (FK), autor_nume, continut, timestamps

Exercițiu

Pentru un domeniu la alegere (o bibliotecă, un cabinet, un magazin), listează entitățile, relațiile dintre ele și, pentru fiecare entitate, coloanele cu tipul potrivit. Respectă convențiile de denumire Laravel.

Greșeli frecvente

  • Folosești float pentru bani și acumulezi erori de rotunjire.
  • Duplici informație (numele categoriei în fiecare produs) fără motiv.
  • Pui totul într-un singur tabel „date" gigant.
  • Ignori convențiile de denumire și apoi lupți cu Eloquent la fiecare relație.

De reținut

  • Entitățile = substantivele domeniului; relațiile = verbele.
  • Tipuri corecte: decimal pentru bani, text pentru conținut lung.
  • Normalizare: fiecare informație într-un singur loc.
  • Respectă convențiile Laravel de denumire.

Pe scurt

O schemă bună modelează domeniul curat: entități clare, tipuri corecte, informație nedublicată, denumiri convenționale.

Schema pornită de la relații și constrângeri

Proiectează tabelul după întrebările aplicației și invariante, nu numai după formularul curent. O cerere aparține unui utilizator, iar un utilizator poate avea mai multe cereri. Cheia străină protejează legătura, dar regula la ștergere trebuie aleasă deliberat. Nu șterge în cascadă date care trebuie păstrate fără să verifici politica produsului. Pentru laborator, compară restrict și nullability înainte de a decide.

Exemplu de lucru

users: id, email unic.
service_requests: id, user_id, service, status, created_at.
Migrare exemplu:
$table->foreignId('user_id')->constrained()->restrictOnDelete();
$table->index(['user_id', 'status']);
Întrebări: cine deține cererea; ce se întâmplă la ștergerea userului; cum căutăm cererile active?

Interpretarea rezultatului

Indexul compus urmărește o interogare concretă, nu se adaugă doar pentru că două câmpuri există. restrictOnDelete poate bloca ștergerea utilizatorului cu cereri; aplicația trebuie să trateze acest rezultat. Verifică motorul DB și constrângerile efective în laborator.

Exercițiu de consolidare

Propune o strategie de anonimizare pentru istoricul păstrat, fără a pretinde că simpla schimbare a emailului rezolvă toate obligațiile.

Aplicație practică

Sarcina

Proiectează tabelele orders și order_items pentru un magazin demonstrativ.

Rezolvare orientativă și verificare

Prețul unitar se salvează în poziția comenzii, în bani întregi, ca istoricul să nu depindă de prețul curent al produsului.

Greșeală de evitat

O comandă veche nu trebuie recalculată după modificarea catalogului.

Documentație și surse

Ți-a plăcut lecția? Continuă cursul complet

Ai citit o lecție gratuită. Celelalte lecții, aplicațiile practice și materialele se deblochează odată cu cursul.

149 lei

Plată unică, acces pe viață la toate cele 6 lecții.

Vezi ce conține cursul →

Folosim cookie-uri esențiale pentru funcționarea site-ului și, cu acordul tău, cookie-uri de analiză și publicitate. Partenerii noștri, inclusiv Google, pot folosi cookie-uri pentru a afișa reclame relevante. Detalii în Politica de cookie-uri.