diff --git a/src/view/AphrontTagView.php b/src/view/AphrontTagView.php --- a/src/view/AphrontTagView.php +++ b/src/view/AphrontTagView.php @@ -89,10 +89,6 @@ return $this->renderChildren(); } - protected function willRender() { - return; - } - final public function render() { $this->willRender(); diff --git a/src/view/AphrontView.php b/src/view/AphrontView.php --- a/src/view/AphrontView.php +++ b/src/view/AphrontView.php @@ -149,6 +149,22 @@ /* -( Rendering )---------------------------------------------------------- */ + /** + * Inconsistent, unreliable pre-rendering hook. + * + * This hook //may// fire before views render. It is not fired reliably, and + * may fire multiple times. + * + * If it does fire, views might use it to register data for later loads, but + * almost no datasources support this now; this is currently only useful for + * tokenizers. This mechanism might eventually see wider support or might be + * removed. + */ + public function willRender() { + return; + } + + abstract public function render(); diff --git a/src/view/form/AphrontFormView.php b/src/view/form/AphrontFormView.php --- a/src/view/form/AphrontFormView.php +++ b/src/view/form/AphrontFormView.php @@ -94,6 +94,7 @@ public function buildLayoutView() { foreach ($this->controls as $control) { $control->setUser($this->getUser()); + $control->willRender(); } return id(new PHUIFormLayoutView()) diff --git a/src/view/form/control/AphrontFormTokenizerControl.php b/src/view/form/control/AphrontFormTokenizerControl.php --- a/src/view/form/control/AphrontFormTokenizerControl.php +++ b/src/view/form/control/AphrontFormTokenizerControl.php @@ -6,6 +6,7 @@ private $disableBehavior; private $limit; private $placeholder; + private $handles; public function setDatasource(PhabricatorTypeaheadDatasource $datasource) { $this->datasource = $datasource; @@ -31,41 +32,17 @@ return $this; } + public function willRender() { + // Load the handles now so we'll get a bulk load later on when we actually + // render them. + $this->loadHandles(); + } + protected function renderInput() { $name = $this->getName(); - $values = nonempty($this->getValue(), array()); - - // Values may either be handles (which are now legacy/deprecated) or - // strings. Load handles for any PHIDs. - $load = array(); - $handles = array(); - $select = array(); - foreach ($values as $value) { - if ($value instanceof PhabricatorObjectHandle) { - $handles[$value->getPHID()] = $value; - $select[] = $value->getPHID(); - } else { - $load[] = $value; - $select[] = $value; - } - } - - // TODO: Once this code path simplifies, move this prefetch to setValue() - // so we can bulk load across multiple controls. - - if ($load) { - $viewer = $this->getUser(); - if (!$viewer) { - // TODO: Clean this up when handles go away. - throw new Exception( - pht('Call setUser() before rendering tokenizer string values.')); - } - $loaded_handles = $viewer->loadHandles($load); - $handles = $handles + iterator_to_array($loaded_handles); - } - // Reorder the list into input order. - $handles = array_select_keys($handles, $select); + $handles = $this->loadHandles(); + $handles = iterator_to_array($handles); if ($this->getID()) { $id = $this->getID(); @@ -112,4 +89,21 @@ return $template->render(); } + private function loadHandles() { + if ($this->handles === null) { + $viewer = $this->getUser(); + if (!$viewer) { + throw new Exception( + pht( + 'Call setUser() before rendering tokenizers. Use appendControl() '. + 'on AphrontFormView to do this easily.')); + } + + $values = nonempty($this->getValue(), array()); + $this->handles = $viewer->loadHandles($values); + } + + return $this->handles; + } + } diff --git a/src/view/phui/PHUIListView.php b/src/view/phui/PHUIListView.php --- a/src/view/phui/PHUIListView.php +++ b/src/view/phui/PHUIListView.php @@ -152,7 +152,7 @@ return $this->items; } - protected function willRender() { + public function willRender() { $key_map = array(); foreach ($this->items as $item) { $key = $item->getKey();