Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
TableBuilder.php
Go to the documentation of this file.
1 <?php
7 
8 use Magento\Catalog\Model\Indexer\Product\Flat\Table\BuilderInterfaceFactory;
9 
11 {
16 
20  protected $_connection;
21 
25  protected $metadataPool;
26 
30  protected $resource;
31 
35  private $tableBuilderFactory;
36 
44  public function __construct(
45  \Magento\Catalog\Helper\Product\Flat\Indexer $productIndexerHelper,
47  BuilderInterfaceFactory $tableBuilderFactory = null
48  ) {
49  $this->_productIndexerHelper = $productIndexerHelper;
50  $this->resource = $resource;
51  $this->_connection = $resource->getConnection();
52  $this->tableBuilderFactory = $tableBuilderFactory ?: \Magento\Framework\App\ObjectManager::getInstance()
53  ->get(BuilderInterfaceFactory::class);
54  }
55 
64  public function build($storeId, $changedIds, $valueFieldSuffix)
65  {
66  $entityTableName = $this->_productIndexerHelper->getTable('catalog_product_entity');
67  $attributes = $this->_productIndexerHelper->getAttributes();
68  $eavAttributes = $this->_productIndexerHelper->getTablesStructure($attributes);
69  $entityTableColumns = $eavAttributes[$entityTableName];
70  $linkField = $this->getMetadataPool()
71  ->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
72  ->getLinkField();
73 
74  $temporaryEavAttributes = $eavAttributes;
75 
76  //add status global value to the base table
77  /* @var $status \Magento\Eav\Model\Entity\Attribute */
78  $status = $this->_productIndexerHelper->getAttribute('status');
79  $temporaryEavAttributes[$status->getBackendTable()]['status'] = $status;
80  //Create list of temporary tables based on available attributes attributes
81  $valueTables = [];
82  foreach ($temporaryEavAttributes as $tableName => $columns) {
83  $valueTables = array_merge(
84  $valueTables,
85  $this->_createTemporaryTable($this->_getTemporaryTableName($tableName), $columns, $valueFieldSuffix)
86  );
87  }
88 
89  //Fill "base" table which contains all available products
90  $this->_fillTemporaryEntityTable($entityTableName, $entityTableColumns, $changedIds);
91 
92  //Add primary key to "base" temporary table for increase speed of joins in future
93  $this->_addPrimaryKeyToTable($this->_getTemporaryTableName($entityTableName));
94  unset($temporaryEavAttributes[$entityTableName]);
95 
96  foreach ($temporaryEavAttributes as $tableName => $columns) {
97  $temporaryTableName = $this->_getTemporaryTableName($tableName);
98 
99  //Add primary key to temporary table for increase speed of joins in future
100  $this->_addPrimaryKeyToTable($temporaryTableName, $linkField);
101 
102  //Create temporary table for composite attributes
103  if (isset($valueTables[$temporaryTableName . $valueFieldSuffix])) {
104  $this->_addPrimaryKeyToTable($temporaryTableName . $valueFieldSuffix, $linkField);
105  }
106 
107  //Fill temporary tables with attributes grouped by it type
108  $this->_fillTemporaryTable($tableName, $columns, $changedIds, $valueFieldSuffix, $storeId);
109  }
110  }
111 
121  protected function _createTemporaryTable($tableName, array $columns, $valueFieldSuffix)
122  {
123  $valueTables = [];
124  if (!empty($columns)) {
125  $valueTableName = $tableName . $valueFieldSuffix;
126  $temporaryTableBuilder = $this->tableBuilderFactory->create(
127  [
128  'connection' => $this->_connection,
129  'tableName' => $tableName
130  ]
131  );
132  $valueTemporaryTableBuilder = $this->tableBuilderFactory->create(
133  [
134  'connection' => $this->_connection,
135  'tableName' => $valueTableName
136  ]
137  );
138  $flatColumns = $this->_productIndexerHelper->getFlatColumns();
139 
140  $temporaryTableBuilder->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER);
141 
142  $temporaryTableBuilder->addColumn('type_id', \Magento\Framework\DB\Ddl\Table::TYPE_TEXT);
143 
144  $temporaryTableBuilder->addColumn('attribute_set_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER);
145 
146  $valueTemporaryTableBuilder->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER);
147 
149  foreach ($columns as $columnName => $attribute) {
150  $attributeCode = $attribute->getAttributeCode();
151  if (isset($flatColumns[$attributeCode])) {
152  $column = $flatColumns[$attributeCode];
153  } else {
154  $column = $attribute->_getFlatColumnsDdlDefinition();
155  $column = $column[$attributeCode];
156  }
157 
158  $temporaryTableBuilder->addColumn(
159  $columnName,
160  $column['type'],
161  isset($column['length']) ? $column['length'] : null
162  );
163 
164  $columnValueName = $attributeCode . $valueFieldSuffix;
165  if (isset($flatColumns[$columnValueName])) {
166  $columnValue = $flatColumns[$columnValueName];
167  $valueTemporaryTableBuilder->addColumn(
168  $columnValueName,
169  $columnValue['type'],
170  isset($columnValue['length']) ? $columnValue['length'] : null
171  );
172  }
173  }
174  $this->_connection->dropTemporaryTable($tableName);
175  $this->_connection->createTemporaryTable($temporaryTableBuilder->getTable());
176 
177  if (count($valueTemporaryTableBuilder->getTable()->getColumns()) > 1) {
178  $this->_connection->dropTemporaryTable($valueTableName);
179  $this->_connection->createTemporaryTable($valueTemporaryTableBuilder->getTable());
180  $valueTables[$valueTableName] = $valueTableName;
181  }
182  }
183  return $valueTables;
184  }
185 
192  protected function _getTemporaryTableName($tableName)
193  {
194  return sprintf('%s_tmp_indexer', $tableName);
195  }
196 
205  protected function _fillTemporaryEntityTable($tableName, array $columns, array $changedIds = [])
206  {
207  if (!empty($columns)) {
208  $select = $this->_connection->select();
209  $temporaryEntityTable = $this->_getTemporaryTableName($tableName);
210  $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
211  $idsColumns = array_unique([$metadata->getLinkField(), 'entity_id', 'type_id', 'attribute_set_id']);
212 
213  $columns = array_merge($idsColumns, array_keys($columns));
214 
215  $select->from(['e' => $tableName], $columns);
216  $onDuplicate = false;
217  if (!empty($changedIds)) {
218  $select->where($this->_connection->quoteInto('e.entity_id IN (?)', $changedIds));
219  $onDuplicate = true;
220  }
221  $sql = $select->insertFromSelect($temporaryEntityTable, $columns, $onDuplicate);
222  $this->_connection->query($sql);
223  }
224  }
225 
233  protected function _addPrimaryKeyToTable($tableName, $columnName = 'entity_id')
234  {
235  $this->_connection->addIndex(
236  $tableName,
237  'entity_id',
238  [$columnName],
239  \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_PRIMARY
240  );
241  }
242 
253  protected function _fillTemporaryTable(
254  $tableName,
255  array $tableColumns,
256  array $changedIds,
257  $valueFieldSuffix,
258  $storeId
259  ) {
260  $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
261  if (!empty($tableColumns)) {
262  $columnsChunks = array_chunk(
263  $tableColumns,
264  Action\Indexer::ATTRIBUTES_CHUNK_SIZE,
265  true
266  );
267  foreach ($columnsChunks as $columnsList) {
268  $select = $this->_connection->select();
269  $selectValue = $this->_connection->select();
270  $entityTableName = $this->_getTemporaryTableName(
271  $this->_productIndexerHelper->getTable('catalog_product_entity')
272  );
273  $temporaryTableName = $this->_getTemporaryTableName($tableName);
274  $temporaryValueTableName = $temporaryTableName . $valueFieldSuffix;
275  $keyColumn = array_unique([$metadata->getLinkField(), 'entity_id']);
276  $columns = array_merge($keyColumn, array_keys($columnsList));
277  $valueColumns = $keyColumn;
278  $flatColumns = $this->_productIndexerHelper->getFlatColumns();
279  $iterationNum = 1;
280 
281  $select->from(['et' => $entityTableName], $keyColumn)
282  ->join(
283  ['e' => $this->resource->getTableName('catalog_product_entity')],
284  'e.entity_id = et.entity_id',
285  []
286  );
287 
288  $selectValue->from(['e' => $temporaryTableName], $keyColumn);
289 
291  foreach ($columnsList as $columnName => $attribute) {
292  $countTableName = 't' . $iterationNum++;
293  $joinCondition = sprintf(
294  'e.%3$s = %1$s.%3$s AND %1$s.attribute_id = %2$d AND %1$s.store_id = 0',
295  $countTableName,
296  $attribute->getId(),
297  $metadata->getLinkField()
298  );
299 
300  $select->joinLeft(
301  [$countTableName => $tableName],
302  $joinCondition,
303  [$columnName => 'value']
304  );
305 
306  if ($attribute->getFlatUpdateSelect($storeId) instanceof \Magento\Framework\DB\Select) {
307  $attributeCode = $attribute->getAttributeCode();
308  $columnValueName = $attributeCode . $valueFieldSuffix;
309  if (isset($flatColumns[$columnValueName])) {
310  $valueJoinCondition = sprintf(
311  'e.%1$s = %2$s.option_id AND %2$s.store_id = 0',
313  $countTableName
314  );
315  $selectValue->joinLeft(
316  [
317  $countTableName => $this->_productIndexerHelper->getTable(
318  'eav_attribute_option_value'
319  ),
320  ],
321  $valueJoinCondition,
322  [$columnValueName => $countTableName . '.value']
323  );
324  $valueColumns[] = $columnValueName;
325  }
326  }
327  }
328 
329  if (!empty($changedIds)) {
330  $select->where($this->_connection->quoteInto('e.entity_id IN (?)', $changedIds));
331  }
332 
333  $sql = $select->insertFromSelect($temporaryTableName, $columns, true);
334  $this->_connection->query($sql);
335 
336  if (count($valueColumns) > 1) {
337  if (!empty($changedIds)) {
338  $selectValue->where($this->_connection->quoteInto('e.entity_id IN (?)', $changedIds));
339  }
340  $sql = $selectValue->insertFromSelect($temporaryValueTableName, $valueColumns, true);
341  $this->_connection->query($sql);
342  }
343  }
344  }
345  }
346 
351  private function getMetadataPool()
352  {
353  if (null === $this->metadataPool) {
355  ->get(\Magento\Framework\EntityManager\MetadataPool::class);
356  }
357  return $this->metadataPool;
358  }
359 }
_addPrimaryKeyToTable($tableName, $columnName='entity_id')
build($storeId, $changedIds, $valueFieldSuffix)
$tableName
Definition: trigger.php:13
_fillTemporaryEntityTable($tableName, array $columns, array $changedIds=[])
$columns
Definition: default.phtml:15
$attributeCode
Definition: extend.phtml:12
__construct(\Magento\Catalog\Helper\Product\Flat\Indexer $productIndexerHelper, \Magento\Framework\App\ResourceConnection $resource, BuilderInterfaceFactory $tableBuilderFactory=null)
$status
Definition: order_status.php:8
$attributes
Definition: matrix.phtml:13