Symfony3 Doctrine queryBuilder for pagerfanta on ManyToMany realationship
Symfony3 Doctrine queryBuilder for pagerfanta on ManyToMany realationship
我这里有一个奇怪的问题,但首先要快速解释一下这个想法是什么。我们拥有所有用户都可以上传文件的系统。每个用户都会有喜欢的文件,我需要添加分页。
我的用户实体:
/**
* User
*
* @ORM\Table(name="users")
* @ORM\Entity(repositoryClass="STInfoSystemBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="email", type="string", length=100, unique=true)
*/
private $email;
/**
* @var string
*
* @ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* @var UserDetails
*
* @ORM\OneToOne(targetEntity="STInfoSystemBundle\Entity\UserDetails", mappedBy="user", cascade={"persist"})
*/
private $userDetails;
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="STInfoSystemBundle\Entity\Role")
* @ORM\JoinTable(name="user_roles",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*
*/
private $roles;
/**
* @var FileUpload[]
* @ORM\ManyToMany(targetEntity="STInfoSystemBundle\Entity\FileUpload")
* @ORM\JoinTable(name="users_favorites",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="file_id", referencedColumnName="id")})
*/
private $favoriteFiles;
/**
* @var FileUpload[]
*
* @ORM\OneToMany(targetEntity="STInfoSystemBundle\Entity\FileUpload", mappedBy="user")
*
*/
private $files;
/** @var Event[]
* @ORM\OneToMany(targetEntity="STInfoSystemBundle\Entity\Event", mappedBy="user")
*/
private $events;
public function __construct()
{
$this->roles = new ArrayCollection();
$this->files = new ArrayCollection();
$this->favoriteFiles = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
我的 FileUpload 实体:
/**
* @ORM\Entity
* @Vich\Uploadable
* @Table(name="uploaded_files")
* @ORM\Entity(repositoryClass="STInfoSystemBundle\Repository\FileUploadRepository")
*/
class FileUpload
{
const FILES_PER_PAGE = 3;
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// * mimeTypesMessage="Allowed files are pdf and MS word.",
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* @Vich\UploadableField(mapping="generic_file", fileNameProperty="fileName")
* @Assert\File(
* maxSize = "5M",
* mimeTypes = {"application/pdf"},
* maxSizeMessage="The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}."
* )
* @var File
*/
private $file;
//TODO: ..... other fields if any
/**
* @ORM\Column(type="string", length=255)
*
* @var string
*/
private $fileName;
/**
* @ORM\Column(type="datetime")
*
* @var \DateTime
*/
private $updatedAt;
/**
* @var
* @ORM\ManyToOne(targetEntity="STInfoSystemBundle\Entity\User", inversedBy="files")
*/
private $user;
/** @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="STInfoSystemBundle\Entity\User", mappedBy="favoriteFiles")
*
*/
private $favoriteUsers;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=255)
*/
private $description;
/** @var Specialty
*
* @ORM\ManyToOne(targetEntity="STInfoSystemBundle\Entity\Specialty", inversedBy="files")
*/
private $specialty;
public function __construct()
{
$this->favoriteUsers = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
文件上传存储库:
/**
* FileUploadRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class FileUploadRepository extends \Doctrine\ORM\EntityRepository
{
/**
* @param $userId
* @return \Doctrine\ORM\Query
*/
public function findUserMaterialsQuery($user)
{
return $this->createQueryBuilder('m')
->andWhere('m.user = :user')
->setParameter('user', $user)
->getQuery();
}
/**
* @param int $page
* @param $user
* @return Pagerfanta
*/
public function findUserMaterials($page = 1,$user){
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserMaterialsQuery($user)), false);
$paginator->setMaxPerPage(FileUpload::FILES_PER_PAGE);
$paginator->setCurrentPage($page);
return $paginator;
}
/**
* @param $specialty
* @return \Doctrine\ORM\Query
*/
public function findUserSpecialtyMaterialsQuery($specialty)
{
return $this->createQueryBuilder('m')
->andWhere('m.specialty = :specialty')
->setParameter('specialty', $specialty)
->getQuery();
}
/**
* @param int $page
* @param $specialty
* @return Pagerfanta
*/
public function findUserSpecialtyMaterials($page = 1,$specialty){
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserSpecialtyMaterialsQuery($specialty)), false);
$paginator->setMaxPerPage(FileUpload::FILES_PER_PAGE);
$paginator->setCurrentPage($page);
return $paginator;
}
}
用户资料库:
class UserRepository extends \Doctrine\ORM\EntityRepository
{
/**
* @param $userId
* @return \Doctrine\ORM\Query
*/
public function findUserFavoriteMaterialsQuery($userId)
{
return $this->createQueryBuilder('u')
->addSelect('u.favoriteFiles')
->andWhere('u.id = :userId')
->setParameter('user', $userId)
->getQuery();
}
/**
* @param int $page
* @param $userId
* @return Pagerfanta
*/
public function findUserFavoriteMaterials($page = 1,$userId){
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserFavoriteMaterialsQuery($userId)), false);
$paginator->setMaxPerPage(3);
$paginator->setCurrentPage($page);
return $paginator;
}
}
materials.html.twig
{% extends 'base.html.twig' %}
{% block main %}
<h4>Favorite/My materials/All materials</h4>
{% for material in materials %}
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ material.title }}</h3>
</div>
<div class="panel-body">
{{ material.shortDescription }}
<hr>
<a href="{{ path("material", {id: material.id}) }}" class="">Read</a>
<br>
<a href="{{ path("toggle_favorite", {id: material.id}) }}" class="">
{% if material.favorite(app.user) %}
Remove from favorites
{% else %}
Add to favorites
{% endif %}
</a>
<br>
{% if app.user.isTeacher %}
Edit/delete - TO BE DONE
{% endif %}
</div>
<div class="panel-footer">
{{ material.specialty }}
{#TODO: pagination#}
</div>
</div>
</div>
{% endfor %}
<div class="col-sm-12">
{% if materials.haveToPaginate %}
<div class="navigation text-center">
{#{{ pagerfanta(events, 'twitter_bootstrap3_translated') }}#}
{{ pagerfanta(materials, 'twitter_bootstrap3_translated', { routeName: routeName }) }}
</div>
{% endif %}
</div>
{% endblock %}
这是工作正常的 MaterialController 部分:
/**
* @Route("/materials", defaults={"page": "1"}, name="materials")
* @Route("/materials/page/{page}", requirements={"page": "[1-9]\d*"}, name="materials_paginated")
*
*/
public function allMaterials($page)
{
/** @var User $user */
$user = $this->getUser();
$userSpecialty = $user->getUserDetails()->getSpecialty();
// $allMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findBy([
// 'specialty' => $user->getUserDetails()->getSpecialty()
// ]);
$allMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findUserSpecialtyMaterials($page, $userSpecialty);
$routeName = 'materials_paginated';
return $this->render('material/materials.html.twig', [
'materials' => $allMaterials,
'routeName' => $routeName
]
);
}
/**
* @Route("/myMaterials", defaults={"page": "1"}, name="my_materials")
* @Route("/myMaterials/page/{page}", requirements={"page": "[1-9]\d*"}, name="my_materials_paginated")
*/
public function myMaterials($page)
{
/** @var User $user */
$user = $this->getUser();
// $myMaterials = $user->getFiles();
$myMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findUserMaterials($page, $user);
$routeName = 'my_materials_paginated';
return $this->render('material/materials.html.twig', [
'materials' => $myMaterials,
'routeName' => $routeName
]
);
}
但下面的内容对我没有任何帮助:
/**
* @Route("/favoriteMaterials", defaults={"page": "1"}, name="favorite_materials")
* @Route("/favoriteMaterials/page/{page}", requirements={"page": "[1-9]\d*"}, name="favorite_materials_paginated")
*
*/
public function favoriteMaterials($page)
{
/** @var User $user */
$user = $this->getUser();
// $favoriteMaterials = $user->getFavoriteFiles();
$favoriteMaterials = $this->getDoctrine()->getRepository(User::class)->findUserFavoriteMaterials($page, $user->getId());
$routeName = 'favorite_materials_paginated';
return $this->render('material/materials.html.twig',[
'materials' => $favoriteMaterials,
'routeName' => $routeName
]
);
}
那么如何对 myMaterials、allMaterials、favoriteMaterials 使用相同的树枝模板并使分页正常工作?
我找到了解决办法。将这些添加到 FileUploadRepository
/**
* @param $user
* @return \Doctrine\ORM\Query
*/
public function findUserFavoriteMaterialsQuery($user)
{
return $this->createQueryBuilder('f')
->andWhere(':user MEMBER OF f.favoriteUsers')
->setParameter('user', $user)
->getQuery();
}
/**
* @param int $page
* @param $user
* @param int $perPage
* @return Pagerfanta
*/
public function findUserFavoriteMaterials($page = 1, $user, $perPage = FileUpload::FILES_PER_PAGE)
{
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserFavoriteMaterialsQuery($user)));
$paginator->setMaxPerPage($perPage);
$paginator->setCurrentPage($page);
return $paginator;
}
然后在我的 MaterialController 中我这样使用它:
/** @var User $user */
$user = $this->getUser();
$favoriteMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findUserFavoriteMaterials($page, $user);
$routeName = 'favorite_materials_paginated';
$title = 'Favorite materials';
return $this->render('material/materials.html.twig', [
'materials' => $favoriteMaterials,
'routeName' => $routeName,
'title' => $title
]
);
我这里有一个奇怪的问题,但首先要快速解释一下这个想法是什么。我们拥有所有用户都可以上传文件的系统。每个用户都会有喜欢的文件,我需要添加分页。
我的用户实体:
/**
* User
*
* @ORM\Table(name="users")
* @ORM\Entity(repositoryClass="STInfoSystemBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="email", type="string", length=100, unique=true)
*/
private $email;
/**
* @var string
*
* @ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* @var UserDetails
*
* @ORM\OneToOne(targetEntity="STInfoSystemBundle\Entity\UserDetails", mappedBy="user", cascade={"persist"})
*/
private $userDetails;
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="STInfoSystemBundle\Entity\Role")
* @ORM\JoinTable(name="user_roles",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*
*/
private $roles;
/**
* @var FileUpload[]
* @ORM\ManyToMany(targetEntity="STInfoSystemBundle\Entity\FileUpload")
* @ORM\JoinTable(name="users_favorites",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="file_id", referencedColumnName="id")})
*/
private $favoriteFiles;
/**
* @var FileUpload[]
*
* @ORM\OneToMany(targetEntity="STInfoSystemBundle\Entity\FileUpload", mappedBy="user")
*
*/
private $files;
/** @var Event[]
* @ORM\OneToMany(targetEntity="STInfoSystemBundle\Entity\Event", mappedBy="user")
*/
private $events;
public function __construct()
{
$this->roles = new ArrayCollection();
$this->files = new ArrayCollection();
$this->favoriteFiles = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
我的 FileUpload 实体:
/**
* @ORM\Entity
* @Vich\Uploadable
* @Table(name="uploaded_files")
* @ORM\Entity(repositoryClass="STInfoSystemBundle\Repository\FileUploadRepository")
*/
class FileUpload
{
const FILES_PER_PAGE = 3;
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// * mimeTypesMessage="Allowed files are pdf and MS word.",
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* @Vich\UploadableField(mapping="generic_file", fileNameProperty="fileName")
* @Assert\File(
* maxSize = "5M",
* mimeTypes = {"application/pdf"},
* maxSizeMessage="The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}."
* )
* @var File
*/
private $file;
//TODO: ..... other fields if any
/**
* @ORM\Column(type="string", length=255)
*
* @var string
*/
private $fileName;
/**
* @ORM\Column(type="datetime")
*
* @var \DateTime
*/
private $updatedAt;
/**
* @var
* @ORM\ManyToOne(targetEntity="STInfoSystemBundle\Entity\User", inversedBy="files")
*/
private $user;
/** @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="STInfoSystemBundle\Entity\User", mappedBy="favoriteFiles")
*
*/
private $favoriteUsers;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=255)
*/
private $description;
/** @var Specialty
*
* @ORM\ManyToOne(targetEntity="STInfoSystemBundle\Entity\Specialty", inversedBy="files")
*/
private $specialty;
public function __construct()
{
$this->favoriteUsers = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
文件上传存储库:
/**
* FileUploadRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class FileUploadRepository extends \Doctrine\ORM\EntityRepository
{
/**
* @param $userId
* @return \Doctrine\ORM\Query
*/
public function findUserMaterialsQuery($user)
{
return $this->createQueryBuilder('m')
->andWhere('m.user = :user')
->setParameter('user', $user)
->getQuery();
}
/**
* @param int $page
* @param $user
* @return Pagerfanta
*/
public function findUserMaterials($page = 1,$user){
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserMaterialsQuery($user)), false);
$paginator->setMaxPerPage(FileUpload::FILES_PER_PAGE);
$paginator->setCurrentPage($page);
return $paginator;
}
/**
* @param $specialty
* @return \Doctrine\ORM\Query
*/
public function findUserSpecialtyMaterialsQuery($specialty)
{
return $this->createQueryBuilder('m')
->andWhere('m.specialty = :specialty')
->setParameter('specialty', $specialty)
->getQuery();
}
/**
* @param int $page
* @param $specialty
* @return Pagerfanta
*/
public function findUserSpecialtyMaterials($page = 1,$specialty){
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserSpecialtyMaterialsQuery($specialty)), false);
$paginator->setMaxPerPage(FileUpload::FILES_PER_PAGE);
$paginator->setCurrentPage($page);
return $paginator;
}
}
用户资料库:
class UserRepository extends \Doctrine\ORM\EntityRepository
{
/**
* @param $userId
* @return \Doctrine\ORM\Query
*/
public function findUserFavoriteMaterialsQuery($userId)
{
return $this->createQueryBuilder('u')
->addSelect('u.favoriteFiles')
->andWhere('u.id = :userId')
->setParameter('user', $userId)
->getQuery();
}
/**
* @param int $page
* @param $userId
* @return Pagerfanta
*/
public function findUserFavoriteMaterials($page = 1,$userId){
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserFavoriteMaterialsQuery($userId)), false);
$paginator->setMaxPerPage(3);
$paginator->setCurrentPage($page);
return $paginator;
}
}
materials.html.twig
{% extends 'base.html.twig' %}
{% block main %}
<h4>Favorite/My materials/All materials</h4>
{% for material in materials %}
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ material.title }}</h3>
</div>
<div class="panel-body">
{{ material.shortDescription }}
<hr>
<a href="{{ path("material", {id: material.id}) }}" class="">Read</a>
<br>
<a href="{{ path("toggle_favorite", {id: material.id}) }}" class="">
{% if material.favorite(app.user) %}
Remove from favorites
{% else %}
Add to favorites
{% endif %}
</a>
<br>
{% if app.user.isTeacher %}
Edit/delete - TO BE DONE
{% endif %}
</div>
<div class="panel-footer">
{{ material.specialty }}
{#TODO: pagination#}
</div>
</div>
</div>
{% endfor %}
<div class="col-sm-12">
{% if materials.haveToPaginate %}
<div class="navigation text-center">
{#{{ pagerfanta(events, 'twitter_bootstrap3_translated') }}#}
{{ pagerfanta(materials, 'twitter_bootstrap3_translated', { routeName: routeName }) }}
</div>
{% endif %}
</div>
{% endblock %}
这是工作正常的 MaterialController 部分:
/**
* @Route("/materials", defaults={"page": "1"}, name="materials")
* @Route("/materials/page/{page}", requirements={"page": "[1-9]\d*"}, name="materials_paginated")
*
*/
public function allMaterials($page)
{
/** @var User $user */
$user = $this->getUser();
$userSpecialty = $user->getUserDetails()->getSpecialty();
// $allMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findBy([
// 'specialty' => $user->getUserDetails()->getSpecialty()
// ]);
$allMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findUserSpecialtyMaterials($page, $userSpecialty);
$routeName = 'materials_paginated';
return $this->render('material/materials.html.twig', [
'materials' => $allMaterials,
'routeName' => $routeName
]
);
}
/**
* @Route("/myMaterials", defaults={"page": "1"}, name="my_materials")
* @Route("/myMaterials/page/{page}", requirements={"page": "[1-9]\d*"}, name="my_materials_paginated")
*/
public function myMaterials($page)
{
/** @var User $user */
$user = $this->getUser();
// $myMaterials = $user->getFiles();
$myMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findUserMaterials($page, $user);
$routeName = 'my_materials_paginated';
return $this->render('material/materials.html.twig', [
'materials' => $myMaterials,
'routeName' => $routeName
]
);
}
但下面的内容对我没有任何帮助:
/**
* @Route("/favoriteMaterials", defaults={"page": "1"}, name="favorite_materials")
* @Route("/favoriteMaterials/page/{page}", requirements={"page": "[1-9]\d*"}, name="favorite_materials_paginated")
*
*/
public function favoriteMaterials($page)
{
/** @var User $user */
$user = $this->getUser();
// $favoriteMaterials = $user->getFavoriteFiles();
$favoriteMaterials = $this->getDoctrine()->getRepository(User::class)->findUserFavoriteMaterials($page, $user->getId());
$routeName = 'favorite_materials_paginated';
return $this->render('material/materials.html.twig',[
'materials' => $favoriteMaterials,
'routeName' => $routeName
]
);
}
那么如何对 myMaterials、allMaterials、favoriteMaterials 使用相同的树枝模板并使分页正常工作?
我找到了解决办法。将这些添加到 FileUploadRepository
/**
* @param $user
* @return \Doctrine\ORM\Query
*/
public function findUserFavoriteMaterialsQuery($user)
{
return $this->createQueryBuilder('f')
->andWhere(':user MEMBER OF f.favoriteUsers')
->setParameter('user', $user)
->getQuery();
}
/**
* @param int $page
* @param $user
* @param int $perPage
* @return Pagerfanta
*/
public function findUserFavoriteMaterials($page = 1, $user, $perPage = FileUpload::FILES_PER_PAGE)
{
$paginator = new Pagerfanta(new DoctrineORMAdapter($this->findUserFavoriteMaterialsQuery($user)));
$paginator->setMaxPerPage($perPage);
$paginator->setCurrentPage($page);
return $paginator;
}
然后在我的 MaterialController 中我这样使用它:
/** @var User $user */
$user = $this->getUser();
$favoriteMaterials = $this->getDoctrine()->getRepository(FileUpload::class)->findUserFavoriteMaterials($page, $user);
$routeName = 'favorite_materials_paginated';
$title = 'Favorite materials';
return $this->render('material/materials.html.twig', [
'materials' => $favoriteMaterials,
'routeName' => $routeName,
'title' => $title
]
);