2012-11-12 18 views
26

Per un progetto Symfony2 ho dovuto creare una relazione tra un post di blog e le cosiddette piattaforme. Una piattaforma definisce un filtro specifico basato sul dominio che si usa per visualizzare il sito. Ad esempio: se ti unisci al sito per url first-example.com, il sito fornirà solo post di blog, che sono collegati a questa piattaforma specifica.Utilizzo di EntityRepository :: findBy() con relazioni Many-To-Many porterà a un E_NOTICE in Doctrine

Per fare ciò, ho creato due entità Post e piattaforma. Successivamente li ho mappati insieme con una relazione Many-To-Many. Sto cercando di recuperare i dati tramite questa relazione Molti-A-Molti dalla funzione integrata findBy() in Doctrines 'EntityRepository.

// every one of these methods will throw the same error 
$posts = $postRepo->findBy(array('platforms' => array($platform))); 
$posts = $postRepo->findByPlatforms($platform); 
$posts = $postRepo->findByPlatforms(array($platform)); 

Dove $postRepo è il repository corretto per il Post entità e $platform un Platform oggetto esistente.
In entrambi i casi: finisco ottenendo il seguente errore:

ErrorException: Notice: Undefined index: joinColumns in [...]/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 1495 

[...]/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php:1495 
[...]/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php:1452 
[...]/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php:1525 
[...]/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php:1018 
[...]/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php:842 
[...]/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php:157 
[...]/src/Foobar/BlogBundle/Tests/ORM/PostTest.php:102 

E 'anche possibile recuperare entités correlati in un molti-a-molti in questo modo, o sono costretto a scrivere queste funzioni da solo? La cosa strana è: Doctrine non genera alcun errore come: "Non è possibile"., Ma uno interno E_NOTICE. Ecco perché credo che dovrebbe essere possibile, ma mi mancano alcuni punti qui.

Spogliato verso parti interessanti, le due Entità sono simili a questa.

<?php 

namespace Foobar\CommunityBundle\Entity; 

use Doctrine\Common\Collections\ArrayCollection; 
use Doctrine\ORM\Mapping as ORM; 

// [...] other namespace stuff 

/** 
* @ORM\Entity(repositoryClass="Foobar\CommunityBundle\Entity\Repository\PlatformRepository") 
* @ORM\Table(name="platforms") 
*/ 
class Platform 
{ 
    /** 
    * @ORM\Id 
    * @ORM\Column(type="integer") 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    protected $id; 

    // [...] other field stuff 
} 
<?php 

namespace Foobar\BlogBundle\Entity; 

use Doctrine\Common\Collections\ArrayCollection; 
use Doctrine\ORM\Mapping as ORM; 

// [...] other namespace stuff 

/** 
* @ORM\Entity(repositoryClass="Foobar\BlogBundle\Entity\Repository\PostRepository") 
* @ORM\Table(name="posts") 
*/ 
class Post implements Likeable, Commentable, Taggable, PlatformAware 
{ 
    /** 
    * @ORM\Id 
    * @ORM\Column(type="integer") 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    protected $id; 

    /** 
    * @ORM\ManyToMany(targetEntity="Foobar\CommunityBundle\Entity\Platform", cascade={"persist"}) 
    * @ORM\JoinTable(name="map_post_platform", 
    *  joinColumns={@ORM\JoinColumn(name="post_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="platform_id", referencedColumnName="id")} 
    *  ) 
    */ 
    protected $platforms; 

    // [...] other fields 

