Learn the "why" behind slow queries and how to fix them in our 2-Part Webinar.
Register now >
Docs 菜单
Docs 主页
/ /

教程:使用 Laravel 和MongoDB构建任务提醒

在本教程中,您可以通过构建简单的任务提醒系统来学习;了解如何在 Laravel应用程序中使用MongoDB 。该应用程序允许您创建、读取、更新和删除任务。此外,它还会根据计划时间发送有关即将执行的任务的电子邮件提醒。

在开始本教程之前,请确保您已在开发环境中安装以下组件:

  • PHPMongoDB PHP扩展

  • Laravel

  • Composer

  • NPM

您还必须设立一个MongoDB Atlas 集群。要学习;了解如何设立集群,请参阅快速入门指南

1

使用 Composer 创建新的 Laravel项目并导航到项目目录:

composer create-project laravel/laravel LaravelMongodbProject
cd LaravelMongodbProject
2

要在应用程序中使用MongoDB ,必须安装 Laravel MongoDB包并在 config/database.php文件中进行配置。

使用以下命令安装此包:

composer require mongodb/laravel-mongodb

提示

如果在运行上述命令时遇到错误,请确保已安装MongoDB PHP扩展。要学习如何安装,请参阅 安装 MongoDB PHP 扩展 步骤,详见入门指南。

3

Laravel MongoDB包安装完成后,将MongoDB 数据库连接添加到 config/database.php文件以完成配置。将以下MongoDB配置添加到包含其他数据库类型配置的 connections数组:

return [
'connections' => [
'mongodb' => [
'driver' => 'mongodb',
'dsn' => env('MONGODB_URI'),
'database' => 'task_reminder',
],
// Other connections here
],
];

您的应用程序从 .env文件中获取 dsn 值。导航到您的 .env文件。然后,为 MONGODB_URI 创建一个值并将其设立为MongoDB连接字符串的值。

提示

要获取MongoDB连接字符串,请导航到MongoDB Atlas中的集群。单击 Connect 按钮,然后单击 Connect your application 下的 Drivers。从 Driver 选择菜单中选择“PHP”。从对话框中复制连接字符串。有关如何获取连接字符串的详细步骤,请参阅创建连接字符串。

.env文件中,将 DB_CONNECTION 值设立为 mongodb,使其成为应用程序的默认连接。编辑 SESSION_DRIVER 值并将其设立为 mongodb,如以下代码所示:

MONGODB_URI=mongodb+srv://YOUR_MONGODB_CONNECTION_STRING
DB_CONNECTION=mongodb
// Other DB_ values here ...
SESSION_DRIVER=mongodb
4

安装并配置应用程序以与MongoDB一起使用后,设立身份验证。Laravel 通过提供 Laravel Breeze、Laravel Fortify 和 Laravel Jetstream 等包来简化身份验证实现。本教程使用 Laravel Breeze 进行身份验证。

使用 Composer 安装 Laravel Breeze:

composer require laravel/breeze --dev

安装完成后,运行以下命令:

php artisan key:generate
php artisan breeze:install
php artisan migrate
php artisan db:seed
npm install
npm run dev

这会提示您选择首选的堆栈和测试包。对于本教程,请选择第一个选项,即带有 Alpine 的 Blade:

┌ Which Breeze stack would you like to install? ───────────────┐
│ > ● Blade with Alpine │
│ ○ Livewire (Volt Class API) with Alpine │
│ ○ Livewire (Volt Functional API) with Alpine │
│ ○ React with Inertia │
│ ○ Vue with Inertia │
│ ○ API only │
└──────────────────────────────────────────────────────────────┘

此命令将身份验证视图、路由、控制器和其他相关资源添加到应用程序。

5

Laravel Breeze 为SQL数据库创建默认用户模型。您必须对其更新才能与MongoDB一起使用并解决速率限制兼容性问题。

