如何在Laravel单个Blade视图中创建Todo-List及其关联多事项?
Got it, let's walk through exactly how to build this. We'll use Laravel's model relationships, form array inputs, and a single controller action to save both the list and its items in one go—no multiple submissions needed.
1. Set Up Models & Relationships
First, make sure your models are wired up with the correct one-to-many relationship (a todo list has many items, each item belongs to one list).
TodoList Model
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; class TodoList extends Model { protected $fillable = ['name']; // Define relationship to TodoItem public function items(): HasMany { return $this->hasMany(TodoItem::class); } }
TodoItem Model
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class TodoItem extends Model { protected $fillable = ['description', 'completed']; // Define relationship to TodoList public function todoList(): BelongsTo { return $this->belongsTo(TodoList::class); } }
2. Create Database Migrations
If you haven't already, create the necessary tables to store your lists and items:
TodoLists Migration
Schema::create('todo_lists', function (Blueprint $table) { $table->id(); $table->string('name'); // Stores the todo list name $table->timestamps(); });
TodoItems Migration
Schema::create('todo_items', function (Blueprint $table) { $table->id(); $table->foreignId('todo_list_id')->constrained()->onDelete('cascade'); // Links to the parent list $table->text('description'); // The todo item text $table->boolean('completed')->default(false); // Optional: track completion status $table->timestamps(); });
Run php artisan migrate to apply these tables to your database.
3. Controller Logic (Store Action)
In your TodoListController, add a store method that first saves the parent list, then bulk-saves all associated items.
<?php namespace App\Http\Controllers; use App\Models\TodoList; use Illuminate\Http\Request; class TodoListController extends Controller { public function store(Request $request) { // Validate incoming form data $validated = $request->validate([ 'todo_list.name' => 'required|string|max:255', 'items' => 'required|array|min:1', // Ensure at least one item is submitted 'items.*.description' => 'required|string|max:500', ]); // Step 1: Create the parent Todo List $todoList = TodoList::create($validated['todo_list']); // Step 2: Prepare items with the parent list ID $items = collect($validated['items'])->map(function ($item) use ($todoList) { return [ 'description' => $item['description'], 'todo_list_id' => $todoList->id, 'completed' => false, // Default to uncompleted (adjust if needed) ]; }); // Bulk insert items (more efficient than creating one-by-one) $todoList->items()->createMany($items); // Redirect back with success message return redirect()->back()->with('success', 'Todo list and items saved successfully!'); } }
4. Blade View Form
Build the form in your Blade template, with dynamic support to add more todo items using JavaScript (so users aren't limited to a fixed number of items).
<!DOCTYPE html> <html> <head> <title>Create New Todo List</title> <!-- Optional: Add Bootstrap for basic styling --> <style> .container { max-width: 600px; margin: 2rem auto; } .input-group { margin-bottom: 0.8rem; } .alert { margin-bottom: 1.5rem; } </style> </head> <body> <div class="container"> <!-- Show success message if submission worked --> @if(session('success')) <div class="alert alert-success"> {{ session('success') }} </div> @endif <form method="POST" action="{{ route('todo-lists.store') }}"> @csrf <!-- Todo List Name Input --> <div class="mb-4"> <label for="listName" class="form-label">Todo List Name</label> <input type="text" class="form-control @error('todo_list.name') border-red-500 @enderror" id="listName" name="todo_list[name]" value="{{ old('todo_list.name') }}" placeholder="e.g., 'Weekly Groceries'" required> @error('todo_list.name') <p class="text-red-500 text-sm mt-1">{{ $message }}</p> @enderror </div> <!-- Todo Items Section --> <div class="mb-4"> <label class="form-label">Todo Items</label> <div id="itemsContainer"> <!-- Initial todo item input --> <div class="input-group"> <input type="text" class="form-control @error('items.0.description') border-red-500 @enderror" name="items[0][description]" value="{{ old('items.0.description') }}" placeholder="Add a todo item..." required> @error('items.0.description') <p class="text-red-500 text-sm mt-1">{{ $message }}</p> @enderror </div> </div> <button type="button" class="btn btn-outline-secondary mt-2" id="addItemBtn">Add Another Item</button> </div> <!-- Single Submit Button --> <button type="submit" class="btn btn-primary">Save Todo List & Items</button> </form> </div> <!-- JavaScript to add dynamic todo item inputs --> <script> document.getElementById('addItemBtn').addEventListener('click', function() { const container = document.getElementById('itemsContainer'); const itemCount = container.children.length; // Create new input group const newItem = document.createElement('div'); newItem.className = 'input-group mt-2'; newItem.innerHTML = ` <input type="text" class="form-control" name="items[${itemCount}][description]" placeholder="Add another todo item..." required> `; container.appendChild(newItem); }); </script> </body> </html>
5. Route Setup
Don't forget to add the route in routes/web.php to point to your controller's store method:
use App\Http\Controllers\TodoListController; Route::post('/todo-lists', [TodoListController::class, 'store'])->name('todo-lists.store');
Key Details to Note:
- We use array input names (
todo_list[name],items[][description]) to group related data, making it easy to handle in the controller. - Bulk inserting items with
createManyis more efficient than looping and saving each item individually. - The JavaScript lets users add as many items as they need dynamically, and validation ensures at least one item is submitted.
- Old form data is retained on validation failure, so users don't lose their input.
内容的提问来源于stack exchange,提问作者Jacobo de la Rosa

