2015-08-06 5 views
6

Sto usando symfony 2 e ho una domanda sulla separazione del codice. Mi piacerebbe essere sicuro di capire correttamente quali elementi dovrebbero essere presenti in un controller, cosa in un servizio e cosa in un'entità.Separazione del codice in symfony 2 - Controller vs Service vs entity

Immaginiamo di avere un elenco di documenti che devo visualizzare. Su ciascun documento prima della visualizzazione, devo anche eseguire alcune operazioni logiche (ad esempio aggiungere due variabili).

Come nel comprendere la classe di entità si occupa solo del recupero dei dati e del funzionamento su singola entità. Non dovrei inserire alcun codice personalizzato. Come ho capito, questo dovrebbe essere fatto da un servizio.

Ma devo:

  • utilizzare un servizio per passare al controller elenco dei documenti sulla base di alcuni
    criteri dopo aver eseguito la logica necessaria,
  • o utilizzare un controller per scaricare l'elenco dei documenti, e di passare il documento al servizio per eseguire qualche logica?

Preferisco pensare che il primo approccio sia appropriato per mantenere il controller sottile (controller sottili, modelli grandi) ma questo approccio è giusto? Quale codice deve essere nell'entità, cosa nel controller e cosa in un servizio?

In particolare, dove dovrei relazionarmi con il gestore di entità - in un controller o piuttosto in servizio?

Supponiamo inoltre che in molti punti della mia app sia necessario verificare se il documento è stato finalizzato prima di consentire all'utente di eseguire qualsiasi azione (ad esempio modificarlo). Questo sicuramente dovrebbe essere o in un servizio, in quanto sarebbe necessario un altro servizio per controllarlo. Dovrei comunque caricare l'oggetto entità documento nel controller, inviarlo al servizio per verificare se può essere finalizzato o piuttosto caricare il documento in servizio e eseguire un controllo?

+1

La filosofia di Symfony2 è: controller sottili, servizi grassi. Si fa riferimento a Entity Manager nella classe Repository entità. Se si desidera modificare i dati con un servizio, si passa al servizio di repository. –

+0

Quindi, principalmente per ciascuna entità, dovrei creare un servizio ("classe repository di entità") per gestire tutte le richieste. Sto comprendendo questo correttamente? – Abdel5

risposta

9

mia architettura Symfony 2 è (con ORM Doctrine):

  1. controllori sottili con solo la logica di routing
  2. Un servizio (anche noto come"Manager") per ciascuna entità (tutta la business logic è qui)
  3. Servizi personalizzati per le altre mie esigenze (ad esempio, per l'utilizzo di strumenti esterni come Amazon S3 o Mandrill Mailing System)
  4. Un repository per ogni entità (solo metodi leggere le entità dal DB)

Ogni azione all'interno di un controller chiama uno o più metodi dal gestore dell'entità; Cerco sempre di evitare di utilizzare direttamente "magici metodi" del respository a favore di metodi su misura: dentro l'azione, invece di chiamare

$this->getDoctrine()->getRepository(<entity>)->findBy(array('parent' => null)); 

creo questo metodo all'interno del repository:

public function findParents() 
{ 
    return $this->findBy(array('parent' => null)); 
} 

e dentro l'azione che uso:

$this->getDoctrine()->getRepository(<entity>)->findParents(); 

Naturalmente questo è un esempio semplice, ma funziona abbastanza bene con più complesse findBy o findOneBy query.

+0

Puoi spiegare di più su findParents() –

+2

Fine: ho ampliato il mio esempio :) –

+0

Grazie per la risposta. –

7

In Symfony2 è una logica di disaccoppiamento estremamente semplice che utilizza repository e servizi. Per esempio:

Un repository entità con il cercatore adizionale personalizzato

use Doctrine\ORM\EntityRepository; 

class MyEntityRepository extends EntityRepository 
{ 
    public function findAllWithX($parameter) 
    { 
     // your DQL. manipule own data. filters. 
    } 
} 

Un servizio di grasso per gestire la logica di business principale

// could be a listener 
class MyFatService 
{ 
    public function __construct(MyEntityRepository $mer, 
           AnotherRepository $aor, 
           MisteriousService $mis) 
    { 
     $this->mer = $mer; 
     $this->aor = $aor; 
     $this->mis = $mis; 
    } 

    public function someBigAction($paramX, $paramY) 
    { 
     $foo = $this->mer->findAllWithX($paramX); 
     $bar = $this->aor->findBy(....); 

     // manipule data. complex operations. 
     // call related services. 
     // manipule data related to many repositories 
    } 
} 

di definire i servizi:

services: 
    my_entity_repository: 
     class: AppBundle\Repository\MyEntityRepository 
     factory: [@doctrine, getRepository] 
     arguments: 
      - %entity.my_entity% 

    my_another_repository: 
     class: AppBundle\Repository\AnotherRepository 
     factory: [@doctrine, getRepository] 
     arguments: 
      - %entity.my_another_entity% 

    my_fat_service: 
     class: AppBundle\MyFatService 
     arguments: 
      - @my_entity_repository 
      - @my_another_repository 
      - @misterious_service 

Nella tua controller:

public function blaAction($x, $y) 
{ 
    // leave the heavy work to services. 
    // just handle request and send the response 
    $data = $this->get('my_fat_service') 
       ->someBigAction($x, $y); 

    return $this->render('template.twig', ['data' => $data]); 
} 

ps: mi dispiace per il mio inglese

1

Ma dovrei:

utilizzare un servizio per passare al controller elenco dei documenti sulla base di alcuni criteri dopo aver eseguito la logica necessaria, o utilizzare un controller per scaricare l'elenco dei documenti, e che passare il documento al servizio di eseguire qualche logica?

Piuttosto, secondo. Solo perché ti consente di capire cosa sta succedendo guardando il codice del tuo controller. Il modo in cui funzionano tutti i framework web deve mappare ogni richiesta all'azione del controller. Seguendo questa logica in attesa di una richiesta, vai al controller. Ti suggerirei di chiamare il servizio direttamente nel controller per eseguire una logica personalizzata sui dati. Ma se è necessario recuperare i dati dal database, è meglio implementarli nella classe del repository non in servizio.

Se è necessario riorganizzare i dati dopo il recupero, è forse necessario ripensare la struttura del database. È molto più facile e molto più conveniente avere la capacità di recuperare ciò di cui hai bisogno senza alcuna azione dopo il recupero.

Preferirei pensare che il primo approccio è opportuno mantenere del controller sottili (controllori sottili, grandi modelli), ma è questo approccio giusto? Quale codice dovrebbe essere nell'entità, cosa nel controller e cosa in un servizio ?

Per mantenere regolatore sottile è abbastanza per non mettere in essa interroga e altra logica difficile: come l'ordinamento, filtri, ecc

In particolare dove dovrei riguardare entity manager - in un controllore o piuttosto in servizio?

È anche abbastanza facile. Dovresti relazionarti con loro là dove è necessario. Non c'è niente di male se la tua logica non è complessa e l'unica necessità che hai prima di mostrare i risultati a un utente è di fare cose molto piccole (come l'impostazione di alcune proprietà virtuali sull'entità recuperata) è assolutamente ok per mettere questa logica in controller. Ma non dimenticare il refactoring del tuo codice con una complessità crescente. Se diventa un codice che dovrebbe essere applicato in posti diversi (codice ripetibile) - entra dal controller in servizio.