Doctrine2 ORM 访问多对多连接表

Posted

技术标签:

【中文标题】Doctrine2 ORM 访问多对多连接表【英文标题】:Doctrine2 ORM Access ManyToMany Join Table 【发布时间】:2015-09-25 08:28:07 【问题描述】:

对我上一个问题的跟进

Doctrine2 ORM OneToOne not working UPDATE changed to ManyToMany but not fully working.

我接受了给出的非常有用的答案,因为它为我指明了正确的方向。不幸的是,我很难让其余的工作正常进行,但是由于问题太长且令人困惑,我开始了一个新问题。

我有一个用户可以在其中写广告的页面。用户还可以将希望稍后在其用户部分看到的广告添加为书签。

我有 3 个数据库表

advert (id, advert_title....)
user (id, user_name....)
bookmarks (advert_id, user_id)

按照我之前的问题中的建议,我创建了 2 个实体,广告和用户实体(见下文)

Advert.php

namespace Advert\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use DateTime;
use Zend\Stdlib\ArrayObject;

/** Advert
 * 
 * @ORM\Table(name="advert")
 * @ORM\Entity(repositoryClass="Advert\Repository\AdvertRepository")
 */

class Advert

/**
 * @var integer
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
     private $id;

/**
 * @ORM\ManyToMany(targetEntity="Advert\Entity\User", mappedBy="bookmarks", cascade="persist")
 * @ORM\JoinTable(name="bookmarks",
 *      joinColumns=@ORM\JoinColumn(name="advert_id", referencedColumnName="id"),
 *      inverseJoinColumns=@ORM\JoinColumn(name="user_id", referencedColumnName="id")
 *      )
 */
    private $bookmarks;


    public function __construct() 
     
        $this->categories = new ArrayCollection();
        $this->images = new ArrayCollection();
        $this->advertCreated = new \DateTime("now");
    

/** 
 * Set ID 
 * 
 * @param integer $id 
 * @return Advert 
 */ 
    public function setId($id) 
     
        $this->id = $id; 
        return $this; 
    

/**
 * Get id
 *
 * @return integer 
 */
    public function getId()
    
        return $this->id;
    

 /**
 * Set bookmark
 *
 * @param  $bookmark
 * @return bookmark
 */
    public function setBookmark($bookmark)
    
        $this->bookmark = $bookmark;
        return $this;
    


/**
 * Get bookmark
 *
 * @return ArrayCollection
 */
    public function getBookmark()
    
        return $this->bookmarks;
    


/**
 * @param Collection $bookmark
 */
    public function addBookmark($bookmarks)
    
        $this->bookmarks->add($bookmarks);
    

/**
 * @param Collection $bookmark
 */
    public function removeBookmark($bookmarks)
    
        $this->bookmarks->removeElement($bookmarks);     
    

User.php

namespace Advert\Entity;

use Doctrine\ORM\Mapping as ORM;
use ZfcUser\Entity\User as ZfcUser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;


/**
 * User
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="Advert\Repository\UserRepository")
 */

class User extends ZfcUser

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
protected $id;

/**
 * @ORM\ManyToMany(targetEntity="Advert\Entity\Advert", inversedBy="bookmarks", cascade="persist")
 * @ORM\JoinTable(name="bookmarks",
 *      joinColumns=@ORM\JoinColumn(name="user_id", referencedColumnName="id"),
 *      inverseJoinColumns=@ORM\JoinColumn(name="advert_id", referencedColumnName="id")
 *      )
 */
 private $bookmarks;


public function __construct()

    $this->bookmarks = new ArrayCollection();  


public function getBookmarks() 
     return $this->bookmarks;


/**
* @param Collection $bookmarks
*/
public function addBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->bookmarks->add($bookmark);
   


/**
 * @param Collection $bookmarks
 */
public function removeBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->bookmarks->removeElement($bookmark);
   
  

我现在有一项服务来检查广告是否已被添加书签、删除书签或设置书签。这意味着我必须将 UserId 和 AdvertId 直接输入到加入表书签中。但是,如果我没有带有 setUserId 和 setAdvertId 的书签实体,我该怎么做?

到目前为止,这是我的服务,最后两种方法(保存和删除)显示了我在删除书签实体之前使用的内容。我现在如何阅读联接表以检查书签是否已经存在,以便检查 userId 和 advertId?再说一次,我将如何访问此表以直接删除书签?如果所有者删除了广告,则书签将被删除,这很好,但显然用户还需要能够只删除一个书签。我如何做到这一点?

BookmarkAdvertService.php

