Browse Source

create project

moell 1 year ago
parent
commit
3c8d8a48bc

+ 88 - 0
app/Http/Controllers/API/ProjectController.php

@@ -0,0 +1,88 @@
+<?php
+
+namespace App\Http\Controllers\API;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\API\Project\CreateOrUpdateRequest;
+use App\Models\Project;
+use App\Models\ProjectAsset;
+use App\Models\ProjectPlan;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use function MongoDB\BSON\toJSON;
+
+class ProjectController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     */
+    public function index()
+    {
+        //
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     */
+    public function store(CreateOrUpdateRequest $request)
+    {
+        $project = new Project();
+
+        $project->mergeFillable([
+            'company_id', 'created_by'
+        ]);
+
+        $project->fill([
+            ...$request->all(),
+            'company_id' => Auth::user()->company_id,
+            'created_by' => Auth::id(),
+            'whitelist' => $request->whitelist ? sprintf(",%s", implode(',', $request->whitelist)) : null,
+        ]);
+
+        $project->save();
+
+        if ($request->has("assets")) {
+            foreach ($request->get("assets", []) as $assetId) {
+                ProjectAsset::create([
+                    'project_id' => $project->id,
+                    'asset_id' => $assetId,
+                ]);
+            }
+        }
+
+        if ($request->has("plans")) {
+            foreach ($request->get("plans", []) as $planId) {
+                ProjectPlan::create([
+                    'project_id' => $project->id,
+                    'plan_id' => $planId,
+                ]);
+            }
+        }
+
+        return $this->created();
+    }
+
+    /**
+     * Display the specified resource.
+     */
+    public function show(string $id)
+    {
+        //
+    }
+
+    /**
+     * Update the specified resource in storage.
+     */
+    public function update(Request $request, string $id)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     */
+    public function destroy(string $id)
+    {
+        //
+    }
+}

+ 78 - 0
app/Http/Requests/API/Project/CreateOrUpdateRequest.php

@@ -0,0 +1,78 @@
+<?php
+
+namespace App\Http\Requests\API\Project;
+
+use App\Models\Asset;
+use App\Models\Enums\AssetStatus;
+use App\Models\Plan;
+use App\Models\User;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Validation\Rule;
+use Illuminate\Validation\Rules\Enum;
+
+class CreateOrUpdateRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'name' => 'required|max:150',
+            'code' => [
+                'required',
+                'max:50',
+            ],
+            'const' => 'numeric',
+            'begin' => 'date',
+            'end' => 'date',
+            'type' => 'required|max:20',
+            'acl' => 'required|in:private,custom',
+            'whitelist' => [
+                'array',
+                function ($attribute, $value, $fail) {
+                    $userCount = User::where("company_id", Auth::user()->company_id)->whereIn('id', $value)->count();
+                    if ($userCount != count($value)) {
+                        $fail('The selected user is invalid.');
+                    }
+                }
+            ],
+            'plans' => [
+                'array',
+                function ($attribute, $value, $fail) {
+                    $count = Plan::where("company_id", Auth::user()->company_id)->whereIn('id', $value)->count();
+                    if ($count != count($value)) {
+                        $fail('The selected plan is invalid.');
+                    }
+                }
+            ],
+            'assets' => [
+                'array',
+                function ($attribute, $value, $fail) {
+                    $count = Asset::query()
+                        ->leftJoin("project_asset", "assets.id", "=", "project_asset.asset_id")
+                        ->where("company_id", Auth::user()->company_id)
+                        ->whereNull("project_asset.id")
+                        ->whereIn('assets.id', $value)->count();
+
+                    if ($count != count($value)) {
+                        $fail('The selected asset is invalid.');
+                    }
+                }
+            ],
+            'latitude' => 'numeric',
+            'longitude' => 'numeric',
+        ];
+    }
+}

+ 17 - 0
app/Models/Plan.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Plan extends Model
+{
+    use HasFactory;
+
+    protected $fillable = [
+        'title'
+    ];
+
+    public $timestamps = false;
+}

+ 15 - 0
app/Models/Project.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Project extends Model
+{
+    use HasFactory;
+
+    protected $fillable = [
+        "name","code","const","available_days","status","begin","end","latitude","longitude","type","acl","whitelist","description"
+    ];
+}

+ 17 - 0
app/Models/ProjectAsset.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class ProjectAsset extends Model
+{
+    use HasFactory;
+
+    protected $table = 'project_asset';
+
+    protected $guarded = ['id'];
+
+    public $timestamps = false;
+}

