1 | <?php |
||
33 | class ActivityContactRecalculateCommand extends ContainerAwareCommand |
||
34 | { |
||
35 | const STATUS_SUCCESS = 0; |
||
36 | const COMMAND_NAME = 'oro:activity-contact:recalculate'; |
||
37 | const BATCH_SIZE = 100; |
||
38 | |||
39 | /** @var OroEntityManager $em */ |
||
40 | protected $em; |
||
41 | |||
42 | /** @var ActivityListRepository $activityListRepository */ |
||
43 | protected $activityListRepository; |
||
44 | |||
45 | /** |
||
46 | * {@inheritdoc} |
||
47 | */ |
||
48 | protected function configure() |
||
54 | |||
55 | /** |
||
56 | * {@inheritdoc} |
||
57 | */ |
||
58 | protected function execute(InputInterface $input, OutputInterface $output) |
||
64 | |||
65 | /** |
||
66 | * @param AbstractLogger $logger |
||
67 | * |
||
68 | * @return int |
||
69 | */ |
||
70 | public function recalculate(AbstractLogger $logger) |
||
71 | { |
||
72 | $logger->info('Recalculating contacting activities...'); |
||
73 | $logger->info(sprintf('<info>Processing started at %s</info>', date('Y-m-d H:i:s'))); |
||
74 | |||
75 | /** @var ConfigProvider $activityConfigProvider */ |
||
76 | $activityConfigProvider = $this->getContainer()->get('oro_entity_config.provider.activity'); |
||
77 | |||
78 | /** @var ActivityContactProvider $activityContactProvider */ |
||
79 | $activityContactProvider = $this->getContainer()->get('orocrm_activity_contact.provider'); |
||
80 | $contactingActivityClasses = $activityContactProvider->getSupportedActivityClasses(); |
||
81 | |||
82 | $entityConfigsWithApplicableActivities = $activityConfigProvider->filter( |
||
83 | function (ConfigInterface $entity) use ($contactingActivityClasses) { |
||
84 | return |
||
85 | $entity->get('activities') |
||
86 | && array_intersect($contactingActivityClasses, $entity->get('activities')); |
||
87 | } |
||
88 | ); |
||
89 | |||
90 | if ($entityConfigsWithApplicableActivities) { |
||
|
|||
91 | $logger->info( |
||
92 | sprintf( |
||
93 | '<comment>Total found %d entities with enabled contacting activities</comment>', |
||
94 | count($entityConfigsWithApplicableActivities) |
||
95 | ) |
||
96 | ); |
||
97 | $this->em = $this->getContainer()->get('doctrine')->getManager(); |
||
98 | $this->activityListRepository = $this->em->getRepository(ActivityList::ENTITY_NAME); |
||
99 | |||
100 | /** @var ActivityListener $activityListener */ |
||
101 | $activityListener = $this->getContainer()->get('orocrm_activity_contact.listener.activity_listener'); |
||
102 | /** @var ActivityListFilterHelper $activityListHelper */ |
||
103 | $activityListHelper = $this->getContainer()->get('oro_activity_list.filter.helper'); |
||
104 | |||
105 | foreach ($entityConfigsWithApplicableActivities as $activityScopeConfig) { |
||
106 | $entityClassName = $activityScopeConfig->getId()->getClassName(); |
||
107 | if (TargetExcludeList::isExcluded($entityClassName)) { |
||
108 | continue; |
||
109 | } |
||
110 | $offset = 0; |
||
111 | $startTimestamp = time(); |
||
112 | $allRecordIds = $this->getTargetIds($entityClassName); |
||
113 | $this->resetRecordsWithoutActivities($entityClassName, $allRecordIds); |
||
114 | while ($allRecords = $this->getRecordsToRecalculate($entityClassName, $allRecordIds, $offset)) { |
||
115 | $needsFlush = false; |
||
116 | foreach ($allRecords as $record) { |
||
117 | $this->resetRecordStatistic($record); |
||
118 | /** @var QueryBuilder $qb */ |
||
119 | $qb = $this->activityListRepository->getBaseActivityListQueryBuilder( |
||
120 | $entityClassName, |
||
121 | $record->getId() |
||
122 | ); |
||
123 | $activityListHelper->addFiltersToQuery( |
||
124 | $qb, |
||
125 | ['activityType' => ['value' => $contactingActivityClasses]] |
||
126 | ); |
||
127 | |||
128 | /** @var ActivityList[] $activities */ |
||
129 | $activities = $qb->getQuery()->getResult(); |
||
130 | if ($activities) { |
||
131 | foreach ($activities as $activityListItem) { |
||
132 | /** @var object $activity */ |
||
133 | $activity = $this->em->getRepository($activityListItem->getRelatedActivityClass()) |
||
134 | ->find($activityListItem->getRelatedActivityId()); |
||
135 | |||
136 | $activityListener->onAddActivity(new ActivityEvent($activity, $record)); |
||
137 | } |
||
138 | $this->em->persist($record); |
||
139 | $needsFlush = true; |
||
140 | } |
||
141 | } |
||
142 | if ($needsFlush) { |
||
143 | $this->em->flush(); |
||
144 | } |
||
145 | $this->em->clear(); |
||
146 | $offset += self::BATCH_SIZE; |
||
147 | } |
||
148 | |||
149 | $endTimestamp = time(); |
||
150 | $logger->info( |
||
151 | sprintf( |
||
152 | 'Entity "%s", %d records processed (<comment>%d sec.</comment>).', |
||
153 | $entityClassName, |
||
154 | count($allRecordIds), |
||
155 | ($endTimestamp - $startTimestamp) |
||
156 | ) |
||
157 | ); |
||
158 | } |
||
159 | } |
||
160 | $logger->info(sprintf('<info>Processing finished at %s</info>', date('Y-m-d H:i:s'))); |
||
161 | |||
162 | return self::STATUS_SUCCESS; |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * @param string $entityClassName |
||
167 | * @param array $recordIdsWithActivities |
||
168 | */ |
||
169 | protected function resetRecordsWithoutActivities($entityClassName, array $recordIdsWithActivities) |
||
170 | { |
||
171 | $offset = 0; |
||
172 | while ($records = $this->getRecordsToReset($entityClassName, $recordIdsWithActivities, $offset)) { |
||
173 | array_map([$this, 'resetRecordStatistic'], $records); |
||
174 | $this->em->flush(); |
||
175 | $this->em->clear(); |
||
176 | $offset += self::BATCH_SIZE; |
||
177 | } |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Resets entity statistics. |
||
182 | * |
||
183 | * @param object $entity |
||
184 | */ |
||
185 | protected function resetRecordStatistic($entity) |
||
197 | |||
198 | /** |
||
199 | * @param string $entityClassName |
||
200 | * @param array $ids |
||
201 | * @param integer $offset |
||
202 | * |
||
203 | * @return array |
||
204 | */ |
||
205 | protected function getRecordsToRecalculate($entityClassName, $ids, $offset) |
||
211 | |||
212 | /** |
||
213 | * @param string $entityClassName |
||
214 | * @param array $excludedIds |
||
215 | * @param integer $offset |
||
216 | * |
||
217 | * @return array |
||
218 | */ |
||
219 | protected function getRecordsToReset($entityClassName, array $excludedIds, $offset) |
||
220 | { |
||
221 | $qb = $this->em->getRepository($entityClassName)->createQueryBuilder('e'); |
||
222 | |||
223 | if ($excludedIds) { |
||
224 | $qb->andWhere($qb->expr()->notIn('e.id', $excludedIds)); |
||
225 | } |
||
226 | |||
227 | return $qb->setMaxResults(static::BATCH_SIZE) |
||
228 | ->setFirstResult($offset) |
||
229 | ->getQuery() |
||
230 | ->getResult(); |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Returns entity ids of records that have associated contacting activities |
||
235 | * |
||
236 | * @param string $className Target entity class name |
||
237 | * |
||
238 | * @return array |
||
239 | */ |
||
240 | protected function getTargetIds($className) |
||
264 | |||
265 | /** |
||
266 | * Get Association name |
||
267 | * |
||
268 | * @param string $className |
||
269 | * |
||
270 | * @return string |
||
271 | */ |
||
272 | protected function getAssociationName($className) |
||
279 | } |
||
280 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.