Vue.js : Best way to implement MPA(Multi page app) in laravel
Asked Answered
S

2

24

I have been looking around for quite a time, But didn't got anything convening.

What will be the best approach and practice to implement Vue MPA architecture in laravel.

Searched for quite a bit. But there isn't anything which will give you a clear idea. Your answer will help alot, Please make it brief.

It will also be helpful to answer the point :

  • Is it a good idea to use just laravel as a data API, And keep Vue separate from laravel ?
  • Best approach for implementing hybrid of SPA and MPA.
Suffer answered 13/1, 2018 at 14:0 Comment(0)
E
32

Some options that I've already used:

Use Laravel to render the "main view" + connect vue.js application.

Basically laravel will render the Vue application and every request goes throught an API.

  1. Easy to setup
  2. Authentication + user validation is easier (you can use laravel session manager for that - don't need to build/use tokens or whatever. "No need to worry about your application state".)
  3. Easy to "disconnect" from Laravel - if you choose in the future to decouple the SPA application.

Use laravel (or lumen) only as an API, and on another server render a SPA.

This can take more time, since you'll need to setup an extra server, prepare cross-origin, etc.

  1. Also easy to setup, but can take more time than option #1
  2. You'll need to create something for user validation/state management, etc.
  3. Easy to place into laravel, if you decide in the future to use "only one app".
  4. Can be easier to maintain/scale (if you have a frontend team, they don't need to worry about laravel - same for your "laravel team", they "won't need to worry" about the frontend)

Laravel + Vue = "one application"

You can use Laravel to render all views + vuejs for components/elements in the page.

  1. Easy to setup. You have laravel + vuejs, and they are already prepared to be used together. https://laravel.com/docs/5.5/frontend#writing-vue-components
  2. Not so easy to decouple. In this case you'll need to create the same views for vue.js. This can take time.
  3. This is the "traditional web development" (in my opinion). If I had to start a project like this today, I wouldn't create all pages in Vue.js + something in Laravel (Controller + new route) to render this view. If you do this (again - my opinion), it's just extra work. If you are worried about SEO, there are "fallbacks"/extra options.

--

All options are testable + scalable.

It also depends on how you start (Should I worry about how I'll decouple the app in the future? Laravel + Vue will be for good?), how your team will work (Does the frontend team really needs to setup laravel or they only need to worry about the frontend code?), etc.

Not sure if i answered your question, if not, please leave a comment.

Elana answered 16/1, 2018 at 9:38 Comment(2)
Great answer, Its well written, But how to implement the MPA again, If you look at the first answer, That's somehow answering a bit. Please explain the technical side a bit. ThanksSuffer
Can you explain whether 1st option is better or 3rd option for my use case? I am rebuilding SaaS legacy application. Current system uses cakephp and jQuery (heavy jQuery dependency) and I am very good at laravel but just familier with Vue.jsPsychokinesis
L
14

You haven't found anything clear because there isn't really anything to talk about other than 'What feels right to your understanding and project needs'. If you found yourself very unsure, feel free to dive into doing whatever makes sense to you and then re-adjust the structure when you gain more experience.

Also, read books about system architecture, those will help a lot.


Is it a good idea to use just laravel as a data API, And keep Vue separate from Laravel?

By this, I'm assuming you mean a SPA? Honestly, if your application is small, then I see this is fine.

Larger applications tend to be difficult to maintain if they were SPA.

Read: https://medium.com/@NeotericEU/single-page-application-vs-multiple-page-application-2591588efe58

If you end up using Laravel as an API endpoint, then use the stripped down version of it, Lumen, because it comes without Blade and few other stuff. Lumen is stripped down version to act as an API-endpoint.


Best approach for implementing hybrid of SPA and MPA.

From my experience having attempted to build 4+ projects as hybrids, here's what I found the most optimal structure:

My example will be about an app that saves 'Posts'.

1. Use a repository design pattern.

This one will save you a lot of headache in maintaining your code and maintain a DRY (Don't Repeat Yourself) concept on your code.

  • Create a directory App\Repositories\

Make a new class PostsRepository. This one will be the one communicating with the database and contains most of the logic.

  • Create the directory App\Services\

Make a new class PostsService. This one will have the PostsRepository in its constructor.

The service class will be one handling taking user's input whether from the Web controller or the API controller.

<?php

namespace App\Service;

use App\Repositories\PostsRepository;

class PostsService;
{
    protected $repository;

    public function __construct(PostsRepository $repository)
    {
        $this->repository = $repository;
    }
}
  • Make a seperation between Web and API controllers.

For web controllers, you create the controller like usual:

php artisan make:controller PostsController

For API controllers, you create the controller to be inside an Api folder.

php artisan make:controller Api\PostsController

The last command will create the directory App\Http\Controllers\Api and have the controller be placed in it.

Recapping

Now we have different controllers to return results appropriate to the startpoint (web / api).

We have services that both the (web / api) controllers send their data to be validated (and have the action taken by the repository).

Examples:

<?php

namespace App\Http\Controllers;

use App\Service\PostsService;

class PostsController extends Controller
{
  protected $service;

  public function __construct(PostsService $service)
  {
      $this->service = $service;
  }

  public function index()
  {
     /**
     * Instead of returning a Blade view and
     * sending the data to it like:
     *
     *          $posts = $this->service->all();
     *          return views('posts.index', compact('posts'));
     *
     * We go ahead and just return the boilerplate of 
     * our posts page (Blade).
     */
     return view('posts.index');
  }
}

...

<?php

namespace App\Http\Controllers\Api;

use App\Service\PostsService;

class PostsController extends Controller
{
  protected $service;

  public function __construct(PostsService $service)
  {
      $this->service = $service;
  }

  /**
  * Returns all posts.
  *
  * A vue component calls this action via a route.
  */
  public function index()
  {
     $posts = $this->service->all();

     return $posts;
  }

  /**
  * Notice we don't have a store() in our
  * Web controller.
  */
  public function store()
  {
     return $this->service->store();
  }
}

...

<?php

namespace App\Services;

use App\Repositories\PostsRepository;

class PostsService extends Controller
{
  protected $repository;

  public function __construct(PostsRepository $repository)
  {
      $this->repository = $repository;
  }

  public function all()
  {
     $posts = $this->repository->all();

     return $posts;
  }

  public function store()
  {
     $request = request()->except('_token');

     $this->validation($request)->validate();

     return $this->repository->store($request);
  }

  public function validation(array $data)
  {
      return Validator::make($data, [
          'content' => 'required|string|max:255',
          //
      ]);
  }
}

In our PostsRepository we actually call methods that save the data. E.g. Post::insert($request);.

2. Dedicate an API group

Route::prefix('api/v1')->middleware('auth')->group(function() {

    Route::post('posts/store', 'Api\PostsController@store')->name('api.posts.store');

});

Giving API routes a ->name() helps when you make phpunit tests.

3. Blade views

Those are ought to be stripped-down simple.

views/posts/index.blade.php:

@extends('layouts.app', ['title' => trans('words.posts')])

@section('content')
  <!-- Your usual grid columns and stuff -->
  <div class="columns">
     <div class="column is-6">
         <!-- This comp. can have a modal included. -->
         <new-post-button></new-post-button>
     <div class="column is-6">
          <posts-index-page></posts-index-page>
     </div>
  </div>
@endsection

4. Vue structure.

https://github.com/pablohpsilva/vuejs-component-style-guide

So those Vue components might live in resources/assets/js/components/posts/ where inside /posts/ I'd have folders titled for example IndexPage, CreateModal, EditModal with each folder having its .vue and README.md.

I'd use the <posts-index-page> in index.blade.php and drop in the <post-create-modal> and <edit-post-modal> whenever I want.

All the vue components will use the API endpoint we specified in our Routes file.

Lick answered 13/1, 2018 at 17:35 Comment(6)
What is the README.md for?Sethrida
@commonsense README.md is to write your own README.md tbh. You specify the purpose of the component, the props it takes and their kinds (if any), how to use it... etc. Pretty much maintaining your own code for your own sake.Lick
@Spacemudd much better.Suffer
What about the edit/update functionality? How do you pass the post_id parameter on your <edit-post-page> component (Assuming you're not using Modal). Are you going to accept :post_id prop on the component that came from App\Http\Controllers\PostController@edit?Trombley
@ChrisLandeza If <edit-post-page> is simple, then yes, I would pass a :post_id prop. However, in more complicated situation where multiple components will require the :post_id, I prefer to work with Vuex to manage my state where I can have my state's post_id accessible from any component without passing props.Lick
This should be in the official documentation!Condescendence

© 2022 - 2024 — McMap. All rights reserved.