This article is part of the CakeDC Advent Calendar 2024 (December 6th 2024) Vite can be easily integrated to manage assets such as JavaScript, CSS, images, and fonts. This integration is particularly useful in modern applications where the frontend and backend work together. Advantages of Using Vite with CakePHP
- Development Optimization: HMR allows developers to see changes instantly in the browser, improving the development experience. There's no need to refresh the page constantly to view updates.
- Efficient Bundling: Production assets are minimized and optimized, reducing loading times.
- Modern Technology Adoption: It enables the integration of modern frontend tools like Vue.js, React, or TypeScript into traditional CakePHP projects.
- Applications with Dynamic Frontends: Ideal for CakePHP projects where the frontend requires interactive components built with React or Vue.js.
- Hybrid Applications: Integration of a SPA (Single Page Application) with a robust backend like CakePHP.
- Enterprise Applications: Management of dashboards with interactive charts and reports, leveraging modern frontend tools while using CakePHP for data handling and business logic.
1.- Create a new View:
src/View/ViteView.phpdeclare(strict_types=1);
namespace App\View;
use Cake\View\View;
class ViteView extends View
{
public function initialize(): void
{
$this->loadHelper('Vite');
}
}
2.- Create a new Trait
src/Traits/ViteResponseTrait.phpnamespace App\Traits;
use App\View\ViteView;
use Cake\Event\EventInterface;
trait ViteResponseTrait
{
public function beforeRender(EventInterface $event)
{
$this->viewBuilder()->setClassName(ViteView::class);
}
}
3.- Create a new Helper
src/View/Helper/ViteHelper.phpnamespace App\View\Helper;
use Cake\Routing\Router;
use Cake\View\Helper;
class ViteHelper extends Helper
{
public array $helpers = ['Html'];
public function loadAssets(): string
{
if (!file_exists(WWW_ROOT . 'hot')) {
$manifest = json_decode(
file_get_contents(WWW_ROOT . 'js' . DS . 'manifest.json'),
true
);
$path = Router::fullBaseUrl() . DS . 'js' . DS;
$firstBlock = [];
$secondBlock = [];
foreach($manifest as $key => $data){
$part = explode('.', $key);
$part = $part[count($part) - 1];
if ($part == 'css') {
$firstBlock[] = $this->Html->tag(
'link',
'',
['as' => 'style', 'rel' => 'preload', 'href' => $path . $data['file']]
);
$secondBlock[] = $this->Html->tag(
'link',
'',
['as' => 'style', 'rel' => 'stylesheet', 'href' => $path . $data['file']]
);
}
if ($part == 'js') {
$firstBlock[] = $this->Html->tag(
'link',
'',
['as' => 'style', 'rel' => 'preload', 'href' => $path . $data['css'][0]]
);
$secondBlock[] = $this->Html->tag(
'link',
'',
['as' => 'style', 'rel' => 'stylesheet', 'href' => $path . $data['css'][0]]
);
$firstBlock[] = $this->Html->tag(
'link',
'',
['rel' => 'modulepreload', 'href' => $path . $data['file']]
);
$secondBlock[] = $this->Html->tag(
'script',
'',
['type' => 'module', 'src' => $path . $data['file']]
);
}
}
return implode('', $firstBlock) . implode('', $secondBlock);
} else {
$domain = file_get_contents(WWW_ROOT . 'hot');
$head = $this->Html->script(
$domain . '/@vite/client',
['rel' => 'preload', 'type' => 'module']
);
$head .= $this->Html->css($domain . '/resources/css/app.css');
$head .= $this->Html->script(
$domain . '/resources/js/app.js',
['rel' => 'preload', 'type' => 'module']
);
return $head;
}
}
}
4.- Create function vite and add the trait to the controller
src/Controller/PagesController.phpuse App\Traits\ViteResponseTrait;
class PagesController extends AppController
{
use ViteResponseTrait;
public function vite()
{
$this->viewBuilder()->setLayout('vite');
}
}
5.- Add a new layout
templates/layout/vite.php6 .- Install and configure Vite (using DDEV)
on .ddev/config.yaml add this new configuration and run ddev restartweb_extra_exposed_ports:
- name: vite
container_port: 5173
http_port: 5172
https_port: 5173
create package.json
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"autoprefixer": "^10.4.20",
"laravel-vite-plugin": "^1.0.0",
"vite": "^5.0.0"
},
"dependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"vue": "^3.5.8",
"vuex": "^4.0.2"
}
}
create vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
const port = 5173;
const origin = `${process.env.DDEV_PRIMARY_URL}:${port}`;
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
publicDirectory: 'webroot',
refresh: true,
}),
vue(),
],
build: {
outDir: 'webroot/js'
},
server: {
host: '0.0.0.0',
port: port,
strictPort: true,
origin: origin
},
});
create .env
APP_NAME=cakePHP
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL="https://advent2024.ddev.site"
to install and configure all run in console: ddev npm install
7. Create an Example App
resources/js/components/ExampleComponent.vueimport { createApp } from 'vue';
import ExampleComponent from './components/ExampleComponent.vue';
import store from './store';
createApp(ExampleComponent).use(store).mount('#app');
resources/js/store.js
import {createStore} from 'vuex';
const store = createStore({
state: {
count: Number(localStorage.getItem('count')) || 0,
},
mutations: {
increment(state) {
state.count++;
localStorage.setItem('count', state.count);
},
decrement(state) {
state.count--;
localStorage.setItem('count', state.count);
},
},
actions: {
increment({ commit }) {
commit('increment');
},
decrement({ commit }) {
commit('decrement');
},
},
getters: {
getCount(state) {
return state.count;
},
},
});
8.- Launch
For development run Vite ddev npm run dev

