Laravel 5.8 REST API CRUD Tutorial - Build a CRM [PART 1]: Eloquent Models and Relationships

Laravel 5.8 is recently released with many improvements so we'll be learning, throughout this tutorial how to create an example REST API CRUD application from scratch. The application we'll be building is a simple CRM with a MySQL database that exposes a set of RESTful API endpoints.

You can see this Upgrade Guide for instructions on how to upgrade an existing web application from Laravel 5.7 to Laravel 5.8

Tutorial Prerequisites

This tutorial has some prerequisites

  • You have at least PHP 7.1.3 installed on your development machine,
  • Working experience of PHP.
  • The OpenSSL, PDO, Mbstring, Tokenizer, XML, Ctype, JSON and BCMath PHP extensions installed with your PHP version.
  • The MySQL database management system,
  • Composer (A PHP dependency management for PHP) installed on your machine. You can head to the official website for instructions how to download install it.

If you have these prerequisites, let's get started by creating our first REST API project with Laravel 5.8.

Introducing REST APIs

According to Wikipedia:

Representational State Transfer (REST) is a software architectural style that defines a set of constraints to be used for creating Web services. Web services that conform to the REST architectural style, termed RESTful Web services (RWS), provide interoperability between computer systems on the Internet.

REST stands for REpresentational State Transfer. It's an architectural style for distributed systems invented by Roy Fielding in 2000.

You can refer to an API (Application Programming Interface) or simply interface as RESTful if it comply with these six REST contstraints:

  • Client–server architecture: Your REST API interface should follow a client-server architecture,
  • Stateless: Your REST API interface should be stateless,
  • Cacheable: Your REST API interface should be cacheable,
  • Uniform interface: Your REST API should have a uniform interface,
  • Layered system ,
  • Code on demand.

What's a REST API Resource

In REST APIs, a resource refers to an object that has a type and data. It has also a group of associated methods that can operate on it. These are standard methods that correspond to the standard HTTP GET, POST, PUT and DELETE methods.

What's a REST API Method

A REST API is an interaface that allows you to interface your web application with other systems like mobile devices and web browsers via a set of methods that correspond to CRUD (create, read, update, delete) operations. In REST rules, you need to map a specific HTTP method to a specific CRUD operation.

These are example HTTP methods mapped to the CRUD actions that can by performed by your REST API.

  • HTTP GET: This REST API method can be mapped to an action to get/retrieve the resource data,
  • HTTP POST: This REST API method can be mapped to an action to create a new resources,
  • HTTP PUT: This REST API method can be mapped to an action to update existing resources,
  • HTTP DELETE: This REST API method can be mapped to an action delete the resource,
  • HTTP PATCH: This REST API method can be mapped to an action to make partial update on a resource.

Creating a Laravel 5.8 Project with PHP Composer

Let's use Composer to create a project based on Laravel 5.8. Open a new terminal and run the following command:

$ composer create-project --prefer-dist laravel/laravel laravel-crud

This command will automatically start installing the latest version of Laravel provided that you have the required dependencies of PHP for Laravel 5.8 installed on your system:

Laravel 5.8 CRUD Tutorial

After finishing the installation process, navigate to the project's folder:

$ cd ./laravel-crud

Next, serve your application using the artisan serve command:

$ php artisan serve

This will start a Laravel development server on the http://127.0.0.1:8000. Just leave it open as any changes we'll be making will automatically get reloaded.

Configuring the MySQL Database

We'll be using MySQL, the most popular database system used by PHP and Laravel developers so make sure you have created a database for your project. You can simply use the mysql client. Open a new terminal window and run the following command:

$ mysql -u root -p

You will get prompted for a password. Enter the one you submitted when you configured your MySQL installation and hit Enter.

When the mysql clients starts, enter the following SQL instruction to create a database:

mysql> create database l58db;

Create MySQL database

Note: You can also use phpMyAdmin to create and work with MySQL databases. phpMyAdmin is a free web interface tool created in PHP, intended to handle the administration of MySQL over the Web. It's beginners friendlier tool that's commonly used by PHP developers.

Now, let's let Laravel know about our created database. Open the .env file in the root of your project and update the MySQL credentials with your own values:

DB_CONNECTION=mysql 
DB_HOST=127.0.0.1  
DB_PORT=3306  
DB_DATABASE=l58db 
DB_USERNAME=root 
DB_PASSWORD=<YOUR_DATABASE_PASSWORD>

This will allow your application to connect to your MySQL database.

You can also configure the database connection in the config/database.php.

The database configuration for your application is located at config/database.php. In this file you may define all of your database connections, as well as specify which connection should be used by default. Examples for most of the supported database systems are provided in this file. The official docs

