Skip to content

Commit

Permalink
Merge pull request #79 from magento-troll/troll-pr
Browse files Browse the repository at this point in the history
[Troll] Bugs P0-P1
  • Loading branch information
balex13 committed Jun 8, 2016
2 parents 5e4ddd4 + ac84bdc commit 54f4cb7
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 44 deletions.
50 changes: 49 additions & 1 deletion app/code/Magento/Catalog/Model/CategoryManagement.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,20 @@ class CategoryManagement implements \Magento\Catalog\Api\CategoryManagementInter
*/
protected $categoryTree;

/**
* @var \Magento\Framework\App\ScopeResolverInterface
*/
private $scopeResolver;

/**
* @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory
*/
private $categoriesFactory;

/**
* @param \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository
* @param Category\Tree $categoryTree
* @param CollectionFactory $categoriesFactory
* @param \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoriesFactory
*/
public function __construct(
\Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
Expand All @@ -45,11 +55,49 @@ public function getTree($rootCategoryId = null, $depth = null)
if ($rootCategoryId !== null) {
/** @var \Magento\Catalog\Model\Category $category */
$category = $this->categoryRepository->get($rootCategoryId);
} elseif ($this->isAdminStore()) {
$category = $this->getTopLevelCategory();
}
$result = $this->categoryTree->getTree($this->categoryTree->getRootNode($category), $depth);
return $result;
}

/**
* Check is request use default scope
*
* @return bool
*/
private function isAdminStore()
{
return $this->getScopeResolver()->getScope()->getCode() == \Magento\Store\Model\Store::ADMIN_CODE;
}

/**
* Get store manager for operations with admin code
*
* @return \Magento\Framework\App\ScopeResolverInterface
*/
private function getScopeResolver()
{
if ($this->scopeResolver == null) {
$this->scopeResolver = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\App\ScopeResolverInterface::class);
}

return $this->scopeResolver;
}

/**
* Get top level hidden root category
*
* @return \Magento\Catalog\Model\Category
*/
private function getTopLevelCategory()
{
$categoriesCollection = $this->categoriesFactory->create();
return $categoriesCollection->addFilter('level', ['eq' => 0])->getFirstItem();
}

/**
* {@inheritdoc}
*/
Expand Down
3 changes: 2 additions & 1 deletion app/code/Magento/Catalog/Model/ImageUploader.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ public function saveFileToTmpDir($fileId)
->getStore()
->getBaseUrl(
\Magento\Framework\UrlInterface::URL_TYPE_MEDIA
) . $this->getFilePath($baseTmpPath, $result['name']);
) . $this->getFilePath($baseTmpPath, $result['file']);
$result['name'] = $result['file'];

if (isset($result['file'])) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ abstract class AbstractAction
*/
protected $metadataPool;

/**
* @var string
*/
protected $tempTreeIndexTableName;

/**
* @param ResourceConnection $resource
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
Expand Down Expand Up @@ -368,6 +373,8 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
$rootCatIds = explode('/', $this->getPathFromCategoryId($store->getRootCategoryId()));
array_pop($rootCatIds);

$temporaryTreeTable = $this->makeTempCategoryTreeIndex();

$productMetadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
$categoryMetadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\CategoryInterface::class);
$productLinkField = $productMetadata->getLinkField();
Expand All @@ -377,17 +384,15 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
['cc' => $this->getTable('catalog_category_entity')],
[]
)->joinInner(
['cc2' => $this->getTable('catalog_category_entity')],
'cc2.path LIKE ' . $this->connection->getConcatSql(
[$this->connection->quoteIdentifier('cc.path'), $this->connection->quote('/%')]
) . ' AND cc.entity_id NOT IN (' . implode(
['cc2' => $temporaryTreeTable],
'cc2.parent_id = cc.entity_id AND cc.entity_id NOT IN (' . implode(
',',
$rootCatIds
) . ')',
[]
)->joinInner(
['ccp' => $this->getTable('catalog_category_product')],
'ccp.category_id = cc2.entity_id',
'ccp.category_id = cc2.child_id',
[]
)->joinInner(
['cpe' => $this->getTable('catalog_product_entity')],
Expand Down Expand Up @@ -459,6 +464,92 @@ protected function createAnchorSelect(\Magento\Store\Model\Store $store)
);
}

/**
* Get temporary table name for concurrent indexing in persistent connection
* Temp table name is NOT shared between action instances and each action has it's own temp tree index
*
* @return string
*/
protected function getTemporaryTreeIndexTableName()
{
if (empty($this->tempTreeIndexTableName)) {
$this->tempTreeIndexTableName = $this->connection->getTableName('temp_catalog_category_tree_index')
. '_'
. substr(md5(time() . rand(0, 999999999)), 0, 8);
}

return $this->tempTreeIndexTableName;
}

