Install the Package
composer require alxishin/sonata-vue
Ensure your project meets the requirements (PHP 8.1+, Symfony 4.4/5.0/6.0, SonataAdminBundle 4.12+).
Enable the Bundle
Add to config/bundles.php:
return [
// ...
Alxishin\SonataVueBundle\SonataVueBundle::class => ['all' => true],
];
Configure Vue Integration
Update config/packages/sonata_admin.yaml to include Vue-specific settings:
sonata_admin:
vue:
enabled: true
# Optional: Custom Vue build path (if not using default)
build_path: 'assets/vue/build.js'
First Use Case: Basic Admin Page with Vue Extend a Sonata admin class to enable Vue reactivity:
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
class PostAdmin extends AbstractAdmin
{
protected function configureDatagridFilters(DatagridMapper $filter): void
{
$filter->add('title');
}
protected function configureFormFields(FormMapper $form): void
{
$form->add('title');
}
protected function configureShowFields(ShowMapper $show): void
{
$show->add('title');
}
// Enable Vue for this admin
public function getVueOptions(): array
{
return [
'reactive_fields' => ['title'], // Fields to make reactive
'custom_components' => [
'post_editor' => '@SonataVueBundle/Component/post_editor.html.twig',
],
];
}
}
Verify Vue Integration
Check the generated admin page (e.g., /admin/app/post). Open browser dev tools (F12) and confirm Vue is loaded and reactive fields are bound.
Use getVueOptions() to bind Sonata form fields to Vue:
public function getVueOptions(): array
{
return [
'reactive_fields' => ['price', 'stock'], // Sync with Vue
'watchers' => [
'price' => 'updateStockPrice', // Call JS method on change
],
];
}
Vue Component Example (resources/assets/vue/components/StockManager.vue):
<template>
<div>
<input v-model="price" @change="updateStockPrice">
<p>Stock Price: {{ computedPrice }}</p>
</div>
</template>
<script>
export default {
data() {
return {
price: this.$options.data.price,
};
},
computed: {
computedPrice() {
return this.price * 1.1; // Example logic
},
},
methods: {
updateStockPrice() {
// Call Symfony controller via API
axios.post('/api/stock/update', { price: this.price });
},
},
};
</script>
Create reusable Vue components for Sonata admins:
templates/components/my_component.html.twig):
<div id="my-component" data-sonata-vue-component="true">
<input v-model="value" @input="updateValue">
</div>
getVueOptions():
public function getVueOptions(): array
{
return [
'custom_components' => [
'my_component' => '@SonataVueBundle/Component/my_component.html.twig',
],
];
}
{{ sonata_vue_component('my_component', { value: entity.someField }) }}
Fetch data via Symfony’s API platform or custom controllers:
<script>
export default {
mounted() {
this.fetchData();
},
methods: {
fetchData() {
axios.get('/api/posts')
.then(response => {
this.posts = response.data;
});
},
},
};
</script>
Override the datagrid to use Vue for filtering/sorting:
public function getVueOptions(): array
{
return [
'datagrid_vue' => true,
'datagrid_filters' => [
'custom_filter' => [
'type' => 'text',
'label' => 'Custom Filter',
],
],
];
}
Vue Filter Component (resources/assets/vue/datagrid_filters.js):
Vue.component('custom-filter', {
template: `
<input v-model="value" @input="applyFilter">
`,
methods: {
applyFilter() {
this.$emit('filter', this.value);
},
},
});
Pass Data to Vue: Use Twig variables in templates:
{{ sonata_vue_init({
'initialData': {
'posts': app.posts,
'user': app.user,
}
}) }}
Access in Vue:
data() {
return {
posts: this.$options.data.initialData.posts,
};
}
Call Symfony Controllers: Use Symfony’s router in Vue:
const route = this.$options.data.routes.update;
axios.post(route, { id: 1, title: 'New Title' });
Pass routes via Twig:
{{ sonata_vue_init({
'routes': {
'update': path('admin_post_update', { id: entity.id })
}
}) }}
// webpack.config.js
Encore
.enableVueLoader()
.addEntry('sonata-vue', './assets/vue/sonata-vue.js')
.setOutputPath('public/build/')
.setPublicPath('/build');
Update SonataVueBundle config:
sonata_admin:
vue:
build_path: '/build/sonata-vue.js'
axios.defaults.headers.common['X-CSRF-Token'] = document.querySelector('meta[name="csrf-token"]').content;
Add to Twig:
<meta name="csrf-token" content="{{ csrf_token() }}">
Vue Version Mismatch
Template Caching
php bin/console cache:clear
Reactive Fields Not Updating
reactive_fields are correctly mapped and use @change events:
<input v-model="formData.title" @change="submitForm">
CSRF Token Missing
Symfony Router Not Available
path() function fails in Vue.{{ sonata_vue_init({
'routes': {
'update': path('admin_post_update', { id: entity.id })
}
}) }}
Check Vue DevTools
Log Symfony-Vue Data
{{ dump(sonata_vue_init_data) }}
Verify Webpack Build
console.log(Vue); // Should show Vue object
Clear Cache Aggressively
php bin/console cache:clear --env=prod --no-debug
How can I help you explore Laravel packages today?