Sari la conținut
← Laravel pentru aplicații web
Progres curs 0 / 7 lecții (0%)

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

Ț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 →

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.