打开 app/Models/User.php 并将其内容替换为以下内容:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;
use MongoDB\Laravel\Auth\User as MongoUser;
class User extends MongoUser {
use HasFactory, Notifiable;
protected $connection = 'mongodb';
protected $collection = 'users';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes to cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
6

使用 Laravel 的内置服务器为项目提供服务,并确认一切正常。在 Laravel项目目录中,运行以下命令:

php artisan serve

该项目在 127.0.0.1:8000 上提供服务。如果端口 8000 已被使用,Laravel 会切换到新的可用端口,并将该端口打印到终端。

要测试身份验证系统,请选择应用程序中的 Register 按钮并创建一个新的用户帐户。这会将用户存储在MongoDB 数据库中,并允许您使用这些凭证登录。对于本教程,您可以将用户名设立为 test@example.com,将密码设置为 password

要验证 Laravel MongoDB包是否配置正确,请创建一个路由来网络探测(ping) MongoDB 集群。将以下内容添加到 routes/web.php

Route::get('/ping', function (Request $request) {
$connection = DB::connection('mongodb');
try {
$connection->command(['ping' => 1]);
$msg = 'MongoDB is accessible!';
} catch (\MongoDB\Driver\Exception\ConnectionException $e) {
$msg = 'You are not connected to MongoDB. Error: ' . $e->getMessage();
}
return ['msg' => $msg];
});

在浏览器中访问此路由。配置正确后,您会看到“MongoDB is可访问!”消息在浏览器中。

7

使用以下命令为任务安排功能创建模型和控制器:

php artisan make:model Task --resource --controller

此命令使用资源方法在 app/Models目录中创建 Task 模型,并在 app/Http/Controllers目录中创建 TaskController

导航到 routes/web.php 并添加以下代码,为 TaskController 创建路由:

use App\Http\Controllers\TaskController;
// Other route definitions...
Route::resource('tasks', TaskController::class)->middleware('auth');
8

导航到 app/Models/Task.php 并使用以下代码替换该文件的内容:

<?php
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;
class Task extends Model {
protected $connection = 'mongodb';
protected $table = 'tasks';
protected $fillable = [
'title',
'description',
'due_date',
'email',
'reminder_time',
'last_notification_date'
];
}

此代码将创建具有以下规范的任务模型:

  • 命名空间后面的 use MongoDB\Laravel\Eloquent\Model声明特定于MongoDB模型。它通过使用MongoDB查询覆盖通过SQL实现的 Eloquent 功能。

  • protected $table = 'tasks'属性是可选的。它指定用于存储此模型中的文档的MongoDB集合的名称。

  • protected $fillable属性指定可批量分配的属性。

提示

MongoDB的独特功能之一是它不需要像关系数据库那样进行迁移。您可以直接在文档中添加新字段,而无需更新模型或创建迁移。此功能对于处理动态数据非常有用。

9

导航到 app/Http/Controllers/TaskController.php 并使用以下代码更新内容:

<?php
namespace App\Http\Controllers;
use App\Models\Task;
use Carbon\Carbon;
use Illuminate\Http\Request;
class TaskController extends Controller {
/**
* Displays a listing of the resource.
*/
public function index()
{
$tasks = Task::where('email', auth()->user()->email)->get();
return view('tasks.index', compact('tasks'));
}
/**
* Shows the form for creating a new resource.
*/
public function create()
{
return view('tasks.create');
}
/**
* Stores a newly created resource in storage.
*/
public function store(Request $request)
{
$data = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'due_date' => 'required|date',
'reminder_time' => 'required|date',
]);
$data['due_date'] = Carbon::parse($request->due_date);
$data['reminder_time'] = Carbon::parse($request->reminder_time);
$data['email'] = auth()->user()->email;
$data['last_notification_date'] = null;
Task::create($data);
return redirect()->route('tasks.index')->with('success', 'Task created successfully.');
}
/**
* Displays the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Shows the form for editing the specified resource.
*/
public function edit(string $id)
{
$tasks = Task::where('id', $id)->get();
return view('tasks.edit', ['tasks' => $tasks]);
}
/**
* Updates the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$data = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'due_date' => 'required|date',
'reminder_time' => 'required|date',
]);
$task = Task::findOrFail($id);
$data['due_date'] = Carbon::parse($request->due_date)->format('Y-m-d H:i:s');
$data['reminder_time'] = Carbon::parse($request->reminder_time)->format('Y-m-d H:i:s');
$task->update($data);
return redirect()->route('tasks.index')->with('success', 'Task updated successfully.');
}
/**
* Removes the specified resource from storage.
*/
public function destroy(string $id)
{
$task = Task::findOrFail($id);
$task->delete();
return redirect()->route('tasks.index')->with('success', 'Task deleted successfully.');
}
}

