Servicii pentru logica de business
Lectură estimată: 3 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.
Un serviciu (sau „action") e o clasă care conține un pas de logică de business, izolat de HTTP. „Publică un articol", „procesează o comandă", „calculează factura" — fiecare devine o clasă cu o metodă clară, ușor de testat și de reutilizat.
Anatomia unui serviciu
class PublicaArticol
{
public function __invoke(Articol $articol): Articol
{
$articol->update([
"publicat" => true,
"publicat_la" => now(),
]);
PublicareArticol::dispatch($articol); // eveniment
return $articol;
}
}Folosirea din controller
public function publica(Articol $articol, PublicaArticol $actiune)
{
$this->authorize("update", $articol);
$actiune($articol);
return back()->with("succes", "Articol publicat.");
}
// controllerul: autorizare + delegare + răspuns. Atât.Ce pui într-un serviciu
- Logica cu mai mulți pași care trebuie să se întâmple împreună.
- Reguli de business (ce înseamnă „a publica", ce validări suplimentare).
- Coordonarea între modele multiple.
Ce NU pui
- Validarea de intrare — aia e în Form Request.
- Formatarea răspunsului — aia e în controller sau resource.
- Interogări simple de citire — pot sta în model sau controller.
Testarea
public function test_publicarea_seteaza_data(): void
{
$articol = Articol::factory()->create(["publicat" => false]);
(new PublicaArticol)($articol);
$this->assertTrue($articol->fresh()->publicat);
$this->assertNotNull($articol->fresh()->publicat_la);
}Un serviciu cu mai mulți pași
class ProceseazaComanda { public function __construct(private Stoc $stoc, private Facturare $facturare) {} public function __invoke(Comanda $comanda): void { DB::transaction(function () use ($comanda) { $this->stoc->rezerva($comanda->linii); $comanda->update(["status" => "confirmata"]); $this->facturare->emite($comanda); }); ComandaConfirmata::dispatch($comanda); } }
Exercițiu
Extrage o acțiune cu 2-3 pași din proiectul tău într-o clasă serviciu invocabilă (__invoke). Scrie un test care verifică efectul ei, fără să treacă prin HTTP. Cheam-o din controller.
Greșeli frecvente
- Serviciul primește un Request și extrage date din el — cuplare de HTTP.
- Serviciul întoarce un răspuns HTTP (view/redirect) în loc de date.
- „Serviciu" care e doar un wrapper de o linie peste un apel de model.
- Nu wrappezi în tranzacție operațiile care trebuie să reușească împreună.
De reținut
- Serviciu/action = un pas de logică de business, fără HTTP.
- Primește modele și valori, întoarce modele și valori.
- Se testează direct, fără cerere HTTP.
- Coordonează modele multiple, adesea într-o tranzacție.
Pe scurt
Serviciile scot logica de business din controllere, o fac testabilă și reutilizabilă. Primesc date curate, produc rezultate, declanșează evenimente.
Serviciu cu actualizare condiționată și rezultat explicit
Pentru o operație de stare, o actualizare condiționată poate preveni suprascrierea unei schimbări concurente. Verifică numărul de rânduri afectate și întoarce un rezultat clar. Nu trimite notificarea înainte ca modificarea să fie confirmată. Exemplul izolează tranziția new→approved; autorizarea trebuie făcută înainte de apel sau inclusă într-un contract explicit al serviciului. Nu presupune că orice model primit aparține utilizatorului curent.
Exemplu de lucru
public function approve(int $id): void {
$changed = ServiceRequest::whereKey($id)
->where('status', 'new')
->update(['status' => 'approved', 'updated_at' => now()]);
if ($changed !== 1) {
throw new DomainException('Cererea nu mai poate fi aprobată.');
}
}
// Notificarea se coordonează separat, după confirmarea schimbării.Interpretarea rezultatului
Primul apel afectează un rând, iar al doilea nu mai găsește statusul new. Contractul alege aici să semnaleze repetarea ca imposibilitate; un alt produs poate alege idempotență cu succes fără schimbare. Definește comportamentul, nu îl lăsa accidental.
Exercițiu de consolidare
Scrie un test pentru două apeluri consecutive și verifică statusul final și numărul notificărilor.
Aplicație practică
Sarcina
Extrage calculul unei oferte într-un serviciu testabil.
Rezolvare orientativă și verificare
Serviciul primește date validate și returnează un rezultat; nu citește direct request-ul HTTP.
Greșeală de evitat
Dependințele globale fac logica greu de reutilizat.
Documentație și surse
- Documentația Laravel 11 — versiunea aplicației demonstrative — Laravel
- OWASP Top 10 — OWASP
Ț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.
249 lei
Plată unică, acces pe viață la toate cele 7 lecții.
Vezi ce conține cursul →