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 TranslatableTest 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 TranslatableTest, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class TranslatableTest extends TestsBase |
||
|
|||
10 | { |
||
11 | public function test_it_finds_the_default_translation_class() |
||
12 | { |
||
13 | $country = new Country(); |
||
14 | $this->assertEquals( |
||
15 | 'Dimsav\Translatable\Test\Model\CountryTranslation', |
||
16 | $country->getTranslationModelNameDefault()); |
||
17 | } |
||
18 | |||
19 | public function test_it_finds_the_translation_class_with_suffix_set() |
||
20 | { |
||
21 | App::make('config')->set('translatable.translation_suffix', 'Trans'); |
||
22 | $country = new Country(); |
||
23 | $this->assertEquals( |
||
24 | 'Dimsav\Translatable\Test\Model\CountryTrans', |
||
25 | $country->getTranslationModelName()); |
||
26 | } |
||
27 | |||
28 | public function test_it_returns_custom_TranslationModelName() |
||
29 | { |
||
30 | $country = new Country(); |
||
31 | |||
32 | $this->assertEquals( |
||
33 | $country->getTranslationModelNameDefault(), |
||
34 | $country->getTranslationModelName() |
||
35 | ); |
||
36 | |||
37 | $country->translationModel = 'MyAwesomeCountryTranslation'; |
||
38 | $this->assertEquals( |
||
39 | 'MyAwesomeCountryTranslation', |
||
40 | $country->getTranslationModelName() |
||
41 | ); |
||
42 | } |
||
43 | |||
44 | public function test_it_returns_relation_key() |
||
45 | { |
||
46 | $country = new Country(); |
||
47 | $this->assertEquals('country_id', $country->getRelationKey()); |
||
48 | |||
49 | $country->translationForeignKey = 'my_awesome_key'; |
||
50 | $this->assertEquals('my_awesome_key', $country->getRelationKey()); |
||
51 | } |
||
52 | |||
53 | public function test_it_returns_the_translation() |
||
54 | { |
||
55 | /** @var Country $country */ |
||
56 | $country = Country::whereCode('gr')->first(); |
||
57 | |||
58 | $englishTranslation = $country->translate('el'); |
||
59 | $this->assertEquals('Ελλάδα', $englishTranslation->name); |
||
60 | |||
61 | $englishTranslation = $country->translate('en'); |
||
62 | $this->assertEquals('Greece', $englishTranslation->name); |
||
63 | |||
64 | $this->app->setLocale('el'); |
||
65 | $englishTranslation = $country->translate(); |
||
66 | $this->assertEquals('Ελλάδα', $englishTranslation->name); |
||
67 | |||
68 | $this->app->setLocale('en'); |
||
69 | $englishTranslation = $country->translate(); |
||
70 | $this->assertEquals('Greece', $englishTranslation->name); |
||
71 | } |
||
72 | |||
73 | public function test_it_returns_the_translation_with_accessor() |
||
74 | { |
||
75 | /** @var Country $country */ |
||
76 | $country = Country::whereCode('gr')->first(); |
||
77 | |||
78 | $this->assertEquals('Ελλάδα', $country->{'name:el'}); |
||
79 | $this->assertEquals('Greece', $country->{'name:en'}); |
||
80 | } |
||
81 | |||
82 | public function test_it_returns_null_when_the_locale_doesnt_exist() |
||
89 | |||
90 | View Code Duplication | public function test_it_saves_translations() |
|
91 | { |
||
92 | $country = Country::whereCode('gr')->first(); |
||
93 | |||
94 | $country->name = '1234'; |
||
95 | $country->save(); |
||
96 | |||
97 | $country = Country::whereCode('gr')->first(); |
||
98 | $this->assertEquals('1234', $country->name); |
||
99 | } |
||
100 | |||
101 | public function test_it_saves_translations_with_mutator() |
||
102 | { |
||
103 | $country = Country::whereCode('gr')->first(); |
||
104 | |||
105 | $country->{'name:en'} = '1234'; |
||
106 | $country->{'name:el'} = '5678'; |
||
107 | $country->save(); |
||
108 | |||
109 | $country = Country::whereCode('gr')->first(); |
||
110 | |||
111 | $this->app->setLocale('en'); |
||
112 | $translation = $country->translate(); |
||
113 | $this->assertEquals('1234', $translation->name); |
||
114 | |||
115 | $this->app->setLocale('el'); |
||
116 | $translation = $country->translate(); |
||
117 | $this->assertEquals('5678', $translation->name); |
||
118 | } |
||
119 | |||
120 | public function test_it_uses_default_locale_to_return_translations() |
||
121 | { |
||
122 | $country = Country::whereCode('gr')->first(); |
||
123 | |||
124 | $country->translate('el')->name = 'abcd'; |
||
125 | |||
126 | $this->app->setLocale('el'); |
||
127 | $this->assertEquals('abcd', $country->name); |
||
128 | $country->save(); |
||
129 | |||
130 | $country = Country::whereCode('gr')->first(); |
||
131 | $this->assertEquals('abcd', $country->translate('el')->name); |
||
132 | } |
||
133 | |||
134 | public function test_it_creates_translations() |
||
135 | { |
||
136 | $country = new Country(); |
||
137 | $country->code = 'be'; |
||
138 | $country->save(); |
||
139 | |||
140 | $country = Country::whereCode('be')->first(); |
||
141 | $country->name = 'Belgium'; |
||
142 | $country->save(); |
||
143 | |||
144 | $country = Country::whereCode('be')->first(); |
||
145 | $this->assertEquals('Belgium', $country->name); |
||
146 | } |
||
147 | |||
148 | View Code Duplication | public function test_it_creates_translations_using_the_shortcut() |
|
149 | { |
||
150 | $country = new Country(); |
||
151 | $country->code = 'be'; |
||
152 | $country->name = 'Belgium'; |
||
153 | $country->save(); |
||
154 | |||
155 | $country = Country::whereCode('be')->first(); |
||
156 | $this->assertEquals('Belgium', $country->name); |
||
157 | } |
||
158 | |||
159 | public function test_it_creates_translations_using_mass_assignment() |
||
160 | { |
||
161 | $data = [ |
||
162 | 'code' => 'be', |
||
163 | 'name' => 'Belgium', |
||
164 | ]; |
||
165 | $country = Country::create($data); |
||
166 | $this->assertEquals('be', $country->code); |
||
167 | $this->assertEquals('Belgium', $country->name); |
||
168 | } |
||
169 | |||
170 | public function test_it_creates_translations_using_mass_assignment_and_locales() |
||
171 | { |
||
172 | $data = [ |
||
173 | 'code' => 'be', |
||
174 | 'en' => ['name' => 'Belgium'], |
||
175 | 'fr' => ['name' => 'Belgique'], |
||
176 | ]; |
||
177 | $country = Country::create($data); |
||
178 | $this->assertEquals('be', $country->code); |
||
179 | $this->assertEquals('Belgium', $country->translate('en')->name); |
||
180 | $this->assertEquals('Belgique', $country->translate('fr')->name); |
||
181 | |||
182 | $country = Country::whereCode('be')->first(); |
||
183 | $this->assertEquals('Belgium', $country->translate('en')->name); |
||
184 | $this->assertEquals('Belgique', $country->translate('fr')->name); |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * @expectedException Illuminate\Database\Eloquent\MassAssignmentException |
||
189 | */ |
||
190 | public function test_it_skips_mass_assignment_if_attributes_non_fillable() |
||
191 | { |
||
192 | $data = [ |
||
193 | 'code' => 'be', |
||
194 | 'en' => ['name' => 'Belgium'], |
||
195 | 'fr' => ['name' => 'Belgique'], |
||
196 | ]; |
||
197 | $country = CountryStrict::create($data); |
||
198 | $this->assertEquals('be', $country->code); |
||
199 | $this->assertNull($country->translate('en')); |
||
200 | $this->assertNull($country->translate('fr')); |
||
201 | } |
||
202 | |||
203 | public function test_it_returns_if_object_has_translation() |
||
204 | { |
||
205 | $country = Country::find(1); |
||
206 | $this->assertTrue($country->hasTranslation('en')); |
||
207 | $this->assertFalse($country->hasTranslation('abc')); |
||
208 | } |
||
209 | |||
210 | public function test_it_returns_default_translation() |
||
211 | { |
||
212 | App::make('config')->set('translatable.fallback_locale', 'de'); |
||
213 | |||
214 | $country = Country::find(1); |
||
215 | $this->assertSame($country->getTranslation('ch', true)->name, 'Griechenland'); |
||
216 | $this->assertSame($country->translateOrDefault('ch')->name, 'Griechenland'); |
||
217 | $this->assertSame($country->getTranslation('ch', false), null); |
||
218 | } |
||
219 | |||
220 | public function test_fallback_option_in_config_overrides_models_fallback_option() |
||
221 | { |
||
222 | App::make('config')->set('translatable.fallback_locale', 'de'); |
||
223 | |||
224 | $country = Country::find(1); |
||
225 | $this->assertEquals($country->getTranslation('ch', true)->locale, 'de'); |
||
226 | |||
227 | $country->useTranslationFallback = false; |
||
228 | $this->assertEquals($country->getTranslation('ch', true)->locale, 'de'); |
||
229 | |||
230 | $country->useTranslationFallback = true; |
||
231 | $this->assertEquals($country->getTranslation('ch')->locale, 'de'); |
||
232 | |||
233 | $country->useTranslationFallback = false; |
||
234 | $this->assertSame($country->getTranslation('ch'), null); |
||
235 | } |
||
236 | |||
237 | public function test_configuration_defines_if_fallback_is_used() |
||
238 | { |
||
239 | App::make('config')->set('translatable.fallback_locale', 'de'); |
||
240 | App::make('config')->set('translatable.use_fallback', true); |
||
241 | |||
242 | $country = Country::find(1); |
||
243 | $this->assertEquals($country->getTranslation('ch')->locale, 'de'); |
||
244 | } |
||
245 | |||
246 | public function test_useTranslationFallback_overrides_configuration() |
||
247 | { |
||
248 | App::make('config')->set('translatable.fallback_locale', 'de'); |
||
249 | App::make('config')->set('translatable.use_fallback', true); |
||
250 | $country = Country::find(1); |
||
251 | $country->useTranslationFallback = false; |
||
252 | $this->assertSame($country->getTranslation('ch'), null); |
||
253 | } |
||
254 | |||
255 | public function test_it_returns_null_if_fallback_is_not_defined() |
||
256 | { |
||
257 | App::make('config')->set('translatable.fallback_locale', 'ch'); |
||
258 | |||
259 | $country = Country::find(1); |
||
260 | $this->assertSame($country->getTranslation('pl', true), null); |
||
261 | } |
||
262 | |||
263 | public function test_it_fills_a_non_default_language_with_fallback_set() |
||
264 | { |
||
265 | App::make('config')->set('translatable.fallback_locale', 'en'); |
||
266 | |||
267 | $country = new Country(); |
||
268 | $country->fill([ |
||
269 | 'code' => 'gr', |
||
270 | 'en' => ['name' => 'Greece'], |
||
271 | 'de' => ['name' => 'Griechenland'], |
||
272 | ]); |
||
273 | |||
274 | $this->assertEquals($country->translate('en')->name, 'Greece'); |
||
275 | } |
||
276 | |||
277 | public function test_it_creates_a_new_translation() |
||
278 | { |
||
279 | App::make('config')->set('translatable.fallback_locale', 'en'); |
||
280 | |||
281 | $country = Country::create(['code' => 'gr']); |
||
282 | $country->getNewTranslation('en')->name = 'Greece'; |
||
283 | $country->save(); |
||
284 | |||
285 | $this->assertEquals($country->translate('en')->name, 'Greece'); |
||
286 | } |
||
287 | |||
288 | public function test_the_locale_key_is_locale_by_default() |
||
289 | { |
||
290 | $country = Country::find(1); |
||
291 | $this->assertEquals($country->getLocaleKey(), 'locale'); |
||
292 | } |
||
293 | |||
294 | public function test_the_locale_key_can_be_overridden_in_configuration() |
||
301 | |||
302 | public function test_the_locale_key_can_be_customized_per_model() |
||
303 | { |
||
304 | $country = CountryWithCustomLocaleKey::find(1); |
||
305 | $this->assertEquals($country->getLocaleKey(), 'language_id'); |
||
306 | } |
||
307 | |||
308 | public function test_it_reads_the_configuration() |
||
309 | { |
||
310 | $this->assertEquals(App::make('config')->get('translatable.translation_suffix'), 'Translation'); |
||
311 | } |
||
312 | |||
313 | public function test_getting_translation_does_not_create_translation() |
||
314 | { |
||
315 | $country = Country::with('translations')->find(1); |
||
316 | $translation = $country->getTranslation('abc', false); |
||
317 | $this->assertSame($translation, null); |
||
318 | } |
||
319 | |||
320 | public function test_getting_translated_field_does_not_create_translation() |
||
321 | { |
||
322 | $this->app->setLocale('en'); |
||
323 | $country = new Country(['code' => 'pl']); |
||
324 | $country->save(); |
||
325 | |||
326 | $country->name; |
||
327 | |||
328 | $this->assertSame($country->getTranslation('en'), null); |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * @expectedException Dimsav\Translatable\Exception\LocalesNotDefinedException |
||
333 | */ |
||
334 | public function test_if_locales_are_not_defined_throw_exception() |
||
339 | |||
340 | public function test_it_has_methods_that_return_always_a_translation() |
||
341 | { |
||
342 | $country = Country::find(1)->first(); |
||
343 | $this->assertSame('abc', $country->translateOrNew('abc')->locale); |
||
344 | } |
||
345 | |||
346 | public function test_it_returns_if_attribute_is_translated() |
||
347 | { |
||
348 | $country = new Country(); |
||
349 | |||
350 | $this->assertTrue($country->isTranslationAttribute('name')); |
||
351 | $this->assertFalse($country->isTranslationAttribute('some-field')); |
||
352 | } |
||
353 | |||
354 | public function test_config_overrides_apps_locale() |
||
355 | { |
||
356 | $country = Country::find(1); |
||
357 | App::make('config')->set('translatable.locale', 'de'); |
||
358 | |||
359 | $this->assertSame('Griechenland', $country->name); |
||
360 | } |
||
361 | |||
362 | public function test_locales_as_array_keys_are_properly_detected() |
||
363 | { |
||
364 | $this->app->config->set('translatable.locales', ['en' => ['US', 'GB']]); |
||
365 | |||
366 | $data = [ |
||
367 | 'en' => ['name' => 'French fries'], |
||
368 | 'en-US' => ['name' => 'American french fries'], |
||
369 | 'en-GB' => ['name' => 'Chips'], |
||
377 | |||
378 | public function test_locale_separator_can_be_configured() |
||
389 | |||
390 | public function test_fallback_for_country_based_locales() |
||
406 | |||
407 | public function test_fallback_for_country_based_locales_with_no_base_locale() |
||
422 | |||
423 | public function test_to_array_and_fallback_with_country_based_locales_enabled() |
||
437 | |||
438 | public function test_it_skips_translations_in_to_array_when_config_is_set() |
||
444 | |||
445 | public function test_it_returns_translations_in_to_array_when_config_is_set_but_translations_are_loaded() |
||
451 | |||
452 | public function test_it_should_mutate_the_translated_attribute_if_a_mutator_is_set_on_model() |
||
459 | |||
460 | public function test_it_deletes_all_translations() |
||
471 | |||
472 | public function test_it_deletes_translations_for_given_locales() |
||
484 | |||
485 | public function test_passing_an_empty_array_should_not_delete_translations() |
||
495 | |||
496 | public function test_fill_with_translation_key() |
||
512 | |||
513 | public function test_it_uses_the_default_locale_from_the_model() |
||
532 | |||
533 | public function test_replicate_entity() |
||
549 | |||
550 | public function test_getTranslationsArray() |
||
568 | |||
569 | public function test_fill_when_locale_key_unknown() |
||
594 | |||
595 | public function test_fill_with_translation_key_when_locale_key_unknown() |
||
620 | |||
621 | public function test_it_uses_fallback_locale_if_default_is_empty() |
||
637 | } |
||
638 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.