VersionRepository.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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\Entity;
  12. use Doctrine\DBAL\Connection;
  13. use Predis\Client;
  14. use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
  15. use Symfony\Bridge\Doctrine\RegistryInterface;
  16. /**
  17. * @author Jordi Boggiano <j.boggiano@seld.be>
  18. */
  19. class VersionRepository extends ServiceEntityRepository
  20. {
  21. private $redis;
  22. protected $supportedLinkTypes = array(
  23. 'require',
  24. 'conflict',
  25. 'provide',
  26. 'replace',
  27. 'devRequire',
  28. 'suggest',
  29. );
  30. public function __construct(RegistryInterface $registry, Client $redisCache)
  31. {
  32. parent::__construct($registry, Version::class);
  33. $this->redis = $redisCache;
  34. }
  35. public function remove(Version $version)
  36. {
  37. $em = $this->getEntityManager();
  38. $version->getPackage()->getVersions()->removeElement($version);
  39. $version->getPackage()->setCrawledAt(new \DateTime);
  40. $version->getPackage()->setUpdatedAt(new \DateTime);
  41. $em->getConnection()->executeQuery('DELETE FROM version_author WHERE version_id=:id', array('id' => $version->getId()));
  42. $em->getConnection()->executeQuery('DELETE FROM version_tag WHERE version_id=:id', array('id' => $version->getId()));
  43. $em->getConnection()->executeQuery('DELETE FROM link_suggest WHERE version_id=:id', array('id' => $version->getId()));
  44. $em->getConnection()->executeQuery('DELETE FROM link_conflict WHERE version_id=:id', array('id' => $version->getId()));
  45. $em->getConnection()->executeQuery('DELETE FROM link_replace WHERE version_id=:id', array('id' => $version->getId()));
  46. $em->getConnection()->executeQuery('DELETE FROM link_provide WHERE version_id=:id', array('id' => $version->getId()));
  47. $em->getConnection()->executeQuery('DELETE FROM link_require_dev WHERE version_id=:id', array('id' => $version->getId()));
  48. $em->getConnection()->executeQuery('DELETE FROM link_require WHERE version_id=:id', array('id' => $version->getId()));
  49. $em->getConnection()->executeQuery('DELETE FROM download WHERE id=:id AND type = :type', ['id' => $version->getId(), 'type' => Download::TYPE_VERSION]);
  50. $em->remove($version);
  51. }
  52. public function refreshVersions($versions)
  53. {
  54. $versionIds = [];
  55. foreach ($versions as $version) {
  56. $versionIds[] = $version->getId();
  57. $this->getEntityManager()->detach($version);
  58. }
  59. $refreshedVersions = $this->findBy(['id' => $versionIds]);
  60. $versionsById = [];
  61. foreach ($refreshedVersions as $version) {
  62. $versionsById[$version->getId()] = $version;
  63. }
  64. $refreshedVersions = [];
  65. foreach ($versions as $version) {
  66. $refreshedVersions[] = $versionsById[$version->getId()];
  67. }
  68. return $refreshedVersions;
  69. }
  70. /**
  71. * @param Version[] $versions
  72. */
  73. public function detachToArray(array $versions, array $versionData, bool $serializeForApi = false): array
  74. {
  75. $res = [];
  76. $em = $this->getEntityManager();
  77. foreach ($versions as $version) {
  78. $res[$version->getVersion()] = $version->toArray($versionData, $serializeForApi);
  79. $em->detach($version);
  80. }
  81. return $res;
  82. }
  83. public function getVersionData(array $versionIds)
  84. {
  85. $links = [
  86. 'require' => 'link_require',
  87. 'devRequire' => 'link_require_dev',
  88. 'suggest' => 'link_suggest',
  89. 'conflict' => 'link_conflict',
  90. 'provide' => 'link_provide',
  91. 'replace' => 'link_replace',
  92. ];
  93. $result = [];
  94. foreach ($versionIds as $id) {
  95. $result[$id] = [
  96. 'require' => [],
  97. 'devRequire' => [],
  98. 'suggest' => [],
  99. 'conflict' => [],
  100. 'provide' => [],
  101. 'replace' => [],
  102. 'authors' => [],
  103. 'tags' => [],
  104. ];
  105. }
  106. foreach ($links as $link => $table) {
  107. $rows = $this->getEntityManager()->getConnection()->fetchAll(
  108. 'SELECT version_id, packageName name, packageVersion version FROM '.$table.' WHERE version_id IN (:ids)',
  109. ['ids' => $versionIds],
  110. ['ids' => Connection::PARAM_INT_ARRAY]
  111. );
  112. foreach ($rows as $row) {
  113. $result[$row['version_id']][$link][] = $row;
  114. }
  115. }
  116. $rows = $this->getEntityManager()->getConnection()->fetchAll(
  117. 'SELECT va.version_id, name, email, homepage, role FROM author a JOIN version_author va ON va.author_id = a.id WHERE va.version_id IN (:ids)',
  118. ['ids' => $versionIds],
  119. ['ids' => Connection::PARAM_INT_ARRAY]
  120. );
  121. foreach ($rows as $row) {
  122. $versionId = $row['version_id'];
  123. unset($row['version_id']);
  124. $result[$versionId]['authors'][] = array_filter($row);
  125. }
  126. $rows = $this->getEntityManager()->getConnection()->fetchAll(
  127. 'SELECT vt.version_id, name FROM tag t JOIN version_tag vt ON vt.tag_id = t.id WHERE vt.version_id IN (:ids)',
  128. ['ids' => $versionIds],
  129. ['ids' => Connection::PARAM_INT_ARRAY]
  130. );
  131. foreach ($rows as $row) {
  132. $versionId = $row['version_id'];
  133. $result[$versionId]['tags'][] = $row['name'];
  134. }
  135. return $result;
  136. }
  137. public function getVersionMetadataForUpdate(Package $package)
  138. {
  139. $rows = $this->getEntityManager()->getConnection()->fetchAll(
  140. 'SELECT id, version, normalizedVersion, source, softDeletedAt, `authors` IS NULL as needs_author_migration FROM package_version v WHERE v.package_id = :id',
  141. ['id' => $package->getId()]
  142. );
  143. $versions = [];
  144. foreach ($rows as $row) {
  145. if ($row['source']) {
  146. $row['source'] = json_decode($row['source'], true);
  147. }
  148. $row['needs_author_migration'] = (int) $row['needs_author_migration'];
  149. $versions[strtolower($row['normalizedVersion'])] = $row;
  150. }
  151. return $versions;
  152. }
  153. public function getFullVersion($versionId)
  154. {
  155. $qb = $this->getEntityManager()->createQueryBuilder();
  156. $qb->select('v', 't', 'a')
  157. ->from('Packagist\WebBundle\Entity\Version', 'v')
  158. ->leftJoin('v.tags', 't')
  159. ->leftJoin('v.authors', 'a')
  160. ->where('v.id = :id')
  161. ->setParameter('id', $versionId);
  162. return $qb->getQuery()->getSingleResult();
  163. }
  164. /**
  165. * Returns the latest versions released
  166. *
  167. * @param string $vendor optional vendor filter
  168. * @param string $package optional vendor/package filter
  169. * @return \Doctrine\ORM\QueryBuilder
  170. */
  171. public function getQueryBuilderForLatestVersionWithPackage($vendor = null, $package = null)
  172. {
  173. $qb = $this->getEntityManager()->createQueryBuilder();
  174. $qb->select('v')
  175. ->from('Packagist\WebBundle\Entity\Version', 'v')
  176. ->where('v.development = false')
  177. ->andWhere('v.releasedAt <= ?0')
  178. ->orderBy('v.releasedAt', 'DESC');
  179. $qb->setParameter(0, date('Y-m-d H:i:s'));
  180. if ($vendor || $package) {
  181. $qb->innerJoin('v.package', 'p')
  182. ->addSelect('p');
  183. }
  184. if ($vendor) {
  185. $qb->andWhere('p.name LIKE ?1');
  186. $qb->setParameter(1, $vendor.'/%');
  187. } elseif ($package) {
  188. $qb->andWhere('p.name = ?1')
  189. ->setParameter(1, $package);
  190. }
  191. return $qb;
  192. }
  193. public function getLatestReleases($count = 10)
  194. {
  195. if ($cached = $this->redis->get('new_releases')) {
  196. return json_decode($cached, true);
  197. }
  198. $qb = $this->getEntityManager()->createQueryBuilder();
  199. $qb->select('v.name, v.version, v.description')
  200. ->from('Packagist\WebBundle\Entity\Version', 'v')
  201. ->where('v.development = false')
  202. ->andWhere('v.releasedAt < :now')
  203. ->orderBy('v.releasedAt', 'DESC')
  204. ->setMaxResults($count)
  205. ->setParameter('now', date('Y-m-d H:i:s'));
  206. $res = $qb->getQuery()->getResult();
  207. $this->redis->setex('new_releases', 600, json_encode($res));
  208. return $res;
  209. }
  210. }