Docs Menu
Docs Home
/ /

Tutorial: Build a Task Reminder With Laravel and MongoDB

In this tutorial, you can learn how to use MongoDB in a Laravel application by building a simple task reminder system. The application allows you to create, read, update, and delete tasks. Additionally, it sends email reminders for upcoming tasks based on a scheduled time.

Before you begin this tutorial, ensure you have the following components installed in your development environment:

  • PHP and the MongoDB PHP extension

  • Laravel

  • Composer

  • NPM

You must also set up a MongoDB Atlas cluster. To learn how to set up a cluster, see the Getting Started guide.

1

Use Composer to create a new Laravel project and navigate to the project directory:

composer create-project laravel/laravel LaravelMongodbProject
cd LaravelMongodbProject
2

To use MongoDB in your application, you must install the Laravel MongoDB package and configure it in the config/database.php file.

Install the package by using the following command:

composer require mongodb/laravel-mongodb

Tip

If you encounter an error when you run the preceding command, ensure that the MongoDB PHP extension is installed. To learn how to install, see the Install the MongoDB PHP extension step in the Get Started guide.

3

After the Laravel MongoDB package installation completes, add your MongoDB database connection to the config/database.php file to complete the configuration. Add the following MongoDB configuration to the connections array that contains configurations for other database types:

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

Your application obtains the dsn value from the .env file. Navigate to your .env file. Then, create a value for MONGODB_URI and set it to the value of your MongoDB connection string.

Tip

To obtain your MongoDB connection string, navigate to your cluster in MongoDB Atlas. Click the Connect button and click the Drivers under Connect your application. Select "PHP" from the Driver selection menu. Copy the connection string from the dialog box. For detailed steps on how to obtain your connection string, see create-a-connection-string.

In your .env file, set the DB_CONNECTION value to mongodb to make it the default connection for your application. Edit the SESSION_DRIVER value and set it to mongodb as shown in the following code:

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

After installing and configuring your application to work with MongoDB, set up authentication. Laravel simplifies authentication implementation by providing packages such as Laravel Breeze, Laravel Fortify and Laravel Jetstream. This tutorial uses Laravel Breeze for authentication.

Install Laravel Breeze by using Composer:

composer require laravel/breeze --dev

After the installation completes, run the following commands:

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

This prompts you to choose your preferred stack and testing package. For this tutorial, select the first option, Blade with Alpine:

┌ 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 │
└──────────────────────────────────────────────────────────────┘

This command adds authentication views, routes, controllers, and other related resources to your application.

5

Laravel Breeze creates a default User model for SQL databases. You must update it to work with MongoDB and resolve rate limiting compatibility issues.

Open app/Models/User.php and replace its contents with the following:

<?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

Serve the project by using Laravel's built-in server and confirm that everything works properly. In the Laravel project directory, run the following command:

php artisan serve

The project serves at 127.0.0.1:8000. If port 8000 is already in use, Laravel switches to a new available port, which it prints to the terminal.

To test the authentication system, select the Register button in your application and create a new user account. This stores the user in your MongoDB database and allows you to log in with those credentials. For this tutorial, you can set the username to test@example.com and the password to password.

To verify that the Laravel MongoDB package was configured properly, create a route to ping your MongoDB cluster. Add the following to 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];
});

Visit this route in your browser. With the correct configuration, you see the message "MongoDB is accessible!" in your browser.

7

Create the model and controller for the task scheduling feature by using the following command:

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

This command creates the Task model in the app/Models directory and the TaskController in the app/Http/Controllers directory with resource methods.

Create a route for the TaskController by navigating to routes/web.php and adding the following code:

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

Navigate to app/Models/Task.php and replace the contents of the file with the following code:

<?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'
];
}

This code creates the task model with the following specifications:

  • The use MongoDB\Laravel\Eloquent\Model statement after the namespace is specific to MongoDB models. It overrides the Eloquent features implemented with SQL by using MongoDB queries.

  • The protected $table = 'tasks' property is optional. It specifies the name of the MongoDB collection used to store the documents from this model.

  • The protected $fillable property specifies the mass-assignable properties.

