laravel 5.8 crud con vue spa

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});

No hay comentarios:

Publicar un comentario

linux ubuntu mint actualizar chrome

 desde una terminal: $ sudo apt update $ sudo apt install google-chrome-stable