Work Laravel 5.3 + Vue.Js 1.0 - Create/Read

Laravel 5.3 + Vue.Js 1.0 - Create/Read

12th Sep, 2016 | Work

Let us see if we can create a Laravel 5.3 + Vue.Js newsletter application step by step over the next few weeks. The end goal is to able to send newsletters to a list of email subscribers and then collect and display stats, abstracting a mainstream email API (such as Mailgun/SparkPost).

First up, the application should have a mailing list feature which allows subscribers to be grouped into mailing lists to whom a newsletter campaign might be targeted. In this post, we will be looking at how to create and read mailing lists using Laravel 5.3 and Vue.Js. In the end, a subscriber can belong to many mailing lists but for the purpose of this post, this doesn't come into play.

Our database migrations will contain these fields:

Schema::create('mailing_lists', function(Blueprint $table)
{
    $table->increments('id');
    $table->string('name')->unique();
    $table->mediumText('meta')->nullable();
    $table->timestamps();
});

There are name, meta and timestamps fields.

Next up is our model:

namespace App;

use Illuminate\Database\Eloquent\Model;

class MailingList extends Model
{
    protected $table = 'mailing_lists';
    public static $rules = ['name' => 'required|unique:mailing_lists|max:255'];

    /**
     * Define Many to Many subscriber relationship
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function subscribers()
    {
        return $this->belongsToMany('App\Subscriber', 'mailing_list_subscriber', 'mailing_list_id', 'subscriber_id');
    }

}

Here we have defined the table the model relates to, a set of validation rules and also the Many to Many subscribers eloquent relationship.

Next we wire up our Controller. This needs to be a resourceful controller which would facilitate the CRUD operations. Check the docs on how to get this up and running:

Finally, we ready our backend code by creating the routes that would be responsible for the CRUD operations of mailing lists. This simply involves adding this line to the web routes:

Route::resource('mailing-lists', 'MailingListController');

And now we are ready to write some frontend code.
Laravel ships with some Vue.js scaffolding to help you quickly get up and running. If, like me, you don't have gulp installed, then do it as this is going to make your aging process significantly slower. Terms and conditions apply. 
You can refer at this free Laracast screencast on how to get this wired up.

Inside resources\assets\js\app.js, add this code:


/**
 * First we will load all of this project's JavaScript dependencies which
 * include Vue and Vue Resource. This gives a great starting point for
 * building robust, powerful web applications using Vue and Laravel.
 */

require('./bootstrap');

/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the body of the page. From here, you may begin adding components to
 * the application, or feel free to tweak this setup for your needs.
 */

Vue.component('mailing-lists', require('./components/MailingLists.vue'));

const app = new Vue({
    el: 'body'
});

This instructs Vue.Js to load our custom component located in resources\assets\js\components\MailingLists.vue
The code inside that file is as follows:

<template xmlns="http://www.w3.org/1999/XSL/Transform">
    <div class="mailing-lists">
        <form v-on:submit='addMList'>
            <div class="form-group">
                <input class="form-control" placeholder="New Mailing List" v-model="newMList.name">
            </div>
            <!--<button class="btn btn-default">Create</button>-->
        </form>
        <table class="table">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Created</th>
                    <th>Updated</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="mList in mLists | orderBy 'name'">
                    <td>{{ mList.name }}</td>
                    <td>{{ mList.created_at }}</td>
                    <td>{{ mList.updated_at }}</td>
                </tr>
            </tbody>
        </table>
    </div>
    <!--{{ mLists | json }}-->
</template>

<script>
    export default {
        data: function() {
            return {
                newMList: { name: ''},
                mLists: []
            }
        },
        ready: function () {
            this.fetchMLists();
            this.resourceUrl = 'mailing-lists';
        },
        methods: {
            fetchMLists: function () {
                var mLists = [];
                this.$http.get(this.resourceUrl).then(function(response) {
                    if ( response.data.data.length ) {
                        this.$set('mLists', response.data.data);
                    }
                }, function(error) {
                    console.log(error);
                });
            },
            addMList: function () {
                var newMListName = this.newMList.name.trim();
                if ( newMListName ) {
                    this.newMList.name = newMListName;
                    var newMList = this.newMList;

                    this.$http.post(this.resourceUrl, newMList).then(function(response) {
                        this.mLists.push(response.data);
                        this.newMList = { name: '' };
                    }, function(error) {
                        if ( error.status && error.status == 422 ) {
                            alert(error.data.name);
                        }
                    });
                }
            }
        }
    }
</script>

This contains a template that would load a simple form for creating a new mailing list as well as a section of the page where the existing mailing lists will be loaded. The Javascript involved in this component would contain three sections: data, ready and methods. There are two methods responsible for fetching mailing lists when the app is ready and adding a new mailing list, respectively. These methods have been wired to the Laravel backend using Vue.Js's resource plugin. What this means is that whenever each is fired, it will send a given request to Laravel routes which would look up the responsible method inside the relevant controller and respond with the right information. This information is then checked on the frontend and the necessary step taken. For the fetch method, we send a HTTP GET request to Laravel. Laravel would automatically know we want a list of mailing lists and so it will use the Controller's index method for this.

Here's the full MailingListController code:

namespace App\Http\Controllers;

use App\MailingList;
use Illuminate\Http\Request;

class MailingListController extends Controller
{
    public $rules;
    public $paginate;
    public $orderCriteria;
    public $orderByFields;

    public function __construct()
    {
        $this->middleware('auth');
        $this->rules = MailingList::$rules;
        $this->paginate = 10;
        $this->orderByFields = ['name', 'created_at', 'updated_at'];
        $this->orderCriteria = ['asc', 'desc'];
    }

    /**
     * Display a listing of the resource.
     * @param Request $request
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|mixed
     */
    public function index(Request $request)
    {
        if ( ! $request->ajax() )
            return view('mailing_list_index');
        else {
            $orderBy = in_array(strtolower($request->orderBy), $this->orderByFields) ? strtolower($request->orderBy) : 'created_at';
            $order = in_array(strtolower($request->order), $this->orderCriteria) ? strtolower($request->order) : 'desc';
            $paginate = (int) $request->perPage ?: $this->paginate;

            return MailingList::getMailingLists($orderBy, $order, $paginate);
        }
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     * @param Request $request
     * @return MailingList
     */
    public function store(Request $request)
    {
        $this->validate($request, $this->rules);

        $mList = new MailingList();
        $mList->name = trim($request->name);
        $mList->save();

        return $mList;
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

You can see we have set up a few defaults and also some advanced functionalities like ordering which we won't cover in this post. The default order is created_at date - although you might have noticed that we have our own order (name) inside the Vue.Js component. In the index method, we first of all check to see where the request is coming from. This is because the same method is used to display the Laravel view where the frontend code would be loaded and also to fetch the mailing lists (which is done by Vue.Js). So the $request->ajax()method comes in handy. We also check to see whether any paginations/order parameters were set in the AJAX request and respond appropriately. In the store method, we do a bit of validation and then store the new mailing list and return it in the response.

We finally extend our MailingList model with code that will return the mailing lists:

/**
 * Get mailing list resource
 * @param string $orderBy
 * @param string $order
 * @param int $paginate
 * @return mixed
 */
public static function getMailingLists($orderBy = 'created_at', $order = 'desc', $paginate = 10)
{
    return static::with('subscribers')->orderBy($orderBy, $order)->paginate($paginate);
}

With that, we are ready to fire up our mailing list CRUD app!
When you add a new mailing list, it is checked then saved in the database and then added to the list on the frontend all within 1 second! Cucumber!

 

Comments