2012-11-12 3 views
26

Pour un projet Symfony2, j'ai dû créer une relation entre un article de blog et des plates-formes. Une plateforme définit un filtre spécifique basé sur le domaine que vous utilisez pour afficher le site. Par exemple: Si vous rejoignez le site par url first-example.com, le site ne fournira que des articles de blog, qui sont connectés à cette plateforme spécifique. Pour ce faire, j'ai créé deux entités Post et Plate-forme. Ensuite, je les ai cartographiés avec une relation de type Many-To-Many. J'essaie de récupérer des données via cette relation Many-To-Many à partir de la fonction intégrée findBy() dans Doctrines 'EntityRepository.L'utilisation de EntityRepository :: findBy() avec des relations many-to-many conduira à un E_NOTICE dans Doctrine

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

$postRepo est le bon référentiel pour l'entité Post et $platform un objet Platform existant.
De toute façon: je finis par obtenir l'erreur suivante:

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 

Est-il même possible de récupérer ENTITES liés dans un Many-to-many de cette façon, ou suis-je obligé d'écrire ces fonctions par moi-même? La chose étrange est: Doctrine ne jettera pas une erreur comme: "Ce n'est pas possible.", Mais un interne E_NOTICE. C'est pourquoi je tente de penser que cela devrait être possible, mais il me manque quelques points ici. Dénué en parties intéressantes, les deux Entités ressemblent à ceci:

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

Et bien sûr, le fichier composer.json (ainsi dépouillé aux lignes concernées)

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

    }, 
    [...] 
} 

Répondre

19

Il est très possible, mais le stock Doctrine du référentiel ne fonctionne pas de cette façon .

Vous avez deux options, selon votre contexte:

Ecrivez une méthode personnalisée dans le référentiel. Ou utilisez les méthodes getter par défaut dans l'objet platform.

$posts = $platform->getPosts(); 

Vous « Bx_boom les parties intéressantes » il est donc pas évident si vous avez cette méthode, mais il est normalement fait sur

app/console doctrine:generate:entities 
+0

Merci pour votre réponse. Juste un petit plus. Votre première méthode consiste à écrire une propre fonction dans un référentiel personnalisé. Je me suis peut-être mal exprimé, mais j'ai essayé d'aller chercher les entités apparentées grâce à la fonction 'find *()' de Doctrine. La deuxième méthode ne fonctionne pas parce que j'ai une association unidirectionnelle. Par conséquent, il n'y a pas de propriété '$ posts' sur' Platform' et, par conséquent, pas de getter et setters. Néanmoins, votre réponse m'aide beaucoup, car je suis maintenant plus sûr qu'il n'est pas possible d'utiliser simplement les méthodes intégrées pour obtenir des associations Many-To-Many filtrées. – devsheeep

1

Cette question semble un problème avec une relation de ManyToMany que vous voulez BIDIRECTIONAL (et est maintenant UNIDIRECTRIONAL). Utilisez mappedBy pour créer bidirectionnalité:

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

pratique:

L'un de vos entités est OWNING SIDE, l'autre côté INVERSE. Dans votre exemple, l'entité nommée Post est propriétaire et l'entité Plate-forme est l'inverse.

OWNING SIDE configuration:

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

Exemple récupérer un tableau d'entités, à partir de l'un des côtés:

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

c'est la bonne solution à cet effet. –

28

Une autre façon, peut-être un peu OO/nettoyeur sans utiliser d'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 meilleur nom de méthode serait findPostsByPlatform

+1

Génial, ne l'a pas vu dans doctrine docs. Qu'en est-il des performances? – Nevertheless

+2

Si vous vous souciez des performances, n'utilisez pas QueryBuilder, ni ORM. – jhvaras

+1

Je suis d'accord pour dire que votre nom est plus descriptif, mais je préfère éviter d'utiliser 'find' dans les noms personnalisés à cause des fonctions magiques de Doctrine qui commencent par 'find'. getPostsByPlatform(). – Lighthart

Questions connexes