public function checkAdvertBookmarkStatus($advertId)

    $userId = $this->getUserEntity()->getId();  
    $advert = $this->getEntityManager()->find('Advert\Entity\Advert', $advertId);
    $bookmarkStatus= $advert->getBookmark();
    return $bookmarkStatus;


public function saveAdvertBookmark($advertId)

    //this is what I used before
    $bookmark = new BookmarkEntity();
    $userId = $this->getUserEntity()->getId();

    // $bookmark->addBookmark($advertId); ??? like this
    $bookmark->setAdvertId($advertId);
    $bookmark->setUserId($userId);

    # write new bookmmark to database tbl bookmark
    $this->getEntityManager()->persist($bookmark);
    $this->getEntityManager()->flush();



public function removeAdvertBookmark($advertId)

    // this is what I used before
    $userId = $this->getUserEntity()->getId();
    $bookmark = $this->getEntityManager()->getRepository('Advert\Entity\Bookmark')
                                         ->findOneBy(array('advertId' => $advertId, 'userId' => $userId));

    # remove bookmmark from tbl bookmark
    $this->getEntityManager()->remove($bookmark);
    $this->getEntityManager()->flush();

更新 1 不起作用

我收到 2 条错误消息:

get_class() expects parameter 1 to be object, string given

vendor\doctrine\common\lib\Doctrine\Common\Persistence\Mapping\MappingException.php:96

Message:

Class '' does not exist

广告\服务\书签广告服务.php

class BookmarkAdvertService 


  public function saveAdvertBookmark($advert)
  
    $user = $this->getUserEntity()->getId(); 

    # create a new, empty entity
    $bookmark = new \Advert\Entity\Bookmark();
    $bookmark->setUser($user);
    $bookmark->setAdvert($advert);
     # write new bookmmark to database tbl bookmark
    $this->getEntityManager()->persist($bookmark);
    $this->getEntityManager()->flush();
  

广告/实体/Bookmark.php

namespace Advert\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;

/** Bookmark
 * 
 * @ORM\Table(name="bookmarks")
 * @ORM\Entity(repositoryClass="Advert\Repository\BookmarkRepository")
 */
class Bookmark

/**
 * @ORM\Id
 * @ORM\ManyToOne(targetEntity="Advert\Entity\Advert", inversedBy="bookmark")
 * @ORM\JoinColumn(name="advert_id", referencedColumnName="id")
 */
private $advert;

/** 
 * @ORM\Id
 * @ORM\ManyToOne(targetEntity="Advert\Entity\User", inversedBy="bookmark")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
private $user;


public function setAdvert($advert)

    $this->advert = $advert;
    return $this;


public function getAdvert()

    return $this->advert;



public function setUser($user)

    $this->user = $user;
    return $this;


public function getUser()

    return $this->user;
    

广告\实体\广告.php

namespace Advert\Entity;
use Advert\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/** Advert
 * 
 * @ORM\Table(name="advert")
 * @ORM\Entity(repositoryClass="Advert\Repository\AdvertRepository")
 */
class Advert

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
private $id;

/**
 * @ORM\OneToMany(targetEntity="Bookmark", mappedBy="advert", cascade="persist", "remove")
 * @ORM\JoinColumn(name="advert_id", referencedColumnName="id")
 */
private $bookmarks;

public function setBookmark($bookmark)

    $this->bookmark = $bookmark;
    return $this;



/**
 * Get bookmark
 *
 * @return ArrayCollection
 */
public function getBookmarks()

    return $this->bookmarks;


/**
 * @param Collection $bookmarks
 */
public function removeBookmarks(Collection $bookmarks)

  foreach ($bookmarks as $bookmark) 
    $this->bookmarks->removeElement($bookmark);


广告\实体\用户.php

namespace Advert\Entity;

use Doctrine\ORM\Mapping as ORM;
use ZfcUser\Entity\User as ZfcUser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="Advert\Repository\UserRepository")
 */

class User extends ZfcUser

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
 protected $id;

 /**
 * @ORM\OneToMany(targetEntity="Bookmark", mappedBy="user", cascade="persist", "remove")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
private $bookmarks;


public function __construct()

    $this->bookmarks = new ArrayCollection();



public function getBookmarks() 

     return $this->bookmarks;


/**
* @param Collection $bookmarks
*/
public function addBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->bookmarks->add($bookmark);
   


/**
* @param Collection $bookmarks
*/
public function removeBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->bookmarks->removeElement($bookmark);
   

更新 2 工作中

public function saveAdvertBookmark($advertId)

    $userId = $this->getUserEntity()->getId(); 
    $user = $this->getEntityManager()->find('Advert\Entity\User', $userId);
    $advert = $this->getEntityManager()->find('Advert\Entity\Advert', $advertId);
     # create a new, empty entity
    $bookmark = new \Advert\Entity\Bookmark();
    $bookmark->setUser($user);
    $bookmark->setAdvert($advert);
     # write new bookmmark to database tbl bookmark
    $this->getEntityManager()->persist($bookmark);
    $this->getEntityManager()->flush();



