aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Wallabag/CoreBundle/Repository
diff options
context:
space:
mode:
Diffstat (limited to 'src/Wallabag/CoreBundle/Repository')
-rw-r--r--src/Wallabag/CoreBundle/Repository/EntryRepository.php138
-rw-r--r--src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php10
-rw-r--r--src/Wallabag/CoreBundle/Repository/TagRepository.php53
3 files changed, 164 insertions, 37 deletions
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
index 83379998..bfd07937 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -3,11 +3,13 @@
3namespace Wallabag\CoreBundle\Repository; 3namespace Wallabag\CoreBundle\Repository;
4 4
5use Doctrine\ORM\EntityRepository; 5use Doctrine\ORM\EntityRepository;
6use Doctrine\ORM\NoResultException;
6use Doctrine\ORM\QueryBuilder; 7use Doctrine\ORM\QueryBuilder;
7use Pagerfanta\Adapter\DoctrineORMAdapter; 8use Pagerfanta\Adapter\DoctrineORMAdapter;
8use Pagerfanta\Pagerfanta; 9use Pagerfanta\Pagerfanta;
9use Wallabag\CoreBundle\Entity\Entry; 10use Wallabag\CoreBundle\Entity\Entry;
10use Wallabag\CoreBundle\Entity\Tag; 11use Wallabag\CoreBundle\Entity\Tag;
12use Wallabag\CoreBundle\Helper\UrlHasher;
11 13
12class EntryRepository extends EntityRepository 14class EntryRepository extends EntityRepository
13{ 15{
@@ -50,7 +52,7 @@ class EntryRepository extends EntityRepository
50 public function getBuilderForArchiveByUser($userId) 52 public function getBuilderForArchiveByUser($userId)
51 { 53 {
52 return $this 54 return $this
53 ->getSortedQueryBuilderByUser($userId) 55 ->getSortedQueryBuilderByUser($userId, 'archivedAt', 'desc')
54 ->andWhere('e.isArchived = true') 56 ->andWhere('e.isArchived = true')
55 ; 57 ;
56 } 58 }
@@ -110,8 +112,7 @@ class EntryRepository extends EntityRepository
110 */ 112 */
111 public function getBuilderForUntaggedByUser($userId) 113 public function getBuilderForUntaggedByUser($userId)
112 { 114 {
113 return $this 115 return $this->sortQueryBuilder($this->getRawBuilderForUntaggedByUser($userId));
114 ->sortQueryBuilder($this->getRawBuilderForUntaggedByUser($userId));
115 } 116 }
116 117
117 /** 118 /**
@@ -129,6 +130,21 @@ class EntryRepository extends EntityRepository
129 } 130 }
130 131
131 /** 132 /**
133 * Retrieve the number of untagged entries for a user.
134 *
135 * @param int $userId
136 *
137 * @return int
138 */
139 public function countUntaggedEntriesByUser($userId)
140 {
141 return (int) $this->getRawBuilderForUntaggedByUser($userId)
142 ->select('count(e.id)')
143 ->getQuery()
144 ->getSingleScalarResult();
145 }
146
147 /**
132 * Find Entries. 148 * Find Entries.
133 * 149 *
134 * @param int $userId 150 * @param int $userId
@@ -139,15 +155,30 @@ class EntryRepository extends EntityRepository
139 * @param string $order 155 * @param string $order
140 * @param int $since 156 * @param int $since
141 * @param string $tags 157 * @param string $tags
158 * @param string $detail 'metadata' or 'full'. Include content field if 'full'
159 *
160 * @todo Breaking change: replace default detail=full by detail=metadata in a future version
142 * 161 *
143 * @return Pagerfanta 162 * @return Pagerfanta
144 */ 163 */
145 public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '') 164 public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '', $detail = 'full')
146 { 165 {
166 if (!\in_array(strtolower($detail), ['full', 'metadata'], true)) {
167 throw new \Exception('Detail "' . $detail . '" parameter is wrong, allowed: full or metadata');
168 }
169
147 $qb = $this->createQueryBuilder('e') 170 $qb = $this->createQueryBuilder('e')
148 ->leftJoin('e.tags', 't') 171 ->leftJoin('e.tags', 't')
149 ->where('e.user = :userId')->setParameter('userId', $userId); 172 ->where('e.user = :userId')->setParameter('userId', $userId);
150 173
174 if ('metadata' === $detail) {
175 $fieldNames = $this->getClassMetadata()->getFieldNames();
176 $fields = array_filter($fieldNames, function ($k) {
177 return 'content' !== $k;
178 });
179 $qb->select(sprintf('partial e.{%s}', implode(',', $fields)));
180 }
181
151 if (null !== $isArchived) { 182 if (null !== $isArchived) {
152 $qb->andWhere('e.isArchived = :isArchived')->setParameter('isArchived', (bool) $isArchived); 183 $qb->andWhere('e.isArchived = :isArchived')->setParameter('isArchived', (bool) $isArchived);
153 } 184 }
@@ -185,10 +216,16 @@ class EntryRepository extends EntityRepository
185 } 216 }
186 } 217 }
187 218
219 if (!\in_array(strtolower($order), ['asc', 'desc'], true)) {
220 throw new \Exception('Order "' . $order . '" parameter is wrong, allowed: asc or desc');
221 }
222
188 if ('created' === $sort) { 223 if ('created' === $sort) {
189 $qb->orderBy('e.id', $order); 224 $qb->orderBy('e.id', $order);
190 } elseif ('updated' === $sort) { 225 } elseif ('updated' === $sort) {
191 $qb->orderBy('e.updatedAt', $order); 226 $qb->orderBy('e.updatedAt', $order);
227 } elseif ('archived' === $sort) {
228 $qb->orderBy('e.archivedAt', $order);
192 } 229 }
193 230
194 $pagerAdapter = new DoctrineORMAdapter($qb, true, false); 231 $pagerAdapter = new DoctrineORMAdapter($qb, true, false);
@@ -269,7 +306,6 @@ class EntryRepository extends EntityRepository
269 * DELETE et FROM entry_tag et WHERE et.entry_id IN ( SELECT e.id FROM entry e WHERE e.user_id = :userId ) AND et.tag_id = :tagId 306 * DELETE et FROM entry_tag et WHERE et.entry_id IN ( SELECT e.id FROM entry e WHERE e.user_id = :userId ) AND et.tag_id = :tagId
270 * 307 *
271 * @param int $userId 308 * @param int $userId
272 * @param Tag $tag
273 */ 309 */
274 public function removeTag($userId, Tag $tag) 310 public function removeTag($userId, Tag $tag)
275 { 311 {
@@ -320,15 +356,44 @@ class EntryRepository extends EntityRepository
320 * Find an entry by its url and its owner. 356 * Find an entry by its url and its owner.
321 * If it exists, return the entry otherwise return false. 357 * If it exists, return the entry otherwise return false.
322 * 358 *
323 * @param $url 359 * @param string $url
324 * @param $userId 360 * @param int $userId
325 * 361 *
326 * @return Entry|bool 362 * @return Entry|false
327 */ 363 */
328 public function findByUrlAndUserId($url, $userId) 364 public function findByUrlAndUserId($url, $userId)
329 { 365 {
366 return $this->findByHashedUrlAndUserId(
367 UrlHasher::hashUrl($url),
368 $userId
369 );
370 }
371
372 /**
373 * Find an entry by its hashed url and its owner.
374 * If it exists, return the entry otherwise return false.
375 *
376 * @param string $hashedUrl Url hashed using sha1
377 * @param int $userId
378 *
379 * @return Entry|false
380 */
381 public function findByHashedUrlAndUserId($hashedUrl, $userId)
382 {
383 // try first using hashed_url (to use the database index)
330 $res = $this->createQueryBuilder('e') 384 $res = $this->createQueryBuilder('e')
331 ->where('e.url = :url')->setParameter('url', urldecode($url)) 385 ->where('e.hashedUrl = :hashed_url')->setParameter('hashed_url', $hashedUrl)
386 ->andWhere('e.user = :user_id')->setParameter('user_id', $userId)
387 ->getQuery()
388 ->getResult();
389
390 if (\count($res)) {
391 return current($res);
392 }
393
394 // then try using hashed_given_url (to use the database index)
395 $res = $this->createQueryBuilder('e')
396 ->where('e.hashedGivenUrl = :hashed_given_url')->setParameter('hashed_given_url', $hashedUrl)
332 ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) 397 ->andWhere('e.user = :user_id')->setParameter('user_id', $userId)
333 ->getQuery() 398 ->getQuery()
334 ->getResult(); 399 ->getResult();
@@ -412,8 +477,8 @@ class EntryRepository extends EntityRepository
412 /** 477 /**
413 * Find all entries by url and owner. 478 * Find all entries by url and owner.
414 * 479 *
415 * @param $url 480 * @param string $url
416 * @param $userId 481 * @param int $userId
417 * 482 *
418 * @return array 483 * @return array
419 */ 484 */
@@ -427,6 +492,49 @@ class EntryRepository extends EntityRepository
427 } 492 }
428 493
429 /** 494 /**
495 * Returns a random entry, filtering by status.
496 *
497 * @param int $userId
498 * @param string $type Can be unread, archive, starred, etc
499 *
500 * @throws NoResultException
501 *
502 * @return Entry
503 */
504 public function getRandomEntry($userId, $type = '')
505 {
506 $qb = $this->getQueryBuilderByUser($userId)
507 ->select('e.id');
508
509 switch ($type) {
510 case 'unread':
511 $qb->andWhere('e.isArchived = false');
512 break;
513 case 'archive':
514 $qb->andWhere('e.isArchived = true');
515 break;
516 case 'starred':
517 $qb->andWhere('e.isStarred = true');
518 break;
519 case 'untagged':
520 $qb->leftJoin('e.tags', 't');
521 $qb->andWhere('t.id is null');
522 break;
523 }
524
525 $ids = $qb->getQuery()->getArrayResult();
526
527 if (empty($ids)) {
528 throw new NoResultException();
529 }
530
531 // random select one in the list
532 $randomId = $ids[mt_rand(0, \count($ids) - 1)]['id'];
533
534 return $this->find($randomId);
535 }
536
537 /**
430 * Return a query builder to be used by other getBuilderFor* method. 538 * Return a query builder to be used by other getBuilderFor* method.
431 * 539 *
432 * @param int $userId 540 * @param int $userId
@@ -456,15 +564,13 @@ class EntryRepository extends EntityRepository
456 /** 564 /**
457 * Return the given QueryBuilder with an orderBy() call. 565 * Return the given QueryBuilder with an orderBy() call.
458 * 566 *
459 * @param QueryBuilder $qb 567 * @param string $sortBy
460 * @param string $sortBy 568 * @param string $direction
461 * @param string $direction
462 * 569 *
463 * @return QueryBuilder 570 * @return QueryBuilder
464 */ 571 */
465 private function sortQueryBuilder(QueryBuilder $qb, $sortBy = 'createdAt', $direction = 'desc') 572 private function sortQueryBuilder(QueryBuilder $qb, $sortBy = 'createdAt', $direction = 'desc')
466 { 573 {
467 return $qb 574 return $qb->orderBy(sprintf('e.%s', $sortBy), $direction);
468 ->orderBy(sprintf('e.%s', $sortBy), $direction);
469 } 575 }
470} 576}
diff --git a/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php
index 36906761..aeadd770 100644
--- a/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php
@@ -19,16 +19,16 @@ class SiteCredentialRepository extends \Doctrine\ORM\EntityRepository
19 /** 19 /**
20 * Retrieve one username/password for the given host and userId. 20 * Retrieve one username/password for the given host and userId.
21 * 21 *
22 * @param string $host 22 * @param array $hosts An array of host to look for
23 * @param int $userId 23 * @param int $userId
24 * 24 *
25 * @return null|array 25 * @return array|null
26 */ 26 */
27 public function findOneByHostAndUser($host, $userId) 27 public function findOneByHostsAndUser($hosts, $userId)
28 { 28 {
29 $res = $this->createQueryBuilder('s') 29 $res = $this->createQueryBuilder('s')
30 ->select('s.username', 's.password') 30 ->select('s.username', 's.password')
31 ->where('s.host = :hostname')->setParameter('hostname', $host) 31 ->where('s.host IN (:hosts)')->setParameter('hosts', $hosts)
32 ->andWhere('s.user = :userId')->setParameter('userId', $userId) 32 ->andWhere('s.user = :userId')->setParameter('userId', $userId)
33 ->setMaxResults(1) 33 ->setMaxResults(1)
34 ->getQuery() 34 ->getQuery()
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php
index 3ae9d414..8464a6a5 100644
--- a/src/Wallabag/CoreBundle/Repository/TagRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php
@@ -3,6 +3,7 @@
3namespace Wallabag\CoreBundle\Repository; 3namespace Wallabag\CoreBundle\Repository;
4 4
5use Doctrine\ORM\EntityRepository; 5use Doctrine\ORM\EntityRepository;
6use Doctrine\ORM\QueryBuilder;
6use Wallabag\CoreBundle\Entity\Tag; 7use Wallabag\CoreBundle\Entity\Tag;
7 8
8class TagRepository extends EntityRepository 9class TagRepository extends EntityRepository
@@ -45,12 +46,8 @@ class TagRepository extends EntityRepository
45 */ 46 */
46 public function findAllTags($userId) 47 public function findAllTags($userId)
47 { 48 {
48 $ids = $this->createQueryBuilder('t') 49 $ids = $this->getQueryBuilderByUser($userId)
49 ->select('t.id') 50 ->select('t.id')
50 ->leftJoin('t.entries', 'e')
51 ->where('e.user = :userId')->setParameter('userId', $userId)
52 ->groupBy('t.id')
53 ->orderBy('t.slug')
54 ->getQuery() 51 ->getQuery()
55 ->getArrayResult(); 52 ->getArrayResult();
56 53
@@ -71,18 +68,30 @@ class TagRepository extends EntityRepository
71 */ 68 */
72 public function findAllFlatTagsWithNbEntries($userId) 69 public function findAllFlatTagsWithNbEntries($userId)
73 { 70 {
74 return $this->createQueryBuilder('t') 71 return $this->getQueryBuilderByUser($userId)
75 ->select('t.id, t.label, t.slug, count(e.id) as nbEntries') 72 ->select('t.id, t.label, t.slug, count(e.id) as nbEntries')
76 ->distinct(true) 73 ->distinct(true)
77 ->leftJoin('t.entries', 'e')
78 ->where('e.user = :userId')
79 ->groupBy('t.id')
80 ->orderBy('t.slug')
81 ->setParameter('userId', $userId)
82 ->getQuery() 74 ->getQuery()
83 ->getArrayResult(); 75 ->getArrayResult();
84 } 76 }
85 77
78 public function findByLabelsAndUser($labels, $userId)
79 {
80 $qb = $this->getQueryBuilderByUser($userId)
81 ->select('t.id');
82
83 $ids = $qb->andWhere($qb->expr()->in('t.label', $labels))
84 ->getQuery()
85 ->getArrayResult();
86
87 $tags = [];
88 foreach ($ids as $id) {
89 $tags[] = $this->find($id);
90 }
91
92 return $tags;
93 }
94
86 /** 95 /**
87 * Used only in test case to get a tag for our entry. 96 * Used only in test case to get a tag for our entry.
88 * 97 *
@@ -101,13 +110,9 @@ class TagRepository extends EntityRepository
101 110
102 public function findForArchivedArticlesByUser($userId) 111 public function findForArchivedArticlesByUser($userId)
103 { 112 {
104 $ids = $this->createQueryBuilder('t') 113 $ids = $this->getQueryBuilderByUser($userId)
105 ->select('t.id') 114 ->select('t.id')
106 ->leftJoin('t.entries', 'e')
107 ->where('e.user = :userId')->setParameter('userId', $userId)
108 ->andWhere('e.isArchived = true') 115 ->andWhere('e.isArchived = true')
109 ->groupBy('t.id')
110 ->orderBy('t.slug')
111 ->getQuery() 116 ->getQuery()
112 ->getArrayResult(); 117 ->getArrayResult();
113 118
@@ -118,4 +123,20 @@ class TagRepository extends EntityRepository
118 123
119 return $tags; 124 return $tags;
120 } 125 }
126
127 /**
128 * Retrieve a sorted list of tags used by a user.
129 *
130 * @param int $userId
131 *
132 * @return QueryBuilder
133 */
134 private function getQueryBuilderByUser($userId)
135 {
136 return $this->createQueryBuilder('t')
137 ->leftJoin('t.entries', 'e')
138 ->where('e.user = :userId')->setParameter('userId', $userId)
139 ->groupBy('t.id')
140 ->orderBy('t.slug');
141 }
121} 142}