    /** 
    * Constructor 
    */ 
    public function __construct() 
    { 
     // [...] 
     $this->platforms = new ArrayCollection(); 
    } 
} 

E, naturalmente, il file composer.json (così ridotta alle linee interessate)

{ 
    [...] 
    "require": { 
     "php": ">=5.3.3", 
     "symfony/symfony": "2.1.*", 
     "doctrine/orm": ">=2.2.3,<2.4-dev", 
     "doctrine/doctrine-bundle": "1.0.*", 
     "doctrine/doctrine-fixtures-bundle": "dev-master", 
     [...] 

    }, 
    [...] 
} 

risposta

19

E 'molto possibile, ma lo stock Dottrina Repository non funziona in questo modo .

avete due opzioni, a seconda del contesto:

Scrivi un metodo personalizzato nel repository.

class PostRepository extends EntityRepository 
{ 
    public function getPosts($id) 
    { 
    $qb = $this->createQueryBuilder('p'); 
    $qb->join('p.platform', 'f') 
     ->where($qb->expr()->eq('f.id', $id)); 
    return $qb; 
    } 
} 

Oppure utilizzare i metodi getter predefiniti nell'oggetto piattaforma.

$posts = $platform->getPosts(); 

You "messo a nudo giù alle parti interessanti" in modo che non è evidente se si dispone di questo metodo, ma è normalmente fatto su

app/console doctrine:generate:entities 
+0

Grazie per la risposta. Solo una piccola aggiunta. Il tuo primo metodo è scrivere una propria funzione in un repository personalizzato. Forse mi sono espresso poco chiaro ma ho provato a recuperare le entità correlate tramite la funzione incorporata 'find *()' di Doctrine. Il secondo metodo non funziona perché ho un'associazione unidirezionale. Pertanto non esiste la proprietà '$ posts' su' Platform' e, a causa di ciò, nessun getter e setter. Tuttavia la tua risposta mi aiuta molto, dato che ora sono più sicuro che non è possibile usare solo i metodi incorporati per ottenere le associazioni molti-a-molti filtrate. – devsheeep

1

Questa domanda sembra un problema con un rapporto ManyToMany cui si voglio BIDIREZIONALE (ed è ora UNIDIRECTRIONAL). Utilizzare mappedBy per creare bidirezionalità:

http://doctrine-orm.readthedocs.org/en/latest/reference/association-mapping.html#many-to-many-bidirectional

pratiche:

Una delle vostre entità è possedere LATO, l'altro INVERSO SIDE. Nell'entità di esempio Post chiamata è il proprietario e l'entità denominata Piattaforma è il lato inverso.

POSSEDERE configurazione SIDE:

Class Post { 
    ...  
    /** 
    * @ManyToMany(targetEntity="Platform") 
    * @JoinTable(name="map_post_platform", 
    *  joinColumns={@JoinColumn(name="post_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@JoinColumn(name="platform_id", referencedColumnName="id", unique=true)}) 
    **/ 
    protected $platforms; 
    ... 
    public function Post() { 
     $this->platforms= new ArrayCollection(); 
    } 
    ... 
    public function assignToPlatform($platform) { 
     $this->platforms[] = $platform; 
    } 
    ... 
    public function getPlatforms() { 
     return $this->platforms; 
    } 
} 

INVERSA Setup SIDE:

Class Platform { 
    ... 
    /** 
    * @ManyToMany(targetEntity="Post", mappedBy="platforms") 
    **/ 
    protected $posts; 
    ... 
    public function Platform() { 
     $this->posts= new ArrayCollection(); 
    } 
    ... 
    public function getPosts() 
    { 
     return $this->posts; 
    } 
} 

ESEMPIO recuperato un array di entità, partendo da uno dei lati:

$post->getPlatforms(); 
$platform->getPosts(); 
+0

questa è la soluzione giusta per questo scopo. –

28

altro modo, forse un po 'OO/pulitore senza utilizzare ID:

public function getPosts(Platform $platform) 
{ 
    $qb = $this->createQueryBuilder("p") 
     ->where(':platform MEMBER OF p.platforms') 
     ->setParameters(array('platform' => $platform)) 
    ; 
    return $qb->getQuery()->getResult(); 
} 

Un nome migliore metodo sarebbe findPostsByPlatform

+1

Fantastico, non l'ho visto nei documenti di Doctrine. Che dire delle prestazioni? – Nevertheless

+2

Se ti interessano le prestazioni, non utilizzare QueryBuilder, né un ORM. – jhvaras

+1

Sono d'accordo sul fatto che il tuo nome sia più descrittivo, ma preferisco evitare di usare "trova" nei nomi personalizzati a causa delle funzioni magiche di Doctrine che iniziano con "trova". getPostsByPlatform(). – Lighthart

Problemi correlati