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.