Ho un'entità Account
che ha una raccolta di entità Section
. Ogni entità Section
ha una raccolta di entità Element
(associazione OneToMany). Il mio problema è che invece di recuperare tutti gli elementi appartenenti a una sezione, voglio recuperare tutti gli elementi che appartengono a una sezione e sono associati a un account specifico. Di seguito è riportato il mio modello di database.Filtro sull'associazione molti a molti con Doctrine2
Così, quando prelevo un account, voglio essere in grado di scorrere le sezioni associate (questa parte non è un problema), e per ogni sezione, voglio scorrere gli elementi che sono associato all'account recuperato. In questo momento ho il seguente codice.
$repository = $this->objectManager->getRepository('MyModule\Entity\Account');
$account = $repository->find(1);
foreach ($account->getSections() as $section) {
foreach ($section->getElements() as $element) {
echo $element->getName() . PHP_EOL;
}
}
Il problema è che recupera tutti gli elementi appartenenti a una determinata sezione, indipendentemente dall'account a cui sono associati. L'SQL generato per il recupero degli elementi di una sezione è il seguente.
SELECT t0.id AS id1, t0.name AS name2, t0.section_id AS section_id3
FROM mydb.element t0
WHERE t0.section_id = ?
Quello che mi serve è qualcosa di simile al seguente (potrebbe essere qualsiasi altro approccio). È importante che il filtraggio avvenga con SQL.
SELECT e.id, e.name, e.section_id
FROM element AS e
INNER JOIN account_element AS ae ON (ae.element_id = e.id)
WHERE ae.account_id = ?
AND e.section_id = ?
so che posso scrivere un metodo getElementsBySection($accountId)
o simile in un repository personalizzato e utilizzare DQL. Se posso farlo e in qualche modo sovrascrivere il metodo getElements()
sull'entità Section
, sarebbe perfetto. Preferisco di gran lunga se ci fosse un modo per farlo attraverso le associazioni di associazioni o almeno usando i metodi getter esistenti. Idealmente, quando si utilizza un oggetto account, mi piacerebbe essere in grado di eseguire il ciclo come nel frammento di codice sopra in modo che il "vincolo dell'account" sia astratto quando si utilizza l'oggetto. Cioè, l'utente dell'oggetto non ha bisogno di chiamare getElementsByAccount()
o simili su un oggetto Section
, perché sembra meno intuitivo.
Ho esaminato l'oggetto Criteria
, ma per quanto mi ricordo, non può essere utilizzato per il filtro sulle associazioni.
Quindi, qual è il modo migliore per realizzare questo? È possibile senza "manualmente" assemblare l'entità Section
con elementi attraverso l'uso di query DQL? Il mio codice sorgente corrente (e abbreviato) può essere visto sotto. Grazie mille in anticipo!
/**
* @ORM\Entity
*/
class Account
{
/**
* @var int
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
protected $id;
/**
* @var string
* @ORM\Column(type="string", length=50, nullable=false)
*/
protected $name;
/**
* @var ArrayCollection
* @ORM\ManyToMany(targetEntity="MyModule\Entity\Section")
* @ORM\JoinTable(name="account_section",
* joinColumns={@ORM\JoinColumn(name="account_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="section_id", referencedColumnName="id")}
*)
*/
protected $sections;
public function __construct()
{
$this->sections = new ArrayCollection();
}
// Getters and setters
}
/**
* @ORM\Entity
*/
class Section
{
/**
* @var int
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @var string
* @ORM\Column(type="string", length=50, nullable=false)
*/
protected $name;
/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="MyModule\Entity\Element", mappedBy="section")
*/
protected $elements;
public function __construct()
{
$this->elements = new ArrayCollection();
}
// Getters and setters
}
/**
* @ORM\Entity
*/
class Element
{
/**
* @var int
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @var string
* @ORM\Column(type="string", length=50, nullable=false)
*/
protected $name;
/**
* @var Section
* @ORM\ManyToOne(targetEntity="MyModule\Entity\Section", inversedBy="elements")
* @ORM\JoinColumn(name="section_id", referencedColumnName="id")
*/
protected $section;
/**
* @var \MyModule\Entity\Account
* @ORM\ManyToMany(targetEntity="MyModule\Entity\Account")
* @ORM\JoinTable(name="account_element",
* joinColumns={@ORM\JoinColumn(name="element_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="account_id", referencedColumnName="id")}
*)
*/
protected $account;
// Getters and setters
}
La prima soluzione dovrebbe funzionare, ma non sembra così attraente perché il filtro viene eseguito in memoria e in SQL se ho capito bene. Con l'approccio del repository, vorrei "assemblare" manualmente gli oggetti 'Section', giusto? Cioè, impostando me stesso gli elementi invece di essere recuperati automaticamente tramite un'associazione. Forse [qualcosa di simile] (http://pastebin.com/CWAadsbB)? In tal caso, potrei anche rimuovere l'associazione sull'entità per garantire che le sezioni errate non vengano recuperate per errore se non si è assemblato l'oggetto prima di utilizzarlo. Buoni punti, a proposito! – Andy0708
Con la prima soluzione il db preleverà semplicemente le entità in base alle associazioni definite. Il filtro su uno specifico account verrà eseguito solo in PHP. Questo è il motivo per cui ti suggerisco di usare un repository, perché puoi permettere al db di recuperare solo quegli 'Element' che vuoi realmente (quindi il db fa il filtro). –
Ho aggiornato la mia risposta con una query di esempio. –