-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#7698 - modularize admin global search #9192
Changes from all commits
36f3410
3af1834
cec4e90
ce0d756
a0d3bc2
5c5d676
3391728
4a3090c
ce31871
3e5a937
4dc0667
6f26c3e
820c116
f95d6e2
fa050cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
/** | ||
* | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
namespace Magento\Backend\Api\Search; | ||
|
||
use Magento\Backend\Model\Search\SearchCriteria; | ||
|
||
/** | ||
* @api | ||
*/ | ||
interface ItemsInterface | ||
{ | ||
/** | ||
* Get the search result items | ||
* | ||
* @param SearchCriteria $searchCriteria | ||
* @return array | ||
*/ | ||
public function getResults(SearchCriteria $searchCriteria); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -6,6 +6,10 @@ | |||||||||||||||
*/ | ||||||||||||||||
namespace Magento\Backend\Controller\Adminhtml\Index; | ||||||||||||||||
|
||||||||||||||||
use Magento\Backend\Model\Search\ItemFactory; | ||||||||||||||||
use Magento\Backend\Model\Search\SearchCriteria; | ||||||||||||||||
use Magento\Backend\Model\Search\SearchCriteriaFactory; | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @api | ||||||||||||||||
*/ | ||||||||||||||||
|
@@ -24,16 +28,44 @@ class GlobalSearch extends \Magento\Backend\Controller\Adminhtml\Index | |||||||||||||||
protected $_searchModules; | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* modules that support preview | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw this controller should be marked with PHP DocBlock as @ api Btw it makes sense to use another extension point for these purposes |
||||||||||||||||
* | ||||||||||||||||
* @var array | ||||||||||||||||
*/ | ||||||||||||||||
private $previewModules; | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @var ItemFactory | ||||||||||||||||
*/ | ||||||||||||||||
private $itemFactory; | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @var SearchCriteriaFactory | ||||||||||||||||
*/ | ||||||||||||||||
private $criteriaFactory; | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* Initialize dependencies | ||||||||||||||||
* | ||||||||||||||||
* @param \Magento\Backend\App\Action\Context $context | ||||||||||||||||
* @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory | ||||||||||||||||
* @param ItemFactory $itemFactory | ||||||||||||||||
* @param SearchCriteriaFactory $criteriaFactory | ||||||||||||||||
* @param array $searchModules | ||||||||||||||||
* @param array $previewModules | ||||||||||||||||
*/ | ||||||||||||||||
public function __construct( | ||||||||||||||||
\Magento\Backend\App\Action\Context $context, | ||||||||||||||||
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, | ||||||||||||||||
array $searchModules = [] | ||||||||||||||||
ItemFactory $itemFactory, | ||||||||||||||||
SearchCriteriaFactory $criteriaFactory, | ||||||||||||||||
array $searchModules = [], | ||||||||||||||||
array $previewModules = [] | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Such changes to the class constructor violate backwards compatibility policy:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like
And then loading the optional constructor if null via |
||||||||||||||||
) { | ||||||||||||||||
$this->itemFactory = $itemFactory; | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please refer to the Adding a constructor parameter section of Backwards Compatible Development Guide for a definition and code example for adding constructor parameters. |
||||||||||||||||
$this->criteriaFactory = $criteriaFactory; | ||||||||||||||||
$this->_searchModules = $searchModules; | ||||||||||||||||
$this->previewModules = $previewModules; | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please consider extracting this logic to a dedicated model, since It does not look like it is in controllers responsibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ishakhsuvarov I agree whit this, but I don't see a solution to move the search logic to a separate class and keep backwards compatibility (as suggested in other comments) at the same time. Maybe I can move this to a separate class, and you release it when you break BC for something else anyway. |
||||||||||||||||
parent::__construct($context); | ||||||||||||||||
$this->resultJsonFactory = $resultJsonFactory; | ||||||||||||||||
} | ||||||||||||||||
|
@@ -55,7 +87,11 @@ public function execute() | |||||||||||||||
'description' => __('You need more permissions to do this.'), | ||||||||||||||||
]; | ||||||||||||||||
} else { | ||||||||||||||||
if (empty($this->_searchModules)) { | ||||||||||||||||
$previewItems = $this->getPreviewItems(); | ||||||||||||||||
$searchItems = $this->getSearchItems(); | ||||||||||||||||
$items = array_merge_recursive($items, $previewItems, $searchItems); | ||||||||||||||||
|
||||||||||||||||
if (empty($items)) { | ||||||||||||||||
$items[] = [ | ||||||||||||||||
'id' => 'error', | ||||||||||||||||
'type' => __('Error'), | ||||||||||||||||
|
@@ -64,34 +100,67 @@ public function execute() | |||||||||||||||
'Please make sure that all global admin search modules are installed and activated.' | ||||||||||||||||
), | ||||||||||||||||
]; | ||||||||||||||||
} else { | ||||||||||||||||
$start = $this->getRequest()->getParam('start', 1); | ||||||||||||||||
$limit = $this->getRequest()->getParam('limit', 10); | ||||||||||||||||
$query = $this->getRequest()->getParam('query', ''); | ||||||||||||||||
foreach ($this->_searchModules as $searchConfig) { | ||||||||||||||||
if ($searchConfig['acl'] && !$this->_authorization->isAllowed($searchConfig['acl'])) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$className = $searchConfig['class']; | ||||||||||||||||
if (empty($className)) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
$searchInstance = $this->_objectManager->create($className); | ||||||||||||||||
$results = $searchInstance->setStart( | ||||||||||||||||
$start | ||||||||||||||||
)->setLimit( | ||||||||||||||||
$limit | ||||||||||||||||
)->setQuery( | ||||||||||||||||
$query | ||||||||||||||||
)->load()->getResults(); | ||||||||||||||||
$items = array_merge_recursive($items, $results); | ||||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** @var \Magento\Framework\Controller\Result\Json $resultJson */ | ||||||||||||||||
$resultJson = $this->resultJsonFactory->create(); | ||||||||||||||||
return $resultJson->setData($items); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please provide a clear description for this function |
||||||||||||||||
* Retrieve links to certain entities in the global search | ||||||||||||||||
* | ||||||||||||||||
* @return array | ||||||||||||||||
*/ | ||||||||||||||||
private function getPreviewItems() | ||||||||||||||||
{ | ||||||||||||||||
$result = []; | ||||||||||||||||
$query = $this->getRequest()->getParam('query', ''); | ||||||||||||||||
foreach ($this->previewModules as $previewConfig) { | ||||||||||||||||
if ($previewConfig['acl'] && !$this->_authorization->isAllowed($previewConfig['acl'])) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
if (!isset($previewConfig['url']) || !isset($previewConfig['text'])) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
$result[] = [ | ||||||||||||||||
'url' => $this->getUrl($previewConfig['url']).'?search='.$query, | ||||||||||||||||
'name' => __($previewConfig['text'], $query) | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we don't have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @maghamed Any pointers on how to handle this so the translation will work and still be able to keep the texts in DI? O maybe move them somewhere else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tzyganu I found a possibility to specify "string" type to be translatable in DI.xml magento2/lib/internal/Magento/Framework/Data/etc/argument/types.xsd Lines 31 to 33 in 99e85cb
Here is how this attribute interpreted magento2/lib/internal/Magento/Framework/Data/Argument/Interpreter/StringUtils.php Lines 41 to 44 in 99e85cb
But there is no any usage of this attribute in Magento. Like this one:
or this one:
|
||||||||||||||||
]; | ||||||||||||||||
} | ||||||||||||||||
return $result; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* Retrieve all entity items that should appear in global search | ||||||||||||||||
* | ||||||||||||||||
* @return array | ||||||||||||||||
*/ | ||||||||||||||||
private function getSearchItems() | ||||||||||||||||
{ | ||||||||||||||||
$items = []; | ||||||||||||||||
$start = $this->getRequest()->getParam('start', 1); | ||||||||||||||||
$limit = $this->getRequest()->getParam('limit', 10); | ||||||||||||||||
$query = $this->getRequest()->getParam('query', ''); | ||||||||||||||||
/** @var SearchCriteria $searchCriteria */ | ||||||||||||||||
$searchCriteria = $this->criteriaFactory->create(); | ||||||||||||||||
$searchCriteria->setLimit($limit); | ||||||||||||||||
$searchCriteria->setStart($start); | ||||||||||||||||
$searchCriteria->setQuery($query); | ||||||||||||||||
foreach ($this->_searchModules as $searchConfig) { | ||||||||||||||||
if ($searchConfig['acl'] && !$this->_authorization->isAllowed($searchConfig['acl'])) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
$className = $searchConfig['class']; | ||||||||||||||||
if (empty($className)) { | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
$searchInstance = $this->itemFactory->create($className); | ||||||||||||||||
$results = $searchInstance->getResults($searchCriteria); | ||||||||||||||||
$items = array_merge_recursive($items, $results); | ||||||||||||||||
} | ||||||||||||||||
return $items; | ||||||||||||||||
} | ||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
|
||
namespace Magento\Backend\Model\Search; | ||
|
||
use Magento\Backend\Api\Search\ItemsInterface; | ||
use Magento\Framework\ObjectManagerInterface; | ||
|
||
class ItemFactory | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brief description for this class may be added here |
||
{ | ||
/** | ||
* @var \Magento\Framework\ObjectManagerInterface | ||
*/ | ||
private $objectManager; | ||
|
||
/** | ||
* @param \Magento\Framework\ObjectManagerInterface $objectManager | ||
*/ | ||
public function __construct(ObjectManagerInterface $objectManager) | ||
{ | ||
$this->objectManager = $objectManager; | ||
} | ||
|
||
/** | ||
* Create new search items provider instance | ||
* | ||
* @param string $instanceName | ||
* @param array $data | ||
* @return ItemsInterface | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @throws annotation may be added here as well |
||
*/ | ||
public function create($instanceName, array $data = []) | ||
{ | ||
$implements = class_implements($instanceName); | ||
if (!isset($implements[ItemsInterface::class])) { | ||
throw new \LogicException( | ||
"The class '{$instanceName}' does not implement " . ItemsInterface::class | ||
); | ||
} | ||
$object = $this->objectManager->get($instanceName, $data); | ||
return $object; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
|
||
namespace Magento\Backend\Model\Search; | ||
|
||
class SearchCriteria | ||
{ | ||
/** | ||
* @var int | ||
*/ | ||
private $limit; | ||
|
||
/** | ||
* @var int | ||
*/ | ||
private $start; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $query; | ||
|
||
/** | ||
* @return int | ||
*/ | ||
public function getLimit() | ||
{ | ||
return $this->limit; | ||
} | ||
|
||
/** | ||
* @param int $limit | ||
* @return void | ||
*/ | ||
public function setLimit($limit) | ||
{ | ||
$this->limit = $limit; | ||
} | ||
|
||
/** | ||
* @return int | ||
*/ | ||
public function getStart() | ||
{ | ||
return $this->start; | ||
} | ||
|
||
/** | ||
* @param int $start | ||
* @return void | ||
*/ | ||
public function setStart($start) | ||
{ | ||
$this->start = $start; | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
public function getQuery() | ||
{ | ||
return $this->query; | ||
} | ||
|
||
/** | ||
* @param string $query | ||
* @return void | ||
*/ | ||
public function setQuery($query) | ||
{ | ||
$this->query = $query; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest renaming this interface, as Items is too broad definition.
Also
getResults
method inItemsInterface
creates confusion, since there may not be results of items.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ishakhsuvarov do you have a suggestion for @tzyganu in this case? I could think that
ResultsInterface
andgetResults
would work well.