TaskController 类包含处理任务模型的增删改查操作的代码:

  • index() 方法检索属于已登录用户的所有任务并将它们发送到 index.blade.php文件以供显示。

  • create() 方法返回用于创建新任务的表单视图。

  • store() 方法验证输入,将登录用户的电子邮件分配给任务,并将其保存到数据库。

  • edit() 方法检索要编辑的特定任务并将其显示在编辑表单中。

  • update() 方法将编辑的任务保存到MongoDB任务集合中。

  • destroy() 方法删除特定任务。

每个操作都会重定向回任务列表,并显示一条成功消息以供用户反馈。

10

resources/views目录中,创建一个名为 tasks 的文件夹,并在其中创建以下文件:

  • create.blade.php

  • edit.blade.php

  • index.blade.php

导航到 resources/views/tasks/create.blade.php 并添加以下内容:

1<x-app-layout>
2 <x-slot name="header">
3 <h2 class="font-semibold text-xl text-gray-800 leading-tight">
4 {{ __('Tasks') }}
5 </h2>
6 </x-slot>
7
8 <div class="py-12">
9 <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
10 <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
11 <div class="container mx-auto p-4">
12 <h2 class="text-2xl font-bold mb-4">Create New Task</h2>
13
14 <form action="{{ route('tasks.store') }}" method="POST">
15 @csrf
16
17 <div class="mb-4">
18 <label for="title" class="block text-gray-700">Title:</label>
19 <input type="text"
20 name="title"
21 id="title"
22 required
23 class="border border-gray-300 p-2 w-full"
24 value="{{ old('title') }}">
25 @error('title')
26 <p class="text-red-500">{{ $message }}</p>
27 @enderror
28 </div>
29
30 <div class="mb-4">
31 <label for="description" class="block text-gray-700">Description:</label>
32 <textarea name="description"
33 id="description"
34 class="border border-gray-300 p-2 w-full">{{ old('description') }}</textarea>
35 </div>
36
37 <div class="mb-4">
38 <label for="due_date" class="block text-gray-700">Due Date:</label>
39 <input type="date"
40 name="due_date"
41 id="due_date"
42 required
43 class="border border-gray-300 p-2 w-full"
44 value="{{ old('due_date') }}">
45 @error('due_date')
46 <p class="text-red-500">{{ $message }}</p>
47 @enderror
48 </div>
49
50 <div class="mb-4">
51 <label for="reminder_time" class="block text-gray-700">Reminder Time:</label>
52 <input type="datetime-local"
53 name="reminder_time"
54 id="reminder_time"
55 class="border border-gray-300 p-2 w-full"
56 value="{{ old('reminder_time') }}">
57 </div>
58
59 <button type="submit"
60 class="bg-green-600 text-white p-2 border rounded">
61 Create Task
62 </button>
63 </form>
64 </div>
65 </div>
66 </div>
67 </div>
68</x-app-layout>

该表单包含 titledescription 的文本输入以及 due datereminder time 的日期和时间输入。

导航到 resources/views/tasks/edit.blade.php 并添加以下内容:

