2012-11-12 10 views
26

Für ein Symfony2-Projekt musste ich eine Beziehung zwischen einem Blogpost und so genannten Plattformen erstellen. Eine Plattform definiert einen bestimmten Filter basierend auf der Domäne, die Sie zum Anzeigen der Site verwenden. Zum Beispiel: Wenn Sie die Website per URL first-example.com betreten, wird die Website nur Blog-Posts bereitstellen, die mit dieser spezifischen Plattform verbunden sind.Die Verwendung von EntityRepository :: findBy() mit Many-To-Many-Beziehungen führt zu einer E_NOTICE in Doctrine

Um dies zu tun, habe ich zwei Entitäten Post und Plattform erstellt. Danach habe ich sie zusammen mit einer Many-To-Many-Beziehung abgebildet. Ich versuche, Daten über diese Viele-zu-Viele-Beziehung von der eingebauten Funktion findBy() in Doctrines 'EntityRepository abrufen.

// 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)); 

Wo $postRepo das richtige Repository für die Post Einheit ist und $platform ein bestehendes Platform Objekt.
So oder so: ich folgende Fehlermeldung am Ende immer:

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 

Ist es sogar möglich, im Zusammenhang entites in einer Viele-zu-Viele-Beziehung abzurufen diese Weise oder bin ich von mir, diese Funktionen zu schreiben gezwungen? Das komische Ding ist: Doctrine wirft keinen Fehler wie: "Es ist nicht möglich.", Aber eine interne E_NOTICE. Deshalb denke ich, dass es möglich sein sollte, aber ich verpasse hier einige Punkte.

Abgerissen auf die interessanten Teile sehen die beiden Entitäten so aus.

<?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(); 
    } 
} 

Und natürlich die composer.json Datei (wie auch auf die entsprechenden Linien abgespeckte)

{ 
    [...] 
    "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", 
     [...] 

    }, 
    [...] 
} 

Antwort

19

Es ist durchaus möglich, aber die Aktie Lehre Repository funktioniert nicht auf diese Weise .

Sie haben zwei Möglichkeiten, je nach Kontext:

eine benutzerdefinierte Methode im Repository schreiben.

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; 
    } 
} 

Oder verwenden Sie die Standard-Getter-Methoden im Plattformobjekt.

$posts = $platform->getPosts(); 

You „abgespeckte auf die interessanten Teile“, so dass es nicht offensichtlich ist, wenn Sie diese Methode, aber es ist in der Regel gemacht auf

app/console doctrine:generate:entities 
+0

. Nur ein kleiner Zusatz. Ihre erste Methode schreibt eine eigene Funktion in einem benutzerdefinierten Repository. Ich habe mich vielleicht unklar ausgedrückt, aber ich habe versucht, die verwandten Entitäten durch die eingebaute 'find *()' Funktion von Doctrine zu holen. Die zweite Methode funktioniert nicht, da ich eine unidirektionale Verknüpfung habe. Daher gibt es auf 'Platform' keine Eigenschaft' $ posts' und daher auch keine Getter und Setter. Nichtsdestotrotz hilft mir Ihre Antwort sehr, da ich jetzt sicherer bin, dass es nicht möglich ist, die eingebauten Wege zu nutzen, um gefilterte Viele-zu-Viele-Assoziationen zu erhalten. – devsheeep

1

Diese Frage scheint ein Problem mit einer ManyToMany Beziehung, die Sie will BIDIREKTIONAL (und ist jetzt UNIDIRECTRIONAL). Verwenden Sie mappedBy Bidirektionalität zu erstellen:

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

Praktisch:

Einer Ihrer Einheiten ist SIDE BESITZEN, die andere INVERSE SIDE. In Ihrem Beispiel hat die Entität mit dem Namen Post eine Seite und die Entität mit dem Namen Platform ist eine inverse Seite.

BESITZEN SIDE Setup:

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; 
    } 
} 

INVERSE SIDE Setup:

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

Beispiel eine Anordnung von Einheiten Abrufen von einer der Seiten ausgehend:

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

das ist die richtige Lösung für diesen Zweck. –

28

Ein anderer Weg, vielleicht ein bisschen OO/Reiniger ohne IDs zu verwenden:

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

Eine bessere Methode Name wäre findPostsByPlatform

+1

Ehrfürchtig, habe es nicht in Doctrine docs gesehen. Was ist mit Leistung? – Nevertheless

+2

Wenn Sie Wert auf Leistung legen, verwenden Sie nicht den QueryBuilder, weder ein ORM. – jhvaras

+1

Ich stimme zu, dass Ihr Name beschreibender ist, aber ich bevorzuge die Verwendung von "Finden" in benutzerdefinierten Namen wegen der magischen Funktionen von Doctrine, die mit "finden" beginnen. getPostsByPlatform(). – Lighthart