When to use NoSQL — Getting started with MongoDB in Laravel

Alex Renoki
Jan 17 · 7 min read
Photo by Manuel Geissinger from Pexels

This post has an audio version thanks to Miguel Piedrafita’s Blogcast app.

In case you have followed me for quite a while, i have published this specific article: Setup MongoDB for Laravel on ploi.io. In case you need to deploy your application to production without “hassle”, ploi.io might seem a great choice. This time, i’ll introduce you to MongoDB on Laravel.

NoSQL (MongoDB) seems quite unpopular on Laravel, not because no one cares, but because not many people are actually going to use Mongo over SQL since SQL is already embedded into Laravel and provides a great experience for newcomers to understand how Laravel works with databases.

In case you don’t know what really are the ups’n’downs of NoSQL over SQL, head back to the previously mentioned article on which not only i’m explaining how to deploy MongoDB on a cloud server instance, but what’s all about running NoSQL with Laravel. This time, i’ll just break through each up’n’down shortly while giving examples and how things work, how to use it locally and how they affect the production environment.

Starting from ground zero — you have to be quite familiar with Laravel Eloquent ORM in order to adventure yourself further. If you are not quite familiar, it’s always time to learn! Be eager to learn things all the time!


Prerequisites & running it locally

ip: 192.168.10.13
memory: 4096
cpus: 2
mongodb: true
mariadb: true
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
- ~/.ssh/id_rsa
folders:
-
map: 'C:\Laravel\homestead'
to: /home/vagrant/code
sites:
-
map: homestead.test
to: /home/vagrant/code/public
php: "7.2"
schedule: true
databases:
- homestead
name: homestead
hostname: homestead

I also encourage you to use the vendor/bin/homestead make command instead of copying the configuration i stated as an example, because the path to your folder should be auto-generated by that command. What i stated in this example was the fact that i have a mongodb: true there.

All you have to do further is to re-provision the machine (or run it with --provision in case you never up-ed your machine).

Congrats! You got your MonogDB server running inside the VM. It can be accessed using localhost as host and homestead and secret as user and password in your .env.


Eloquent ORM for NoSQL

The setup is pretty straightforward if you read the documentation — it will help you install it through Composer, setup a database driver. We’ll focus just on the things that are important.

This package offers a Moloquent model, as the documentation states. It is basically an Eloquent model, but made for Mongo. If you worked with ORM for a while, you would be familiar with this kind of look:

class Post extends Model
{
//
}

In this case, we will no longe extend from Model, the basic model which Laravel gives us.

use Jenssegers\Mongodb\Eloquent\Model;class Post extends Model
{
//
}

SQL works with tables, NoSQL works with collections. Instead of the $tablevariable, we’ll have a $collection variable. Also, it’s pretty important to note that the primary key cannot be set through $primaryKey and the $incrementing is unavailable. Additionally, you might want to specify that the model belongs to mongodbconnection you have created earlier:

use Jenssegers\Mongodb\Eloquent\Model;class Post extends Model
{
protected $connection = 'mongodb';
}

You must be at peace with the fact that MongoDB auto-assign primary keys to documents (the equivalent of rows fromSQL). So, to access the primary key of a document, you just have to us the same attribute name like in the basic model:

echo 'The post ID is: '. $post->_id;
echo 'The post ID is '. $post->id; // they both work

Using find() uses the primary key field to retrieve the result:

$post = Post::find('517c43667db388101e00000f');

Almost no schemas to define

Schema::create('posts', function ($collection) {
$collection->index('slug');
$collection->unique('slug');
});

The migration process is the same. To be able to run the migrations, make sure that the default driver set (the DB_CONNECTION env variable) is set to mongodb.

php artisan migrate

In case you wanna run both SQL and NoSQL in the same project, i can give you some hints:

  • move your SQL migrations to a folder inside the migrations folder. I’ll call it mysql.
  • all models that work with other than the default database driver, should extend the right model

Running migrations should be done for both ways, either it’s local or in production.

For the default driver (i.e. when DB_CONNECTION is set to mongodb):

php artisan migrate

For the other driver (i.e. for MySQL):

php artisan migrate --database=mysql --path=database/migrations/mysql/

Authenticating users with MongoDB

use Jenssegers\Mongodb\Auth\User as Authenticatable;class User extends Authenticatable
{
//
}

In case you want to use Laravel Passport with MongoDB, make sure you use designmynight/laravel-mongodb-passport. It will provide you another Authenticatable class that will do the job for you!

NoSQL = NoJoins

For example, a belongsToMany relationship would look like this:

class User extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User');
}
}

Basically, either you’re trying to access $user->roles or $role->users, you will need a third table called role_userthat stores data.

In this case, MongoDB does something nasty: it allows you many-to-many, but it does not need a third table — it simply will store ids in each other’s model. Make sure that if you want to set custom foreign keys, you set the second parameter of the belongsToMany function to null.

class User extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role', null, 'users_ids', 'roles_ids');
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User', null, 'roles_ids', 'users_ids');
}
}
$role = Role::named('admin')->first();$user->attach($role);
$role->attach($user);

After doing this, you’ll end up with something like this:

The User document looks like this:

{
"_id": "user_id_here",
"username": "dummy",
...,
"roles_ids": [
"role_id_here"
]
}

The Role document looks like this:

{
"_id": "role_id_here",
"name": "administrator",
...,
"users_ids": [
"user_id_here"
]
}

At the same time, the rest of the other relationships, excepting morphToManyworks the same way as they do in SQL.

Schema is your playground

This is our user document:

{
"_id": "...",
"name": "John",
"password": "...",
"old_field1": "value1",
"old_field2": "value2",
}

So, if we want to drop some fields, we ca do it using the drop method:

$john = User::where('name', 'John')->first();$john->drop('old_field1');// or$john->drop(['old_field1', 'old_field2']); // works with more

In the end, it will simply remove the field from our document:

{
"_id": "...",
"name": "John",
"password": "..."
}

Pushing it into production

Too hard to understand? Reach me!

Alex Renoki

Written by

Certified Laravel Developer. Working with full-stack web applications, AWS, Kubernetes and buzzwords like cloud computing.