feat: add version

This commit is contained in:
2026-06-24 18:10:30 +03:30
parent 40b7efac2f
commit 706b349130
4 changed files with 152 additions and 0 deletions
@@ -0,0 +1,93 @@
<?php
namespace App\Http\Controllers;
use App\Models\AppVersion;
use App\Traits\HandlesImageUpload;
use Illuminate\Http\Request;
class AppVersionController extends Controller
{
use HandlesImageUpload;
// Admin list — every version, highest version_code first.
public function index()
{
return response()->json([
'data' => AppVersion::with('image')->orderByDesc('version_code')->get(),
]);
}
// App side: the newest version (highest version_code) for update checks.
public function latest()
{
return response()->json([
'data' => AppVersion::with('image')->orderByDesc('version_code')->first(),
]);
}
public function store(Request $request)
{
$data = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'version_name' => 'required|string|max:255',
'version_code' => 'required|integer|min:0',
'link' => 'nullable|string|max:500',
'image_id' => 'nullable|exists:images,id',
'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192',
]);
// An uploaded image file takes precedence over a provided image_id.
$data['image_id'] = $this->uploadedImageId($request) ?? ($data['image_id'] ?? null);
$version = AppVersion::create($data);
return response()->json([
'message' => 'Version created successfully',
'data' => $version->load('image'),
], 201);
}
public function show($id)
{
return response()->json(
AppVersion::with('image')->findOrFail($id)
);
}
public function update(Request $request, $id)
{
$version = AppVersion::findOrFail($id);
$data = $request->validate([
'title' => 'sometimes|string|max:255',
'description' => 'nullable|string',
'version_name' => 'sometimes|string|max:255',
'version_code' => 'sometimes|integer|min:0',
'link' => 'nullable|string|max:500',
'image_id' => 'nullable|exists:images,id',
'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192',
]);
// An uploaded image file takes precedence over a provided image_id.
if (($uploadedImageId = $this->uploadedImageId($request)) !== null) {
$data['image_id'] = $uploadedImageId;
}
$version->update($data);
return response()->json([
'message' => 'Version updated successfully',
'data' => $version->load('image'),
]);
}
public function destroy($id)
{
$version = AppVersion::findOrFail($id);
$version->delete();
return response()->json(['message' => 'Version deleted successfully']);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AppVersion extends Model
{
protected $fillable = [
'image_id', 'title', 'description', 'version_name', 'version_code', 'link',
];
protected $casts = [
'version_code' => 'integer',
];
public function image(): BelongsTo
{
return $this->belongsTo(Image::class);
}
}
@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('app_versions', function (Blueprint $table) {
$table->id();
$table->foreignId('image_id')->nullable()->constrained('images')->nullOnDelete();
$table->string('title');
$table->text('description')->nullable();
$table->string('version_name'); // e.g. "1.2.0"
$table->unsignedInteger('version_code'); // monotonic build number, e.g. 42
$table->string('link')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('app_versions');
}
};
+10
View File
@@ -19,6 +19,7 @@
use App\Http\Controllers\FaqCategoryController; use App\Http\Controllers\FaqCategoryController;
use App\Http\Controllers\SliderController; use App\Http\Controllers\SliderController;
use App\Http\Controllers\AnnouncementController; use App\Http\Controllers\AnnouncementController;
use App\Http\Controllers\AppVersionController;
use App\Http\Controllers\SceneController; use App\Http\Controllers\SceneController;
use App\Http\Controllers\ThemeController; use App\Http\Controllers\ThemeController;
use App\Http\Controllers\BellSoundController; use App\Http\Controllers\BellSoundController;
@@ -208,6 +209,15 @@
Route::post('/announcements/{id}', [AnnouncementController::class, 'update']); // update (multipart: image) Route::post('/announcements/{id}', [AnnouncementController::class, 'update']); // update (multipart: image)
Route::delete('/announcements/{id}', [AnnouncementController::class, 'destroy']); // delete Route::delete('/announcements/{id}', [AnnouncementController::class, 'destroy']); // delete
/// app versions
Route::get('/versions/latest', [AppVersionController::class, 'latest']); // newest version (app update check)
Route::get('/versions', [AppVersionController::class, 'index']); // list all (admin)
Route::post('/versions', [AppVersionController::class, 'store']); // create
Route::get('/versions/{id}', [AppVersionController::class, 'show']); // single
Route::put('/versions/{id}', [AppVersionController::class, 'update']); // update (json)
Route::post('/versions/{id}', [AppVersionController::class, 'update']); // update (multipart: image)
Route::delete('/versions/{id}', [AppVersionController::class, 'destroy']); // delete
/// themes (color themes a scene can use) /// themes (color themes a scene can use)
Route::get('/themes', [ThemeController::class, 'index']); Route::get('/themes', [ThemeController::class, 'index']);