+ Log::error('Failed to save user avatar image', ['exception' => $e]);
+ }
+ }
+
+ /**
+ * Assign a new avatar image to the given user using the given image data.
+ */
+ public function assignToUserFromExistingData(User $user, string $imageData, string $extension): void
+ {
+ try {
+ $this->destroyAllForUser($user);
+ $avatar = $this->createAvatarImageFromData($user, $imageData, $extension);
+ $user->avatar()->associate($avatar);
+ $user->save();
+ } catch (Exception $e) {
+ Log::error('Failed to save user avatar image', ['exception' => $e]);
+ }
+ }
+
+ /**
+ * Assign a new avatar image to the given user by fetching from a remote URL.
+ */
+ public function assignToUserFromUrl(User $user, string $avatarUrl): void
+ {
+ try {
+ $this->destroyAllForUser($user);
+ $imageData = $this->getAvatarImageData($avatarUrl);
+
+ $mime = (new WebSafeMimeSniffer())->sniff($imageData);
+ [$format, $type] = explode('/', $mime, 2);
+ if ($format !== 'image' || !ImageService::isExtensionSupported($type)) {
+ return;
+ }
+
+ $avatar = $this->createAvatarImageFromData($user, $imageData, $type);
+ $user->avatar()->associate($avatar);
+ $user->save();
+ } catch (Exception $e) {
+ Log::error('Failed to save user avatar image from URL', [
+ 'exception' => $e->getMessage(),
+ 'url' => $avatarUrl,
+ 'user_id' => $user->id,
+ ]);
+ }
+ }
+
+ /**
+ * Destroy all user avatars uploaded to the given user.
+ */
+ public function destroyAllForUser(User $user): void
+ {
+ $profileImages = Image::query()->where('type', '=', 'user')
+ ->where('uploaded_to', '=', $user->id)
+ ->get();
+
+ foreach ($profileImages as $image) {
+ $this->imageService->destroy($image);