Для проекта Symfony2 мне пришлось создать связь между сообщением в блоге и так называемыми платформами. Платформа определяет определенный фильтр на основе домена, который вы используете для просмотра сайта. Например: если вы присоединитесь к сайту по url first-example.com, сайт будет предоставлять только сообщения в блогах, которые связаны с этой конкретной платформой.
Для этого я создал два объекта Post и Platform. Впоследствии я сопоставил их вместе со многими отношениями «многие-ко-многим». Я пытаюсь получить данные через это отношение «Многие ко многим» из встроенной функции findBy()
в EntityRepository
Doctrines.
// 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
– это правильный репозиторий для объекта Post
и $platform
– существующий объект Platform
.
В любом случае: я получаю следующую ошибку:
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
Возможно ли вообще получить связанные связи в отношениях «многие-ко-многим», или я вынужден самостоятельно написать эти функции? Странная вещь: Doctrine не будет делать никаких ошибок, таких как: «Это невозможно», но внутренний E_NOTICE
. Вот почему я палач, чтобы думать, что это должно быть возможно, но я упускаю некоторые моменты здесь.
Разделенные на интересные части, эти Сущности выглядят так.
<?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(); } }
И, конечно же, файл composer.json (также разделенный на соответствующие строки)
{ [...] "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", [...] }, [...] }
Это очень возможно, но хранилище Doctrine 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; } }
Или используйте методы getter по умолчанию в объекте платформы.
$posts = $platform->getPosts();
Вы «разделились до интересных частей», поэтому это не очевидно, если у вас есть этот метод, но он обычно делается на
app/console doctrine:generate:entities
Другой способ, возможно, немного OO / очиститель без использования идентификаторов:
public function getPosts(Platform $platform) { $qb = $this->createQueryBuilder("p") ->where(':platform MEMBER OF p.platforms') ->setParameters(array('platform' => $platform)) ; return $qb->getQuery()->getResult(); }
Лучшим именем метода будет findPostsByPlatform
Этот вопрос кажется проблемой с отношениями ManyToMany, которые вы хотите получить BIDIRECTIONAL (и теперь UNIDIRECTRIONAL). Используйте MappedBy для создания двунаправленной:
Практически:
Одна из ваших образований – ОСНОВНАЯ СТОРОНА, другая ОБРАТНАЯ СТОРОНА. В вашем примере объект с именем Post является стороной-владельцем, а объект с именем Platform – обратная сторона.
Настройка OWNING 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; } }
ОБРАТНАЯ СВЯЗЬ Настройка:
Class Platform { ... /** * @ManyToMany(targetEntity="Post", mappedBy="platforms") **/ protected $posts; ... public function Platform() { $this->posts= new ArrayCollection(); } ... public function getPosts() { return $this->posts; } }
ПРИМЕР получения массива объектов, начиная с одной из сторон:
$post->getPlatforms(); $platform->getPosts();