2012-11-12 14 views
26

Đối với một dự án Symfony2, tôi phải tạo mối quan hệ giữa một bài đăng trên blog và các nền tảng được gọi là. Nền tảng xác định bộ lọc cụ thể dựa trên tên miền bạn sử dụng để xem trang web. Ví dụ: Nếu bạn tham gia trang web theo url first-example.com, trang web sẽ chỉ cung cấp các bài đăng trên blog, được kết nối với nền tảng cụ thể này.Sử dụng EntityRepository :: findBy() với quan hệ Nhiều-Tới-Nhiều sẽ dẫn đến một E_NOTICE trong Doctrine

Để làm như vậy, tôi đã tạo hai thực thể Bài đăng và Nền tảng. Sau đó tôi lập bản đồ chúng cùng với mối quan hệ Nhiều-Nhiều. Tôi đang cố truy xuất dữ liệu qua mối quan hệ Nhiều-Nhiều này từ hàm dựng sẵn findBy() trong 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)); 

đâu $postRepo là Repository chính xác cho các Post thực thể và $platform một Platform đối tượng hiện có.
Dù bằng cách nào: Tôi kết thúc nhận được lỗi sau:

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 

Là nó thậm chí có thể lấy entites liên quan trong một Nhiều-To-Nhiều mối quan hệ theo cách này, hoặc là tôi buộc phải viết các chức năng này bởi bản thân mình? Điều kỳ lạ là: Doctrine sẽ không ném bất kỳ lỗi nào như: "Không thể.", Nhưng nội bộ E_NOTICE. Đó là lý do tại sao tôi nghĩ rằng nó sẽ là có thể, nhưng tôi đang thiếu một số điểm ở đây.

Tước xuống các phần thú vị, hai thực thể trông như thế này.

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

Và tất nhiên các tập tin composer.json (cũng lột xuống dòng liên quan)

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

    }, 
    [...] 
} 

Trả lời

19

Nó là rất tốt, nhưng thị trường chứng thuyết Repository không hoạt động theo cách này .

Bạn có hai tùy chọn, tùy thuộc vào ngữ cảnh của bạn:

Viết phương thức tùy chỉnh trong Kho lưu trữ.

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

Hoặc sử dụng các phương thức getter mặc định trong đối tượng nền tảng.

$posts = $platform->getPosts(); 

Bạn "lột xuống phần thú vị" vì vậy nó không phải là rõ ràng nếu bạn có phương pháp này nhưng nó thường được thực hiện trên

app/console doctrine:generate:entities 
+0

Cảm ơn bạn đã trả lời của bạn. Chỉ cần một chút bổ sung. Phương pháp đầu tiên của bạn là viết một hàm riêng trong một kho lưu trữ tùy chỉnh. Tôi có thể đã thể hiện bản thân mình không rõ ràng nhưng tôi đã cố gắng tìm nạp các thực thể liên quan thông qua hàm 'find *()' được xây dựng sẵn của Doctrine. Phương pháp thứ hai không hoạt động vì tôi có một liên kết đơn hướng. Do đó không có thuộc tính '$ posts' trên' Platform' và do đó, không có getter và setters. Tuy nhiên câu trả lời của bạn sẽ giúp tôi rất nhiều, vì bây giờ tôi chắc chắn hơn rằng không thể chỉ sử dụng các cách tích hợp để có được các liên kết Nhiều-Nhiều-Nhiều được lọc. – devsheeep

1

Câu hỏi này dường như là một vấn đề với một mối quan hệ ManyToMany mà bạn muốn BIDIRECTIONAL (và bây giờ là UNIDIRECTRIONAL). Sử dụng MappedBy để tạo bidirectionality:

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

thực tiễn:

Một trong các đơn vị của bạn SỞ HỮU SIDE, nghịch đảo phía bên kia. Trong thực thể ví dụ của bạn có tên là Đăng là chủ sở hữu, và thực thể có tên là Nền tảng là mặt nghịch đảo.

SỞ HỮU thiết lập 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; 
    } 
} 

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

VÍ DỤ lấy một mảng của các tổ chức, bắt đầu từ một trong hai bên:

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

đây là giải pháp phù hợp cho mục đích này. –

28

Một cách khác, có thể một chút OO/sạch hơn mà không cần sử dụng ID:

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

Một tên phương pháp tốt hơn sẽ là findPostsByPlatform

+1

Tuyệt vời, chưa từng thấy trong tài liệu Doctrine. Điều gì về hiệu suất? – Nevertheless

+2

Nếu bạn quan tâm đến hiệu suất thì không sử dụng QueryBuilder, không phải ORM. – jhvaras

+1

Tôi đồng ý rằng tên của bạn mang tính mô tả hơn, nhưng tôi muốn tránh sử dụng 'find' trong các tên tùy chỉnh vì các hàm giống như ma thuật của Doctrine bắt đầu bằng 'find'. getPostsByPlatform(). – Lighthart

Các vấn đề liên quan