Next, let's create the database tables using the following command:

$ php artisan migrate

Note: Until now, we didn't create any models but Laravel makes use of multiple tables so we need to run the previous command to create them in our database. After we create our own models, we can run the artisan migrate command again to update the database structure.

This is the output of the command:

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table

You can see that our project has already the users and password_resets tables.

The Application We'll Be Building

We'll be building a simple CRM application that allows sales managers to manage contacts, accounts, leads, opportunities, tasks and related activities.

For the sake of simplicity we'll try to add few interfaces as we can in our application. The main interface is a dashboard which contains the table of contacts and their status (lead, opportunity and customer). We'll not add login and authentication in this tutorial as we'll be the subject of another tutorial.

In our CRM database we'll be making use of the following tables:

  • contacts — contains information about contacts/customers such as name, address, company/account, ,
  • activities —  contains activities (phone calles, meetings and emails etc.) about the contacts,
  • accounts —  contains information about contact companies,
  • users —  contains information about the application users

We'll also be using the following JOIN tables:

  • contact_status — contains contact status such as lead, opportunity or customer which indicates the stage in the sales cycle
  • activity_status — the activity status can be either pending, ongoing or completed,
  • contact_source —  contains contact source.

The contacts table has the following fields:

  • id
  • title,
  • first name,
  • last name,
  • email,
  • phone,
  • address,
  • source_id,
  • date of first contact,
  • account_id,
  • status_id,
  • user_id,

The contact_status table has the following fields:

  • id,
  • status = (lead, proposal, customer, archived)

The contact_source table:

  • id,
  • name

The accounts table has the following fields:

  • id,
  • name,
  • description

The activities table has the following fields:

  • id,
  • date,
  • description,
  • contact_id
  • status_id

The activity_status table has the following fields:

  • id,
  • status

Creating Laravel 5.8 Models of Our REST API Application

According to the database structure above, we'll need to create the followng Eloquent models:

  • Contact
  • Account
  • Activity
  • ContactStatus
  • ContactSource
  • ActivityStatus

Head back to your terminal and run the following commands:

$ php artisan make:model Contact --migration
$ php artisan make:model Account --migration
$ php artisan make:model Activity --migration
$ php artisan make:model ContactStatus --migration
$ php artisan make:model ContactSource --migration
$ php artisan make:model ActivityStatus --migration

This will create models with the corresponding migrations files. The models exist in the app folder and you can find the migration files in the database/migrations folder.

The -m flag will also create the corresponding migration file for the model.

Next, in your terminal, run the following command to create the base tables:

$ php artisan migrate

You will get the following output:

Migration table created successfully.
Migrating: 2019_03_12_223818_create_contacts_table
Migrated:  2019_03_12_223818_create_contacts_table
Migrating: 2019_03_12_223832_create_accounts_table
Migrated:  2019_03_12_223832_create_accounts_table
Migrating: 2019_03_12_223841_create_activities_table
Migrated:  2019_03_12_223841_create_activities_table
Migrating: 2019_03_12_223855_create_contact_statuses_table
Migrated:  2019_03_12_223855_create_contact_statuses_table
Migrating: 2019_03_12_223904_create_contact_sources_table
Migrated:  2019_03_12_223904_create_contact_sources_table
Migrating: 2019_03_12_223912_create_activity_statuses_table
Migrated:  2019_03_12_223912_create_activity_statuses_table

In Laravel, you can specify the structure (table fields) in the migration files. Let's start with the contacts table. Open the database/migrations/2019_03_12_223818_create_contacts_table.php file (the date prefix for the file will be different for you) and add the following changes:

    public function up()
    {
        Schema::create('contacts', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('title');
            $table->string('first_name');
            $table->string('last_name');
            $table->string('email');
            $table->string('phone');
            $table->string('address');
            $table->date('date');

            $table->biginteger('user_id')->unsigned(); 
            $table->foreign('user_id')->references('id')->on('users');                                        
        });
    }

Next, open the database/migrations/<YOUR_TIMESTAMP>_create_accounts_table.php file and change accordingly:

    public function up()
    {
        Schema::create('accounts', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('name');
            $table->description('description');           

        });
    }

Next, open the database/migrations/<YOUR_TIMESTAMP>_create_activities_table.php file and change accordingly:

    public function up()
    {
        Schema::create('activities', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('description');

        });
    }

Next, open the database/migrations/<YOUR_TIMESTAMP>_create_contact_statuses_table.php file and change accordingly:

    public function up()
    {
        Schema::create('contact_statuses', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('status');
        });
    }