public function removeAdvertBookmark($advert)

    $user = $this->getUserEntity()->getId();  
    $advert = $this->getEntityManager()->getRepository('Advert\Entity\Bookmark')
                                         ->findOneBy(array('advert' => $advert, 'user' => $user));
    if ($advert !== NULL)

         # write new bookmmark to database tbl bookmark
        $this->getEntityManager()->remove($advert);
        $this->getEntityManager()->flush();
    else
        return false;
    

【问题讨论】:

【参考方案1】:

您的映射似乎有误。 在我看来,您的书签本身也是一个实体。该实体有一个User 和一个Advert。这两者结合起来创建了一个复合键。

所以从UserBookmark 的关系是一对多的。并为用户多对一添加书签。

AdvertBookmark 的关系是一对多的,从BookmarkAdvert 是多对一的。

您的Bookmark 实体应如下所示:

class Bookmark

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Advert\Entity\Advert", inversedBy="bookmarks")
     * @ORM\JoinColumn(name="advert_id", referencedColumnName="id")
     */
    private $advert;

    /** 
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="User\Entity\User", inversedBy="bookmarks")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;

    /**
     * @param Advert $advert
     * @return self
     */
    public function setAdvert(Advert $advert)
    
        $this->advert = $advert;
        $advert->addBookMark($this);
        return $this;
    

    /**
     * @return Advert
     */
    public function getAdvert()
    
        return $this->advert;
    

    /**
     * @param User $user
     * @return self
     */
    public function setUser(User $user)
    
        $this->user = $user;
        $user->addBookMark($this);
        return $this;
    

    /**
     * @return User
     */
    public function getUser()
    
        return $this->user;
        

然后在你的User:

/**
 * @ORM\OneToMany(targetEntity="Advert\Entity\BookMark", mappedBy="user", cascade="remove")
 */
 private $bookmarks;

/**
 * @return Collection
 */
public function getBookmarks() 

     return $this->bookmarks;


/**
 * @param BookMark $bookmark
 */
public function addBookmark(BookMark $bookmark)

   $this->bookmarks->add($bookmark);


/**
 * @param Collection $bookmarks
 */
public function addBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->addBookMark($bookmark);
   


/**
 * @param BookMark $bookmark
 */
public function removeBookmark(BookMark $bookmark)

   $this->bookmarks->removeElement($bookmark);


/**
 * @param Collection $bookmarks
 */
public function removeBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->removeBookmark($bookmark);
   

在你的Advert:

/**
 * @ORM\OneToMany(targetEntity="Advert\Entity\BookMark", mappedBy="advert", cascade="remove")
 */
 private $bookmarks;

/**
 * @return Collection
 */
public function getBookmarks() 

     return $this->bookmarks;


/**
 * @param BookMark $bookmark
 */
public function addBookmark(BookMark $bookmark)

   $this->bookmarks->add($bookmark);


/**
 * @param Collection $bookmarks
 */
public function addBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->addBookMark($bookmark);
   


/**
 * @param BookMark $bookmark
 */
public function removeBookmark(BookMark $bookmark)

   $this->bookmarks->removeElement($bookmark);


/**
 * @param Collection $bookmarks
 */
public function removeBookmarks(Collection $bookmarks)

   foreach ($bookmarks as $bookmark) 
       $this->removeBookmark($bookmark);
   

为您服务:

/**
 * @param int $advertId
 */
public function saveAdvertBookmark($advertId)

    $entityManager = $this->getEntityManager():
    $user = $this->getUserEntity(); 

    $advert = $entityManager->find('Advert\Entity\Advert', $advertId):
    $bookmark = new \Advert\Entity\Bookmark();
    $bookmark->setUser($user);
    $bookmark->setAdvert($advert);

    $entityManager->persist($bookmark);
    $entityManager->flush();

要在删除 UserAdvert 时删除所有书签,只需在这些实体中的书签关系中添加 cascade="remove"

【讨论】:

以上是关于Doctrine2 ORM 访问多对多连接表的主要内容,如果未能解决你的问题,请参考以下文章

多对多关系上的 Doctrine2 findBy()

如何避免与 Doctrine2 和 Zend Framework 2 的多对多关系重复?

十一 .Django 多对多表ManyToManyField (ORM)

6django操作表多对多实战

在 Zend 框架 2 中使用 MappedSuperclass 的 Doctrine 2 多对多

多对多、一对多或多对一