[英]Doctrine2 in Symfony3 ManyToMany association query
你好,歡迎Stackoverflowers? 花卉?! 沒關系:)
一段時間以前,我開始密集學習Symfony3,到目前為止,您在這里提出的問題對我來說已經足夠了。 但是今天這一天到來了,我個人有一個問題。
那么讓我們勾勒出這種情況:
我正在為我的小型CMS開發一個簡單的標記功能。 我希望我的文章(也可能是后來的其他內容)包含我附加到它們的一些標簽。 為此,我使用Doctrine創建了一組兩個對象: Article & Tag (下面的一些代碼):
文章模型
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Article
*
* @ORM\Table(name="article")
* @ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
*/
class Article extends ContentItem
{
/**
* @var string
*
* @ORM\Column(name="content", type="text")
*/
private $content;
/**
*
* @ORM\ManyToOne(targetEntity="ContentTypeDict", inversedBy="articles")
* @ORM\JoinColumn(name="type_id", referencedColumnName="id", nullable=false)
*/
private $type;
/**
* @ORM\ManyToMany(targetEntity="Tag", inversedBy="articles")
* @ORM\JoinTable(name="tags_of_articles")
*/
protected $tags;
public function __construct()
{
$this->tags = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set type
*
* @param boolean $type
*
* @return Article
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Get type
*
* @return bool
*/
public function getType()
{
return $this->type;
}
/**
* Set content
*
* @param string $content
*
* @return Article
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* Add tag
*
* @param \AppBundle\Entity\tag $tag
*
* @return Article
*/
public function addTag(\AppBundle\Entity\tag $tag)
{
$this->tags[] = $tag;
return $this;
}
/**
* Remove tag
*
* @param \AppBundle\Entity\tag $tag
*/
public function removeTag(\AppBundle\Entity\tag $tag)
{
$this->tags->removeElement($tag);
}
/**
* Get tags
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
}
標簽模型
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Tag
*
* @ORM\Table(name="tag")
* @ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
*/
class Tag
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* @var int
*
* @ORM\Column(name="popularity", type="integer")
*/
private $popularity;
/**
*
* @ORM\ManyToMany(targetEntity="Article", mappedBy="tags")
*/
private $articles;
/**
*
* @ORM\ManyToMany(targetEntity="Asset", inversedBy="tags")
* @ORM\JoinTable(name="assets_tags")
*/
private $assets;
public function __construct()
{
$this->articles = new ArrayCollection();
$this->assets = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Tag
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set popularity
*
* @param integer $popularity
*
* @return Tag
*/
public function setPopularity($popularity)
{
$this->popularity = $popularity;
return $this;
}
/**
* Get popularity
*
* @return int
*/
public function getPopularity()
{
return $this->popularity;
}
/**
* Add content
*
* @param \AppBundle\Entity\ContentItem $content
*
* @return Tag
*/
public function addContent(\AppBundle\Entity\ContentItem $content)
{
$this->content[] = $content;
return $this;
}
/**
* Remove content
*
* @param \AppBundle\Entity\ContentItem $content
*/
public function removeContent(\AppBundle\Entity\ContentItem $content)
{
$this->content->removeElement($content);
}
/**
* Get content
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getContent()
{
return $this->content;
}
/**
* Add article
*
* @param \AppBundle\Entity\ContentItem $article
*
* @return Tag
*/
public function addArticle(\AppBundle\Entity\ContentItem $article)
{
$this->articles[] = $article;
return $this;
}
/**
* Remove article
*
* @param \AppBundle\Entity\ContentItem $article
*/
public function removeArticle(\AppBundle\Entity\ContentItem $article)
{
$this->articles->removeElement($article);
}
/**
* Get articles
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getArticles()
{
return $this->articles;
}
/**
* Add asset
*
* @param \AppBundle\Entity\ContentItem $asset
*
* @return Tag
*/
public function addAsset(\AppBundle\Entity\ContentItem $asset)
{
$this->assets[] = $asset;
return $this;
}
/**
* Remove asset
*
* @param \AppBundle\Entity\ContentItem $asset
*/
public function removeAsset(\AppBundle\Entity\ContentItem $asset)
{
$this->assets->removeElement($asset);
}
/**
* Get assets
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getAssets()
{
return $this->assets;
}
}
如您所見,這些對象與名為“ tags_of_articles”的表的ManyToMany關聯相關
現在有了這兩個對象,我希望能夠通過提供標簽或(可能稍后)一堆標簽來獲得文章。
為此,我在ArticleController中編寫了一個簡短的函數
/**
* @Route("/showArticlesByTag/{tag}")
*/
public function showArticlesByTag($tag)
{
$em = $this->getDoctrine()->getManager();
$tagRepo = $em->getRepository('AppBundle:Tag');
$tagData = $tagRepo->findOneBy(array('name' => $tag,));
$dataRepo = $em->getRepository('AppBundle:Article');
$data = $dataRepo->findOneBy(array('tags' => $tagData->getId(),));
var_dump($data);
//return $this->render('AppBundle:Article:show_article.html.twig', array( ));
}
而且,執行的結果,讓我們說
本地主機/〜用戶/ MyCMS /網絡/ app_dev.php / showArticlesByTag / Architecto
(標簽Architecto中存在標簽)
我得到一個例外:
執行'SELECT t0.content AS content_1,t0.id AS id_2,t0.title AS title_3,t0.description AS description_4,t0.enabled AS enabled_5,t0.type_id AS type_id_6 FROM article t0 WHERE tags_of_articles.tag_id = ? 限制1'與params [3]:
SQLSTATE [42S22]:找不到列:1054'where子句'中的未知列'tags_of_articles.tag_id'
在結尾處我附上了我在SQL中的表結構,來自doctrine:
- --------------------------------------------------------
--
-- Table structure for table `article`
--
CREATE TABLE IF NOT EXISTS `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type_id` int(11) NOT NULL,
`content` longtext COLLATE utf8_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`description` longtext COLLATE utf8_unicode_ci NOT NULL,
`enabled` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_23A0E66C54C8C93` (`type_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;
-- --------------------------------------------------------
--
-- Table structure for table `tag`
--
CREATE TABLE IF NOT EXISTS `tag` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`popularity` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_389B7835E237E06` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=5 ;
-- --------------------------------------------------------
--
-- Table structure for table `tags_of_articles`
--
CREATE TABLE IF NOT EXISTS `tags_of_articles` (
`tag_id` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
KEY `IDX_D429AC71BAD26311` (`tag_id`),
KEY `IDX_D429AC717294869C` (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
現在的問題是:我做錯了什么? 或許,我錯過了什么?
可能還有其他方法可以實現,但我會通過向findByTags
添加方法findByTags
來實現:
像這樣添加一個方法到你的ArticleRepository(你沒有自定義存儲庫?檢查: 自定義存儲庫類 ):
class ArticleRepository extends EntityRepository
{
// ...
public function findyByTags(array $tags)
{
$qb = $this->createQueryBuilder('a');
$qb->join('a.tags', 't');
for($i = 0; $i < count($tags); $i++) {
$qb->andWhere($qb->expr()->eq('t.name', ':tag' . $i));
$qb->setParameter('tag' . $i, $tags[$i]);
}
return $qb->getQuery()->getResult();
}
}
在您的控制器中,您可以像這樣使用它:
public function showArticlesByTag($tag)
{
$dataRepo = $this->getDoctrine()->getRepository('AppBundle:Article');
$data = $dataRepo->findByTags(array($tag));
// ...
}
如果將多個標記傳遞給此方法,則文章必須返回所有標記。 這段代碼可能有錯誤,因為我當然無法測試它。 但我希望它得到我的觀點。 ;-)
我似乎需要改變這條線
$data = $dataRepo->findOneBy(array('tags' => $tagData->getId(),));
如下:
$data = $dataRepo->findBy(array('tag' => $tagData));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.