Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Url.php
Go to the documentation of this file.
1 <?php
7 
18 
26 {
32  protected $_stores;
33 
39  protected $_categoryAttributes = [];
40 
46  protected $_productAttributes = [];
47 
53  protected $_productLimit = 250;
54 
60  protected $_rootChildrenIds = [];
61 
65  protected $_logger;
66 
72  protected $_catalogCategory;
73 
79  protected $_catalogProduct;
80 
86  protected $_eavConfig;
87 
93  protected $_storeManager;
94 
98  protected $productResource;
99 
103  protected $metadataPool;
104 
108  private $tableMaintainer;
109 
121  public function __construct(
122  \Magento\Framework\Model\ResourceModel\Db\Context $context,
124  \Magento\Eav\Model\Config $eavConfig,
126  \Magento\Catalog\Model\Category $catalogCategory,
127  \Psr\Log\LoggerInterface $logger,
128  $connectionName = null,
129  TableMaintainer $tableMaintainer = null
130  ) {
131  $this->_storeManager = $storeManager;
132  $this->_eavConfig = $eavConfig;
133  $this->productResource = $productResource;
134  $this->_catalogCategory = $catalogCategory;
135  $this->_logger = $logger;
136  parent::__construct($context, $connectionName);
137  $this->tableMaintainer = $tableMaintainer ?: ObjectManager::getInstance()->get(TableMaintainer::class);
138  }
139 
145  protected function _construct()
146  {
147  $this->_init('url_rewrite', 'url_rewrite_id');
148  }
149 
156  public function getStores($storeId = null)
157  {
158  if ($this->_stores === null) {
159  $this->_stores = $this->_prepareStoreRootCategories($this->_storeManager->getStores());
160  }
161  if ($storeId && isset($this->_stores[$storeId])) {
162  return $this->_stores[$storeId];
163  }
164  return $this->_stores;
165  }
166 
175  protected function _getCategoryAttribute($attributeCode, $categoryIds, $storeId)
176  {
177  $linkField = $this->getMetadataPool()->getMetadata(CategoryInterface::class)->getLinkField();
178  $identifierFiled = $this->getMetadataPool()->getMetadata(CategoryInterface::class)->getIdentifierField();
179 
180  $connection = $this->getConnection();
181  if (!isset($this->_categoryAttributes[$attributeCode])) {
182  $attribute = $this->_catalogCategory->getResource()->getAttribute($attributeCode);
183 
184  $this->_categoryAttributes[$attributeCode] = [
185  'entity_type_id' => $attribute->getEntityTypeId(),
186  'attribute_id' => $attribute->getId(),
187  'table' => $attribute->getBackend()->getTable(),
188  'is_global' => $attribute->getIsGlobal(),
189  'is_static' => $attribute->isStatic(),
190  ];
191  unset($attribute);
192  }
193 
194  if (!is_array($categoryIds)) {
195  $categoryIds = [$categoryIds];
196  }
197 
198  $attributeTable = $this->_categoryAttributes[$attributeCode]['table'];
199  $select = $connection->select();
200  $bind = [];
201  if ($this->_categoryAttributes[$attributeCode]['is_static']) {
202  $select->from(
203  $this->getTable('catalog_category_entity'),
204  ['value' => $attributeCode, 'entity_id' => 'entity_id']
205  )->where(
206  'entity_id IN(?)',
207  $categoryIds
208  );
209  } elseif ($this->_categoryAttributes[$attributeCode]['is_global'] || $storeId == 0) {
210  $select->from(
211  ['t1' =>$this->getTable('catalog_category_entity')],
212  [$identifierFiled]
213  )->joinLeft(
214  ['e' => $attributeTable],
215  "t1.{$linkField} = e.{$linkField}",
216  ['value']
217  )->where(
218  "t1.{$identifierFiled} IN(?)",
219  $categoryIds
220  )->where(
221  'e.attribute_id = :attribute_id'
222  )->where(
223  'e.store_id = ?',
224  0
225  );
226 
227  $bind['attribute_id'] = $this->_categoryAttributes[$attributeCode]['attribute_id'];
228  } else {
229  $valueExpr = $connection->getCheckSql('t2.value_id > 0', 't2.value', 't1.value');
230  $select->from(
231  ['t1' => $attributeTable],
232  [$identifierFiled => 'e.'.$identifierFiled, 'value' => $valueExpr]
233  )->joinLeft(
234  ['t2' => $attributeTable],
235  "t1.{$linkField} = t2.{$linkField} AND t1.attribute_id = t2.attribute_id AND t2.store_id = :store_id",
236  []
237  )->joinLeft(
238  ['e' => $this->getTable('catalog_category_entity')],
239  "e.{$linkField} = t1.{$linkField}",
240  []
241  )->where(
242  't1.store_id = ?',
243  0
244  )->where(
245  't1.attribute_id = :attribute_id'
246  )->where(
247  "e.entity_id IN(?)",
248  $categoryIds
249  )->group('e.entity_id');
250 
251  $bind['attribute_id'] = $this->_categoryAttributes[$attributeCode]['attribute_id'];
252  $bind['store_id'] = $storeId;
253  }
254 
255  $rowSet = $connection->fetchAll($select, $bind);
256 
257  $attributes = [];
258  foreach ($rowSet as $row) {
259  $attributes[$row[$identifierFiled]] = $row['value'];
260  }
261  unset($rowSet);
262  foreach ($categoryIds as $categoryId) {
263  if (!isset($attributes[$categoryId])) {
264  $attributes[$categoryId] = null;
265  }
266  }
267 
268  return $attributes;
269  }
270 
280  {
281  $connection = $this->getConnection();
282  if (!isset($this->_productAttributes[$attributeCode])) {
283  $attribute = $this->productResource->getAttribute($attributeCode);
284 
285  $this->_productAttributes[$attributeCode] = [
286  'entity_type_id' => $attribute->getEntityTypeId(),
287  'attribute_id' => $attribute->getId(),
288  'table' => $attribute->getBackend()->getTable(),
289  'is_global' => $attribute->getIsGlobal(),
290  ];
291  unset($attribute);
292  }
293 
294  if (!is_array($productIds)) {
296  }
297  $bind = ['attribute_id' => $this->_productAttributes[$attributeCode]['attribute_id']];
298  $select = $connection->select();
299  $attributeTable = $this->_productAttributes[$attributeCode]['table'];
300  if ($this->_productAttributes[$attributeCode]['is_global'] || $storeId == 0) {
301  $select->from(
302  $attributeTable,
303  ['entity_id', 'value']
304  )->where(
305  'attribute_id = :attribute_id'
306  )->where(
307  'store_id = ?',
308  0
309  )->where(
310  'entity_id IN(?)',
312  );
313  } else {
314  $valueExpr = $connection->getCheckSql('t2.value_id > 0', 't2.value', 't1.value');
315  $select->from(
316  ['t1' => $attributeTable],
317  ['entity_id', 'value' => $valueExpr]
318  )->joinLeft(
319  ['t2' => $attributeTable],
320  't1.entity_id = t2.entity_id AND t1.attribute_id = t2.attribute_id AND t2.store_id=:store_id',
321  []
322  )->where(
323  't1.store_id = ?',
324  0
325  )->where(
326  't1.attribute_id = :attribute_id'
327  )->where(
328  't1.entity_id IN(?)',
330  );
331  $bind['store_id'] = $storeId;
332  }
333 
334  $rowSet = $connection->fetchAll($select, $bind);
335 
336  $attributes = [];
337  foreach ($rowSet as $row) {
338  $attributes[$row['entity_id']] = $row['value'];
339  }
340  unset($rowSet);
341  foreach ($productIds as $productId) {
342  if (!isset($attributes[$productId])) {
343  $attributes[$productId] = null;
344  }
345  }
346 
347  return $attributes;
348  }
349 
356  protected function _prepareCategoryParentId(\Magento\Framework\DataObject $category)
357  {
358  if ($category->getPath() != $category->getId()) {
359  $split = explode('/', $category->getPath());
360  $category->setParentId($split[count($split) - 2]);
361  } else {
362  $category->setParentId(0);
363  }
364  return $this;
365  }
366 
374  {
375  $rootCategoryIds = [];
376  foreach ($stores as $store) {
377  /* @var $store \Magento\Store\Model\Store */
378  $rootCategoryIds[$store->getRootCategoryId()] = $store->getRootCategoryId();
379  }
380  if ($rootCategoryIds) {
381  $categories = $this->_getCategories($rootCategoryIds);
382  }
383  foreach ($stores as $store) {
384  /* @var $store \Magento\Store\Model\Store */
385  $rootCategoryId = $store->getRootCategoryId();
386  if (isset($categories[$rootCategoryId])) {
387  $store->setRootCategoryPath($categories[$rootCategoryId]->getPath());
388  $store->setRootCategory($categories[$rootCategoryId]);
389  } else {
390  unset($stores[$store->getId()]);
391  }
392  }
393  return $stores;
394  }
395 
407  protected function _getCategories($categoryIds, $storeId = null, $path = null)
408  {
409  $isActiveAttribute = $this->_eavConfig->getAttribute(\Magento\Catalog\Model\Category::ENTITY, 'is_active');
410  $categories = [];
411  $connection = $this->getConnection();
412 
413  $meta = $this->getMetadataPool()->getMetadata(CategoryInterface::class);
414  $linkField = $meta->getLinkField();
415 
416  if (!is_array($categoryIds)) {
417  $categoryIds = [$categoryIds];
418  }
419  $isActiveExpr = $connection->getCheckSql('c.value_id > 0', 'c.value', 'c.value');
420  $select = $connection->select()->from(
421  ['main_table' => $this->getTable('catalog_category_entity')],
422  [
423  'main_table.entity_id',
424  'main_table.parent_id',
425  'main_table.level',
426  'is_active' => $isActiveExpr,
427  'main_table.path'
428  ]
429  );
430 
431  // Prepare variables for checking whether categories belong to store
432  if ($path === null) {
433  $select->where('main_table.entity_id IN(?)', $categoryIds);
434  } else {
435  // Ensure that path ends with '/', otherwise we can get wrong results - e.g. $path = '1/2' will get '1/20'
436  if (substr($path, -1) != '/') {
437  $path .= '/';
438  }
439 
440  $select->where('main_table.path LIKE ?', $path . '%')->order('main_table.path');
441  }
442  $table = $this->getTable('catalog_category_entity_int');
443  $select->joinLeft(
444  ['d' => $table],
445  "d.attribute_id = :attribute_id AND d.store_id = 0 AND d.{$linkField} = main_table.{$linkField}",
446  []
447  )->joinLeft(
448  ['c' => $table],
449  "c.attribute_id = :attribute_id AND c.store_id = :store_id AND c.{$linkField} = main_table.{$linkField}",
450  []
451  );
452 
453  if ($storeId !== null) {
454  $rootCategoryPath = $this->getStores($storeId)->getRootCategoryPath();
455  $rootCategoryPathLength = strlen($rootCategoryPath);
456  }
457  $bind = ['attribute_id' => (int)$isActiveAttribute->getId(), 'store_id' => (int)$storeId];
458 
459  $rowSet = $connection->fetchAll($select, $bind);
460  foreach ($rowSet as $row) {
461  if ($storeId !== null) {
462  // Check the category to be either store's root or its descendant
463  // First - check that category's start is the same as root category
464  if (substr($row['path'], 0, $rootCategoryPathLength) != $rootCategoryPath) {
465  continue;
466  }
467  // Second - check non-root category - that it's really a descendant, not a simple string match
468  if (strlen($row['path']) > $rootCategoryPathLength && $row['path'][$rootCategoryPathLength] != '/') {
469  continue;
470  }
471  }
472 
473  $category = new \Magento\Framework\DataObject($row);
474  $category->setId($row['entity_id']);
475  $category->setEntityId($row['entity_id']);
476  $category->setStoreId($storeId);
478 
479  $categories[$category->getId()] = $category;
480  }
481  unset($rowSet);
482 
483  if ($storeId !== null && $categories) {
484  foreach (['name', 'url_key', 'url_path'] as $attributeCode) {
486  $attributeCode,
487  array_keys($categories),
488  $category->getStoreId()
489  );
490  foreach ($attributes as $categoryId => $attributeValue) {
491  $categories[$categoryId]->setData($attributeCode, $attributeValue);
492  }
493  }
494  }
495 
496  return $categories;
497  }
498 
506  public function getCategory($categoryId, $storeId)
507  {
508  if (!$categoryId || !$storeId) {
509  return false;
510  }
511 
512  $categories = $this->_getCategories($categoryId, $storeId);
513  if (isset($categories[$categoryId])) {
514  return $categories[$categoryId];
515  }
516  return false;
517  }
518 
526  public function getCategories($categoryIds, $storeId)
527  {
528  if (!$categoryIds || !$storeId) {
529  return false;
530  }
531 
532  return $this->_getCategories($categoryIds, $storeId);
533  }
534 
544  protected function _getProducts($productIds, $storeId, $entityId, &$lastEntityId)
545  {
546  $products = [];
547  $websiteId = $this->_storeManager->getStore($storeId)->getWebsiteId();
548  $connection = $this->getConnection();
549  if ($productIds !== null) {
550  if (!is_array($productIds)) {
552  }
553  }
554  $bind = ['website_id' => (int)$websiteId, 'entity_id' => (int)$entityId];
555  $select = $connection->select()->useStraightJoin(
556  true
557  )->from(
558  ['e' => $this->getTable('catalog_product_entity')],
559  ['entity_id']
560  )->join(
561  ['w' => $this->getTable('catalog_product_website')],
562  'e.entity_id = w.product_id AND w.website_id = :website_id',
563  []
564  )->where(
565  'e.entity_id > :entity_id'
566  )->order(
567  'e.entity_id'
568  )->limit(
569  $this->_productLimit
570  );
571  if ($productIds !== null) {
572  $select->where('e.entity_id IN(?)', $productIds);
573  }
574 
575  $rowSet = $connection->fetchAll($select, $bind);
576  foreach ($rowSet as $row) {
577  $product = new \Magento\Framework\DataObject($row);
578  $product->setId($row['entity_id']);
579  $product->setEntityId($row['entity_id']);
580  $product->setCategoryIds([]);
581  $product->setStoreId($storeId);
582  $products[$product->getId()] = $product;
583  $lastEntityId = $product->getId();
584  }
585 
586  unset($rowSet);
587 
588  if ($products) {
589  $select = $connection->select()->from(
590  $this->getTable('catalog_category_product'),
591  ['product_id', 'category_id']
592  )->where(
593  'product_id IN(?)',
594  array_keys($products)
595  );
596  $categories = $connection->fetchAll($select);
597  foreach ($categories as $category) {
598  $productId = $category['product_id'];
599  $categoryIds = $products[$productId]->getCategoryIds();
600  $categoryIds[] = $category['category_id'];
601  $products[$productId]->setCategoryIds($categoryIds);
602  }
603 
604  foreach (['name', 'url_key', 'url_path'] as $attributeCode) {
605  $attributes = $this->_getProductAttribute($attributeCode, array_keys($products), $storeId);
606  foreach ($attributes as $productId => $attributeValue) {
607  $products[$productId]->setData($attributeCode, $attributeValue);
608  }
609  }
610  }
611 
612  return $products;
613  }
614 
622  public function getProduct($productId, $storeId)
623  {
624  $entityId = 0;
625  $products = $this->_getProducts($productId, $storeId, 0, $entityId);
626  if (isset($products[$productId])) {
627  return $products[$productId];
628  }
629  return false;
630  }
631 
639  public function getProductsByStore($storeId, &$lastEntityId)
640  {
641  return $this->_getProducts(null, $storeId, $lastEntityId, $lastEntityId);
642  }
643 
658  public function getRewriteByProductStore(array $products)
659  {
660  $result = [];
661 
662  if (empty($products)) {
663  return $result;
664  }
665  $connection = $this->getConnection();
666 
667  $storesProducts = [];
668  foreach ($products as $productId => $storeId) {
669  $storesProducts[$storeId][] = $productId;
670  }
671 
672  foreach ($storesProducts as $storeId => $productIds) {
673  $select = $connection->select()->from(
674  ['i' => $this->tableMaintainer->getMainTable($storeId)],
675  ['product_id', 'store_id', 'visibility']
676  )->joinLeft(
677  ['u' => $this->getMainTable()],
678  'i.product_id = u.entity_id AND i.store_id = u.store_id'
679  . ' AND u.entity_type = "' . ProductUrlRewriteGenerator::ENTITY_TYPE . '"',
680  ['request_path']
681  )->joinLeft(
682  ['r' => $this->getTable('catalog_url_rewrite_product_category')],
683  'u.url_rewrite_id = r.url_rewrite_id AND r.category_id is NULL',
684  []
685  );
686 
687  $bind = [];
688  foreach ($productIds as $productId) {
689  $catId = $this->_storeManager->getStore($storeId)->getRootCategoryId();
690  $productBind = 'product_id' . $productId;
691  $storeBind = 'store_id' . $storeId;
692  $catBind = 'category_id' . $catId;
693  $bindArray = [
694  'i.product_id = :' . $productBind,
695  'i.store_id = :' . $storeBind,
696  'i.category_id = :' . $catBind
697  ];
698  $cond = '(' . implode(' AND ', $bindArray) . ')';
699  $bind[$productBind] = $productId;
700  $bind[$storeBind] = $storeId;
701  $bind[$catBind] = $catId;
702  $select->orWhere($cond);
703  }
704 
705  $rowSet = $connection->fetchAll($select, $bind);
706  foreach ($rowSet as $row) {
707  $result[$row['product_id']] = [
708  'store_id' => $row['store_id'],
709  'visibility' => $row['visibility'],
710  'url_rewrite' => $row['request_path'],
711  ];
712  }
713  }
714 
715  return $result;
716  }
717 
721  private function getMetadataPool()
722  {
723  if (null === $this->metadataPool) {
725  ->get(\Magento\Framework\EntityManager\MetadataPool::class);
726  }
727  return $this->metadataPool;
728  }
729 }
getCategory($categoryId, $storeId)
Definition: Url.php:506
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$storeManager
_getProductAttribute($attributeCode, $productIds, $storeId)
Definition: Url.php:279
getRewriteByProductStore(array $products)
Definition: Url.php:658
$logger
_getCategories($categoryIds, $storeId=null, $path=null)
Definition: Url.php:407
_prepareCategoryParentId(\Magento\Framework\DataObject $category)
Definition: Url.php:356
$attributeCode
Definition: extend.phtml:12
$attributes
Definition: matrix.phtml:13
getCategories($categoryIds, $storeId)
Definition: Url.php:526
getProduct($productId, $storeId)
Definition: Url.php:622
$categories
_getCategoryAttribute($attributeCode, $categoryIds, $storeId)
Definition: Url.php:175
$connection
Definition: bulk.php:13
$table
Definition: trigger.php:14
getProductsByStore($storeId, &$lastEntityId)
Definition: Url.php:639
_getProducts($productIds, $storeId, $entityId, &$lastEntityId)
Definition: Url.php:544
__construct(\Magento\Framework\Model\ResourceModel\Db\Context $context, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Eav\Model\Config $eavConfig, Product $productResource, \Magento\Catalog\Model\Category $catalogCategory, \Psr\Log\LoggerInterface $logger, $connectionName=null, TableMaintainer $tableMaintainer=null)
Definition: Url.php:121