HandlesS3Files.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. namespace App\Traits;
  3. use App\Services\MemberFileService;
  4. use Illuminate\Support\Facades\Log;
  5. trait HandlesS3Files
  6. {
  7. /**
  8. * Get file service instance
  9. */
  10. protected function getFileService(): MemberFileService
  11. {
  12. return app(MemberFileService::class);
  13. }
  14. /**
  15. * Get file URL for display
  16. */
  17. public function getFileUrl(?string $filePath, string $expiresIn = '+1 hour'): ?string
  18. {
  19. if (!$filePath) {
  20. return null;
  21. }
  22. return $this->getFileService()->getFileUrl($filePath, $expiresIn);
  23. }
  24. /**
  25. * Check if file exists in S3
  26. */
  27. public function fileExists(?string $filePath): bool
  28. {
  29. if (!$filePath) {
  30. return false;
  31. }
  32. try {
  33. return \Illuminate\Support\Facades\Storage::disk('s3')->exists($filePath);
  34. } catch (\Exception $e) {
  35. Log::warning("Error checking file existence", [
  36. 'path' => $filePath,
  37. 'error' => $e->getMessage()
  38. ]);
  39. return false;
  40. }
  41. }
  42. /**
  43. * Get file info
  44. */
  45. public function getFileInfo(?string $filePath): ?array
  46. {
  47. if (!$filePath) {
  48. return null;
  49. }
  50. return $this->getFileService()->getFileInfo($filePath);
  51. }
  52. /**
  53. * Delete file from S3
  54. */
  55. public function deleteFile(string $filePath): bool
  56. {
  57. return $this->getFileService()->deleteFile($filePath);
  58. }
  59. /**
  60. * Get file name from path
  61. */
  62. public function getFileName(?string $filePath): ?string
  63. {
  64. if (!$filePath) {
  65. return null;
  66. }
  67. return basename($filePath);
  68. }
  69. /**
  70. * Check if path is S3 path
  71. */
  72. public function isS3Path(?string $path): bool
  73. {
  74. if (!$path) {
  75. return false;
  76. }
  77. return strpos($path, '/members/') !== false ||
  78. strpos($path, session('currentClient', 'default')) === 0;
  79. }
  80. /**
  81. * Convert file paths array to string for database storage
  82. */
  83. public function filePathsToString(array $paths): string
  84. {
  85. return implode('|', array_filter($paths));
  86. }
  87. /**
  88. * Convert file paths string from database to array
  89. */
  90. public function stringToFilePaths(?string $pathsString): array
  91. {
  92. if (!$pathsString) {
  93. return [];
  94. }
  95. return array_filter(explode('|', $pathsString));
  96. }
  97. /**
  98. * Get multiple file URLs
  99. */
  100. public function getMultipleFileUrls(array $filePaths): array
  101. {
  102. $urls = [];
  103. foreach ($filePaths as $path) {
  104. $url = $this->getFileUrl($path);
  105. if ($url) {
  106. $urls[$path] = $url;
  107. }
  108. }
  109. return $urls;
  110. }
  111. /**
  112. * Clean up files for deleted member
  113. */
  114. public function cleanupMemberFiles(int $memberId): void
  115. {
  116. try {
  117. $currentClient = session('currentClient', 'default');
  118. $memberPath = $currentClient . '/members/' . $memberId . '/';
  119. $disk = \Illuminate\Support\Facades\Storage::disk('s3');
  120. // Delete all files in member's folder
  121. $files = $disk->allFiles($memberPath);
  122. foreach ($files as $file) {
  123. $disk->delete($file);
  124. }
  125. // Delete empty directories
  126. $directories = $disk->allDirectories($memberPath);
  127. foreach (array_reverse($directories) as $directory) {
  128. $disk->deleteDirectory($directory);
  129. }
  130. Log::info("Cleaned up files for deleted member", ['member_id' => $memberId]);
  131. } catch (\Exception $e) {
  132. Log::error("Error cleaning up member files", [
  133. 'member_id' => $memberId,
  134. 'error' => $e->getMessage()
  135. ]);
  136. }
  137. }
  138. /**
  139. * Validate file upload
  140. */
  141. public function validateFileUpload($file, string $type = 'document'): array
  142. {
  143. $errors = [];
  144. if (!$file) {
  145. $errors[] = 'No file provided';
  146. return $errors;
  147. }
  148. if (!$file->isValid()) {
  149. $errors[] = 'Invalid file upload';
  150. return $errors;
  151. }
  152. $extension = strtolower($file->getClientOriginalExtension());
  153. $size = $file->getSize();
  154. if ($type === 'image') {
  155. $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
  156. $maxSize = 2048 * 1024; // 2MB
  157. if (!in_array($extension, $allowedExtensions)) {
  158. $errors[] = 'Invalid image format. Allowed: ' . implode(', ', $allowedExtensions);
  159. }
  160. if ($size > $maxSize) {
  161. $errors[] = 'Image too large. Maximum size: 2MB';
  162. }
  163. } elseif ($type === 'document') {
  164. $allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'];
  165. $maxSize = 10240 * 1024; // 10MB
  166. if (!in_array($extension, $allowedExtensions)) {
  167. $errors[] = 'Invalid document format. Allowed: ' . implode(', ', $allowedExtensions);
  168. }
  169. if ($size > $maxSize) {
  170. $errors[] = 'Document too large. Maximum size: 10MB';
  171. }
  172. }
  173. return $errors;
  174. }
  175. }