2016-03-10 13 views
6

È possibile risolvere un'entità di destinazione su più gestori di entità?Risolvi entità di destinazione con più gestori di entità

ho una persona di classe (in un fascio riutilizzabile):

/** 
* 
* @ORM\Entity 
* @ORM\Table(name="my_vendor_person") 
*/ 
class Person 
{ 
    /** 
    * Unique Id 
    * 
    * @var integer $id 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    protected $id; 

    /** 
    * First Name 
    * 
    * @var string $name 
    * 
    * @ORM\Column(name="first_name", type="string", length=32) 
    */ 
    protected $firstName; 
    // etc... 

e un utente di classe (nella mia applicazione principale):

/** 
* @ORM\Entity 
* 
* @ORM\Table(name="my_financial_user") 
* 
*/ 
class User extends BaseUser 
{ 

    /** 
    * @ORM\OneToOne(targetEntity="My\FinancialBundle\Model\PersonInterface") 
    * @var PersonInterface 
    */ 
    protected $person; 

Fondamentalmente voglio paio all'utente a una persona dal pacchetto riutilizzabile.

avevo impostato opzione entità di destinazione determinazione nella configurazione di dottrina che ho pensato che mi permette di fare questo:

doctrine: 
    orm: 
     auto_generate_proxy_classes: "%kernel.debug%" 
     default_entity_manager: default 
     resolve_target_entities: 
      My\FinanceBundle\Model\PersonInterface: My\VendorBundle\Entity\Person 
     entity_managers: 
      default: 
       naming_strategy: doctrine.orm.naming_strategy.underscore 
       connection: default 
       mappings: 
        MyFinanceBundle: ~ 
      second: 
       naming_strategy: doctrine.orm.naming_strategy.underscore 
       auto_mapping: false 
       connection: second 
       mappings: 
        MyVendorBundle: ~ 
        MyVendorUserBundle: ~ 

Inoltre, la classe utente nel pacchetto principale, si estende un utente di base nel fascio fornitore. La classe utente, ovviamente, viene mantenuta, nelle applicazioni principali db.

Con questa configurazione mi dà un errore.

[Doctrine\Common\Persistence\Mapping\MappingException]                      
The class 'My\VendorBundle\Entity\Person' was not found in the chain configured namespaces My\FinanceBundle\Entity, FOS\UserBundle\Model 

Qualcuno sa come risolvere questo?

+1

Vedere queste domande correlate: http://stackoverflow.com/questions/14403863/entities-associations-across-different-managers e http://stackoverflow.com/questions/9330018/issues-in-entities-from- diversi-bundle-using-different-entity-managers – takeit

+1

No. I gestori di entità non parlano tra loro. Dovrai trovare un modo per includere tutte le entità necessarie in un singolo gestore di entità. – Cerad

+0

che è sfortunato. Penso che questo sia un caso legittimo per gli utenti, speriamo che il supporto ci sia in futuro .. grazie sia per il tuo commento.per ora quello che farò è assegnare dinamicamente la persona all'utente nei controller dove è necessario ($ user = $ this-> getUser(); $ user-> person = $ person) – apfz

risposta

3

Come menzionato nel commento, i gestori delle entità non parlano tra loro, quindi è necessario aggirarli. Un modo è utilizzare un listener di dottrina e inserire un callback che può essere richiamato dal metodo getPerson.

AppBundle \ Doctrine \ InsertPersonListener

use Doctrine\Common\Persistence\Event\LifecycleEventArgs; 
use My\FinancialBundle\Entity\Person; 
use My\VendorBundle\Entity\User; 
use Symfony\Bridge\Doctrine\RegistryInterface; 

class InsertPersonListsner 
{ 
    /** 
    * @var RegistryInterface 
    */ 
    private $registry; 

    /** 
    * Insert the registry into the listener rather than the entity manager 
    * to avoid a cyclical dependency issue 
    * 
    * @param RegistryInterface $registry 
    */ 
    public function __construct(RegistryInterface $registry) 
    { 
     $this->registry = $registry; 
    } 

