Total Complexity | 62 |
Total Lines | 351 |
Duplicated Lines | 10.26 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like FamilyBookController 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 FamilyBookController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
27 | class FamilyBookController extends ChartController { |
||
28 | /** @var string Whether to show spouse details '0' or '1' */ |
||
29 | public $show_spouse; |
||
30 | |||
31 | /** @var int Number of descendancy generations to show */ |
||
32 | public $descent; |
||
33 | |||
34 | /** @var int Number of ascendancy generations to show */ |
||
35 | public $generations; |
||
36 | |||
37 | /** @var int Number of descendancy generations that exist */ |
||
38 | private $dgenerations; |
||
39 | |||
40 | /** @var int Half height of personbox */ |
||
41 | public $bhalfheight; |
||
42 | |||
43 | /** |
||
44 | * Create a family-book controller |
||
45 | */ |
||
46 | public function __construct() { |
||
47 | parent::__construct(); |
||
48 | |||
49 | // Extract the request parameters |
||
50 | $this->show_spouse = Filter::get('show_spouse', '[01]', '0'); |
||
51 | $this->descent = Filter::getInteger('descent', 0, 9, 5); |
||
52 | $this->generations = Filter::getInteger('generations', 2, $this->tree()->getPreference('MAX_DESCENDANCY_GENERATIONS'), 2); |
||
|
|||
53 | |||
54 | $this->bhalfheight = $this->getBoxDimensions()->height / 2; |
||
55 | if ($this->root && $this->root->canShowName()) { |
||
56 | $this->setPageTitle( |
||
57 | /* I18N: %s is an individual’s name */ |
||
58 | I18N::translate('Family book of %s', $this->root->getFullName()) |
||
59 | ); |
||
60 | } else { |
||
61 | $this->setPageTitle(I18N::translate('Family book')); |
||
62 | } |
||
63 | //Checks how many generations of descendency is for the person for formatting purposes |
||
64 | $this->dgenerations = $this->maxDescendencyGenerations($this->root->getXref(), 0); |
||
65 | |||
66 | if ($this->dgenerations < 1) { |
||
67 | $this->dgenerations = 1; |
||
68 | } |
||
69 | } |
||
70 | |||
71 | /** |
||
72 | * Prints descendency of passed in person |
||
73 | * |
||
74 | * @param Individual|null $person |
||
75 | * @param int $generation |
||
76 | * |
||
77 | * @return int |
||
78 | */ |
||
79 | private function printDescendency(Individual $person = null, $generation) { |
||
80 | if ($generation > $this->dgenerations) { |
||
81 | return 0; |
||
82 | } |
||
83 | |||
84 | echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; |
||
85 | $numkids = 0; |
||
86 | |||
87 | // Load children |
||
88 | $children = []; |
||
89 | if ($person) { |
||
90 | // Count is position from center to left, dgenerations is number of generations |
||
91 | if ($generation < $this->dgenerations) { |
||
92 | // All children, from all partners |
||
93 | foreach ($person->getSpouseFamilies() as $family) { |
||
94 | foreach ($family->getChildren() as $child) { |
||
95 | $children[] = $child; |
||
96 | } |
||
97 | } |
||
98 | } |
||
99 | } |
||
100 | if ($generation < $this->dgenerations) { |
||
101 | if (!empty($children)) { |
||
102 | // real people |
||
103 | echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
||
104 | foreach ($children as $i => $child) { |
||
105 | echo '<tr><td>'; |
||
106 | $kids = $this->printDescendency($child, $generation + 1); |
||
107 | $numkids += $kids; |
||
108 | echo '</td>'; |
||
109 | // Print the lines |
||
110 | if (count($children) > 1) { |
||
111 | if ($i === 0) { |
||
112 | // Adjust for the first column on left |
||
113 | $h = round(((($this->getBoxDimensions()->height) * $kids) + 8) / 2); // Assumes border = 1 and padding = 3 |
||
114 | // Adjust for other vertical columns |
||
115 | if ($kids > 1) { |
||
116 | $h = ($kids - 1) * 4 + $h; |
||
117 | } |
||
118 | echo '<td class="align-bottom">', |
||
119 | '<img id="vline_', $child->getXref(), '" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $h - 4, '"></td>'; |
||
120 | } elseif ($i === count($children) - 1) { |
||
121 | // Adjust for the first column on left |
||
122 | $h = round(((($this->getBoxDimensions()->height) * $kids) + 8) / 2); |
||
123 | // Adjust for other vertical columns |
||
124 | if ($kids > 1) { |
||
125 | $h = ($kids - 1) * 4 + $h; |
||
126 | } |
||
127 | echo '<td class="align-top">', |
||
128 | '<img class="bvertline" width="3" id="vline_', $child->getXref(), '" src="', Theme::theme()->parameter('image-vline'), '" height="', $h - 2, '"></td>'; |
||
129 | } else { |
||
130 | echo '<td class="align-bottomm"style="background: url(', Theme::theme()->parameter('image-vline'), ');">', |
||
131 | '<img class="spacer" width="3" src="', Theme::theme()->parameter('image-spacer'), '"></td>'; |
||
132 | } |
||
133 | } |
||
134 | echo '</tr>'; |
||
135 | } |
||
136 | echo '</table>'; |
||
137 | } else { |
||
138 | // Hidden/empty boxes - to preserve the layout |
||
139 | echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; |
||
140 | $numkids += $this->printDescendency(null, $generation + 1); |
||
141 | echo '</td></tr></table>'; |
||
142 | } |
||
143 | echo '</td>'; |
||
144 | echo '<td>'; |
||
145 | } |
||
146 | |||
147 | if ($numkids === 0) { |
||
148 | $numkids = 1; |
||
149 | } |
||
150 | echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td>'; |
||
151 | if ($person) { |
||
152 | FunctionsPrint::printPedigreePerson($person); |
||
153 | echo '</td><td>', |
||
154 | '<img class="linef1" src="', Theme::theme()->parameter('image-hline'), '" width="8" height="3">'; |
||
155 | } else { |
||
156 | echo '<div style="width:', $this->getBoxDimensions()->width + 19, 'px; height:', $this->getBoxDimensions()->height + 8, 'px;"></div>', |
||
157 | '</td><td>'; |
||
158 | } |
||
159 | |||
160 | // Print the spouse |
||
161 | if ($generation === 1) { |
||
162 | if ($this->show_spouse === '1') { |
||
163 | foreach ($person->getSpouseFamilies() as $family) { |
||
164 | $spouse = $family->getSpouse($person); |
||
165 | echo '</td></tr><tr><td>'; |
||
166 | FunctionsPrint::printPedigreePerson($spouse); |
||
167 | $numkids += 0.95; |
||
168 | echo '</td><td>'; |
||
169 | } |
||
170 | } |
||
171 | } |
||
172 | echo '</td></tr></table>'; |
||
173 | echo '</td></tr>'; |
||
174 | echo '</table>'; |
||
175 | |||
176 | return $numkids; |
||
177 | } |
||
178 | |||
179 | /** |
||
180 | * Prints pedigree of the person passed in |
||
181 | * |
||
182 | * @param Individual $person |
||
183 | * @param int $count |
||
184 | */ |
||
185 | private function printPersonPedigree($person, $count) { |
||
186 | if ($count >= $this->generations) { |
||
187 | return; |
||
188 | } |
||
189 | |||
190 | $genoffset = $this->generations; // handle pedigree n generations lines |
||
191 | //-- calculate how tall the lines should be |
||
192 | $lh = ($this->bhalfheight) * pow(2, ($genoffset - $count - 1)); |
||
193 | // |
||
194 | //Prints empty table columns for children w/o parents up to the max generation |
||
195 | //This allows vertical line spacing to be consistent |
||
196 | if (count($person->getChildFamilies()) == 0) { |
||
197 | echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
||
198 | $this->printEmptyBox(); |
||
199 | |||
200 | //-- recursively get the father’s family |
||
201 | $this->printPersonPedigree($person, $count + 1); |
||
202 | echo '</td><td></tr>'; |
||
203 | $this->printEmptyBox(); |
||
204 | |||
205 | //-- recursively get the mother’s family |
||
206 | $this->printPersonPedigree($person, $count + 1); |
||
207 | echo '</td><td></tr></table>'; |
||
208 | } |
||
209 | |||
210 | // Empty box section done, now for regular pedigree |
||
211 | foreach ($person->getChildFamilies() as $family) { |
||
212 | echo '<table cellspacing="0" cellpadding="0" border="0" ><tr><td class="align-bottom">'; |
||
213 | // Determine line height for two or more spouces |
||
214 | // And then adjust the vertical line for the root person only |
||
215 | $famcount = 0; |
||
216 | if ($this->show_spouse === '1') { |
||
217 | // count number of spouses |
||
218 | $famcount += count($person->getSpouseFamilies()); |
||
219 | } |
||
220 | $savlh = $lh; // Save current line height |
||
221 | if ($count == 1 && $genoffset <= $famcount) { |
||
222 | $linefactor = 0; |
||
223 | // genoffset of 2 needs no adjustment |
||
224 | if ($genoffset > 2) { |
||
225 | $tblheight = $this->getBoxDimensions()->height + 8; |
||
226 | if ($genoffset == 3) { |
||
227 | if ($famcount == 3) { |
||
228 | $linefactor = $tblheight / 2; |
||
229 | } elseif ($famcount > 3) { |
||
230 | $linefactor = $tblheight; |
||
231 | } |
||
232 | } |
||
233 | View Code Duplication | if ($genoffset == 4) { |
|
234 | if ($famcount == 4) { |
||
235 | $linefactor = $tblheight; |
||
236 | } elseif ($famcount > 4) { |
||
237 | $linefactor = ($famcount - $genoffset) * ($tblheight * 1.5); |
||
238 | } |
||
239 | } |
||
240 | View Code Duplication | if ($genoffset == 5) { |
|
241 | if ($famcount == 5) { |
||
242 | $linefactor = 0; |
||
243 | } elseif ($famcount > 5) { |
||
244 | $linefactor = $tblheight * ($famcount - $genoffset); |
||
245 | } |
||
246 | } |
||
247 | } |
||
248 | $lh = (($famcount - 1) * ($this->getBoxDimensions()->height) - ($linefactor)); |
||
249 | if ($genoffset > 5) { |
||
250 | $lh = $savlh; |
||
251 | } |
||
252 | } |
||
253 | echo '<img class="line3 pvline" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $lh, '"></td>', |
||
254 | '<td>', |
||
255 | '<img class="linef2" src="', Theme::theme()->parameter('image-hline'), '" height="3"></td>', |
||
256 | '<td>'; |
||
257 | $lh = $savlh; // restore original line height |
||
258 | //-- print the father box |
||
259 | FunctionsPrint::printPedigreePerson($family->getHusband()); |
||
260 | echo '</td>'; |
||
261 | if ($family->getHusband()) { |
||
262 | echo '<td>'; |
||
263 | //-- recursively get the father’s family |
||
264 | $this->printPersonPedigree($family->getHusband(), $count + 1); |
||
265 | echo '</td>'; |
||
266 | } else { |
||
267 | echo '<td>'; |
||
268 | if ($genoffset > $count) { |
||
269 | echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
||
270 | for ($i = 1; $i < (pow(2, ($genoffset) - $count) / 2); $i++) { |
||
271 | $this->printEmptyBox(); |
||
272 | echo '</tr>'; |
||
273 | } |
||
274 | echo '</table>'; |
||
275 | } |
||
276 | } |
||
277 | echo '</tr><tr>', |
||
278 | '<td class="align-top"><img class="pvline" src="', Theme::theme()->parameter('image-vline'), '" width="3" height="', $lh, '"></td>', |
||
279 | '<td><img class="linef3" src="', Theme::theme()->parameter('image-hline'), '" height="3"></td>', |
||
280 | '<td>'; |
||
281 | //-- print the mother box |
||
282 | FunctionsPrint::printPedigreePerson($family->getWife()); |
||
283 | echo '</td>'; |
||
284 | if ($family->getWife()) { |
||
285 | echo '<td>'; |
||
286 | //-- recursively print the mother’s family |
||
287 | $this->printPersonPedigree($family->getWife(), $count + 1); |
||
288 | echo '</td>'; |
||
289 | View Code Duplication | } else { |
|
290 | echo '<td>'; |
||
291 | if ($count < $genoffset - 1) { |
||
292 | echo '<table cellspacing="0" cellpadding="0" border="0" >'; |
||
293 | for ($i = 1; $i < (pow(2, ($genoffset - 1) - $count) / 2) + 1; $i++) { |
||
294 | $this->printEmptyBox(); |
||
295 | echo '</tr>'; |
||
296 | $this->printEmptyBox(); |
||
297 | echo '</tr>'; |
||
298 | } |
||
299 | echo '</table>'; |
||
300 | } |
||
301 | } |
||
302 | echo '</tr>', |
||
303 | '</table>'; |
||
304 | break; |
||
305 | } |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * Calculates number of generations a person has |
||
310 | * |
||
311 | * @param string $pid |
||
312 | * @param int $depth |
||
313 | * |
||
314 | * @return int |
||
315 | */ |
||
316 | private function maxDescendencyGenerations($pid, $depth) { |
||
317 | if ($depth > $this->generations) { |
||
318 | return $depth; |
||
319 | } |
||
320 | $person = Individual::getInstance($pid, $this->tree()); |
||
321 | if (is_null($person)) { |
||
322 | return $depth; |
||
323 | } |
||
324 | $maxdc = $depth; |
||
325 | foreach ($person->getSpouseFamilies() as $family) { |
||
326 | View Code Duplication | foreach ($family->getChildren() as $child) { |
|
327 | $dc = $this->maxDescendencyGenerations($child->getXref(), $depth + 1); |
||
328 | if ($dc >= $this->generations) { |
||
329 | return $dc; |
||
330 | } |
||
331 | if ($dc > $maxdc) { |
||
332 | $maxdc = $dc; |
||
333 | } |
||
334 | } |
||
335 | } |
||
336 | $maxdc++; |
||
337 | if ($maxdc == 1) { |
||
338 | $maxdc++; |
||
339 | } |
||
340 | |||
341 | return $maxdc; |
||
342 | } |
||
343 | |||
344 | /** |
||
345 | * Print empty box |
||
346 | */ |
||
347 | |||
348 | private function printEmptyBox() { |
||
349 | echo Theme::theme()->individualBoxEmpty(); |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * Print a “Family Book” for an individual |
||
354 | * |
||
355 | * @param Individual $person |
||
356 | * @param int $descent_steps |
||
357 | */ |
||
358 | public function printFamilyBook(Individual $person, $descent_steps) { |
||
378 | } |
||
379 | } |
||
380 | } |
||
381 | } |
||
382 | } |
||
383 |