Complex classes like WorkflowService 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 WorkflowService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class WorkflowService implements PermissionProvider { |
||
10 | |||
11 | /** |
||
12 | * An array of templates that we can create from |
||
13 | * |
||
14 | * @var array |
||
15 | */ |
||
16 | protected $templates; |
||
17 | |||
18 | public function __construct() { |
||
20 | |||
21 | |||
22 | /** |
||
23 | * Set the list of templates that can be created |
||
24 | * |
||
25 | * @param type $templates |
||
26 | */ |
||
27 | public function setTemplates($templates) { |
||
30 | |||
31 | /** |
||
32 | * Return the list of available templates |
||
33 | * @return type |
||
34 | */ |
||
35 | public function getTemplates() { |
||
38 | |||
39 | /** |
||
40 | * Get a template by name |
||
41 | * |
||
42 | * @param string $name |
||
43 | * @return WorkflowTemplate |
||
44 | */ |
||
45 | public function getNamedTemplate($name) { |
||
59 | |||
60 | /** |
||
61 | * Gets the workflow definition for a given dataobject, if there is one |
||
62 | * |
||
63 | * Will recursively query parent elements until it finds one, if available |
||
64 | * |
||
65 | * @param DataObject $dataObject |
||
66 | */ |
||
67 | public function getDefinitionFor(DataObject $dataObject) { |
||
68 | if ($dataObject->hasExtension('WorkflowApplicable') || $dataObject->hasExtension('FileWorkflowApplicable')) { |
||
69 | if ($dataObject->WorkflowDefinitionID) { |
||
70 | return DataObject::get_by_id('WorkflowDefinition', $dataObject->WorkflowDefinitionID); |
||
71 | } |
||
72 | if ($dataObject->hasMethod('useInheritedWorkflow') && !$this->useInheritedWorkflow()) { |
||
73 | return null; |
||
74 | } |
||
75 | if ($dataObject->ParentID) { |
||
76 | return $this->getDefinitionFor($dataObject->Parent()); |
||
77 | } |
||
78 | if ($dataObject->hasMethod('workflowParent')) { |
||
79 | $obj = $dataObject->workflowParent(); |
||
80 | if ($obj) { |
||
81 | return $this->getDefinitionFor($obj); |
||
82 | } |
||
83 | } |
||
84 | } |
||
85 | return null; |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * Retrieves a workflow definition by ID for a data object. |
||
90 | * |
||
91 | * @param data object |
||
92 | * @param integer |
||
93 | * @return workflow definition |
||
94 | */ |
||
95 | |||
96 | public function getDefinitionByID($object, $workflowID) { |
||
113 | |||
114 | /** |
||
115 | * Retrieves and collates the workflow definitions for a data object, where the first element will be the main workflow definition. |
||
116 | * |
||
117 | * @param data object |
||
118 | * @return array |
||
119 | */ |
||
120 | |||
121 | public function getDefinitionsFor($object) { |
||
136 | |||
137 | /** |
||
138 | * Gets the workflow for the given item |
||
139 | * |
||
140 | * The item can be |
||
141 | * |
||
142 | * a data object in which case the ActiveWorkflow will be returned, |
||
143 | * an action, in which case the Workflow will be returned |
||
144 | * an integer, in which case the workflow with that ID will be returned |
||
145 | * |
||
146 | * @param mixed $item |
||
147 | * |
||
148 | * @return WorkflowInstance |
||
149 | */ |
||
150 | public function getWorkflowFor($item, $includeComplete = false) { |
||
162 | |||
163 | /** |
||
164 | * Get all the workflow action instances for an item |
||
165 | * |
||
166 | * @return DataObjectSet |
||
167 | */ |
||
168 | public function getWorkflowHistoryFor($item, $limit = null){ |
||
174 | |||
175 | /** |
||
176 | * Get all the available workflow definitions |
||
177 | * |
||
178 | * @return DataList |
||
179 | */ |
||
180 | public function getDefinitions() { |
||
183 | |||
184 | /** |
||
185 | * Given a transition ID, figure out what should happen to |
||
186 | * the given $subject. |
||
187 | * |
||
188 | * In the normal case, this will load the current workflow instance for the object |
||
189 | * and then transition as expected. However, in some cases (eg to start the workflow) |
||
190 | * it is necessary to instead create a new instance. |
||
191 | * |
||
192 | * @param DataObject $target |
||
193 | * @param int $transitionId |
||
194 | */ |
||
195 | public function executeTransition(DataObject $target, $transitionId) { |
||
213 | |||
214 | /** |
||
215 | * Starts the workflow for the given data object, assuming it or a parent has |
||
216 | * a definition specified. |
||
217 | * |
||
218 | * @param DataObject $object |
||
219 | */ |
||
220 | public function startWorkflow(DataObject $object, $workflowID = null) { |
||
246 | |||
247 | /** |
||
248 | * Get all the workflows that this user is responsible for |
||
249 | * |
||
250 | * @param Member $user |
||
251 | * The user to get workflows for |
||
252 | * |
||
253 | * @return ArrayList |
||
254 | * The list of workflow instances this user owns |
||
255 | */ |
||
256 | public function usersWorkflows(Member $user) { |
||
290 | |||
291 | /** |
||
292 | * Get items that the passed-in user has awaiting for them to action |
||
293 | * |
||
294 | * @param Member $member |
||
295 | * @return DataList $userInstances |
||
296 | */ |
||
297 | public function userPendingItems(Member $user) { |
||
317 | |||
318 | /** |
||
319 | * Get items that the passed-in user has submitted for workflow review |
||
320 | * |
||
321 | * @param Member $member |
||
322 | * @return DataList $userInstances |
||
323 | */ |
||
324 | public function userSubmittedItems(Member $user) { |
||
336 | |||
337 | /** |
||
338 | * Generate a workflow definition based on a template |
||
339 | * |
||
340 | * @param WorkflowDefinition $definition |
||
341 | * @param string $templateName |
||
342 | */ |
||
343 | public function defineFromTemplate(WorkflowDefinition $definition, $templateName) { |
||
367 | |||
368 | /** |
||
369 | * Reorders actions within a definition |
||
370 | * |
||
371 | * @param WorkflowDefinition|WorkflowAction $objects |
||
372 | * The objects to be reordered |
||
373 | * @param array $newOrder |
||
374 | * An array of IDs of the actions in the order they should be. |
||
375 | */ |
||
376 | public function reorder($objects, $newOrder) { |
||
391 | |||
392 | /** |
||
393 | * |
||
394 | * @return array |
||
395 | */ |
||
396 | public function providePermissions() { |
||
430 | |||
431 | } |
||
432 | |||
434 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.