Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
ProductAttributesCleanUp.php
Go to the documentation of this file.
1 <?php
7 
9 use Symfony\Component\Console\Input\InputInterface;
10 use Symfony\Component\Console\Output\OutputInterface;
12 
17 class ProductAttributesCleanUp extends \Symfony\Component\Console\Command\Command
18 {
23 
28 
32  protected $attributeResource;
33 
37  protected $appState;
38 
42  protected $metadata;
43 
51  public function __construct(
52  \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository,
53  \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource,
54  \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
55  \Magento\Framework\App\State $appState,
56  \Magento\Framework\EntityManager\MetadataPool $metadataPool
57  ) {
58  $this->productAttributeRepository = $productAttributeRepository;
59  $this->searchCriteriaBuilder = $searchCriteriaBuilder;
60  $this->attributeResource = $attributeResource;
61  $this->appState = $appState;
62  $this->metadata = $metadataPool->getMetadata(ProductInterface::class);
63  parent::__construct();
64  }
65 
69  protected function configure()
70  {
71  $this->setName('catalog:product:attributes:cleanup');
72  $this->setDescription('Removes unused product attributes.');
73  }
74 
78  protected function execute(InputInterface $input, OutputInterface $output)
79  {
80  $output->setDecorated(true);
81  $this->appState->setAreaCode(\Magento\Framework\App\Area::AREA_GLOBAL);
82  $connection = $this->attributeResource->getConnection();
83  $attributeTables = $this->getAttributeTables();
84 
85  $progress = new \Symfony\Component\Console\Helper\ProgressBar($output, count($attributeTables));
86  $progress->setFormat('<comment>%message%</comment> %current%/%max% [%bar%] %percent:3s%% %elapsed%');
87 
88  $this->attributeResource->beginTransaction();
89  try {
90  // Find and remove unused attributes
91  foreach ($attributeTables as $attributeTable) {
92  $progress->setMessage($attributeTable . ' ');
93  $affectedIds = $this->getAffectedAttributeIds($connection, $attributeTable);
94  if (count($affectedIds) > 0) {
95  $connection->delete($attributeTable, ['value_id in (?)' => $affectedIds]);
96  }
97  $progress->advance();
98  }
99  $this->attributeResource->commit();
100 
101  $output->writeln("");
102  $output->writeln("<info>Unused product attributes successfully cleaned up:</info>");
103  $output->writeln("<comment> " . implode("\n ", $attributeTables) . "</comment>");
104  return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
105  } catch (\Exception $exception) {
106  $this->attributeResource->rollBack();
107 
108  $output->writeln("");
109  $output->writeln("<error>{$exception->getMessage()}</error>");
110  // we must have an exit code higher than zero to indicate something was wrong
111  return \Magento\Framework\Console\Cli::RETURN_FAILURE;
112  }
113  }
114 
119  private function getAttributeTables()
120  {
121  $searchResult = $this->productAttributeRepository->getList($this->searchCriteriaBuilder->create());
122  $attributeTables = [];
123 
125  foreach ($searchResult->getItems() as $productAttribute) {
126  $attributeTable = $productAttribute->getBackend()->getTable();
127  if (!in_array($attributeTable, $attributeTables)
128  && $attributeTable != $this->attributeResource->getTable('catalog_product_entity')
129  ) {
130  $attributeTables[] = $attributeTable;
131  }
132  }
133  return $attributeTables;
134  }
135 
142  private function getAffectedAttributeIds(AdapterInterface $connection, $attributeTableName)
143  {
144  $linkField = $this->metadata->getLinkField();
145  $select = $connection->select()->reset();
146  $select->from(['e' => $this->attributeResource->getTable('catalog_product_entity')], 'ei.value_id');
147  $select->join(
148  ['ei' => $attributeTableName],
149  'ei.' . $linkField . ' = e.' . $linkField . ' AND ei.store_id != 0',
150  ''
151  );
152  $select->join(['s' => $this->attributeResource->getTable('store')], 's.store_id = ei.store_id', '');
153  $select->join(['sg' => $this->attributeResource->getTable('store_group')], 'sg.group_id = s.group_id', '');
154  $select->joinLeft(
155  ['pw' => $this->attributeResource->getTable('catalog_product_website')],
156  'pw.website_id = sg.website_id AND pw.product_id = e.entity_id',
157  ''
158  );
159  $select->where('pw.product_id is null');
160  return $connection->fetchCol($select);
161  }
162 }
execute(InputInterface $input, OutputInterface $output)
__construct(\Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository, \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource, \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, \Magento\Framework\App\State $appState, \Magento\Framework\EntityManager\MetadataPool $metadataPool)
$connection
Definition: bulk.php:13