前言
其实没想好写什么项目,先把laravel学习过程写出来把。这章主要写一些laravel的入口项目。所有的练习小项目全部放在这章。这章应该算是长期更新的。目前1.To-Do List应用
2.书签管理
3.API接口 电影管理
什么是laravel
Laravel 是一个 PHP 框架,简化了 Web 开发的复杂工作。它基于 MVC(模型-视图-控制器)架构,提供了很多开箱即用的功能,比如数据库操作(Eloquent ORM)、路由、模板引擎(Blade)等。适合开发博客、电商网站、API 等项目。
准备开发环境
- PHP:Laravel 11 要求 PHP 8.2 或更高版本。
- Composer:PHP 的包管理工具,用于安装 Laravel。
- Web 服务器:推荐用 Laravel 内置的 Artisan 开发服务器,或者用 Nginx/Apache。
- 数据库:可以选择 MySQL、PostgreSQL 或 SQLite(初学者用 SQLite 最简单)。
- 代码编辑器:推荐 VS Code、PHPStorm 或 Sublime Text。
这里我说一下我的版本是8.0,懒的换了。版本低点就低点好了
php
php下载地址 另外环境变量自己加一下。加完之后运行cmd然后再命令行输入php -v
。出现以下信息就算安装成功
PHP 8.0.2 (cli) (built: Feb 3 2021 18:36:37) ( NTS Visual C++ 2019 x64 )
Copyright (c) The PHP Group
Zend Engine v4.0.2, Copyright (c) Zend Technologies
composer
composer下载。下载完成之后也是cmd,输入composer --version
,出现下面信息就算安装成功
Composer version 2.8.9 2025-05-13 14:01:37
PHP version 8.0.2 (D:\phpstudy_pro\Extensions\php\php8.0.2nts\php.exe)
Run the "diagnose" command to get more detailed diagnostics output.
mysql,编辑工具之类的我想你们应该都有了,我就不来写了,在不懂私信或者评论。
安装laravel
打开终端,运行以下命令来安装 Laravel 的全局安装器:
composer global require laravel/installer
创建新项目:
laravel new my-first-laravel
进入项目目录
cd my-first-laravel
启动开发服务器
php artisan serve
然后在浏览器访问 http://localhost:8000
,你会看到 Laravel 的欢迎页面!
另外提一句我用的加速器,所以镜像和下载速度慢的问题我是没有考虑的。大家要是感觉太慢的话,切下国内镜像或者用下这个加速器 加速器地址。如果能看到欢迎页面那就OK,否则再查看下教程把。
到这里laravel已经安装完了。我先去看文档写一个项目出来,大家加油
To-Do List
env配置数据库
我用的mysql数据库,大家自行安装,数据库我名字跟项目一个样,反正学习练手。配置大家改成自己的,编辑.env
文件
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_first_laravel
DB_USERNAME=root
DB_PASSWORD=root
数据库配置和生成
运行 Artisan 命令生成迁移文件:
php artisan make:migration create_tasks_table
编辑该文件database/migrations/2025_06_04_091610_create_tasks_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id(); // 自增主键
$table->string('title'); // 任务标题
$table->text('description')->nullable(); // 任务描述,可为空
$table->timestamps(); // 创建和更新时间
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('tasks');
}
};
运行该文件命令
php artisan migrate
自己去mysql里查一下表有没有生成,有的话继续,没有回头找问题把。
生成模型和控制器
执行命令
php artisan make:model Task -c
-c表示同时生成控制器
这会在 app/Models/Task.php
创建模型文件,在 app/Http/Controllers/TaskController.php
创建控制器。
最后打开模型,app/Model/Task
.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
protected $fillable = ['title', 'description'];
}
路由
打开路由文件routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TaskController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
// Route::get('/', function () {
// return view('welcome');
// });
// taks
Route::get('/', [TaskController::class, 'index'])->name('tasks.index');
Route::get('/tasks/create', [TaskController::class, 'create'])->name('tasks.create');
Route::post('/tasks', [TaskController::class, 'store'])->name('tasks.store');
Route::get('/tasks/{task}/edit', [TaskController::class, 'edit'])->name('tasks.edit');
Route::put('/tasks/{task}', [TaskController::class, 'update'])->name('tasks.update');
Route::delete('/tasks/{task}', [TaskController::class, 'destroy'])->name('tasks.destroy');
- index:显示任务列表。
- create:显示添加任务的表单。
- store:处理表单提交,保存新任务。
- edit:显示编辑任务的表单。
- update:更新任务。
- destroy:删除任务。
控制器
编写app/Http/Controllers/TaskController.php
<?php
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
class TaskController extends Controller
{
// 显示任务列表
public function index()
{
$tasks = Task::all();
return view('tasks.index', compact('tasks'));
}
// 显示添加任务表单
public function create()
{
return view('tasks.create');
}
// 保存新任务
public function store(Request $request)
{
$request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
]);
Task::create($request->all());
return redirect()->route('tasks.index')->with('success', '任务添加成功!');
}
// 显示编辑任务表单
public function edit(Task $task)
{
return view('tasks.edit', compact('task'));
}
// 更新任务
public function update(Request $request, Task $task)
{
$request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
]);
$task->update($request->all());
return redirect()->route('tasks.index')->with('success', '任务更新成功!');
}
// 删除任务
public function destroy(Task $task)
{
$task->delete();
return redirect()->route('tasks.index')->with('success', '任务删除成功!');
}
}
视图
注意路径哦。
创建resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>To-Do List</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.container { max-width: 800px; margin: auto; }
.success { color: green; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<div class="container">
@if (session('success'))
<div class="success">{{ session('success') }}</div>
@endif
@yield('content')
</div>
</body>
</html>
创建resources/views/tasks/index.blade.php
@extends('layouts.app')
@section('content')
<h1>任务列表</h1>
<a href="{{ route('tasks.create') }}">添加新任务</a>
@if ($tasks->isEmpty())
<p>暂无任务</p>
@else
<table>
<tr>
<th>标题</th>
<th>描述</th>
<th>操作</th>
</tr>
@foreach ($tasks as $task)
<tr>
<td>{{ $task->title }}</td>
<td>{{ $task->description ?? '无描述' }}</td>
<td>
<a href="{{ route('tasks.edit', $task) }}">编辑</a>
<form action="{{ route('tasks.destroy', $task) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" onclick="return confirm('确定删除吗?')">删除</button>
</form>
</td>
</tr>
@endforeach
</table>
@endif
@endsection
创建resources/views/tasks/create.blade.php
@extends('layouts.app')
@section('content')
<h1>添加任务</h1>
<form action="{{ route('tasks.store') }}" method="POST">
@csrf
<div>
<label>标题:</label>
<input type="text" name="title" required>
</div>
<div>
<label>描述:</label>
<textarea name="description"></textarea>
</div>
<button type="submit">保存</button>
</form>
<a href="{{ route('tasks.index') }}">返回</a>
@endsection
创建resources/views/tasks/edit.blade.php
@extends('layouts.app')
@section('content')
<h1>编辑任务</h1>
<form action="{{ route('tasks.update', $task) }}" method="POST">
@csrf
@method('PUT')
<div>
<label>标题:</label>
<input type="text" name="title" value="{{ $task->title }}" required>
</div>
<div>
<label>描述:</label>
<textarea name="description">{{ $task->description }}</textarea>
</div>
<button type="submit">更新</button>
</form>
<a href="{{ route('tasks.index') }}">返回</a>
@endsection
写完了,http://localhost:8000
能输出下面页面就算成功,大家自己测试把。
书签管理
继续练习下CURD操作,还是这个项目里写哈,数据库之类的都已经配过了,就不来写了。
生成模型和迁移文件
运行命令
php artisan make:migration create_bookmarks_table
文件里加上
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('bookmarks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('url');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('bookmarks');
}
};
运行迁移
php artisan migrate
生成模型命令
php artisan make:model Bookmark
文件添加
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Bookmark extends Model
{
use HasFactory;
protected $fillable = ['title', 'url'];
}
控制器
运行生成命令
php artisan make:controller BookmarkController --resource
编写代码app/Http/Controllers/BookmarkController.php
<?php
namespace App\Http\Controllers;
use App\Models\Bookmark;
use Illuminate\Http\Request;
class BookmarkController extends Controller
{
// 显示书签列表
public function index()
{
$bookmarks = Bookmark::all();
return view('bookmarks.index', compact('bookmarks'));
}
// 显示添加书签表单
public function create()
{
return view('bookmarks.create');
}
// 保存新书签
public function store(Request $request)
{
$request->validate([
'title' => 'required|string|max:255',
'url' => 'required|url',
]);
Bookmark::create($request->all());
return redirect()->route('bookmarks.index')->with('success', '书签添加成功!');
}
// 显示编辑书签表单
public function edit(Bookmark $bookmark)
{
return view('bookmarks.edit', compact('bookmark'));
}
// 更新书签
public function update(Request $request, Bookmark $bookmark)
{
$request->validate([
'title' => 'required|string|max:255',
'url' => 'required|url',
]);
$bookmark->update($request->all());
return redirect()->route('bookmarks.index')->with('success', '书签更新成功!');
}
// 删除书签
public function destroy(Bookmark $bookmark)
{
$bookmark->delete();
return redirect()->route('bookmarks.index')->with('success', '书签删除成功!');
}
}
路由
修改路由文件routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TaskController;
use App\Http\Controllers\BookmarkController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
// Route::get('/', function () {
// return view('welcome');
// });
// taks
Route::get('/', [TaskController::class, 'index'])->name('tasks.index');
Route::get('/tasks/create', [TaskController::class, 'create'])->name('tasks.create');
Route::post('/tasks', [TaskController::class, 'store'])->name('tasks.store');
Route::get('/tasks/{task}/edit', [TaskController::class, 'edit'])->name('tasks.edit');
Route::put('/tasks/{task}', [TaskController::class, 'update'])->name('tasks.update');
Route::delete('/tasks/{task}', [TaskController::class, 'destroy'])->name('tasks.destroy');
// bookmark
Route::get('bookmarks', [BookmarkController::class, 'index'])->name('bookmarks.index');
Route::get('bookmarks/create', [BookmarkController::class, 'create'])->name('bookmarks.create');
Route::post('bookmarks', [BookmarkController::class, 'store'])->name('bookmarks.store');
Route::get('bookmarks/{bookmark}/edit', [BookmarkController::class, 'edit'])->name('bookmarks.edit');
Route::put('bookmarks/{bookmark}', [BookmarkController::class, 'update'])->name('bookmarks.update');
Route::delete('bookmarks/{bookmark}', [BookmarkController::class, 'destroy'])->name('bookmarks.destroy');
页面
这里加了bootstrap美化页面
创建resources/views/layouts/appbook.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>书签管理</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4">
<div class="container">
<a class="navbar-brand" href="{{ route('bookmarks.index') }}">书签管理</a>
<div class="navbar-nav">
<a class="nav-link" href="{{ route('bookmarks.index') }}">书签列表</a>
<a class="nav-link" href="{{ route('bookmarks.create') }}">添加书签</a>
</div>
</div>
</nav>
<div class="container">
@yield('content')
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
创建文件resources/views/bookmarks/index.blade.php
@extends('layouts.appbook')
@section('content')
<h1 class="mb-4">书签列表</h1>
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
<a href="{{ route('bookmarks.create') }}" class="btn btn-primary mb-3">添加新书签</a>
<table class="table table-striped table-bordered">
<thead class="table-dark">
<tr>
<th>标题</th>
<th>URL</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@forelse ($bookmarks as $bookmark)
<tr>
<td>{{ $bookmark->title }}</td>
<td><a href="{{ $bookmark->url }}" target="_blank">{{ $bookmark->url }}</a></td>
<td>
<a href="{{ route('bookmarks.edit', $bookmark->id) }}" class="btn btn-sm btn-warning">编辑</a>
<form action="{{ route('bookmarks.destroy', $bookmark->id) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('确定删除?')">删除</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="3" class="text-center">暂无书签</td>
</tr>
@endforelse
</tbody>
</table>
@endsection
创建文件resources/views/bookmarks/create.blade.php
@extends('layouts.appbook')
@section('content')
<h1 class="mb-4">添加书签</h1>
<form action="{{ route('bookmarks.store') }}" method="POST">
@csrf
<div class="mb-3">
<label for="title" class="form-label">书签标题</label>
<input type="text" name="title" id="title" class="form-control" value="{{ old('title') }}">
@error('title')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="url" class="form-label">URL</label>
<input type="text" name="url" id="url" class="form-control" value="{{ old('url') }}">
@error('url')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-success">保存</button>
<a href="{{ route('bookmarks.index') }}" class="btn btn-secondary">取消</a>
</form>
@endsection
创建文件resources/views/bookmarks/edit.blade.php
@extends('layouts.appbook')
@section('content')
<h1 class="mb-4">编辑书签</h1>
<form action="{{ route('bookmarks.update', $bookmark->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label for="title" class="form-label">书签标题</label>
<input type="text" name="title" id="title" class="form-control" value="{{ old('title', $bookmark->title) }}">
@error('title')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="url" class="form-label">URL</label>
<input type="text" name="url" id="url" class="form-control" value="{{ old('url', $bookmark->url) }}">
@error('url')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-success">更新</button>
<a href="{{ route('bookmarks.index') }}" class="btn btn-secondary">取消</a>
</form>
@endsection
OK了,能输出以下内容就算成功,大家自己测试一下
电影管理 API接口
模型和迁移
php artisan make:model Movie -m
编辑迁移
$table->string('title');
$table->string('director');
编辑模型
protected $fillable = ['title', 'director'];
运行迁移
php artisan migrate
控制器
创建命令
php artisan make:controller Api/MovieController --api
编辑文件
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Movie;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class MovieController extends Controller
{
// 获取所有电影
public function index(): JsonResponse
{
$movies = Movie::all();
return response()->json([
'status' => 'success',
'data' => $movies,
], 200);
}
// 获取单个电影
public function show(Movie $movie): JsonResponse
{
return response()->json([
'status' => 'success',
'data' => $movie,
], 200);
}
// 添加新电影
public function store(Request $request): JsonResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'director' => 'required|string|max:255',
], [
'title.required' => '电影标题不能为空',
'title.max' => '标题不能超过255个字符',
'director.required' => '导演名称不能为空',
'director.max' => '导演名称不能超过255个字符',
]);
$movie = Movie::create($validated);
return response()->json([
'status' => 'success',
'message' => '电影添加成功',
'data' => $movie,
], 201);
}
// 更新电影
public function update(Request $request, Movie $movie): JsonResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'director' => 'required|string|max:255',
], [
'title.required' => '电影标题不能为空',
'title.max' => '标题不能超过255个字符',
'director.required' => '导演名称不能为空',
'director.max' => '导演名称不能超过255个字符',
]);
$movie->update($validated);
return response()->json([
'status' => 'success',
'message' => '电影更新成功',
'data' => $movie,
], 200);
}
// 删除电影
public function destroy(Movie $movie): JsonResponse
{
$movie->delete();
return response()->json([
'status' => 'success',
'message' => '电影删除成功',
], 204);
}
}
每个方法返回 JSON 响应,包含 status、message(可选)和 data(可选)。
HTTP 状态码:
- 200:成功(index, show, update)。
- 201:创建成功(store)。
- 204:删除成功,无内容返回。
路由
编辑routes/api.php
use App\Http\Controllers\Api\MovieController;
Route::apiResource('movies', MovieController::class);
这会生成以下 API 端点:
- GET /api/movies → index
- GET /api/movies/{movie} → show
- POST /api/movies → store
- PUT /api/movies/{movie} → update
- DELETE /api/movies/{movie} → destroy
注意:Laravel 的 API 路由默认带 /api 前缀,访问时需使用 http://localhost:8000/api/movies。
写完了。应该没什么问题了。大家自己测试吧
`