Użycie EntityRepository:: findBy() z relacjami Many-to-Many doprowadzi do E NOTICE w doktrynie
Dla projektu Symfony2 musiałem stworzyć relację między wpisem na blogu a tzw. platformami. Platforma definiuje konkretny filtr na podstawie domeny używanej do wyświetlania witryny. Na przykład: jeśli dołączysz do witryny przez url first-example.com, witryna będzie dostarczać tylko posty na blogu, które są połączone z tą konkretną platformą.
W tym celu utworzyłem dwa podmioty Post i Platform. Potem zmapowałem je razem z relacją wielu do wielu.
Próbuję odzyskać dane. poprzez tę relację wielu do wielu z wbudowanej funkcji findBy()
w doktrynach ' 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));
Gdzie $postRepo
jest właściwym repozytorium dla Post
encji i $platform
istniejącego Platform
obiektu.
Tak czy inaczej: w końcu otrzymuję następujący błąd:
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
Czy jest w ogóle możliwe, aby odzyskać powiązane entity w relacji wiele do wielu w ten sposób, czy też jestem zmuszony do pisania tych funkcji przez siebie?
Dziwne jest to, że doktryna nie wyrzuci żadnego błędu w stylu: "to nie możliwe.", ale wewnętrzny E_NOTICE
. Dlatego uważam, że to powinno być możliwe, ale brakuje mi tu kilku punktów.
Rozebrane do interesujących części, dwa byty wyglądają tak.
<?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();
}
}
I oczywiście kompozytor.plik json (jak również rozebrany do odpowiednich linii)
{
[...]
"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",
[...]
},
[...]
}
3 answers
Jest to bardzo możliwe, ale repozytorium Stock Doctrine nie działa w ten sposób.
Masz dwie opcje, w zależności od kontekstu:
Napisz własną metodę w repozytorium.
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;
}
}
Lub użyj domyślnych metod getter w obiekcie platform.
$posts = $platform->getPosts();
Jesteś "rozebrany do interesujących części", więc nie jest oczywiste, jeśli masz tę metodę, ale zwykle jest ona wykonana na
app/console doctrine:generate:entities
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-24 19:19:57
Inny sposób, może trochę oo / cleaner bez użycia IDs:
public function getPosts(Platform $platform)
{
$qb = $this->createQueryBuilder("p")
->where(':platform MEMBER OF p.platforms')
->setParameters(array('platform' => $platform))
;
return $qb->getQuery()->getResult();
}
Lepsza nazwa metody to findPostsByPlatform
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-22 13:11:35
To pytanie wydaje się problemem z wieloma relacjami, które chcesz dwukierunkową (i jest teraz jednokierunkowa). Użyj MappedBy do utworzenia dwukierunkowości:
Praktyczny:
Jeden z Twoich Bytów jest właścicielem strony, druga strona odwrotna. W przykładzie encja o nazwie Post jest własnością strony, a encja o nazwie platforma jest odwrotna bok.
Ustawienie Strony:
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;
}
}
Odwrotna Konfiguracja strony:
Class Platform {
...
/**
* @ManyToMany(targetEntity="Post", mappedBy="platforms")
**/
protected $posts;
...
public function Platform() {
$this->posts= new ArrayCollection();
}
...
public function getPosts()
{
return $this->posts;
}
}
Przykład pobierania tablicy encji, zaczynając od jednej ze stron:
$post->getPlatforms();
$platform->getPosts();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-17 14:51:28