Aplicando lo aprendido en el “Curso de Single Page Applications en Laravel con Inertia y Vue.js”, pero ahora veremos cómo podemos empezar a usar todo esto con Symfony y Vue.
Utilizaremos el adaptador para Symfony de Inertia que se recomienda en la página web de InertiaJS.
Puedes ver más detalles de los cambios en https://github.com/AngelFQC/notes-inertia-demo/commit/066877ac3f8d0430fef509dbfb3ddc26503c64e5
Empezamos agregando las dependencias iniciales con composer y yarn.
$ symfony new notes-inertia --full
$ cd notes-inertia/
$ composer require encore
$ composer require rompetomp/inertia-bundle
$ yarn install
$ yarn add vue vue-loader@15.9.5 vue-template-compiler --dev
$ yarn add sass-loader sass --dev
$ yarn add @inertiajs/inertia @inertiajs/inertia-vue
Editamos webpack.config.js
para habilitar lo necesario.
const Encore = require('@symfony/webpack-encore');
const path = require('path');
// Manually configure the runtime environment if not already configured yet by the "encore" command.// It's useful when you use tools that rely on webpack.config.js file.if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
.enableVueLoader()
.addAliases({
vue$: 'vue/dist/vue.runtime.esm.js',
'@': path.resolve('assets/vue')
})
.addEntry('app', './assets/app.js')
.enableStimulusBridge('./assets/controllers.json')
.splitEntryChunks()
.enableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.configureBabel((config) => {
config.plugins.push('@babel/plugin-proposal-class-properties');
})
// enables @babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
// enables Sass/SCSS support
.enableSassLoader()
;
module.exports = Encore.getWebpackConfig();
Editamos el archivo template en templates/base.html.twig
<!DOCTYPE html><html><head><metacharset="UTF-8"><title>{% block title %}Welcome!{% endblock %}</title>{% block stylesheets %}{{ encore_entry_link_tags('app') }}{% endblock %}</head><body>{{ inertia(page) }}{% block javascripts %}{{ encore_entry_script_tags('app') }}{% endblock %}</body></html>
Editamos assets/app.js
import'./styles/app.css';
import Vue from'vue'import { createInertiaApp } from'@inertiajs/inertia-vue'
createInertiaApp({
resolve: name => require(`./vue/Pages/${name}`),
setup({ el, app, props }) {
new Vue({
render: h => h(app, props),
}).$mount(el)
},
})
Puedes ver más detalles de los cambios en https://github.com/AngelFQC/notes-inertia-demo/commit/7996c9d571bdc158a767a159cd5cee0cb95802ca
Creamos credenciales de base de datos
GRANT ALL PRIVILEGESON inertianotes.* TO inertianotes@localhost IDENTIFIEDBY'inertianotes';
FLUSHPRIVILEGES;
Editamos .env
y agregar la línea de conexión
DATABASE_URL="mysql://inertianotes:[email protected]:3306/inertianotes?serverVersion=mariadb-10.5.10"
Creamos una base de datos con Doctrine
$ php bin/consoledoctrine:database:create
Puedes ver más detalles de los cambios en https://github.com/AngelFQC/notes-inertia-demo/commit/98481506854e39ab90ed092b03c706eaf334a0d4
Controladores de Symfony con la opción --no-template
para que no nos cree el archhivo template de Twig.
$ php bin/console make:controller IndexController --no-template
$ php bin/console make:controller PageController --no-template
$ php bin/console make:controller NoteController --no-template
Symfony asume que vamos a usar Twig para renderizar nuestros templates.
Pero aquí vamos a necesitar renderizar con Inertia y el inertia-bundle que estamos usando.
Por lo que utilizaremos una clase base para los controladores que se encargue que llamar a Inertia.
Creamos BaseController inicializando una instancia de InertiaInterface que utilizaremos
en los contradoles que hereden de esta clase.
<?phpnamespaceApp\Controller;
useRompetomp\InertiaBundle\Service\InertiaInterface;
useSymfony\Bundle\FrameworkBundle\Controller\AbstractController;
abstractclassBaseControllerextendsAbstractController{
protected InertiaInterface $inertia;
publicfunction__construct(InertiaInterface $inertia){
$this->inertia = $inertia;
}
}
IndexController y un método index
que retorna una respuesta de InertiaInterface::render
.
<?phpnamespaceApp\Controller;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\Annotation\Route;
classIndexControllerextendsBaseController{
#[Route('/', name: 'index')]publicfunctionindex(): Response{
return $this->inertia->render('Index');
}
}
Componente Vue para la página Index en assets/vue/Pages/Index.vue
<template><div>Index</div></template><script>exportdefault {
name: "Index"
}
</script>
Puedes ver más detalles de los cambios en https://github.com/AngelFQC/notes-inertia-demo/commit/4522b9046f3c487daa8093fe3495ca7a3ed88e52
Creamos una entidad Note que almacenará las notas.
Esta entidad tendrá un contenido (content) y un extracto (excerpt).
$ php bin/consolemake:entity Note
$ php bin/console make:migration
$ php bin/console doctrine:migrations:migrate
Modificamos el controlador NoteController para poder listar las notas.
namespace App\Controller;
use App\Entity\Note;
+use App\Repository\NoteRepository;+use Rompetomp\InertiaBundle\Service\InertiaInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/note')]
class NoteController extends BaseController
{
+ private NoteRepository $noteRepository;++ public function __construct(InertiaInterface $inertia, NoteRepository $noteRepository)+ {+ parent::__construct($inertia);++ $this->noteRepository = $noteRepository;+ }+
#[Route('/', name: 'note')]
public function index(): Response
{
- $noteRepo = $this->getDoctrine()->getRepository(Note::class);-- $notes = $noteRepo->findAll();+ $notes = $this->noteRepository->findAll();
return $this->inertia->render(
Modificamos el componente Vue en Note/Index para permitir renderizar la lista de notas.
- <div>Note/Index</div>+ <app-layout>+ <template #header>+ <h1>Módulo de notas</h1>+ </template>++ <h3>Listado de notas</h3>++ <p>Toma el registro correcto y ejecuta cualquier función (ver, editar o eliminar)</p>++ <table>+ <thead>+ <tr>+ <th>Extracto</th>+ </tr>+ </thead>+ <tbody>+ <tr v-for="note in notes">+ <td>{{ note.excerpt }}</td>+ </tr>+ </tbody>+ </table>+ </app-layout>
</template>
<script>
+import AppLayout from "../../Layout/AppLayout";+
export default {
- name: "Index"+ name: "Index",+ components: {AppLayout},+ props: {+ notes: Array,+ }
}
Y con esto podemos entrar a la ruta https://localhost:8000/note
y ver la página con el componente Vue renderizado.
$ yarn encore dev
$ symfony server:start -d
Puedes ver más detalles de los cambios en https://github.com/AngelFQC/notes-inertia-demo/commit/502c4b166e92d2946234341786e4a7a5ed72bd89
Necesitaremos una forma para tener en JS las rutas definidas en los controladores con PHP.
Entonces agregamos FOSJsRoutingBundle como dependencia con Composer
y seguir las instrucciones de instalación según documentación en
https://symfony.com/doc/current/bundles/FOSJsRoutingBundle/installation.html.
$ composer require friendsofsymfony/jsrouting-bundle
Editamos controllers para exponerlos al router:
IndexController
public function index(): Response
{
- return $this->inertia->render('Index');+ return $this->redirectToRoute('dashboard');
}
}
NoteController
use Symfony\Component\Routing\Annotation\Route;
-#[Route('/note')]+#[Route('/note', options: ['expose' => true])]
class NoteController extends BaseController
{
private NoteRepository $noteRepository;
PageController
class PageController extends BaseController
{
- #[Route('/dashboard', name: 'dashboard')]+ #[Route('/dashboard', name: 'dashboard', options: ['expose' => true])]
public function dashboard(): Response
{
return $this->inertia->render('Dashboard');
Luego, generamos el archivo fos_js_router.json
que contendrá con la configuración con las rutas pero en JS.
Por defecto lo genera en una carpeta /web
pero ahora lo necesitamos dentro de /public/js
.
$ php bin/console fos:js-routing:dump --format=json --target=public/js/fos_js_routes.json
También necesitaremos que se cargue el router y las rutas de fosjsrouting.
Modificamos el template base.html.twig
.
{% endblock %}
+ <script src="{{ asset('bundles/fosjsrouting/js/router.min.js') }}"></script>+ <script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script>+ <script>+ // the world's tiniest adapter to handle tighten/ziggy's 'route' function calls with FOSJSRoutingBundle+ window.route = function(name, params) {+ if (typeof(params) === 'number') {+ params = { 'id': params }+ }+ return Routing.generate(name, params)+ }+ </script>
</head>
Aquí, la función route
trata de imitar un poco a la misma función que se usa en Laravel para generar rutas con Vue.
Entonces para que podamos usarla dentro de componentes Vue, la agregar como mixin.
import { createInertiaApp } from '@inertiajs/inertia-vue'
+Vue.mixin({ methods: { route: window.route } })+
createInertiaApp({
Por último, necesitamos agregar los enlaces a modo de menú de navegación para que se carguen con Inertia.
Utilizaremos <inertia-link>
.
AppLayout.vue
<div>
+ <nav>+ <ul>+ <li>+ <inertia-link :href="route('dashboard')">Dashboard</inertia-link>+ </li>+ <li>+ <inertia-link :href="route('note')">Notas</inertia-link>+ </li>+ </ul>+ </nav>
<header>
Dashboard.vue
<template>
- <div>- Dashboard- </div>+ <app-layout>+ <template #header>+ Dashboard+ </template>+++ </app-layout>
</template>
<script>
+import AppLayout from "../Layout/AppLayout";+
export default {
- name: "Dashboard"+ name: "Dashboard",+ components: {AppLayout}
}
Excelente tutorial lo pondre en practica.