Changeset View
Standalone View
src/applications/badges/query/PhabricatorBadgesAwardQuery.php
<?php | <?php | ||||
final class PhabricatorBadgesAwardQuery | final class PhabricatorBadgesAwardQuery | ||||
extends PhabricatorCursorPagedPolicyAwareQuery { | extends PhabricatorCursorPagedPolicyAwareQuery { | ||||
private $badgePHIDs; | private $badgePHIDs; | ||||
private $recipientPHIDs; | private $recipientPHIDs; | ||||
private $awarderPHIDs; | private $awarderPHIDs; | ||||
private $badgeStatuses = null; | |||||
protected function willFilterPage(array $awards) { | protected function willFilterPage(array $awards) { | ||||
$badge_phids = array(); | $badge_phids = array(); | ||||
foreach ($awards as $key => $award) { | foreach ($awards as $key => $award) { | ||||
$badge_phids[] = $award->getBadgePHID(); | $badge_phids[] = $award->getBadgePHID(); | ||||
} | } | ||||
$badges = id(new PhabricatorBadgesQuery()) | $badges = id(new PhabricatorBadgesQuery()) | ||||
->setViewer($this->getViewer()) | ->setViewer($this->getViewer()) | ||||
->withPHIDs($badge_phids) | ->withPHIDs($badge_phids) | ||||
->execute(); | ->execute(); | ||||
$badges = mpull($badges, null, 'getPHID'); | $badges = mpull($badges, null, 'getPHID'); | ||||
foreach ($awards as $key => $award) { | foreach ($awards as $key => $award) { | ||||
$award_badge = idx($badges, $award->getBadgePHID()); | $award_badge = idx($badges, $award->getBadgePHID()); | ||||
if (!$award_badge) { | |||||
unset($awards[$key]); | |||||
$this->didRejectResult($award); | |||||
continue; | |||||
} | |||||
$award->attachBadge($award_badge); | $award->attachBadge($award_badge); | ||||
} | } | ||||
epriestley: Slightly more consistent is something like this:
```
if (!$award_badge) {
unset($awards… | |||||
return $awards; | return $awards; | ||||
} | } | ||||
Not Done Inline ActionsDoing this filtering here -- instead of in the query itself -- means that we may do a very large amount of unnecessary work. Suppose the user has been awarded 5,000 badges, and the first 4,995 have been archived (perhaps they were awarded in a terrible badge accident). We'll end up loading all 4,995 of those awards, then trying to load their badges, failing, and loading another group of awards. The page size for queries is normally 100, so this will issue 50 extra queries (with setLimit(2), it will issue about 2,500 extra queries). Instead, withBadgeStatuses() should cause us to ... JOIN badge ON badge.phid = award.badgePHID ... WHERE badge.status IN (%Ls) ... in the query. You can look at withAuditorPHIDs() / shouldJoinAuditor() in DiffusionCommitQuery for a similar example of this, I think. By using a JOIN, MySQL will do this work for us and status filtering won't require more queries. epriestley: Doing this filtering here -- instead of in the query itself -- means that we may do a very… | |||||
public function withBadgePHIDs(array $phids) { | public function withBadgePHIDs(array $phids) { | ||||
$this->badgePHIDs = $phids; | $this->badgePHIDs = $phids; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function withRecipientPHIDs(array $phids) { | public function withRecipientPHIDs(array $phids) { | ||||
$this->recipientPHIDs = $phids; | $this->recipientPHIDs = $phids; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function withAwarderPHIDs(array $phids) { | public function withAwarderPHIDs(array $phids) { | ||||
$this->awarderPHIDs = $phids; | $this->awarderPHIDs = $phids; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function withBadgeStatuses(array $statuses) { | |||||
$this->badgeStatuses = $statuses; | |||||
return $this; | |||||
} | |||||
private function shouldJoinBadge() { | |||||
return (bool)$this->badgeStatuses; | |||||
} | |||||
protected function loadPage() { | protected function loadPage() { | ||||
return $this->loadStandardPage($this->newResultObject()); | return $this->loadStandardPage($this->newResultObject()); | ||||
} | } | ||||
public function newResultObject() { | public function newResultObject() { | ||||
return new PhabricatorBadgesAward(); | return new PhabricatorBadgesAward(); | ||||
} | } | ||||
protected function getPrimaryTableAlias() { | |||||
return 'badges_award'; | |||||
} | |||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { | protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { | ||||
$where = parent::buildWhereClauseParts($conn); | $where = parent::buildWhereClauseParts($conn); | ||||
if ($this->badgePHIDs !== null) { | if ($this->badgePHIDs !== null) { | ||||
$where[] = qsprintf( | $where[] = qsprintf( | ||||
$conn, | $conn, | ||||
'badgePHID IN (%Ls)', | 'badges_award.badgePHID IN (%Ls)', | ||||
$this->badgePHIDs); | $this->badgePHIDs); | ||||
} | } | ||||
if ($this->recipientPHIDs !== null) { | if ($this->recipientPHIDs !== null) { | ||||
$where[] = qsprintf( | $where[] = qsprintf( | ||||
$conn, | $conn, | ||||
'recipientPHID IN (%Ls)', | 'badges_award.recipientPHID IN (%Ls)', | ||||
$this->recipientPHIDs); | $this->recipientPHIDs); | ||||
} | } | ||||
if ($this->awarderPHIDs !== null) { | if ($this->awarderPHIDs !== null) { | ||||
$where[] = qsprintf( | $where[] = qsprintf( | ||||
$conn, | $conn, | ||||
'awarderPHID IN (%Ls)', | 'badges_award.awarderPHID IN (%Ls)', | ||||
$this->awarderPHIDs); | $this->awarderPHIDs); | ||||
} | } | ||||
if ($this->badgeStatuses !== null) { | |||||
$where[] = qsprintf( | |||||
$conn, | |||||
'badges_badge.status IN (%Ls)', | |||||
$this->badgeStatuses); | |||||
} | |||||
return $where; | return $where; | ||||
} | } | ||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { | |||||
$join = parent::buildJoinClauseParts($conn); | |||||
$badges = new PhabricatorBadgesBadge(); | |||||
if ($this->shouldJoinBadge()) { | |||||
$join[] = qsprintf( | |||||
$conn, | |||||
'JOIN %T badges_badge ON badges_award.badgePHID = badges_badge.phid', | |||||
$badges->getTableName()); | |||||
} | |||||
return $join; | |||||
} | |||||
public function getQueryApplicationClass() { | public function getQueryApplicationClass() { | ||||
return 'PhabricatorBadgesApplication'; | return 'PhabricatorBadgesApplication'; | ||||
} | } | ||||
} | } |
Slightly more consistent is something like this:
...and then return $awards; at the bottom, without using $results.
The didRejectResult() will let us do a slightly better job in telling the user what happened in some situations. For example, Handles will (theoretically) be able to render "Restricted Badge Award" instead of "Invalid Badge Award". This will probably never matter in this case but does in some other cases.