Skip to content

Commit 8ee22a4

Browse files
committed
BelongsToManyField to represent many to many relationship (nova) in field
0 parents  commit 8ee22a4

15 files changed

+262
-0
lines changed

.gitignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/.idea
2+
/vendor
3+
/node_modules
4+
package-lock.json
5+
composer.phar
6+
composer.lock
7+
phpunit.xml
8+
.phpunit.result.cache
9+
.DS_Store
10+
Thumbs.db

composer.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "benjacho/BelongsToManyField",
3+
"description": "belongsToMany nova representation in field.",
4+
"keywords": [
5+
"laravel",
6+
"nova"
7+
],
8+
"license": "MIT",
9+
"require": {
10+
"php": ">=7.1.0"
11+
},
12+
"autoload": {
13+
"psr-4": {
14+
"Benjacho\\BelongsToManyField\\": "src/"
15+
}
16+
},
17+
"extra": {
18+
"laravel": {
19+
"providers": [
20+
"Benjacho\\BelongsToManyField\\FieldServiceProvider"
21+
]
22+
}
23+
},
24+
"config": {
25+
"sort-packages": true
26+
},
27+
"minimum-stability": "dev",
28+
"prefer-stable": true
29+
}

dist/css/field.css

Whitespace-only changes.

dist/js/field.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mix-manifest.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"/js/field.js": "/js/field.js",
3+
"/css/field.css": "/css/field.css"
4+
}

package.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"private": true,
3+
"scripts": {
4+
"dev": "npm run development",
5+
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
6+
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
7+
"watch-poll": "npm run watch -- --watch-poll",
8+
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
9+
"prod": "npm run production",
10+
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
11+
},
12+
"devDependencies": {
13+
"cross-env": "^5.0.0",
14+
"laravel-mix": "^1.0",
15+
"laravel-nova": "^1.0"
16+
},
17+
"dependencies": {
18+
"vue": "^2.5.0",
19+
"vue-multiselect": "^2.1.3"
20+
}
21+
}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<template>
2+
<panel-item :field="field" />
3+
</template>
4+
5+
<script>
6+
export default {
7+
props: ['resource', 'resourceName', 'resourceId', 'field'],
8+
}
9+
</script>

resources/js/components/FormField.vue

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<template>
2+
<default-field :field="field" :errors="errors">
3+
<template slot="field">
4+
<multi-select
5+
:options="options"
6+
:multiple="true"
7+
label="name"
8+
track-by="name"
9+
:class="errorClasses"
10+
:placeholder="field.name"
11+
v-model="value"
12+
/>
13+
</template>
14+
</default-field>
15+
</template>
16+
17+
<script>
18+
import { FormField, HandlesValidationErrors } from 'laravel-nova';
19+
import MultiSelect from 'vue-multiselect';
20+
21+
export default {
22+
mixins: [FormField, HandlesValidationErrors],
23+
24+
props: ['resourceName', 'resourceId', 'field'],
25+
26+
components: {
27+
MultiSelect
28+
},
29+
30+
data(){
31+
return {
32+
options: [],
33+
}
34+
},
35+
36+
methods: {
37+
/*
38+
* Set the initial, internal value for the field.
39+
*/
40+
setInitialValue() {
41+
this.options = this.field.options;
42+
this.value = this.field.value || ''
43+
},
44+
45+
/**
46+
* Fill the given FormData object with the field's internal value.
47+
*/
48+
fill(formData) {
49+
formData.append(this.field.attribute, JSON.stringify(this.value) || '')
50+
},
51+
52+
/**
53+
* Update the field's internal value.
54+
*/
55+
handleChange(value) {
56+
this.value = value
57+
},
58+
},
59+
}
60+
</script>
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<template>
2+
<span>{{ field.value }}</span>
3+
</template>
4+
5+
<script>
6+
export default {
7+
props: ['resourceName', 'field'],
8+
}
9+
</script>

resources/js/field.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Nova.booting((Vue, router, store) => {
2+
Vue.component('index-BelongsToManyField', require('./components/IndexField'))
3+
Vue.component('detail-BelongsToManyField', require('./components/DetailField'))
4+
Vue.component('form-BelongsToManyField', require('./components/FormField'))
5+
})

resources/sass/field.scss

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Nova Tool CSS

src/BelongsToManyField.php

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Benjacho\BelongsToManyField;
4+
5+
use Laravel\Nova\Fields\Field;
6+
use Laravel\Nova\Http\Requests\NovaRequest;
7+
8+
class BelongsToManyField extends Field
9+
{
10+
/**
11+
* The field's component.
12+
*
13+
* @var string
14+
*/
15+
public $component = 'BelongsToManyField';
16+
17+
public $relationModel;
18+
19+
public function options($options)
20+
{
21+
$options = collect($options);
22+
return $this->withMeta(['options' => $options]);
23+
}
24+
25+
public function relationModel($model)
26+
{
27+
$this->relationModel = $model;
28+
return $this;
29+
}
30+
31+
public function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute)
32+
{
33+
$requestValue = json_decode($request[$requestAttribute]);
34+
$class = get_class($model);
35+
$class::saved(function ($model) use ($requestValue, $attribute) {
36+
$model->syncManyValues($requestValue, $attribute, $this->relationModel);
37+
});
38+
}
39+
40+
public function resolve($resource, $attribute = null)
41+
{
42+
parent::resolve($resource, $attribute);
43+
$value = json_decode($resource->{$this->attribute});
44+
if ($value) {
45+
$this->value = $value;
46+
}
47+
}
48+
}

src/FieldServiceProvider.php

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Benjacho\BelongsToManyField;
4+
5+
use Laravel\Nova\Nova;
6+
use Laravel\Nova\Events\ServingNova;
7+
use Illuminate\Support\ServiceProvider;
8+
9+
class FieldServiceProvider extends ServiceProvider
10+
{
11+
/**
12+
* Bootstrap any application services.
13+
*
14+
* @return void
15+
*/
16+
public function boot()
17+
{
18+
Nova::serving(function (ServingNova $event) {
19+
Nova::script('BelongsToManyField', __DIR__.'/../dist/js/field.js');
20+
Nova::style('BelongsToManyField', __DIR__.'/../dist/css/field.css');
21+
});
22+
}
23+
24+
/**
25+
* Register any application services.
26+
*
27+
* @return void
28+
*/
29+
public function register()
30+
{
31+
//
32+
}
33+
}

src/HasBelongsToMany.php

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: benjamin
5+
* Date: 2/6/19
6+
* Time: 5:10 PM
7+
*/
8+
9+
namespace Benjacho\BelongsToManyField;
10+
11+
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
12+
13+
trait HasBelongsToMany
14+
{
15+
16+
public function model($relationModel): BelongsToMany
17+
{
18+
$model = app($relationModel);
19+
return $this->belongsToMany($model);
20+
}
21+
22+
public function syncManyValues($values, $attribute, $relationModel)
23+
{
24+
$arrayIds = array_column($values, 'id');
25+
$this->model($relationModel)->sync($arrayIds);
26+
}
27+
}

webpack.mix.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let mix = require('laravel-mix')
2+
3+
mix.setPublicPath('dist')
4+
.js('resources/js/field.js', 'js')
5+
.sass('resources/sass/field.scss', 'css')

0 commit comments

Comments
 (0)