diff --git a/app/Helper/FileHelper.php b/app/Helper/FileHelper.php index af7eb61..69059a0 100644 --- a/app/Helper/FileHelper.php +++ b/app/Helper/FileHelper.php @@ -13,8 +13,15 @@ class FileHelper { return implode(', ', $allowedFileExtensions); } - static function convertToStrLaraValidation(array $allowedFileExtensions) { - return implode(',', $allowedFileExtensions); + static function convertToStrLaraValidation(array $allowedFileExtensions, $type = 'string') { + $validations = [ + 'mimes:' . implode(',', $allowedFileExtensions), + 'extensions:' . implode(',', $allowedFileExtensions) + ]; + + if($type == 'string') return implode('|', $validations); + else if($type == 'array') return $validations; + else throw new \Exception('Type not valid'); } } ?> \ No newline at end of file diff --git a/app/Http/Controllers/api/superadmin/VideoUploadController.php b/app/Http/Controllers/api/superadmin/VideoUploadController.php new file mode 100644 index 0000000..cfa045f --- /dev/null +++ b/app/Http/Controllers/api/superadmin/VideoUploadController.php @@ -0,0 +1,30 @@ +validate([ + 'perPage' => 'nullable|integer|min:1', + ...DatabaseHelper::getOrderBysValidations(), + 'search' => DatabaseHelper::getSearchValidation() + ]); + + $data = VideoUpdate::multiSearch($request->search, ['file_name']) + ->multiOrderBy($request->orderBys, 'created_at desc') + ->paginate($request->perPage ?? 10 ); + return JSONResponse::Success(['data' => $data]); + } + + public function save(Request $request) { return VideoUpdate::upsertFromRequest($request); } + public function update(Request $request) { return VideoUpdate::upsertFromRequest($request); } + public function delete(Request $request) { return VideoUpdate::deleteFromRequest($request); } + + public function changeSelectedVideo(Request $request) { return VideoUpdate::changeSelectedVideoFromRequest($request); } +} diff --git a/app/Models/VideoUpdate.php b/app/Models/VideoUpdate.php index 3e93c3d..1bedafe 100644 --- a/app/Models/VideoUpdate.php +++ b/app/Models/VideoUpdate.php @@ -2,11 +2,146 @@ namespace App\Models; +use App\Helper\Common; +use App\Helper\FileHelper; +use App\Helper\JSONResponse; +use App\Helper\Traits\Models\CanMultiOrderBy; +use App\Helper\Traits\Models\CanMultiSearch; +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Http\File; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; class VideoUpdate extends Model { use HasFactory; + use CanMultiSearch; + use CanMultiOrderBy; protected $table = 'video_updates'; + protected $hidden = ['file']; + protected $appends = ['file_url']; + + public static function upsertFromRequest(Request $request) { + $request->validate([ + 'id' => 'nullable|integer|exists:App\Models\VideoUpdate,id', + 'is_selected' => 'nullable|in:true,false', + 'file' => 'required_without:id|file|' . FileHelper::convertToStrLaraValidation(FileHelper::$allowedVideoExtensions), + 'file_name' => 'required|string', + ], [ + 'file' => ['required_without' => 'The file field is required.'] + ]); + + $delOldDbFileLocation = ''; + $newDbFileLocation = ''; + try { + // save photo + if($request->file) $newDbFileLocation = self::saveFile($request->file)['db_url']; + + // try to upsert data + DB::beginTransaction(); + $videoUpdate = null; + if(!$request->id) $videoUpdate = new VideoUpdate(); + else $videoUpdate = VideoUpdate::findOrFail($request->id); + + // del old db file location if has old file + if($newDbFileLocation) { + if($videoUpdate->file) $delOldDbFileLocation = $videoUpdate->file; + $videoUpdate->file = $newDbFileLocation; + } + $videoUpdate->file_name = $request->file_name; + + if($request->is_selected == 'true') { + VideoUpdate::where('is_selected', true)->update(['is_selected' => false]); + $videoUpdate->is_selected = true; + } else $videoUpdate->is_selected = false; + + // renew data + $videoUpdate->save(); + + // delete old file if exist + if($delOldDbFileLocation) self::deleteFile($delOldDbFileLocation); + DB::commit(); + return JSONResponse::Success(); + } catch (\Throwable $th) { + DB::rollBack(); + if($newDbFileLocation) self::deleteFile($newDbFileLocation); + throw $th; + } + } + + public static function deleteFromRequest(Request $request) { + $request->validate(['id' => 'required|integer|exists:App\Models\VideoUpdate,id']); + try { + DB::beginTransaction(); + $videoUpdate = VideoUpdate::findOrFail($request->id); + if($videoUpdate->is_selected) throw new \Exception("Cannot delete video when 'is Selected' is true"); + + $oldDbFile = $videoUpdate->file; + $videoUpdate->delete(); + + if($oldDbFile) self::deleteFile($oldDbFile); + DB::commit(); + return JSONResponse::Success(); + } catch (\Throwable $th) { + DB::rollBack(); + throw $th; + } + + } + + public static function changeSelectedVideoFromRequest(Request $request) { + $request->validate(['id' => 'required|integer|exists:App\Models\VideoUpdate,id']); + + try { + DB::beginTransaction(); + + $videoUpdate = VideoUpdate::findOrFail($request->id); + $videoUpdate->is_selected = !$videoUpdate->is_selected; + $videoUpdate->save(); + DB::commit(); + + if($videoUpdate->is_selected) { + VideoUpdate::where([ + ['id', '!=', $videoUpdate->id], + ['is_selected', true] + ])->update(['is_selected' => false]); + } + return JSONResponse::Success(); + } catch (\Throwable $th) { + DB::rollBack(); + throw $th; + } + } + + // -- File UTILITIES + protected function fileUrl(): Attribute { + return Attribute::make( + fn() => $this->file ? Storage::disk('s3')->url($this->file) : '' + ); + } + private static function fileFolder() { return env('STORAGE_FOLDER', 'tivi') . '/video-upload'; } + + public static function saveFile($file) { + if (!$file->isValid()) throw new \Exception('File is not valid'); + + //Save file to local data + $fileName = self::getFileName($file); + $path = self::fileFolder(); + + Storage::disk('s3')->put($path . '/' . $fileName, file_get_contents(new File($file)), 'public'); + return ['db_url' => "$path/$fileName"]; + } + private static function getFileName($file) { + $tz = Carbon::now()->timestamp; + $extension = $file->getClientOriginalExtension(); + $name = "$tz-" . Common::generateRandomString(); + + return "$name.$extension"; + } + public static function deleteFile($dbUrl) { Storage::disk('s3')->delete($dbUrl); } + // -- END File UTILITIES } diff --git a/routes/api.php b/routes/api.php index 5e2846e..884c484 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,7 @@ group(function() { Route::post('/auth/logout', 'logout'); }); }); + +Route::middleware(USER_MIDDLEWARES)->prefix('superadmin')->group(function() { + Route::controller(VideoUploadController::class)->group(function() { + Route::post('/video-upload', 'init'); + Route::post('/video-upload/save', 'save'); + Route::post('/video-upload/update', 'update'); + Route::post('/video-upload/delete', 'delete'); + Route::post('/video-upload/change-selected-video', 'changeSelectedVideo'); + }); +}); +// tmux session, tmux attach session -t \ No newline at end of file