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
        ]
    );