1<x-app-layout>
2 <x-slot name="header">
3 <h2 class="font-semibold text-xl text-gray-800 leading-tight">
4 {{ __('Edit Task') }}
5 </h2>
6 </x-slot>
7
8 <div class="py-12">
9 <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
10 <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
11 <div class="p-6 text-gray-900">
12 @foreach($tasks as $task)
13 <form action="{{ route('tasks.update', $task->id) }}" method="POST">
14 @csrf
15 @method('PUT')
16
17 <div class="mb-4">
18 <label for="title" class="block text-gray-700">Title:</label>
19 <input type="text"
20 name="title"
21 id="title"
22 required
23 class="border border-gray-300 p-2 w-full"
24 value="{{ old('title', $task->title) }}">
25 @error('title')
26 <p class="text-red-500">{{ $message }}</p>
27 @enderror
28 </div>
29
30 <div class="mb-4">
31 <label for="description" class="block text-gray-700">Description:</label>
32 <textarea name="description"
33 id="description"
34 class="border border-gray-300 p-2 w-full">{{ old('description', $task->description) }}</textarea>
35 </div>
36
37 <div class="mb-4">
38 <label for="due_date" class="block text-gray-700">Due Date:</label>
39 <input type="date"
40 name="due_date"
41 id="due_date"
42 required
43 class="border border-gray-300 p-2 w-full"
44 value="{{ old('due_date', $task->due_date) }}">
45 @error('due_date')
46 <p class="text-red-500">{{ $message }}</p>
47 @enderror
48 </div>
49
50 <div class="mb-4">
51 <label for="reminder_time" class="block text-gray-700">Reminder Time:</label>
52 <input type="datetime-local"
53 name="reminder_time"
54 id="reminder_time"
55 class="border border-gray-300 p-2 w-full"
56 value="{{ old('reminder_time', $task->reminder_time) }}">
57 </div>
58
59 <button type="submit"
60 class="bg-blue-500 text-white p-2 rounded">
61 Update Task
62 </button>
63 </form>
64 @endforeach
65 </div>
66 </div>
67 </div>
68 </div>
69</x-app-layout>

编辑表单包含与创建表单相同的输入字段,并加载当前正在编辑的任务的数据。

导航到 resources/views/tasks/index.blade.php 并添加以下内容:

1<x-app-layout>
2 <x-slot name="header">
3 <h2 class="font-semibold text-xl text-gray-800 leading-tight">
4 {{ __('Tasks') }}
5 </h2>
6 </x-slot>
7
8 <div class="py-12">
9 <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
10 <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
11 <div class="p-6 text-gray-900">
12 <div class="mb-2">
13 <a href="{{ route('tasks.create') }}"
14 class="p-2 border mb-4">
15 Create New Task
16 </a>
17 </div>
18
19 <ul class="mt-4">
20 @foreach ($tasks as $task)
21 <div class="mt-2">
22 <hr>
23 </div>
24
25 <li>
26 <h1 class="text-2xl">
27 <strong>{{ $task->title }}</strong>
28 - Due: {{ $task->due_date }}
29 </h1>
30
31 <p class="text-gray-600">
32 {{ $task->description }}
33 </p>
34
35 <div class="flex gap-2 mt-4">
36 <div class="p-2 text-white bg-gray-700">
37 <a href="{{ route('tasks.edit', $task->id) }}">
38 Edit
39 </a>
40 </div>
41
42 <div class="p-2 text-white bg-red-700 rounded">
43 <form action="{{ route('tasks.destroy', $task->id) }}"
44 method="POST"
45 style="display:inline;">
46 @csrf
47 @method('DELETE')
48 <button type="submit">Delete</button>
49 </form>
50 </div>
51 </div>
52 </li>
53 @endforeach
54 </ul>
55 </div>
56 </div>
57 </div>
58 </div>
59</x-app-layout>

此视图包含指向创建表单的链接,并循环浏览所有任务以显示属于经过身份验证的用户的任务。

11

从应用程序导航中添加任务功能的链接。Goresources/views/layouts/navigation.blade.php,在仪表盘导航链接后添加以下代码:

