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 UserApi 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 UserApi, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 20 | class UserApi extends AbstractApi |
||
| 21 | { |
||
| 22 | const AVATAR_SIZE_SMALL = '24'; |
||
| 23 | const AVATAR_SIZE_MEDIUM = '40'; |
||
| 24 | const AVATAR_SIZE_LARGE = '80'; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * @var string Base URL for user avatars |
||
| 28 | */ |
||
| 29 | protected $avatarsBaseUrl = '//point.im/avatar/'; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @var EntityManager |
||
| 33 | */ |
||
| 34 | protected $em; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * @var EntityRepository |
||
| 38 | */ |
||
| 39 | protected $userRepository; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * @var Serializer |
||
| 43 | */ |
||
| 44 | private $serializer; |
||
| 45 | |||
| 46 | |||
| 47 | 12 | public function __construct(ClientInterface $httpClient, EntityManager $entityManager, Serializer $serializer) |
|
|
|
|||
| 48 | { |
||
| 49 | 12 | parent::__construct($httpClient); |
|
| 50 | |||
| 51 | 12 | $this->em = $entityManager; |
|
| 52 | 12 | $this->serializer = $serializer; |
|
| 53 | 12 | $this->userRepository = $this->em->getRepository('SkobkinPointToolsBundle:User'); |
|
| 54 | 12 | } |
|
| 55 | |||
| 56 | public function isAuthDataValid(string $login, string $password): bool |
||
| 68 | |||
| 69 | public function authenticate(string $login, string $password): Auth |
||
| 89 | |||
| 90 | public function logout(Auth $auth): bool |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Get user subscribers by user login |
||
| 109 | * |
||
| 110 | * @param string $login |
||
| 111 | * |
||
| 112 | * @return User[] |
||
| 113 | * |
||
| 114 | * @throws ApiException |
||
| 115 | * @throws InvalidResponseException |
||
| 116 | * @throws UserNotFoundException |
||
| 117 | */ |
||
| 118 | View Code Duplication | public function getUserSubscribersByLogin(string $login): array |
|
| 132 | |||
| 133 | /** |
||
| 134 | * Get user subscribers by user id |
||
| 135 | * |
||
| 136 | * @param int $id |
||
| 137 | * |
||
| 138 | * @return User[] |
||
| 139 | * |
||
| 140 | * @throws ApiException |
||
| 141 | * @throws InvalidResponseException |
||
| 142 | * @throws UserNotFoundException |
||
| 143 | */ |
||
| 144 | View Code Duplication | public function getUserSubscribersById(int $id): array |
|
| 158 | |||
| 159 | /** |
||
| 160 | * Get user subscriptions by user login |
||
| 161 | * |
||
| 162 | * @param string $login |
||
| 163 | * |
||
| 164 | * @return User[] |
||
| 165 | * |
||
| 166 | * @throws ApiException |
||
| 167 | * @throws InvalidResponseException |
||
| 168 | * @throws UserNotFoundException |
||
| 169 | */ |
||
| 170 | View Code Duplication | public function getUserSubscriptionsByLogin(string $login): array |
|
| 184 | |||
| 185 | /** |
||
| 186 | * Get user subscriptions by user id |
||
| 187 | * |
||
| 188 | * @param int $id |
||
| 189 | * |
||
| 190 | * @return User[] |
||
| 191 | * |
||
| 192 | * @throws ApiException |
||
| 193 | * @throws InvalidResponseException |
||
| 194 | * @throws UserNotFoundException |
||
| 195 | */ |
||
| 196 | View Code Duplication | public function getUserSubscriptionsById(int $id): array |
|
| 210 | |||
| 211 | /** |
||
| 212 | * Get single user by login |
||
| 213 | * |
||
| 214 | * @param string $login |
||
| 215 | * |
||
| 216 | * @return User |
||
| 217 | * |
||
| 218 | * @throws UserNotFoundException |
||
| 219 | * @throws RequestException |
||
| 220 | */ |
||
| 221 | View Code Duplication | public function getUserByLogin(string $login): User |
|
| 235 | |||
| 236 | /** |
||
| 237 | * Get single user by id |
||
| 238 | * |
||
| 239 | * @param int $id |
||
| 240 | * |
||
| 241 | * @return User |
||
| 242 | * |
||
| 243 | * @throws UserNotFoundException |
||
| 244 | * @throws RequestException |
||
| 245 | */ |
||
| 246 | View Code Duplication | public function getUserById(int $id): User |
|
| 260 | |||
| 261 | /** |
||
| 262 | * Finds and updates or create new user from API response data |
||
| 263 | * |
||
| 264 | * @param array $userInfo |
||
| 265 | * |
||
| 266 | * @return User |
||
| 267 | * |
||
| 268 | * @throws ApiException |
||
| 269 | * @throws InvalidResponseException |
||
| 270 | */ |
||
| 271 | public function getUserFromUserInfo(array $userInfo): User |
||
| 293 | |||
| 294 | /** |
||
| 295 | * Get array of User objects from API response containing user list |
||
| 296 | * |
||
| 297 | * @param array $users |
||
| 298 | * |
||
| 299 | * @return User[] |
||
| 300 | * |
||
| 301 | * @throws ApiException |
||
| 302 | * @throws InvalidResponseException |
||
| 303 | */ |
||
| 304 | private function getUsersFromList(array $users = []): array |
||
| 332 | |||
| 333 | /** |
||
| 334 | * Creates URL of avatar with specified size by User object |
||
| 335 | * |
||
| 336 | * @param User $user |
||
| 337 | * @param string $size |
||
| 338 | * |
||
| 339 | * @return string |
||
| 340 | */ |
||
| 341 | public function getAvatarUrl(User $user, string $size): string |
||
| 345 | |||
| 346 | 3 | /** |
|
| 347 | * Creates URL of avatar with specified size by login string |
||
| 348 | * |
||
| 349 | * @param string $login |
||
| 350 | * @param string $size |
||
| 351 | * |
||
| 352 | * @return string |
||
| 353 | */ |
||
| 354 | public function getAvatarUrlByLogin(string $login, string $size): string |
||
| 362 | } |
||
| 363 |
The
EntityManagermight become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManageris closed. Any other code which depends on the same instance of theEntityManagerduring this request will fail.On the other hand, if you instead inject the
ManagerRegistry, thegetManager()method guarantees that you will always get a usable manager instance.