ExploreController.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <?php
  2. /*
  3. * This file is part of Packagist.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. * Nils Adermann <naderman@naderman.de>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Packagist\WebBundle\Controller;
  12. use Doctrine\DBAL\ConnectionException;
  13. use Packagist\WebBundle\Entity\Package;
  14. use Packagist\WebBundle\Entity\PackageRepository;
  15. use Packagist\WebBundle\Entity\VersionRepository;
  16. use Pagerfanta\Adapter\FixedAdapter;
  17. use Pagerfanta\Pagerfanta;
  18. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
  19. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  20. use Symfony\Component\HttpFoundation\JsonResponse;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\Routing\Annotation\Route;
  23. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  24. /**
  25. * @Route("/explore")
  26. */
  27. class ExploreController extends Controller
  28. {
  29. /**
  30. * @Template()
  31. * @Route("/", name="browse")
  32. */
  33. public function exploreAction()
  34. {
  35. /** @var PackageRepository $pkgRepo */
  36. $pkgRepo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
  37. /** @var VersionRepository $verRepo */
  38. $verRepo = $this->get('packagist.version_repository');
  39. $newSubmitted = $pkgRepo->getQueryBuilderForNewestPackages()->setMaxResults(10)
  40. ->getQuery()->useResultCache(true, 60)->getResult();
  41. $newReleases = $verRepo->getLatestReleases(10);
  42. $maxId = $this->getDoctrine()->getConnection()->fetchColumn('SELECT max(id) FROM package');
  43. $random = $pkgRepo
  44. ->createQueryBuilder('p')->where('p.id >= :randId')->andWhere('p.abandoned = 0')
  45. ->setParameter('randId', rand(1, $maxId))->setMaxResults(10)
  46. ->getQuery()->getResult();
  47. try {
  48. $popular = array();
  49. $popularIds = $this->get('snc_redis.default')->zrevrange('downloads:trending', 0, 9);
  50. if ($popularIds) {
  51. $popular = $pkgRepo->createQueryBuilder('p')->where('p.id IN (:ids)')->setParameter('ids', $popularIds)
  52. ->getQuery()->useResultCache(true, 900)->getResult();
  53. usort($popular, function ($a, $b) use ($popularIds) {
  54. return array_search($a->getId(), $popularIds) > array_search($b->getId(), $popularIds) ? 1 : -1;
  55. });
  56. }
  57. } catch (ConnectionException $e) {
  58. $popular = array();
  59. }
  60. return array(
  61. 'newlySubmitted' => $newSubmitted,
  62. 'newlyReleased' => $newReleases,
  63. 'random' => $random,
  64. 'popular' => $popular,
  65. );
  66. }
  67. /**
  68. * @Template()
  69. * @Route("/popular.{_format}", name="browse_popular", defaults={"_format"="html"})
  70. * @Cache(smaxage=900)
  71. */
  72. public function popularAction(Request $req)
  73. {
  74. try {
  75. $redis = $this->get('snc_redis.default');
  76. $perPage = $req->query->getInt('per_page', 15);
  77. if ($perPage <= 0 || $perPage > 100) {
  78. if ($req->getRequestFormat() === 'json') {
  79. return new JsonResponse(array(
  80. 'status' => 'error',
  81. 'message' => 'The optional packages per_page parameter must be an integer between 1 and 100 (default: 15)',
  82. ), 400);
  83. }
  84. $perPage = max(0, min(100, $perPage));
  85. }
  86. $popularIds = $redis->zrevrange(
  87. 'downloads:trending',
  88. ($req->get('page', 1) - 1) * $perPage,
  89. $req->get('page', 1) * $perPage - 1
  90. );
  91. $popular = $this->getDoctrine()->getRepository('PackagistWebBundle:Package')
  92. ->createQueryBuilder('p')->where('p.id IN (:ids)')->setParameter('ids', $popularIds)
  93. ->getQuery()->useResultCache(true, 900)->getResult();
  94. usort($popular, function ($a, $b) use ($popularIds) {
  95. return array_search($a->getId(), $popularIds) > array_search($b->getId(), $popularIds) ? 1 : -1;
  96. });
  97. $packages = new Pagerfanta(new FixedAdapter($redis->zcard('downloads:trending'), $popular));
  98. $packages->setMaxPerPage($perPage);
  99. $packages->setCurrentPage($req->get('page', 1), false, true);
  100. } catch (ConnectionException $e) {
  101. $packages = new Pagerfanta(new FixedAdapter(0, array()));
  102. }
  103. $data = array(
  104. 'packages' => $packages,
  105. );
  106. $data['meta'] = $this->getPackagesMetadata($data['packages']);
  107. if ($req->getRequestFormat() === 'json') {
  108. $result = array(
  109. 'packages' => array(),
  110. 'total' => $packages->getNbResults(),
  111. );
  112. /** @var Package $package */
  113. foreach ($packages as $package) {
  114. $url = $this->generateUrl('view_package', array('name' => $package->getName()), UrlGeneratorInterface::ABSOLUTE_URL);
  115. $result['packages'][] = array(
  116. 'name' => $package->getName(),
  117. 'description' => $package->getDescription() ?: '',
  118. 'url' => $url,
  119. 'downloads' => $data['meta']['downloads'][$package->getId()],
  120. 'favers' => $data['meta']['favers'][$package->getId()],
  121. );
  122. }
  123. if ($packages->hasNextPage()) {
  124. $params = array(
  125. '_format' => 'json',
  126. 'page' => $packages->getNextPage(),
  127. );
  128. if ($perPage !== 15) {
  129. $params['per_page'] = $perPage;
  130. }
  131. $result['next'] = $this->generateUrl('browse_popular', $params, UrlGeneratorInterface::ABSOLUTE_URL);
  132. }
  133. return new JsonResponse($result);
  134. }
  135. return $data;
  136. }
  137. }