Tip

One of the unique features of MongoDB is that it doesn't require migrations like relational databases. You can add new fields directly to your documents without updating the model or creating migrations. This feature is useful for handling dynamic data.

9

Navigate to app/Http/Controllers/TaskController.php and update the content with the following code:

<?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.');
}
}

The TaskController class contains code that handles the CRUD operations of the task model:

  • The index() method retrieves all tasks belonging to the logged-in user and sends them to the index.blade.php file for display.

  • The create() method returns the form view for creating a new task.

  • The store() method validates input, assigns the logged-in user's email to the task, and saves it to the database.

  • The edit() method retrieves the specific task to be edited and displays it in an edit form.

  • The update() method saves the edited task to the MongoDB task collection.

  • The destroy() method deletes a specific task.

Each operation redirects back to the task list with a success message for user feedback.

10

In the resources/views directory, create a folder named tasks and create the following files in it:

  • create.blade.php

  • edit.blade.php

  • index.blade.php

Navigate to resources/views/tasks/create.blade.php and add the following content:

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>

The form contains text inputs for title and description and date and time inputs for due date and reminder time.

Navigate to resources/views/tasks/edit.blade.php and add the following content:

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>

The edit form contains the same input fields as the create form and is loaded with the data of the task currently being edited.

Navigate to resources/views/tasks/index.blade.php and add the following content:

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>

This view includes a link to the create form and loops through all the tasks to display those belonging to the authenticated user.

11

Add a link to the task feature from your application navigation. Go to resources/views/layouts/navigation.blade.php and add the following code after the dashboard navigation link:

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>

At this point, you can test the CRUD operations of your task management system. Ensure that everything works correctly before you move on to the next section.

12

To implement the reminder system for due tasks, create a custom Artisan command to send reminders through email and register the command for automatic scheduling.

To create a custom Artisan command, run the following code in your terminal:

php artisan make:command SendTaskReminders

After you create the command, update the contents of the app/Console/Commands/SendTaskReminders.php file with the following code:

<?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;
}
}

The main logic of the custom Artisan command is written in the handle() method. The code gets the current timestamp by using Carbon::now().

Next, it queries the database to get all the tasks where reminder_time is less than or equal to the current time and where reminder_time is greater than or equal to 10 minutes before the current time. In MongoDB, all dates are stored in UTC. Even if your server uses a different time zone, you don't need to change the time zone.

The query fetches all tasks that are due in the next 10 minutes. The code loops through the results and sends reminders to the user emails of tasks that will be due in the next 10 minutes.

Note

For the task scheduler to work properly, you must configure your application to send emails. Mailtrap.io is a useful tool to test email sending. To learn more, see Send Email in Laravel.

13

To complete the setup, schedule the Artisan command created in the previous step to run every minute. This approach automatically runs php artisan app:send-task-reminders every minute.

Navigate to routes/console.php and add the following code:

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

To test that this works, run the following command:

php artisan schedule:run

On a production server, you must configure a cron job to run the php artisan schedule:run command at regular intervals.

On a Linux- or Unix-based server, you can open your cron configuration file by using the following command:

crontab -e

Add the following code to the cron configuration tab:

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

Replace /usr/bin/php with the path to your PHP binary and /path-to-your-project with the full path to your Laravel project on your server. Save and exit the file.

To verify that the cron job is set up correctly, run the following command:

crontab -l

You now have a task scheduler application that uses Laravel and MongoDB. This tutorial demonstrated how to complete the following actions:

  • Configure a Laravel project to work with MongoDB

  • Implement CRUD features for the task scheduler

  • Create a custom Laravel Artisan command for the reminder system

  • Schedule a task to run the Artisan command at intervals

  • Configure the cron job on a Linux-based server

To learn more about the concepts covered in this tutorial, see the following resources:

  • Usage Examples for code examples of common operations

  • Eloquent Models for working with Eloquent models

Back

Run a Command