/**
* Build and populate the temporary category tree index table
*
* Returns the name of the temporary table to use in queries.
*
* @return string
*/
protected function makeTempCategoryTreeIndex()
{
// Note: this temporary table is per-connection, so won't conflict by prefix.
$temporaryName = $this->getTemporaryTreeIndexTableName();

$temporaryTable = $this->connection->newTable($temporaryName);
$temporaryTable->addColumn(
'parent_id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['nullable' => false, 'unsigned' => true]
);
$temporaryTable->addColumn(
'child_id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['nullable' => false, 'unsigned' => true]
);
// Each entry will be unique.
$temporaryTable->addIndex(
'idx_primary',
['parent_id', 'child_id'],
['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_PRIMARY]
);

// Drop the temporary table in case it already exists on this (persistent?) connection.
$this->connection->dropTemporaryTable($temporaryName);
$this->connection->createTemporaryTable($temporaryTable);

$this->fillTempCategoryTreeIndex($temporaryName);

return $temporaryName;
}

/**
* Populate the temporary category tree index table
*
* @param string $temporaryName
*/
protected function fillTempCategoryTreeIndex($temporaryName)
{
// This finds all children (cc2) that descend from a parent (cc) by path.
// For example, cc.path may be '1/2', and cc2.path may be '1/2/3/4/5'.
$temporarySelect = $this->connection->select()->from(
['cc' => $this->getTable('catalog_category_entity')],
['parent_id' => 'entity_id']
)->joinInner(
['cc2' => $this->getTable('catalog_category_entity')],
'cc2.path LIKE ' . $this->connection->getConcatSql(
[$this->connection->quoteIdentifier('cc.path'), $this->connection->quote('/%')]
),
['child_id' => 'entity_id']
);

$this->connection->query(
$temporarySelect->insertFromSelect(
$temporaryName,
['parent_id', 'child_id']
)
);
}

/**
* Retrieve select for reindex products of non anchor categories
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ public function setStore($store)
/**
* Set store scope
*
* @param int|string|\Magento\Store\Model\Store $storeId
* @param int|string|\Magento\Store\Api\Data\StoreInterface $storeId
* @return $this
*/
public function setStoreId($storeId)
{
if ($storeId instanceof \Magento\Store\Model\Store) {
if ($storeId instanceof \Magento\Store\Api\Data\StoreInterface) {
$storeId = $storeId->getId();
}
$this->_storeId = (int)$storeId;
Expand Down
122 changes: 117 additions & 5 deletions app/code/Magento/Catalog/Test/Unit/Model/CategoryManagementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,52 @@ class CategoryManagementTest extends \PHPUnit_Framework_TestCase
*/
protected $categoriesFactoryMock;

/**
* @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
*/
protected $objectManagerHelper;

/**
* @var \Magento\Framework\App\ScopeResolverInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $scopeResolverMock;

/**
* @var \Magento\Framework\App\ScopeInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $scopeMock;

protected function setUp()
{

$this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->categoryRepositoryMock = $this->getMock('Magento\Catalog\Api\CategoryRepositoryInterface');
$this->categoryTreeMock = $this->getMock('Magento\Catalog\Model\Category\Tree', [], [], '', false);
$this->categoriesFactoryMock = $this->getMock(
'Magento\Catalog\Model\ResourceModel\Category\CollectionFactory',
['create'],
['create', 'addFilter', 'getFirstItem'],
[],
'',
false
);
$this->model = new \Magento\Catalog\Model\CategoryManagement(
$this->categoryRepositoryMock,
$this->categoryTreeMock,
$this->categoriesFactoryMock

$this->model = $this->objectManagerHelper->getObject(
'\Magento\Catalog\Model\CategoryManagement',
[
'categoryRepository' => $this->categoryRepositoryMock,
'categoryTree' => $this->categoryTreeMock,
'categoriesFactory' => $this->categoriesFactoryMock
]
);

$this->scopeResolverMock = $this->getMock('\Magento\Framework\App\ScopeResolverInterface');

$this->scopeMock = $this->getMock('Magento\Framework\App\ScopeInterface');

$this->objectManagerHelper->setBackwardCompatibleProperty(
$this->model,
'scopeResolver',
$this->scopeResolverMock
);
}

Expand Down Expand Up @@ -82,12 +113,93 @@ public function testGetTreeWithNullArguments()
$this->categoryRepositoryMock->expects($this->never())->method('get');
$this->categoryTreeMock->expects($this->once())->method('getRootNode')->with($category)->willReturn(null);
$this->categoryTreeMock->expects($this->exactly(2))->method('getTree')->with($category, $depth);

$this->scopeResolverMock
->expects($this->once())
->method('getScope')
->willReturn($this->scopeMock);

$this->scopeMock
->expects($this->once())
->method('getCode')
->willReturn(1);

$this->assertEquals(
$this->model->getTree($rootCategoryId, $depth),
$this->categoryTreeMock->getTree(null, null)
);
}

/**
* Check is possible to get all categories for all store starting from top level root category
*/
public function testGetTreeForAllScope()
{

$rootCategoryId = null;
$depth = null;
$category = null;
$categoriesMock = $this->getMock(
'\Magento\Catalog\Model\ResourceModel\Category\Collection',
[],
[],
'',
false
);
$categoryMock = $this->getMock(
'\Magento\Catalog\Model\Category',
[],
[],
'categoryMock',
false
);
$categoriesMock
->expects($this->once())
->method('getFirstItem')
->willReturn($categoryMock);
$categoriesMock
->expects($this->once())
->method('addFilter')
->with('level', ['eq' => 0])
->willReturnSelf();
$this->categoriesFactoryMock
->expects($this->once())
->method('create')
->willReturn($categoriesMock);
$nodeMock = $this->getMock(
'\Magento\Framework\Data\Tree\Node',
[],
[],
'',
false
);

$this->categoryTreeMock
->expects($this->once())
->method('getTree')
->with($nodeMock, $depth);
$this->categoryRepositoryMock
->expects($this->never())
->method('get');
$this->categoryTreeMock
->expects($this->once())
->method('getRootNode')
->with($categoryMock)
->willReturn($nodeMock);

$this->scopeResolverMock
->expects($this->once())
->method('getScope')
->willReturn($this->scopeMock);

$this->scopeMock
->expects($this->once())
->method('getCode')
->willReturn(\Magento\Store\Model\Store::ADMIN_CODE);

$this->model->getTree();
}

public function testMove()
{
$categoryId = 2;
Expand Down
Loading

0 comments on commit 54f4cb7

Please sign in to comment.