How to Use MongoDB With Laravel
Tags: laravel, mongodb, php, tutorial
Published: 2021-08-26 07:21:53

---
title: How to Use MongoDB With Laravel
published: true
date: 2021-08-26 06:49:39 UTC
tags: Laravel,MongoDB,PHP,tutorial
canonical_url: https://blog.shahednasser.com/how-to-use-mongodb-with-laravel/
cover_image: https://blog.shahednasser.com/static/68cf050879e04ae26b89b493169c4a81/mohammad-rahmani-Y5yxdx2a4PI-unsplash-2.jpg
---

_This article was originally posted on [my personal blog](https://blog.shahednasser.com/how-to-use-mongodb-with-laravel/)._

[Laravel](https://laravel.com) is a popular PHP framework that has been around for a long time now. With Laravel, you can easily build web apps, APIs, and more.

[MongoDB](https://www.mongodb.com) is a popular NoSQL database that has also been popular for a while now. With MongoDB, you can build databases that are collections of JSON-like documents.

Laravel is usually used with relational databases like MySQL and provides easy interfaces, facades, and methods to access, insert, update and delete the data in it.

Laravel does not come with native support for MongoDB. In order to use MongoDB with Laravel, some configurations and libraries are required to successfully integrate the two.

In this tutorial, we'll learn how to integrate MongoDB with Laravel. We'll create a simple blog with authentication and posts management.

You can find the code for this tutorial on [this GitHub Repository](https://github.com/shahednasser/laravel-mongodb-tutorial).

## Prerequisites

### Required

Before going through the tutorial, make sure you have the following installed on your machine:

- [PHP](https://www.php.net/downloads.php) >= 7.3
- [Composer](https://getcomposer.org/download/)
- [MongoDB](https://www.mongodb.com/try/download/community)

In addition, you need to install [MongoDB's PHP extension](https://docs.mongodb.com/drivers/php/). You can do so with the following command:

```
pecl install mongodb
```

If you're running on a Mac OS with Apple M1 and you get an error about `pcre2.h`, run the following command:

```
cp /opt/homebrew/Cellar/pcre2/10.36/include/pcre2.h /opt/homebrew/Cellar/php\@7.*/7.*.*/include/php/ext/pcre/pcre2.h
```

Where `7.*` and `7.*.*` depend on your PHP version installed.

These dependencies are essential for development. So, make sure you have them all installed.

### Optional

The following are not required, but are optional to be able to follow with the article:

- [Node.js](https://nodejs.org/en/)
- [Mingo](https://blog.shahednasser.com/use-mingo-to-easily-manage-your-mongodb-databases/), a software that allows you to easily manage and view the MongoDB databases on a server.

## Create Laravel Website

In your terminal, run the following command to create a new Laravel website:

```
composer create-project laravel/laravel laravel-mongodb
```

Then, change to the directory of the website you just created:

```
cd laravel-mongodb
```

Try to start the server:

```
php artisan serve
```

If everything is ok, the website will be available at `localhost:8000`.

## Install Integration Library

In order to integrate MongoDB with Laravel, we need to use the package [jenssegers/mongodb](https://github.com/jenssegers/laravel-mongodb). So, we'll install it with Composer:

```
composer require jenssegers/mongodb
```

## Configure the Connection

After installing the library, we need to configure the connection to our MongoDB server.

Make sure that your MongoDB server is running. Then, go to `.env` in your Laravel project and update the following keys:

```
DB_CONNECTION=mongodb
DB_HOST=127.0.0.1
DB_PORT=27017
DB_DATABASE=blog
DB_USERNAME=
DB_PASSWORD=
```

Notice that we're using the default values for `DB_HOST` and `DB_PORT` for a local MongoDB server.

Make sure to add `DB_USERNAME` and `DB_PASSWORD` based on your MongoDB username and password. If you don't have any, then you can leave it empty.

Next, open `config/database.php` and add the following inside the array of the `connections` key:

```
'connections' => [
	....,
	'mongodb' => [
      'driver' => 'mongodb',
      'host' => env('DB_HOST', '127.0.0.1'),
      'port' => env('DB_PORT', 27017),
      'database' => env('DB_DATABASE', 'blog'),
      'username' => env('DB_USERNAME', ''),
      'password' => env('DB_PASSWORD', '')
    ],
],
```

That's all that we need to do to create the connection between MongoDB and Laravel.

To test it out, we can migrate the default migrations that come with a Laravel project and see if the database will be created in MongoDB:

```
php artisan migrate
```

If the connection was done correctly, the migrations will run successfully with no errors.

To check that the database and collections have been created successfully, you can go to your MongoDB server whether by using the command line/terminal or using a GUI like [Mingo](https://blog.shahednasser.com/use-mingo-to-easily-manage-your-mongodb-databases/). You'll see that the database has been created successfully with the necessary collections.

## Change the Models

By default, Laravel uses [Eloquent](https://laravel.com/docs/8.x/eloquent) for all its models. As Eloquent does not support MongoDB, we need to change the class that our models extend. The class is provided from the package `jenssegers/mongodb` that we installed earlier. It allows us to use our models and access data just like we would when using MySQL or other supported databases.

Currently, we only have one model, which is the `User` model. Go to `app/Models/User.php` and change the class it extends by changing the `Authenticatable` class used at the beginning of the file:

```
use Jenssegers\Mongodb\Auth\User as Authenticatable;
```

As the `User` is a model that can undergo authentication like registering and logging in, it should extend `Jenssegers\Mongodb\Auth\User`.

The next change we need to make is related to casting dates. In order to use dates as Carbon objects, add the following inside the `User` class:

```
 /**
   * The attributes that should be cast to native types.
   *
   * @var array
   */
protected $dates = ['email_verified_at'];
```

That's all that is required to make a model compatible with both Laravel's Eloquent and MongoDB. We'll see in the next sections how we can access, add, modify and delete the data.

## Add Authentication

We'll add authentication to our website to allow users to register and log in. To do this and save time on creating the authentication forms and routes, we'll use [Laravel's Breeze](https://laravel.com/docs/8.x/starter-kits#laravel-breeze).

Breeze provides sleek-looking authentication forms using [Tailwind CSS](https://tailwindcss.com) and [AlpineJS](https://alpinejs.dev). You don't need to know either to go through the tutorial.

The next few parts of the tutorial that are related to installing and configuring Breeze are optional. If you're not interested in following this part of the tutorial you can skip it.

### Install Breeze

First, we'll install Breeze:

```
php artisan breeze:install
```

Then, we need to install the required NPM dependencies and compile the Breeze assets:

```
npm install && npm run dev
```

That's it! Now, we'll have nice-looking authentication forms and routes.

### Testing Authentication

Start the server if it isn't already started and go to `localhost:8000/register`. You'll see a registration form that includes the basic user fields required by default in Laravel.

Try creating a user. You'll then be redirected to `localhost:8000/dashboard`, which is the default in Breeze.

### Changing Default Route

By default, Breeze redirects authenticated users to the route `/dashboard`. We'll change it to the home page.

Go to `routes/web.php` and change the content to the following:

```
Route::get('/', [PostController::class, 'home'])->middleware(['auth'])->name('home');
```

Now, when the user goes to `localhost:8000` and they're not logged in, they will be redirected to the sign-in form. If they're logged in, they can access the blog.

Next, create the controller that will handle this request:

```
php artisan make:controller PostController
```

Inside the controller, add the following method:

```
public function home() {
    return view('home');
}
```

This will just return the view `home`. This view is actually now named `dashboard.blade.php` and it's in `resources/views`. So, rename it to `home.blade.php`.

Then, change the link for the home page in the navigation by replacing `route('dashboard')` in `resources/views/layouts/navigation.blade.php` with `route('home')` everywhere it's used. Additionally, replace texts that have `Dashboard` in them with `Home`.

Finally, change the route that should be redirected to when the user is authenticated in `app/Providers/RouteServiceProvider.php`:

```
public const HOME = '/';
```

If you try to access the website on `localhost:8000`, if you're still logged in you'll see the page we saw earlier after signing up.

## Implement CRUD Operations

In this section, we'll see how to create a new model that's compatible with MongoDB and perform Create, Read, Update and Delete (CRUD) operations.

### Create the Migration

First, create the migration which will create a new `posts` collection in the database:

```
php artisan make:migration create_posts_table
```

This will create the migration file `database/migration/YEAR_MONTH_DAY_TIME_create_posts_table`, where `YEAR`, `MONTH`, `DAY`, and `TIME` depend on when you create this migration.

Open the file and inside the `up` method, change the code to the following:

```
Schema::create('posts', function (Blueprint $table) {
      $table->id();
      $table->string('title');
      $table->longText('content');
      $table->foreignId('user_id')->constrained('users')->cascadeOnDelete()->cascadeOnUpdate();
      $table->timestamps();
});
```

This will create a collection where documents inside will have the fields `_id`, `title`, `content`, `user_id` which will act as a foreign key towards the `users` table, and timestamps fields like `created_at` and `updated_at`.

Run the following to execute the migration:

```
php artisan migrate
```

This will add a new collection `posts` in your database.

### Create the Model

Next, we'll create the `Post` model for the `posts` table. Run the following command:

```
php artisan make:model Post
```

As we did with the `User` model, we need to change the class the model extends. For `User`, we used `Jenssegers\Mongodb\Auth\User` as the model was authenticatable.

The `Post` model is not authenticatable. So, it will just extend the class `Jenssegers\Mongodb\Eloquent\Model`.

At the beginning of `app/Models/Post.php` change the class `Model` used to the following:

```
use Jenssegers\Mongodb\Eloquent\Model;
```

Inside the class, add the following to define the model's fields:

```
protected $fillable = [
    'title',
    'content',
    'user_id'
];

protected $dates = ['created_at', 'updated_at'];

public function user () {
	return $this->belongsTo(User::class);
}
```

We have set the fillable fields to be `title`, `content`, and `user_id`. We've also set the dates to be `created_at` and `updated_at`. Finally, we've added a `belongsTo` relationship between `Post` and `User`.

### Show Posts

We'll add a blade component that will be used to display posts. Create the file `resources/views/components/post.blade.php` with the following content:

```
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
 <div class="p-6 bg-white border-b border-gray-200">
   <h1 class="text-xl md:text-2xl">{{ $post['title']}}</h1>
   <p class="my-2">{{ $post['content'] }}</p>
   <small class="text-gray-500">{{ $post['user']['name'] }} - {{ $post['created_at'] }}</small>
 </div>
</div>
```

We're just showing the title, content, name of the user, and the date the post is created.

Next, we'll change the home page to display all posts, if there are any.

Go to `resources/views/home.blade.php` and change the content to the following:

```
<x-app-layout>
  <x-slot name="header">
    <h2 class="font-semibold text-xl text-gray-800 leading-tight">
      {{ __('Home') }}
    </h2>
  </x-slot>

  <div class="py-12">
    <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
      @empty($posts)
        <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
          <div class="p-6 bg-white border-b border-gray-200">
            There are no posts
          </div>
        </div>
      @endempty

      @foreach ($posts as $post)
        @component('components.post', ['post' => $post])
        @endcomponent
      @endforeach
    </div>
  </div>
</x-app-layout>
```

Now, if there are any posts, they'll be each displayed as cards. If there aren't any, the message "There are no posts" will be shown.

Finally, we need to pass the `$posts` variable from the controller to the view. Change the `home` method to the following:

```
public function home() {
    $posts = Post::with(['user'])->get();
    return view('home', ['posts' => $posts->toArray()]);
}
```

We are able to read data from our MongoDB database the same way we would with the MySQL database, using the same Eloquent methods.

If you try opening the blog now, you'll see that the message "there are no posts is showing."

### Create and Update a Post

**Create Post**

We'll create a form that allows the logged-in user to create a post.

First, add the route in `web/routes.php`:

```
Route::get('/posts/create', [PostController::class, 'createForm'])->middleware(['auth'])->name('post.form');
```

Then, add the `createForm` method in `PostController`:

```
public function createForm() {
    return view('post_form');
}
```

Finally, create the view file `resources/view/post_form.blade.php` with the following content:

```
<x-app-layout>
 <x-slot name="header">
   <h2 class="font-semibold text-xl text-gray-800 leading-tight">
     {{ isset($post) ? __('Edit Post') :__('Create Post') }}
   </h2>
 </x-slot>

 <div class="py-12">
   <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
    <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
     <div class="p-6 bg-white border-b border-gray-200">

      <!-- Session Status -->

      <x-auth-session-status class="mb-4" :status="session('status')" />

      <!-- Validation Errors -->

      <x-auth-validation-errors class="mb-4" :errors="$errors" />
      
      <form method="POST" action="{{ route('post.save') }}">
       @csrf
       @if (isset($post))
        <input type="hidden" name="id" value="{{ $post->_id }}" />
       @endif
       <div>
        <x-label for="title" :value="__('Title')" />
		<x-input id="title" class="block mt-1 w-full" type="text" name="title" :value="old('title') ?: (isset($post) ? $post->title : '')" required autofocus />
       </div>
       <div class="mt-3">
        <x-label for="content" :value="__('Content')" />
        <textarea id="content" name="content" class="block mt-1 w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" rows="5">{{ old('content') ?: (isset($post) ? $post->content : '') }}</textarea>
       </div>
       <div class="flex items-center justify-end mt-4">
        <x-button>
          {{ __('Save') }}
        </x-button>
       </div>
      </form>
     </div>
    </div>
   </div>
 </div>
</x-app-layout>
```

Note that we're making the form ready for editing as well (which we'll go over later). The form has 2 fields, `title` and `content`.

Our form is now ready, the last thing we need to do is add a link to it in the navigation bar. In `resources/views/layouts/navigation.blade.php` add the following under the link for "Home":

```
<x-nav-link :href="route('post.form')" :active="request()->routeIs('post.form')">
	 {{ __('Create Post') }}
 </x-nav-link>
```

If you go on the website now, you'll see a new link in the navigation bar for "Create Post".

Go to that page. You'll see the form we created which has the 2 fields we mentioned.

Next, we need to implement the post method that will handle saving the post.

In `routes/web.php`, add the following new route:

```
Route::post('/posts/create', [PostController::class, 'save'])->middleware(['auth'])->name('post.save');
```

Then, add the `save` method in `PostController`:

```
public function save(Request $request) {

    Validator::validate($request->all(), [
      'id' => 'nullable|exists:posts,_id',
      'title' => 'required|min:1',
      'content' => 'required|min:1'
    ]);

    /** @var User $user */
    $user = Auth::user();
    $id = $request->get('id');

    if ($id) {
      $post = Post::query()->find($id);
      if ($post->user->_id !== $user->_id) {
        return redirect()->route('home');
      }
    } else {
      $post = new Post();
      $post->user()->associate($user);
    }

    $post->title = $request->get('title');
    $post->content = $request->get('content');

    $post->save();

    return redirect()->route('home');
  }
```

In this method, we're first validating the fields that are required, which are `title` and `content`. We're also validating `id` which will be passed only when the post is being edited. So, it can be nullable, but when it's available, it should exist in the `posts` collection in the field `_id`.

Suggested Read: [Beginner’s Guide to Validation in Laravel](https://blog.shahednasser.com/beginners-guide-to-validation-in-laravel/)

Next, if the post is being edited, we're validating that the user editing this post is actually the user that created it. The post's user can be easily accessed through the relationship we defined in the class.

Finally, we're creating or updating the post, setting the title, content, and user creating it. Then, we redirect back home.

You can now try adding a post. Open the form and enter a title and content, then click Save. If everything is done correctly, you'll be redirected to the home page and you can see the new post added.

You can also check on your MongoDB server if the new post has been added successfully.

**Edit Post**

As mentioned earlier, the form is ready to be used for editing a post.

We'll add an edit button for posts that will allow the user to edit a post.

First, create the file `resources/views/components/edit.blade.php` with the following content:

```
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
```

This component is just an edit icon from [Heroicons](https://heroicons.com).

Now, we'll use this icon to add an edit button. Change `resources/views/components/post.blade.php` to the following:

```
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
 <div class="p-6 bg-white border-b border-gray-200">
   <h1 class="text-xl md:text-2xl">{{ $post['title']}}</h1>
   <p class="my-2">{{ $post['content'] }}</p>
   <small class="text-gray-500">{{ $post['user']['name'] }} - {{ $post['created_at'] }}</small>
   @if(\Auth::user()->_id === $post['user']['_id'])
    <a href="{{ route('post.edit.form', ['id' => $post['_id']]) }}" class="inline-block align-middle pb-1 text-decoration-none text-gray-600">
     @component('components.edit')
     @endcomponent
    </a>
   @endif
 </div>
</div>
```

This will add the link to edit the post only when the post belongs to the current user.

Next, add a new route to go to the form and edit the post:

```
Route::get('/posts/{id}/edit', [PostController::class, 'editForm'])->middleware(['auth'])->name('post.edit.form');
```

Finally, add the method `editForm` in `PostController`:

```
public function editForm(Request $request, $id) {
    $post = Post::query()->find($id);
    if (!$post) {
      return redirect()->route('home');
    }
    return view('post_form', ['post' => $post]);
  }
```

That's all we need to add to be able to edit a post. Try opening the website and clicking on the edit icon for any of the posts. It will take you to the same post form, but with the values filled.

Try making edits to the post and click save. You'll be redirected to the home page and you can see the post has been updated.

### Delete Post

The last thing we need to add is the ability to delete a post.

Create the file `resources/views/components/delete.blade.php` with the following content:

```
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
```

Similar to the edit button, we're creating a component for a delete icon to use for the button.

In `resources/views/components/post.blade.php` add the following after the edit link:

```
<form method="POST" action="{{ route('post.delete') }}" class="inline-block align-middle">
	@csrf
	<input type="hidden" name="id" value="{{ $post['_id'] }}" />
	<button type="submit" class="border-0 bg-transparent text-red-400">
		@component('components.delete')
		@endcomponent
	</button>
</form>
```

Now, add a new route in `routes/web.php`:

```
Route::post('/posts/delete', [PostController::class, 'delete'])->middleware(['auth'])->name('post.delete');
```

Finally, create the `delete` method in `PostController`:

```
public function delete(Request $request) {
    Validator::validate($request->all(), [
      'id' => 'exists:posts,_id'
    ]);

    $post = Post::query()->find($request->get('id'));
    $post->delete();

    return redirect()->route('home');
  }
```

First, we're validating that the ID is sent in the request and that it's a post that exists. Then, we're deleting the post.

Notice how to delete we can use the same eloquent methods we use in Laravel.

For simplicity, we're omitting the validation check to make sure that the post belongs to the logged-in user.

Go to the home page now. You'll see a delete icon next to each post. Try clicking on one of them. You'll be redirected back to the home page and the post will be deleted.

## Conclusion

In this tutorial, we were able to connect a Laravel website to a MongoDB server. As we saw, the integration is simple and seamless.

Even with the integration, you can use Eloquent models like you would when using other supported databases.

More can be done with `jenssegers/mongodb` package. You can try adding to the website we created a search bar or advanced search with filters. Make sure to check out the documentation of the package, as well.