Next, open the database/migrations/<YOUR_TIMESTAMP>_create_contact_sources_table.php file and change accordingly:

    public function up()
    {
        Schema::create('contact_sources', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('name');
        });
    }

Next, open the database/migrations/<YOUR_TIMESTAMP>_create_activity_statuses_table.php file and change accordingly:

    public function up()
    {
        Schema::create('activity_statuses', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('status');            
        });
    }

You can see that we didn't create any foreign keys between the tables. That's because we need to avoid any issues to creating a foreign key to a table that doesn't exist yet. The order of the migrations is important so you either make sure that the tables that are being referenced are created first or create the tables without any foreign keys and then add a migration to update the tables with the required relationships after the tables are created.

Now, let's create the update_contacts_table migration by running the following command:

$ php artisan make:migration update_contacts_table --table=contacts
Created Migration: 2019_03_12_235456_update_contacts_table

Open the database/migrations/<YOUR_TIMESTAMP>_update_contacts_table.php file and update accordingly:

    public function up()
    {
        Schema::table('contacts', function (Blueprint $table) {

            $table->biginteger('source_id')->unsigned();  
            $table->foreign('source_id')->references('id')->on('contact_sources');   

            $table->biginteger('account_id')->unsigned(); 
            $table->foreign('account_id')->references('id')->on('accounts');   

            $table->biginteger('status_id')->unsigned(); 
            $table->foreign('status_id')->references('id')->on('contact_statuses');

        });
    }

We create three foreign key relationships to the contact_sources, accounts and contact_statuses tables.

Next, let's create the update_activities_table migration by running the following command:

$ php artisan make:migration update_activities_table --table=activities
Created Migration: 2019_03_13_002644_update_activities_table

Open the database/migrations/<YOUR_TIMESTAMP>_update_activities_table.php file and update accordingly:

    public function up()
    {
        Schema::table('activities', function (Blueprint $table) {
            $table->biginteger('contact_id')->unsigned();  
            $table->foreign('contact_id')->references('id')->on('contacts');   

            $table->biginteger('status_id')->unsigned(); 
            $table->foreign('status_id')->references('id')->on('activity_statuses');   

        });
    }

We create two foreign keys to the contacts and activity_statuses table.

Now, run the following command to migrate your database:

$ php artisan migrate

Implementing the REST API Models

The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table. The official docs

We can interact with our database tables using the corresponding Eloquent models so we need implement the required methods in each model.

Defining the Relationships between Models

  • A contact belongs to a source, a status, an account and to a user and has many activities.
  • An account belongs to a user (i.e created by a user) and has many contacts.
  • An activity belongs to a status, a contact and to a user.
  • A contact status has many contacts.
  • A contact source has many contacts.
  • An activity status has many activities

Open the app/Account.php file and change accordingly:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Account extends Model
{
    public function contacts(){
        return $this->hasMany('App\Contact');
    }
    public function user(){
        return $this->belongsTo('App\User');
    }
}

Next, open the app/Activity.php file and change accordingly:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Activity extends Model
{
    public function contact(){
        return $this->belongsTo('App\Contact');
    }

    public function status(){
        return $this->belongsTo('App\ActivityStatus');
    }
    public function user(){
        return $this->belongsTo('App\User');
    }
}

Next, open the app/ActivityStatus.php file and change accordingly:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ActivityStatus extends Model
{
    public function activities(){
        return $this->hasMany('App\Activiy');
    }
}

Next, open the app/Contact.php file and update accordingly:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
    protected $fillable = [
        'title',
        'first_name',
        'last_name',
        'email',
        'phone',
        'address',
        'date'       
    ];

    public function source(){
        return $this->belongsTo('App\ContactSource');
    }

    public function status(){
        return $this->belongsTo('App\ContactStatus');
    }

    public function account(){
        return $this->belongsTo('App\Account');
    }

    public function user(){
        return $this->belongsTo('App\User');
    }

    public function activities(){
        return $this->hasMany('App\Contact');
    }

}

Next, open the app/ContactSource.php file and update accordingly:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ContactSource extends Model
{
    public function contacts(){
        $this->hasMany('App\Contact');
    }
}

Next, open the app/ContactStatus.php file and update accordingly:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ContactStatus extends Model
{
    //
    public function contacts(){
        $this->hasMany('App\Contact');
    }
}

Finally, open the app/User.php file and update as follows:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function contacts(){
        $this->hasMany('App\Contact');
    }

    public function activities(){
        return $this->hasMany('App\Activiy');
    }
    public function accounts(){
        return $this->hasMany('App\Account');
    }
}

Next we'll be creating the REST API controllers.


Author


SUBSCRIBE


JOIN OUR COMMUNITY!


comments powered by Disqus