fuente original:
https://appdividend.com/2018/11/17/vue-laravel-crud-example-tutorial-from-scratch
parte 1
creamos la aplicacion
laravel new vue_laravel_crud
(ya te crea el .env con la key)
en linux dar permisos a la carpeta del proyecto
chmod -R 777 vue_laravel_crud
vamos a la carpeta creada vue_laravel_crud
e instalamos las dependencias del frontend.
npm install
(en linux usar
sudo npm install)
se puede ahora ejecutar npm run dev para compilar los assets y guardarlos
o ejecutar
npm run watch para que los compile mientras se cree codigo nuevo o se modifique el existente
Parte 2
instalar vue-router y vue-axios
router para manejar el direccionamiento
axios para manejar las peticiones al servidor
npm install vue-router vue-axios --save
y configuramos estas librerias en
rosurces/js/app.js
import VueRouter from 'vue-router';
Vue.use(VueRouter);
import VueAxios from 'vue-axios';
import axios from 'axios';
Vue.use(VueAxios, axios);
const router = new VueRouter({ mode: 'history'});
const app = new Vue(Vue.util.extend({ router })).$mount('#app');
y ahora en resources/views/ creamos
post.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">
<link href="{{ mix('css/app.css') }}" type="text/css" rel="stylesheet" />
<meta name="csrf-token" value="{{ csrf_token() }}" />
</head>
<body>
<div id="app">
</div>
<script src="{{ mix('js/app.js') }}" type="text/javascript"></script>
</body>
</html>
ahora editamos routes/web.php
<?php
Route::get('/{any}', function () {
return view('post');
})->where('any', '.*');
para poder capturar cualquier ruta que se escriba en el browser
en el sector de id="app" vamos a cargar el component App.vue que contendra nuestro
router-view
si existe el router lo renderizara sino no. y quedara vacio.
resources/js/App.vue
<template>
<div class="container">
<transition name="fade">
<router-view></router-view>
</transition>
</div>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-active {
opacity: 0
}
</style>
<script>
export default{
mounted() {
console.log('App.vue mounted.')
}
}
</script>
editaremos app.js para crear las routes de los componentes importados y asignarselo a nuestro
objeto Vue de la aplicacion
import HomeComponent from './components/HomeComponent.vue';
import CreateComponent from './components/CreateComponent.vue';
import IndexComponent from './components/IndexComponent.vue';
import EditComponent from './components/EditComponent.vue';
const routes = [
{
name: 'home',
path: '/home',
component: HomeComponent
},
{
name: 'create',
path: '/create',
component: CreateComponent
},
{
name: 'posts',
path: '/posts',
component: IndexComponent
},
{
name: 'edit',
path: '/edit/:id',
component: EditComponent
}
];
import App from './App.vue';
const router = new VueRouter({ mode: 'history', routes: routes});
const app = new Vue(
Vue.util.extend({ router }, App)
).$mount('#app');
los componentes
hay que crearlos en resources/js/components/
por ejemplo el CreateComponent.vue seria
<template>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card card-default">
<div class="card-header">Create Component</div>
<div class="card-body">
I'm the Create Component component.
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('CreateComponent mounted.')
}
}
</script>
al App.vue
le vamos a agregar una cabecera para navegar
<template>
<div class="container">
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<ul class="navbar-nav">
<li class="nav-item">
<router-link to="/home" class="nav-link">Home</router-link>
</li>
<li class="nav-item">
<router-link to="/create" class="nav-link">Create Post</router-link>
</li>
<li class="nav-item">
<router-link to="/posts" class="nav-link">Posts</router-link>
</li>
</ul>
</nav><br />
<transition name="fade">
<router-view></router-view>
</transition>
</div>
</template>
Parte 3: Crear el Backend
Vamos a crear el modelo Post, el controlador y una collection de Post como resorce.
php artisan make:model Post -mc
-mc : crea el archivo de migracion y el controlador de un saque
para crear la collection PostCollection
php artisan make:resource PostCollection
editar en el archivo de migracion:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
en el modelo Post.php, indicar los campos que se pueden llenar masivamente:
protected $fillable = ['title', 'body'];
implementamos los metodos del controlador PostController.php
use Illuminate\Http\Request;
use App\Http\Resources\PostCollection;
use App\Post;
class PostController extends Controller
{
public function store(Request $request)
{
$post = new Post([
'title' => $request->get('title'),
'body' => $request->get('body')
]);
$post->save();
return response()->json('successfully added');
}
public function index()
{
return new PostCollection(Post::all());
}
public function edit($id)
{
$post = Post::find($id);
return response()->json($post);
}
public function update($id, Request $request)
{
$post = Post::find($id);
$post->update($request->all());
return response()->json('successfully updated');
}
public function delete($id)
{
$post = Post::find($id);
$post->delete();
return response()->json('successfully deleted');
}
}
Parte 4: Definir las routes en la api
en routes/api.php:
use Illuminate\Http\Request;
Route::post('/post/create', 'PostController@store');
Route::get('/post/edit/{id}', 'PostController@edit');
Route::post('/post/update/{id}', 'PostController@update');
Route::delete('/post/delete/{id}', 'PostController@delete');
Route::get('/posts', 'PostController@index');
Parte 5: Implementar los componentes de Creacion, Listado y actualizacion
notar el uso de axios para los request a nuestra api
CreateComponent.vue
<template>
<div>
<h1>Create A Post</h1>
<form @submit.prevent="addPost">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Post Title:</label>
<input type="text" class="form-control" v-model="post.title">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Post Body:</label>
<textarea class="form-control" v-model="post.body" rows="5"></textarea>
</div>
</div>
</div><br />
<div class="form-group">
<button class="btn btn-primary">Create</button>
</div>
</form>
</div>
</template>
<script>
export default {
mounted() {
console.log('CreateComponent mounted.')
},
data(){
return {
post:{}
}
},
methods: {
addPost(){
console.log(this.post);
let server = 'http://127.0.0.1:8001';
let uri = server + '/api/post/create';
this.axios.post(uri, this.post).then((response) => {
this.$router.push({name: 'posts'});
});
}
}
}
</script>
we have used the push() method to change the route programmatically.
IndexComponent.vue
<template>
<div>
<h1>Posts</h1>
<div class="row">
<div class="col-md-10"></div>
<div class="col-md-2">
<router-link :to="{ name: 'create' }" class="btn btn-primary">Create Post</router-link>
</div>
</div><br />
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Item Name</th>
<th>Item Body</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="post in posts" :key="post.id">
<td>{{ post.id }}</td>
<td>{{ post.title }}</td>
<td>{{ post.body }}</td>
<td><router-link :to="{name: 'edit', params: { id: post.id }}" class="btn btn-primary">Edit</router-link></td>
<td><button class="btn btn-danger" @click.prevent="deletePost(post.id)">Delete</button></td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
mounted() {
console.log('IndexComponent mounted.')
},
data() {
return {
posts: []
}
},
created() {
let server = 'http://127.0.0.1:8001';
let uri = server + '/api/posts';
this.axios.get(uri).then(response => {
this.posts = response.data.data;
});
},
methods: {
deletePost(id)
{
let server = 'http://127.0.0.1:8001';
let uri = `${server}/api/post/delete/${id}`;
this.axios.delete(uri).then(response => {
let i = this.posts.map(item => item.id).indexOf(id); // find index of your object
this.posts.splice(i, 1)
});
}
}
}
</script>
en el listado se colocará acciones por cada item: link de editar y boton de eliminar
si se elimina un item el listado se modifica sin que se recargue la pagina
EditComponent.vue
<template>
<div>
<h1>Edit Post</h1>
<form @submit.prevent="updatePost">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Post Title:</label>
<input type="text" class="form-control" v-model="post.title">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Post Body:</label>
<textarea class="form-control" v-model="post.body" rows="5"></textarea>
</div>
</div>
</div><br />
<div class="form-group">
<button class="btn btn-primary">Update</button>
</div>
</form>
</div>
</template>
<script>
export default {
mounted() {
console.log('EditComponent mounted.')
},
data() {
return {
post: {}
}
},
created() {
let server = 'http://127.0.0.1:8001';
let uri = `${server}/api/post/edit/${this.$route.params.id}`;
this.axios.get(uri).then((response) => {
this.post = response.data;
});
},
methods: {
updatePost() {
let server = 'http://127.0.0.1:8001';
let uri = `${server}/api/post/update/${this.$route.params.id}`;
this.axios.post(uri, this.post).then((response) => {
this.$router.push({name: 'posts'});
});
}
}
}
</script>
por un tema de organizacion de codigo, las routes se pueden definir en un arhivo e importarlo luego
en app.js.
entonces creamos en resorces/js/routes.js
import HomeComponent from './components/HomeComponent.vue';
import CreateComponent from './components/CreateComponent.vue';
import IndexComponent from './components/IndexComponent.vue';
import EditComponent from './components/EditComponent.vue';
const routes = [
{
name: 'home',
path: '/home',
component: HomeComponent
},
{
name: 'create',
path: '/create',
component: CreateComponent
},
{
name: 'posts',
path: '/posts',
component: IndexComponent
},
{
name: 'edit',
path: '/edit/:id',
component: EditComponent
}
];
export default routes;
y en app.js
import routes_file from './routes';
const router = new VueRouter({ mode: 'history', routes: routes_file});