Total Complexity | 50 |
Total Lines | 434 |
Duplicated Lines | 0 % |
Changes | 6 | ||
Bugs | 0 | Features | 0 |
Complex classes like AddToCampaignHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AddToCampaignHandler, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
52 | class AddToCampaignHandler |
||
53 | { |
||
54 | use Injectable; |
||
55 | |||
56 | /** |
||
57 | * Parent controller for this form |
||
58 | * |
||
59 | * @var Controller |
||
60 | */ |
||
61 | protected $controller; |
||
62 | |||
63 | /** |
||
64 | * The submitted form data |
||
65 | * |
||
66 | * @var array |
||
67 | */ |
||
68 | protected $data; |
||
69 | |||
70 | /** |
||
71 | * Form name to use |
||
72 | * |
||
73 | * @var string |
||
74 | */ |
||
75 | protected $name; |
||
76 | |||
77 | /** |
||
78 | * AddToCampaignHandler constructor. |
||
79 | * |
||
80 | * @param Controller $controller Controller for this form |
||
81 | * @param array|DataObject $data The data submitted as part of that form |
||
82 | * @param string $name Form name |
||
83 | */ |
||
84 | public function __construct($controller = null, $data = [], $name = 'AddToCampaignForm') |
||
85 | { |
||
86 | $this->controller = $controller; |
||
87 | if ($data instanceof DataObject) { |
||
88 | $data = $data->toMap(); |
||
89 | } |
||
90 | $this->data = $data; |
||
91 | $this->name = $name; |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Perform the action. Either returns a Form or performs the action, as per the class doc |
||
96 | * |
||
97 | * @return DBHTMLText|HTTPResponse |
||
98 | */ |
||
99 | public function handle() |
||
100 | { |
||
101 | Deprecation::notice('5.0', 'handle() will be removed. Use addToCampaign or Form directly'); |
||
102 | $object = $this->getObject($this->data['ID'], $this->data['ClassName']); |
||
103 | |||
104 | if (empty($this->data['Campaign'])) { |
||
105 | return $this->Form($object)->forTemplate(); |
||
106 | } else { |
||
107 | return $this->addToCampaign($object, $this->data['Campaign']); |
||
108 | } |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Get what ChangeSets are available for an item to be added to by this user |
||
113 | * |
||
114 | * @return ArrayList|ChangeSet[] |
||
115 | */ |
||
116 | protected function getAvailableChangeSets() |
||
117 | { |
||
118 | return ChangeSet::get() |
||
119 | ->filter([ |
||
120 | 'State' => ChangeSet::STATE_OPEN, |
||
121 | 'IsInferred' => 0 |
||
122 | ]) |
||
123 | ->filterByCallback(function ($item) { |
||
124 | /** @var ChangeSet $item */ |
||
125 | return $item->canView(); |
||
126 | }); |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Get changesets that a given object is already in |
||
131 | * |
||
132 | * @param DataObject |
||
133 | * @return ArrayList[ChangeSet] |
||
|
|||
134 | */ |
||
135 | protected function getInChangeSets($object) |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Safely get a DataObject from a client-supplied ID and ClassName, checking: argument |
||
152 | * validity; existence; and canView permissions. |
||
153 | * |
||
154 | * @param int $id The ID of the DataObject |
||
155 | * @param string $class The Class of the DataObject |
||
156 | * @return DataObject The referenced DataObject |
||
157 | * @throws HTTPResponse_Exception |
||
158 | */ |
||
159 | protected function getObject($id, $class) |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * Builds a Form that mirrors the parent editForm, but with an extra field to collect the ChangeSet ID |
||
202 | * |
||
203 | * @param DataObject $object The object we're going to be adding to whichever ChangeSet is chosen |
||
204 | * @return Form |
||
205 | */ |
||
206 | public function Form($object) |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * Performs the actual action of adding the object to the ChangeSet, once the ChangeSet ID is known |
||
285 | * |
||
286 | * @param DataObject $object The object to add to the ChangeSet |
||
287 | * @param array|int $data Post data for this campaign form, or the ID of the campaign to add to |
||
288 | * @return HTTPResponse |
||
289 | * @throws ValidationException |
||
290 | */ |
||
291 | public function addToCampaign($object, $data) |
||
292 | { |
||
293 | // Extract $campaignID from $data |
||
294 | $campaignID = $this->getOrCreateCampaign($data); |
||
295 | |||
296 | /** @var ChangeSet $changeSet */ |
||
297 | $changeSet = ChangeSet::get()->byID($campaignID); |
||
298 | |||
299 | if (!$changeSet) { |
||
300 | throw new ValidationException(_t( |
||
301 | __CLASS__ . '.ErrorNotFound', |
||
302 | 'That {Type} couldn\'t be found', |
||
303 | ['Type' => 'Campaign'] |
||
304 | )); |
||
305 | } |
||
306 | |||
307 | if (!$changeSet->canEdit()) { |
||
308 | throw new ValidationException(_t( |
||
309 | __CLASS__ . '.ErrorCampaignPermissionDenied', |
||
310 | 'It seems you don\'t have the necessary permissions to add {ObjectTitle} to {CampaignTitle}', |
||
311 | [ |
||
312 | 'ObjectTitle' => $object->Title, |
||
313 | 'CampaignTitle' => $changeSet->Title |
||
314 | ] |
||
315 | )); |
||
316 | } |
||
317 | |||
318 | $changeSet->addObject($object); |
||
319 | |||
320 | $childObjects = $object->findRelatedObjects('cascade_add_to_campaign'); |
||
321 | if ($childObjects) { |
||
322 | foreach ($childObjects as $childObject) { |
||
323 | $changeSet->addObject($childObject); |
||
324 | } |
||
325 | } |
||
326 | |||
327 | $request = $this->controller->getRequest(); |
||
328 | $message = _t( |
||
329 | __CLASS__ . '.Success', |
||
330 | 'Successfully added <strong>{ObjectTitle}</strong> to <strong>{CampaignTitle}</strong>', |
||
331 | [ |
||
332 | 'ObjectTitle' => Convert::raw2xml($object->Title), |
||
333 | 'CampaignTitle' => Convert::raw2xml($changeSet->Title) |
||
334 | ] |
||
335 | ); |
||
336 | if ($request->getHeader('X-Formschema-Request')) { |
||
337 | return $message; |
||
338 | } elseif (Director::is_ajax()) { |
||
339 | $response = new HTTPResponse($message, 200); |
||
340 | |||
341 | $response->addHeader('Content-Type', 'text/html; charset=utf-8'); |
||
342 | return $response; |
||
343 | } else { |
||
344 | return $this->controller->redirectBack(); |
||
345 | } |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Get descriptive alert to display at the top of the form |
||
350 | * |
||
351 | * @param ArrayList $inChangeSets List of changesets this item exists in |
||
352 | * @param ArrayList $candidateChangeSets List of changesets this item could be added to |
||
353 | * @param bool $canCreate |
||
354 | * @return string |
||
355 | */ |
||
356 | protected function getFormAlert($inChangeSets, $candidateChangeSets, $canCreate) |
||
357 | { |
||
358 | // In a subset of changesets |
||
359 | if ($inChangeSets->count() > 0 && $candidateChangeSets->count() > 0) { |
||
360 | return sprintf( |
||
361 | '<div class="alert alert-info"><strong>%s</strong><br/>%s</div>', |
||
362 | _t( |
||
363 | __CLASS__ . '.AddToCampaignInChangsetLabel', |
||
364 | 'Heads up, this item is already in campaign(s):' |
||
365 | ), |
||
366 | Convert::raw2xml(implode(', ', $inChangeSets->column('Name'))) |
||
367 | ); |
||
368 | } |
||
369 | |||
370 | // In all changesets |
||
371 | if ($inChangeSets->count() > 0) { |
||
372 | return sprintf( |
||
373 | '<div class="alert alert-info"><strong>%s</strong><br/>%s</div>', |
||
374 | _t( |
||
375 | __CLASS__ . '.AddToCampaignInChangsetLabelAll', |
||
376 | 'Heads up, this item is already in ALL campaign(s):' |
||
377 | ), |
||
378 | Convert::raw2xml(implode(', ', $inChangeSets->column('Name'))) |
||
379 | ); |
||
380 | } |
||
381 | |||
382 | // Create only |
||
383 | if ($candidateChangeSets->count() === 0 && $canCreate) { |
||
384 | return sprintf( |
||
385 | '<div class="alert alert-info">%s</div>', |
||
386 | _t( |
||
387 | __CLASS__ . '.NO_CAMPAIGNS', |
||
388 | "You currently don't have any campaigns. " |
||
389 | . "You can edit campaign details later in the Campaigns section." |
||
390 | ) |
||
391 | ); |
||
392 | } |
||
393 | |||
394 | // Can't select or create |
||
395 | if ($candidateChangeSets->count() === 0 && !$canCreate) { |
||
396 | return sprintf( |
||
397 | '<div class="alert alert-warning">%s</div>', |
||
398 | _t( |
||
399 | __CLASS__ . '.NO_CREATE', |
||
400 | "Oh no! You currently don't have any campaigns created. " |
||
401 | . "Your current login does not have privileges to create campaigns. " |
||
402 | . "Campaigns can only be created by users with Campaigns section rights." |
||
403 | ) |
||
404 | ); |
||
405 | } |
||
406 | return null; |
||
407 | } |
||
408 | |||
409 | /** |
||
410 | * Find or build campaign from posted data |
||
411 | * |
||
412 | * @param array|int $data |
||
413 | * @return int |
||
414 | * @throws ValidationException |
||
415 | */ |
||
416 | protected function getOrCreateCampaign($data) |
||
417 | { |
||
418 | // Create new campaign if selected |
||
419 | if (is_array($data) && !empty($data['AddNewSelect']) // Explicitly click "Add to a new campaign" |
||
420 | || (is_array($data) && !isset($data['Campaign']) && isset($data['NewTitle'])) // This is the only option |
||
421 | ) { |
||
422 | // Permission |
||
423 | if (!ChangeSet::singleton()->canCreate()) { |
||
424 | throw $this->validationResult( |
||
425 | _t(__CLASS__ . '.CREATE_DENIED', 'You do not have permission to create campaigns') |
||
426 | ); |
||
427 | } |
||
428 | |||
429 | // Check title is valid |
||
430 | $title = $data['NewTitle']; |
||
431 | if (empty($title)) { |
||
432 | throw $this->validationResult( |
||
433 | _t(__CLASS__ . '.MISSING_TITLE', 'Campaign name is required'), |
||
434 | 'NewTitle' |
||
435 | ); |
||
436 | } |
||
437 | |||
438 | // Prevent duplicates |
||
439 | $hasExistingName = Changeset::get() |
||
440 | ->filter('Name:nocase', $title) |
||
441 | ->count() > 0; |
||
442 | |||
443 | if ($hasExistingName) { |
||
444 | throw $this->validationResult( |
||
445 | _t( |
||
446 | 'SilverStripe\\CampaignAdmin\\CampaignAdmin.ERROR_DUPLICATE_NAME', |
||
447 | 'Name "{Name}" already exists', |
||
448 | ['Name' => $title] |
||
449 | ), |
||
450 | 'NewTitle' |
||
451 | ); |
||
452 | } |
||
453 | |||
454 | // Create and return |
||
455 | $campaign = ChangeSet::create(); |
||
456 | $campaign->Name = $title; |
||
457 | $campaign->write(); |
||
458 | return $campaign->ID; |
||
459 | } |
||
460 | |||
461 | // Get selected campaign ID |
||
462 | $campaignID = null; |
||
463 | if (is_array($data) && !empty($data['Campaign'])) { |
||
464 | $campaignID = $data['Campaign']; |
||
465 | } elseif (is_numeric($data)) { |
||
466 | $campaignID = (int)$data; |
||
467 | } |
||
468 | if (empty($campaignID)) { |
||
469 | throw $this->validationResult(_t(__CLASS__ . '.NONE_SELECTED', 'No campaign selected')); |
||
470 | } |
||
471 | return $campaignID; |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * Raise validation error |
||
476 | * |
||
477 | * @param string $message |
||
478 | * @param string $field |
||
479 | * @return ValidationException |
||
480 | */ |
||
481 | protected function validationResult($message, $field = null) |
||
486 | } |
||
487 | } |
||
488 |