1// ...existing code ...
2<div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
3 <x-nav-link :href="route('tasks.index')" :active="request()->routeIs('tasks.index')">
4 {{ __('Tasks') }}
5 </x-nav-link>
6</div>

点,您可以测试任务管理系统的增删改查操作。在继续下一个部分之前,请确保一切正常。

12

要实现到期任务的提醒系统,请创建自定义 Artisan 命令以通过电子邮件发送提醒,并注册该命令以进行自动安排。

要创建自定义 Artisan 命令,请在终端中运行以下代码:

php artisan make:command SendTaskReminders

创建命令后,使用以下代码更新app/Console/Commands/SendTaskReminders.php文件的内容:

<?php
namespace App\Console\Commands;
use App\Models\Task;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendTaskReminders extends Command {
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:send-task-reminders';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle() {
$now = Carbon::now();
$upcomingTasks = Task::where('last_notification_date', null)->get();
$upcomingTasks = Task::where('last_notification_date', null)
->where('reminder_time', '>=', $now->clone()->subMinutes(10))
->get();
foreach ($upcomingTasks as $task) {
$emailBody = <<<EOF
Hello,
This is a reminder for your task:
Title: {$task->title}
Description: {$task->description}
Due Date: {$task->due_date}
Please make sure to complete it on time!
Regards,
Your Task Reminder App
EOF;
Mail::raw($emailBody, function ($message) use ($task) {
$message->to($task->email)
->subject("Task Reminder: {$task->title}");
});
$task->last_notification_date = $now;
$task->save();
$this->info("Reminder email sent for task: {$task->title}");
}
return self::SUCCESS;
}
}

自定义 Artisan 命令的主要逻辑是在 handle() 方法中编写的。代码使用 Carbon::now() 获取当前时间戳。

接下来,它查询数据库以获取 reminder_time 小于或等于当前时间且 reminder_time 大于或等于当前时间前 10 分钟的所有任务。在MongoDB中,所有日期都以 UTC 格式存储。即使您的服务器使用不同的时区域,您也无需更改时区域。

该查询会获取在接下来的 10 分钟内到期的所有任务。代码循环遍历结果,并向用户电子邮件发送有关将在下一个 10 分钟内到期的任务的提醒。

注意

为了使任务计划程序正常工作,必须将应用程序配置为发送电子邮件。Mailtrap.io 是测试电子邮件发送的有用工具。要学习;了解更多信息,请参阅在 Laravel 中发送电子邮件。

13

要完成设置,安排在上一步中创建的 Artisan 命令每分钟运行。此方法每分钟自动运行 php artisan app:send-task-reminders

导航到 routes/console.php 并添加以下代码:

// ...existing code ...
Schedule::command('app:send-task-reminders')->everyMinute();

要测试其是否有效,运行以下命令:

php artisan schedule:run

在生产服务器上,您必须配置一个 cron作业以定期运行php artisan schedule:run 命令。

在基于Linux或 Unix 的服务器上,可以使用以下命令打开 cron 配置文件:

crontab -e

将以下代码添加到 cron 配置标签页:

* * * * * /usr/bin/php /path-to-your-project/artisan schedule:run >>
/dev/null 2>&1

/usr/bin/php 替换为PHP二进制文件的路径,将 /path-to-your-project 替换为服务器上 Laravel项目的完整路径。保存并退出文件。

要验证 cron作业是否设立正确,运行以下命令:

crontab -l

您现在拥有一个使用 Laravel 和 MongoDB 的任务计划应用程序。本教程演示了如何完成以下操作:

  • 配置 Laravel项目以使用MongoDB

  • 为任务计划程序实现CRUD功能

  • 为提醒系统创建自定义 Laravel Artisan 命令

  • 安排任务以定期运行Artisan 命令

  • 在基于 Linux 的服务器上配置 cron作业

要学习;了解有关本教程所涵盖概念的更多信息,请参阅以下资源:

  • 使用示例,了解常见操作的代码示例

  • 用于使用 Eloquent 模型的 Eloquent 模型

后退

运行命令