+ 17 - 0
app/Models/ProjectPlan.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class ProjectPlan extends Model
+{
+    use HasFactory;
+
+    protected $table = 'project_plan';
+
+    protected $guarded = ['id'];
+
+    public $timestamps = false;
+}

+ 25 - 0
database/factories/PlanFactory.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Database\Factories;
+
+use Illuminate\Database\Eloquent\Factories\Factory;
+use Illuminate\Support\Facades\Auth;
+
+/**
+ * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Plan>
+ */
+class PlanFactory extends Factory
+{
+    /**
+     * Define the model's default state.
+     *
+     * @return array<string, mixed>
+     */
+    public function definition(): array
+    {
+        return [
+            'title' => fake()->title(),
+            'company_id' => Auth::user()->company_id,
+        ];
+    }
+}

+ 36 - 0
database/factories/ProjectFactory.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Database\Factories;
+
+use Carbon\Carbon;
+use Illuminate\Database\Eloquent\Factories\Factory;
+
+/**
+ * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Project>
+ */
+class ProjectFactory extends Factory
+{
+    /**
+     * Define the model's default state.
+     *
+     * @return array<string, mixed>
+     */
+    public function definition(): array
+    {
+        return [
+            'name' => fake()->name(),
+            'code' => fake()->randomNumber(5),
+            'const' => fake()->randomNumber(5),
+            'available_days' => fake()->randomNumber(2),
+            'status' => 'undone',
+            'begin' => Carbon::now(),
+            'end' => Carbon::now()->addDays(10),
+            'latitude' => fake()->latitude(),
+            'longitude' => fake()->longitude(),
+            'type' => fake()->randomElement(['project', 'task']),
+            'acl' => 'private',
+            'whitelist' => ',1,',
+            'description' => fake()->text(),
+        ];
+    }
+}

+ 43 - 0
database/migrations/2024_01_21_031515_create_projects_table.php

@@ -0,0 +1,43 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('projects', function (Blueprint $table) {
+            $table->id();
+            $table->string('name', 150);
+            $table->integer("company_id");
+            $table->string('code', 50);
+            $table->decimal('const', 10, 2)->nullable();
+            $table->integer('available_days')->default(0);
+            $table->string("status", 20)->default("undone")->comment("undone, pending_review, suspended, closed");
+            $table->timestamp("begin")->nullable();
+            $table->timestamp("end")->nullable();
+            $table->decimal('latitude', 10, 6)->nullable();
+            $table->decimal('longitude', 10, 6)->nullable();
+            $table->string("type", 20)->nullable();
+            $table->enum('acl', ['private', 'custom'])->default('private');
+            $table->string("whitelist")->nullable();
+            $table->text("description")->nullable();
+            $table->integer("created_by");
+            $table->softDeletes();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('projects');
+    }
+};

+ 28 - 0
database/migrations/2024_01_21_064530_create_project_asset_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('project_asset', function (Blueprint $table) {
+            $table->id();
+            $table->integer('project_id')->index();
+            $table->integer('asset_id')->index();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('project_asset');
+    }
+};

+ 28 - 0
database/migrations/2024_01_21_064545_create_project_plan_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('project_plan', function (Blueprint $table) {
+            $table->id();
+            $table->integer('project_id')->index();
+            $table->integer('plan_id')->index();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('project_plan');
+    }
+};

+ 28 - 0
database/migrations/2024_01_21_074157_create_plans_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('plans', function (Blueprint $table) {
+            $table->id();
+            $table->string('title');
+            $table->integer('company_id');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('plans');
+    }
+};

+ 1 - 0
routes/api.php

@@ -30,5 +30,6 @@ Route::middleware(['auth:sanctum'])->group(function () {
         'asset' => API\AssetController::class,
         'requirement-group' => API\RequirementGroupController::class,
         'requirement' => API\RequirementController::class,
+        'project' => API\ProjectController::class,
     ]);
 });

+ 35 - 0
tests/Feature/API/ProjectTest.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Tests\Feature\API;
+
+use App\Models\Asset;
+use App\Models\Plan;
+use App\Models\Project;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Foundation\Testing\WithFaker;
+use Tests\Feature\TestCase;
+
+class ProjectTest extends TestCase
+{
+    public function test_project_create(): void
+    {
+        $form = Project::factory()->make();
+        $form->whitelist = [
+            1
+        ];
+
+        $form->plans = [
+            Plan::factory()->create()->id,
+            Plan::factory()->create()->id,
+        ];
+
+        $form->assets = [
+            Asset::factory()->create()->id,
+            Asset::factory()->create()->id,
+        ];
+
+        $response = $this->post(route('project.store'), $form->toArray());
+
+        $response->assertStatus(201);
+    }
+}