    /** 
    * On postLoad insert person callback 
    * 
    * @var LifecycleEventArgs $args 
    */ 
    public function postLoad(LifecycleEventArgs $args) 
    { 
     $user = $args->getObject(); 

     // If not a user entity ignore 
     if (!$user instanceof User) { 
      return; 
     } 

     $reflectionClass = new \ReflectionClass(User::class); 

     $property = $reflectionClass->getProperty('personId'); 
     $property->setAccessible(true); 

     // if personId is not set ignore 
     if (null === $personId = $property->getValue($user)) { 
      return; 
     } 

     // get the repository for your person class 
     // - changed to your version from the comments 
     $repository = $this->registry 
      ->getManagerForClass(Person::class) 
      ->getRepository('MyVendorBundle:Person'); 

     // set the value as a callback rather than the entity 
     // so it's not called unless necessary 
     $property = $reflectionClass->getProperty('personCallback'); 
     $property->setAccessible(true); 
     $property->setValue($user, function() use ($repository, $personId) { 
      return $repository->find($personId); 
     }); 
    } 
} 

mio \ VendorBundle \ Entity \ User

use My\FinancialBundle\Entity\Person; 

class User 
{ 
    //.. 

    /** 
    * @var Person 
    */ 
    private $person; 

    /** 
    * @var integer 
    */ 
    private personId; 

    /** 
    * @var callable 
    */ 
    private $personCallback; 

    /** 
    * Set person 
    * 
    * @param Person|null $person 
    * @return $this 
    */ 
    public function setPerson(Person $person = null) 
    { 
     $this->person = $person; 

     // if the person is null reset the personId and 
     // callback so that it's not called again 
     if (null === $person) { 
      $this->personId = null; 
      $this->personCallback = null; 
     } else { 
      // set the personId to be stored on the db for the next postLoad event 
      $this->personId = $person->getId(); 
     } 

     return null; 
    } 

    /** 
    * Get person 
    * 
    * @return Person|null 
    */ 
    public function getPerson() 
    { 
     // if the person has not been set and the callback is callable, 
     // call the function and set the result (of $repository->find($personId)) 
     // to the person property and then return it. The callback is 
     // reset to stop unnecessary repository calls in case of a null response 
     if (null === $this->person && is_callable($this->personCallback)) { 
      $this->person = call_user_func($this->personCallback); 
      $this->personCallback = null; 
     } 

     return $this->person; 
    } 
} 

AppBundle \ Resources \ services.yml

app.listener.insert_person: 
    class: AppBundle\Doctrine\InsertPersonListener 
    arguments: 
     - '@doctrine' 
    tags: 
     - { name: doctrine.event_listener, event: postLoad } 

UPDATE

Dopo aver guardato per un esempio di questo nella vita reale ho trovato this. Da questo ho visto che è possibile modificare il

$reflectionClass = new \ReflectionClass(User::class) 

a

$reflectionClass = $args->getObjectManager() 
    ->getClassMetadata(User::class) 
    ->reflClass 

che, presumo, vorrebbe dire uno in meno new \ReflectionClass(...) chiamata al carico.

+0

questo è veramente buono! non solo una risposta pratica, ma ha imparato molto. grazie per aver postato questo Spero che questo aiuti gli altri che affrontano lo stesso problema. – apfz

+0

Presumo in postLoad, $ proprietà-> getValue ($ utente), dovrebbe essere $ proprietà-> getValue ($ entità), in quanto $ utente non è definito. anche $ args-> getEntity(), potrebbe essere $ args-> getObject() dato che getEntity è deprecato. – apfz

+0

Il mio male. Per qualche ragione ho deciso di scriverlo direttamente nella casella di testo piuttosto che usare un IDE che avrebbe reso tutti questi veramente ovvi. Ho anche ribattezzato '$ entity' in' $ user', quindi è più esplicito e ha aggiunto il file di configurazione 'services.yml' che ho dimenticato. – qooplmao

Problemi correlati