Complex classes like Money 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 Money, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
25 | class Money extends DBField implements CompositeDBField { |
||
26 | |||
27 | /** |
||
28 | * @var string $getCurrency() |
||
29 | */ |
||
30 | protected $currency; |
||
31 | |||
32 | /** |
||
33 | * @var float $currencyAmount |
||
34 | */ |
||
35 | protected $amount; |
||
36 | |||
37 | /** |
||
38 | * @var boolean $isChanged |
||
39 | */ |
||
40 | protected $isChanged = false; |
||
41 | |||
42 | /** |
||
43 | * @var string $locale |
||
44 | */ |
||
45 | protected $locale = null; |
||
46 | |||
47 | /** |
||
48 | * @var Zend_Currency |
||
49 | */ |
||
50 | protected $currencyLib; |
||
51 | |||
52 | /** |
||
53 | * Limit the currencies |
||
54 | * @var array $allowedCurrencies |
||
55 | */ |
||
56 | protected $allowedCurrencies; |
||
57 | |||
58 | /** |
||
59 | * @param array |
||
60 | */ |
||
61 | private static $composite_db = array( |
||
62 | "Currency" => "Varchar(3)", |
||
63 | "Amount" => 'Decimal(19,4)' |
||
64 | ); |
||
65 | |||
66 | public function __construct($name = null) { |
||
67 | $this->currencyLib = new Zend_Currency(null, i18n::get_locale()); |
||
68 | |||
69 | parent::__construct($name); |
||
70 | } |
||
71 | |||
72 | public function compositeDatabaseFields() { |
||
75 | |||
76 | public function requireField() { |
||
77 | $fields = $this->compositeDatabaseFields(); |
||
78 | if($fields) foreach($fields as $name => $type){ |
||
|
|||
79 | DB::require_field($this->tableName, $this->name.$name, $type); |
||
80 | } |
||
81 | } |
||
82 | |||
83 | public function writeToManipulation(&$manipulation) { |
||
84 | if($this->getCurrency()) { |
||
85 | $manipulation['fields'][$this->name.'Currency'] = $this->prepValueForDB($this->getCurrency()); |
||
86 | } else { |
||
87 | $manipulation['fields'][$this->name.'Currency'] |
||
88 | = DBField::create_field('Varchar', $this->getCurrency())->nullValue(); |
||
89 | } |
||
90 | |||
91 | if($this->getAmount()) { |
||
92 | $manipulation['fields'][$this->name.'Amount'] = $this->getAmount(); |
||
93 | } else { |
||
94 | $manipulation['fields'][$this->name.'Amount'] |
||
95 | = DBField::create_field('Decimal', $this->getAmount())->nullValue(); |
||
96 | } |
||
97 | } |
||
98 | |||
99 | public function addToQuery(&$query) { |
||
100 | parent::addToQuery($query); |
||
101 | $query->selectField(sprintf('"%sAmount"', $this->name)); |
||
102 | $query->selectField(sprintf('"%sCurrency"', $this->name)); |
||
103 | } |
||
104 | |||
105 | public function setValue($value, $record = null, $markChanged = true) { |
||
106 | // Convert an object to an array |
||
107 | if($record && $record instanceof DataObject) { |
||
108 | $record = $record->getQueriedDatabaseFields(); |
||
109 | } |
||
110 | |||
111 | // @todo Allow resetting value to NULL through Money $value field |
||
112 | if ($value instanceof Money && $value->exists()) { |
||
113 | $this->setCurrency($value->getCurrency(), $markChanged); |
||
114 | $this->setAmount($value->getAmount(), $markChanged); |
||
115 | if($markChanged) $this->isChanged = true; |
||
116 | } else if($record && isset($record[$this->name . 'Amount'])) { |
||
117 | if($record[$this->name . 'Amount']) { |
||
118 | if(!empty($record[$this->name . 'Currency'])) { |
||
119 | $this->setCurrency($record[$this->name . 'Currency'], $markChanged); |
||
120 | } else if($currency = (string)$this->config()->default_currency) { |
||
121 | $this->setCurrency($currency, $markChanged); |
||
122 | } |
||
123 | |||
124 | $this->setAmount($record[$this->name . 'Amount'], $markChanged); |
||
125 | } else { |
||
126 | $this->value = $this->nullValue(); |
||
127 | } |
||
128 | if($markChanged) $this->isChanged = true; |
||
129 | } else if (is_array($value)) { |
||
130 | if (array_key_exists('Currency', $value)) { |
||
131 | $this->setCurrency($value['Currency'], $markChanged); |
||
132 | } |
||
133 | if (array_key_exists('Amount', $value)) { |
||
134 | $this->setAmount($value['Amount'], $markChanged); |
||
135 | } |
||
136 | if($markChanged) $this->isChanged = true; |
||
137 | } else { |
||
138 | // @todo Allow to reset a money value by passing in NULL |
||
139 | //user_error('Invalid value in Money->setValue()', E_USER_ERROR); |
||
140 | } |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * @return string |
||
145 | */ |
||
146 | public function Nice($options = array()) { |
||
147 | $amount = $this->getAmount(); |
||
148 | if(!isset($options['display'])) $options['display'] = Zend_Currency::USE_SYMBOL; |
||
149 | if(!isset($options['currency'])) $options['currency'] = $this->getCurrency(); |
||
150 | if(!isset($options['symbol'])) { |
||
151 | $options['symbol'] = $this->currencyLib->getSymbol($this->getCurrency(), $this->getLocale()); |
||
152 | } |
||
153 | return (is_numeric($amount)) ? $this->currencyLib->toCurrency($amount, $options) : ''; |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * @return string |
||
158 | */ |
||
159 | public function NiceWithShortname($options = array()){ |
||
163 | |||
164 | /** |
||
165 | * @return string |
||
166 | */ |
||
167 | public function NiceWithName($options = array()){ |
||
171 | |||
172 | /** |
||
173 | * @return string |
||
174 | */ |
||
175 | public function getCurrency() { |
||
178 | |||
179 | /** |
||
180 | * @param string |
||
181 | */ |
||
182 | public function setCurrency($currency, $markChanged = true) { |
||
186 | |||
187 | /** |
||
188 | * @todo Return casted Float DBField? |
||
189 | * |
||
190 | * @return float |
||
191 | */ |
||
192 | public function getAmount() { |
||
195 | |||
196 | /** |
||
197 | * @param float $amount |
||
198 | */ |
||
199 | public function setAmount($amount, $markChanged = true) { |
||
203 | |||
204 | /** |
||
205 | * @return boolean |
||
206 | */ |
||
207 | public function exists() { |
||
210 | |||
211 | /** |
||
212 | * @return boolean |
||
213 | */ |
||
214 | public function hasAmount() { |
||
218 | |||
219 | public function isChanged() { |
||
222 | |||
223 | /** |
||
224 | * @param string $locale |
||
225 | */ |
||
226 | public function setLocale($locale) { |
||
230 | |||
231 | /** |
||
232 | * @return string |
||
233 | */ |
||
234 | public function getLocale() { |
||
237 | |||
238 | /** |
||
239 | * @return string |
||
240 | */ |
||
241 | public function getSymbol($currency = null, $locale = null) { |
||
242 | |||
243 | if($locale === null) $locale = $this->getLocale(); |
||
244 | if($currency === null) $currency = $this->getCurrency(); |
||
245 | |||
246 | return $this->currencyLib->getSymbol($currency, $locale); |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * @return string |
||
251 | */ |
||
252 | public function getShortName($currency = null, $locale = null) { |
||
253 | if($locale === null) $locale = $this->getLocale(); |
||
254 | if($currency === null) $currency = $this->getCurrency(); |
||
255 | |||
256 | return $this->currencyLib->getShortName($currency, $locale); |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * @return string |
||
261 | */ |
||
262 | public function getName($currency = null, $locale = null) { |
||
263 | if($locale === null) $locale = $this->getLocale(); |
||
264 | if($currency === null) $currency = $this->getCurrency(); |
||
265 | |||
266 | return $this->currencyLib->getName($currency, $locale); |
||
267 | } |
||
268 | |||
269 | /** |
||
270 | * @param array $arr |
||
271 | */ |
||
272 | public function setAllowedCurrencies($arr) { |
||
275 | |||
276 | /** |
||
277 | * @return array |
||
278 | */ |
||
279 | public function getAllowedCurrencies() { |
||
282 | |||
283 | /** |
||
284 | * Returns a CompositeField instance used as a default |
||
285 | * for form scaffolding. |
||
286 | * |
||
287 | * Used by {@link SearchContext}, {@link ModelAdmin}, {@link DataObject::scaffoldFormFields()} |
||
288 | * |
||
289 | * @param string $title Optional. Localized title of the generated instance |
||
290 | * @return FormField |
||
291 | */ |
||
292 | public function scaffoldFormField($title = null) { |
||
293 | $field = new MoneyField($this->name); |
||
294 | $field->setAllowedCurrencies($this->getAllowedCurrencies()); |
||
295 | $field->setLocale($this->getLocale()); |
||
296 | |||
297 | return $field; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * For backwards compatibility reasons |
||
302 | * (mainly with ecommerce module), |
||
303 | * this returns the amount value of the field, |
||
304 | * rather than a {@link Nice()} formatting. |
||
305 | */ |
||
306 | public function __toString() { |
||
309 | } |
||
310 |
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.