2015-01-04 22 views
7

Io uso symfony 2.3 e php dottrina 2.logica di business separazione da PHP Doctrine 2

Il programma ha i seguenti modelli:

  • entità Order - un tipico ordine del cliente
  • entità BadOrderEntry (campi : id, ordine - relazione one-to-one unidirezionale con Order, createdAt)
  • factory BadOrderEntryFactory per l'entità di creazione BadOrderEntry
  • repository BadOrderEntryReposi tory per i metodi di ricerca di soggetti BadOrderEntry
  • responsabile BadOrderEntryManager per salvare/modificare/cancellare i metodi di soggetti BadOrderEntry
  • E PRINCIPALI CLASSE BadOrderList - elenco degli ordini cattivi, il codice di questa classe:

    private $factory; 
    private $repository; 
    private $manager; 
    
    public function __construct(
        BadOrderEntryFactory $f, 
        BadOrderEntryRepository $r, 
        BadOrderEntryManager $m 
    ) { 
        $this->factory = $f; 
        $this->repository = $r; 
        $this->manager = $m; 
    } 
    
    public function has(Order $order) 
    { 
        return $this->repository->existsByOrder($order); 
    } 
    
    public function add(Order $order) 
    { 
        if (! $this->has($order)) { 
         $entry = $this->factory->create($order); 
         $this->manager->save($entry); 
        } 
    } 
    
    public function remove(Order $order) 
    { 
        $entry = $this->repository->findOneByOrder($order); 
        if ($entry !== null) { 
         $this->manager->delete($entry); 
        } 
    } 
    

Mi piace molto il design di questa classe. Ci ho pensato molto. Tutto è meraviglioso. MA! C'è un problema: le operazioni nei metodi add e remove devono essere eseguite nelle transazioni.

Codice transazione in PHP Docrine 2 si presenta così:

<?php 
$em->getConnection()->beginTransaction(); 
try { 
    //... do some work 
    $em->getConnection()->commit(); 
} catch (Exception $e) { 
    $em->getConnection()->rollback(); 
    throw $e; 
} 

Ma come posso chiamare questo codice all'interno BadOrderList?

Ho dedicato molto tempo e rimosso a seconda del database (e di conseguenza di PHP Doctrine 2), e di nuovo per crearlo? Ora la dipendenza è nascosta nelle classi BadOrderEntryRepository e BadOrderEntryManager.

Come nascondere la dipendenza dal meccanismo di transazione nella classe BadOrderList?

+0

aggiungere la gestione delle transazioni per il vostro 'Gestione :: add' e' delete'I suggerire anche di ripensare il vostro design. Non è davvero bello. Rendi il tuo modello persistente indipendente. – Ziumin

+0

@Ziumin Come posso aggiungere la gestione delle transazioni a Manager :: add (o delete)? Quali problemi di progettazione? Manager è semplicemente un ulteriore livello di astrazione su doctrine object manager. Non è male e non va bene. Ma dà più controllo. – stalxed

+0

Puoi farlo nello stesso modo in cui hai citato nel tuo esempio. http://doctrine-orm.readthedocs.org/en/latest/reference/transactions-and-concurrency.html#approach-2-explicitly. Per quanto riguarda i problemi di progettazione, perché pensi che la tua lista sia l'oggetto principale. Principale per quale parte della tua architettura? Hai pensato a metodi e nomi di classe? Puoi testare tutto il tuo modello senza la dottrina? – Ziumin

risposta

4

Dopo la nostra discussione ho una risposta alla tua domanda. La domanda non è davvero "Come nascondere la dipendenza dal meccanismo di transazione nella classe BadOrderList?", ma Come disaccoppiare un modello da un livello di persistenza? (Doctrine2 in quel caso particolare).

ho cercato di illustrare il mio suggerimento con un po 'di codice

class BadOrderEntry 
// Bad - is too bad word to describe an order here. Why is it bad? Is it Declined? Cancelled? 
{ 
    private $order; 
    // some code 
} 
class BadOrderEntryFactory 
{ 
    // If there is not to much processing to build BadOrderEntry better use factory method like BadOrderEntry::fromOrder($order); 
} 
class BadOrderEntryRepository 
{ 
    // here is some read model 
} 
class BadOrderEntryManager 
// ITS a part of our model and shouldn't be coupled to ORM 
{ 
    public function save(BadEntry $be) 
    { 
    // some model events, model actions 
    $this->doSave($be); // here we should hide our storage manipulation 
    } 

    protected function doSave($be) // it can be abstract, but may contain some basic storage actions 
    { 
    } 

    // similar code for delete/remove and other model code 
} 
class ORMBadOrderEntryManager extends BadOrderEntryManager 
// IT'S NOT the part of your model. There is no business logic. There is only persistent logic and transaction manipulation 
{ 
    protected $entityManager; 

    // some constructor to inject doctrine entitymanager 

    protected doSave($be) 
    { 
    $em = $this->entityManager; 
    $em->getConnection()->beginTransaction(); // suspend auto-commit 
    try { 
     $em->persist($be); 
     $em->flush(); 
     $em->getConnection()->commit(); 
    } catch (Exception $e) { 
     $em->getConnection()->rollback(); 
     throw $e; 
    } 
    } 
} 
// You can also implement ODMBadOrderEntryManager, MemcacheBadOrderEntryManager etc. 

Quindi, se si parla di struttura di directory, tutto il vostro modello può essere spostato fuori fagotto e utilizzato ovunque. La vostra struttura Bundle sarà come:

BadEntryBundle 
| 
+ Entity 
| | 
| --- BadOrderEntryEntity.php 
| 
+ ORM 
| | 
| --- ORMBadOrderEntryManager.php 

E poi ti basta iniettare ORMBadOrderEntryManager al vostro BadOrderEntryList

+0

Questa è davvero una soluzione interessante! Ho visto una soluzione simile in JMSPaymentCoreBundle. Ma non ci ho pensato ... hai aperto gli occhi! Grazie mille! – stalxed

1

È possibile trasformare la classe come un servizio e chiamarla come si desidera dopo aver iniettato il contenitore dei servizi all'interno della classe. è possibile trovare ulteriori informazioni qui circa dependency injection:

$injectedContainerOfService->get("id_of_your_service") 
+0

Thx. È una soluzione molto semplice ed ovvia. Ma non pratico/verificabile/manutenibile. – Ziumin

+0

è praticamente testabile ma per questo dovresti disaccoppiare il controller e trasformarlo come servizio se vuoi testarlo – lsroudi

+0

dove vedi un controller? – Ziumin