├── UPGRADE.md ├── CHANGELOG.md ├── composer.json ├── LICENSE.md ├── src ├── ManyToManyAttribute.php └── SyncManyToManyAttribute.php └── README.md /UPGRADE.md: -------------------------------------------------------------------------------- 1 | Upgrading Instructions for Sync Many-to-Many via Attribute 2 | ========================================================== 3 | 4 | !!!IMPORTANT!!! 5 | 6 | The following upgrading instructions are cumulative. That is, 7 | if you want to upgrade from version A to version C and there is 8 | version B between A and C, you need to following the instructions 9 | for both A and B. 10 | 11 | Upgrade from 1.0.1 12 | ------------------ 13 | 14 | * "illuminate/database" package requirements were raised to 6.0. Make sure to upgrade your code accordingly. 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Sync Many-to-Many via Attribute Change Log 2 | ========================================== 3 | 4 | 1.1.6, March 6, 2025 5 | -------------------- 6 | 7 | - Enh: Added support for "illuminate/database" 12.0 (klimov-paul) 8 | 9 | 10 | 1.1.5, March 25, 2024 11 | --------------------- 12 | 13 | - Enh: Added support for "illuminate/database" 11.0 (klimov-paul) 14 | 15 | 16 | 1.1.4, February 27, 2023 17 | ------------------------ 18 | 19 | - Enh: Added support for "illuminate/database" 10.0 (klimov-paul) 20 | 21 | 22 | 1.1.3, February 9, 2022 23 | ----------------------- 24 | 25 | - Enh: Added support for "illuminate/database" 9.0 (klimov-paul) 26 | 27 | 28 | 1.1.2, September 9, 2020 29 | ------------------------ 30 | 31 | - Enh: Added support for "illuminate/database" 8.0 (klimov-paul) 32 | 33 | 34 | 1.1.1, March 4, 2020 35 | -------------------- 36 | 37 | - Enh: Added support for "illuminate/database" 7.0 (klimov-paul) 38 | 39 | 40 | 1.1.0, September 6, 2019 41 | ------------------------ 42 | 43 | - Enh: Added support for "illuminate/database" 6.0 (klimov-paul) 44 | 45 | 46 | 1.0.0, February 12, 2019 47 | ------------------------ 48 | 49 | - Initial release. 50 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "illuminatech/sync-many-attribute", 3 | "description": "Allows control over Eloquent many-to-many relation via array attribute", 4 | "keywords": ["laravel", "eloquent", "many-to-many", "attribute", "sync"], 5 | "license": "BSD-3-Clause", 6 | "support": { 7 | "issues": "https://github.com/illuminatech/sync-many-attribute/issues", 8 | "wiki": "https://github.com/illuminatech/sync-many-attribute/wiki", 9 | "source": "https://github.com/illuminatech/sync-many-attribute" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Paul Klimov", 14 | "email": "klimov.paul@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "illuminate/database": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" 19 | }, 20 | "require-dev": { 21 | "illuminate/events": "*", 22 | "phpunit/phpunit": "^7.5 || ^8.0 || ^9.3 || ^10.5" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Illuminatech\\SyncManyAttribute\\": "src" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "Illuminatech\\SyncManyAttribute\\Test\\": "tests" 32 | } 33 | }, 34 | "extra": { 35 | "branch-alias": { 36 | "dev-master": "1.0.x-dev" 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free software. It is released under the terms of the 2 | following BSD License. 3 | 4 | Copyright © 2019 by Illuminatech (https://github.com/illuminatech) 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | * Neither the name of Illuminatech nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /src/ManyToManyAttribute.php: -------------------------------------------------------------------------------- 1 | 21 | * @since 1.0 22 | */ 23 | class ManyToManyAttribute 24 | { 25 | /** 26 | * @var string underlying many-to-many relation name. 27 | */ 28 | protected $relationName; 29 | 30 | /** 31 | * @var Closure|array|null pivot attributes to be applied at relation synchronization. 32 | */ 33 | protected $pivotAttributes = []; 34 | 35 | /** 36 | * Constructor. 37 | * 38 | * @param array|string|null $definition 39 | */ 40 | public function __construct($definition = null) 41 | { 42 | if ($definition === null) { 43 | return; 44 | } 45 | 46 | if (is_array($definition)) { 47 | if (count($definition) !== 1) { 48 | throw new InvalidArgumentException('Attribute definition must be refer to exact one relation.'); 49 | } 50 | 51 | $definitionKeys = array_keys($definition); 52 | 53 | $this->relationName(array_shift($definitionKeys)); 54 | $this->pivotAttributes(array_shift($definition)); 55 | 56 | return; 57 | } 58 | 59 | $this->relationName($definition); 60 | } 61 | 62 | /** 63 | * Sets relation name for this definition. 64 | * 65 | * @param string $relationName relation name. 66 | * @return static self reference. 67 | */ 68 | public function relationName(string $relationName): self 69 | { 70 | $this->relationName = $relationName; 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Sets pivot attributes to be applied at relation synchronization. 77 | * 78 | * @param Closure|array|null $pivotAttributes 79 | * @return static self reference. 80 | */ 81 | public function pivotAttributes($pivotAttributes): self 82 | { 83 | if ($pivotAttributes !== null && ! is_array($pivotAttributes) && ! $pivotAttributes instanceof Closure) { 84 | throw new InvalidArgumentException('"'.get_class($this).'::$pivotAttributes" must be null, array or Closure.'); 85 | } 86 | 87 | $this->pivotAttributes = $pivotAttributes; 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * Returns relation instance from given model. 94 | * 95 | * @param \Illuminate\Database\Eloquent\Model $model model instance to get relation from. 96 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|mixed relation instance. 97 | */ 98 | public function getRelation($model) 99 | { 100 | return $model->{$this->relationName}(); 101 | } 102 | 103 | /** 104 | * Get all of the IDs for the related models. 105 | * 106 | * @param \Illuminate\Database\Eloquent\Model $model 107 | * @return array all of the IDs for the related models. 108 | */ 109 | public function getRelatedIds($model): array 110 | { 111 | return $this->getRelation($model)->allRelatedIds()->toArray(); 112 | } 113 | 114 | /** 115 | * Synchronizes relation with a list of IDs. 116 | * 117 | * @param \Illuminate\Database\Eloquent\Model $model model to be synchronized. 118 | * @param array $ids list of IDs from related models. 119 | * @return array sync changes report. 120 | */ 121 | public function sync($model, array $ids) 122 | { 123 | $relation = $this->getRelation($model); 124 | 125 | if (empty($this->pivotAttributes)) { 126 | return $relation->sync($ids); 127 | } 128 | 129 | if ($this->pivotAttributes instanceof Closure) { 130 | $pivotAttributes = call_user_func($this->pivotAttributes, $model); 131 | } else { 132 | $pivotAttributes = array_map(function ($value) use ($model) { 133 | if (is_callable($value)) { 134 | return call_user_func($value, $model); 135 | } 136 | 137 | return $value; 138 | }, $this->pivotAttributes); 139 | } 140 | 141 | $ids = array_fill_keys($ids, $pivotAttributes); 142 | 143 | return $relation->sync($ids); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/SyncManyToManyAttribute.php: -------------------------------------------------------------------------------- 1 | 'categories', 36 | * 'tag_ids' => [ 37 | * 'tags' => [ 38 | * 'created_at' => function ($model) { 39 | * return now(); 40 | * } 41 | * ], 42 | * ], 43 | * 'article_ids' => (new ManyToManyAttribute) 44 | * ->relationName('articles') 45 | * ->pivotAttributes(['type' => 'help-content']), 46 | * ]; 47 | * } 48 | * 49 | * public function categories(): BelongsToMany 50 | * { 51 | * return $this->belongsToMany(Category::class); 52 | * } 53 | * 54 | * public function tags(): BelongsToMany 55 | * { 56 | * return $this->belongsToMany(Tag::class)->withPivot(['created_at']); 57 | * } 58 | * 59 | * public function articles(): BelongsToMany 60 | * { 61 | * return $this->belongsToMany(Article::class)->withPivot(['type']); 62 | * } 63 | * 64 | * // ... 65 | * } 66 | * ``` 67 | * 68 | * Usage example: 69 | * 70 | * ```php 71 | * $item = new Item(); 72 | * $item->category_ids = Category::query()->pluck('id')->toArray(); 73 | * // ... 74 | * $item->save(); // relation `Item::categories` synchronized automatically 75 | * 76 | * $item = $item->fresh(); 77 | * var_dump($item->category_ids); // outputs array of category IDs like `[1, 3, 8, ...]` 78 | * ``` 79 | * 80 | * @see ManyToManyAttribute 81 | * @see \Illuminate\Database\Eloquent\Relations\BelongsToMany 82 | * 83 | * @mixin \Illuminate\Database\Eloquent\Model 84 | * 85 | * @author Paul Klimov 86 | * @since 1.0 87 | */ 88 | trait SyncManyToManyAttribute 89 | { 90 | /** 91 | * @var array[] values of the attributes for many-to-many synchronization in format: `attributeName => [values]`. 92 | */ 93 | protected $syncManyToManyAttributes = []; 94 | 95 | /** 96 | * @var ManyToManyAttribute[] definitions of the attributes for many-to-many synchronization. 97 | */ 98 | private $syncManyToManyAttributeDefinitions = []; 99 | 100 | /** 101 | * Defines list of attributes for many-to-many synchronization. 102 | * Method should return an array, which keys are the names of attributes, and values are the names 103 | * of the matching `BelongsToMany` relation. 104 | * 105 | * For example: 106 | * 107 | * ```php 108 | * return [ 109 | * 'category_ids' => 'categories', 110 | * 'tag_ids' => [ 111 | * 'tags' => [ 112 | * 'created_at' => function ($model) { 113 | * return now(); 114 | * } 115 | * ], 116 | * ], 117 | * 'article_ids' => (new ManyToManyAttribute) 118 | * ->relationName('articles') 119 | * ->pivotAttributes(['type' => 'help-content']), 120 | * ]; 121 | * ``` 122 | * 123 | * @see ManyToManyAttribute 124 | * 125 | * @return array attribute definitions. 126 | */ 127 | abstract protected function syncManyToManyAttributes(): array; 128 | 129 | /** 130 | * Boots this trait in the scope of the owner model, attaching necessary event handlers. 131 | * @see \Illuminate\Database\Eloquent\Model::bootTraits() 132 | */ 133 | protected static function bootSyncManyToManyAttribute() 134 | { 135 | static::saved(function ($model) { 136 | /* @var $model \Illuminate\Database\Eloquent\Model|static */ 137 | $model->syncManyToManyFromAttributes(); 138 | }); 139 | } 140 | 141 | /** 142 | * Set a given attribute on the model. 143 | * @see \Illuminate\Database\Eloquent\Model::setAttribute() 144 | * 145 | * @param string $key attribute name. 146 | * @param mixed $value attribute value. 147 | * @return \Illuminate\Database\Eloquent\Model|static|mixed 148 | */ 149 | public function setAttribute($key, $value) 150 | { 151 | if ($this->hasSyncManyToManyAttribute($key)) { 152 | return $this->setSyncManyToManyAttribute($key, $value); 153 | } 154 | 155 | return parent::setAttribute($key, $value); 156 | } 157 | 158 | /** 159 | * Get an attribute from the model. 160 | * @see \Illuminate\Database\Eloquent\Model::getAttribute() 161 | * 162 | * @param string $key attribute name. 163 | * @return mixed attribute value. 164 | */ 165 | public function getAttribute($key) 166 | { 167 | if ($this->hasSyncManyToManyAttribute($key)) { 168 | return $this->getSyncManyToManyAttribute($key); 169 | } 170 | 171 | return parent::getAttribute($key); 172 | } 173 | 174 | /** 175 | * Checks whether particular attribute for many-to-many synchronization has been defined. 176 | * 177 | * @param string $key attribute name. 178 | * @return bool 179 | */ 180 | public function hasSyncManyToManyAttribute($key): bool 181 | { 182 | if (isset($this->syncManyToManyAttributes[$key])) { 183 | return true; 184 | } 185 | 186 | $definitions = $this->syncManyToManyAttributes(); 187 | 188 | return isset($definitions[$key]); 189 | } 190 | 191 | /** 192 | * Sets the value of the attribute for many-to-many synchronization. 193 | * 194 | * @param string $key attribute name. 195 | * @param array|mixed|null $value attribute value, it will be automatically casted to array. 196 | * @return \Illuminate\Database\Eloquent\Model|static self reference. 197 | */ 198 | public function setSyncManyToManyAttribute($key, $value): self 199 | { 200 | $this->syncManyToManyAttributes[$key] = Arr::wrap($value); 201 | 202 | return $this; 203 | } 204 | 205 | /** 206 | * Returns value of the attribute for many-to-many synchronization. 207 | * 208 | * @param string $key attribute name. 209 | * @return array attribute value. 210 | */ 211 | public function getSyncManyToManyAttribute($key): array 212 | { 213 | if (isset($this->syncManyToManyAttributes[$key])) { 214 | return $this->syncManyToManyAttributes[$key]; 215 | } 216 | 217 | $this->syncManyToManyAttributes[$key] = $this->getSyncManyToManyAttributeDefinition($key) 218 | ->getRelatedIds($this); 219 | 220 | return $this->syncManyToManyAttributes[$key]; 221 | } 222 | 223 | /** 224 | * Synchronizes many-to-many relations according to the current {@see $syncManyToManyAttributes} values. 225 | * Synchronization is performed only for attributes, which has been set explicitly. 226 | * 227 | * @return void 228 | */ 229 | private function syncManyToManyFromAttributes(): void 230 | { 231 | foreach ($this->syncManyToManyAttributes as $key => $values) { 232 | $this->getSyncManyToManyAttributeDefinition($key) 233 | ->sync($this, $values); 234 | 235 | unset($this->syncManyToManyAttributes[$key]); 236 | } 237 | } 238 | 239 | /** 240 | * Returns definition of the sync many-to-many attribute as object. 241 | * 242 | * @param string $key attribute name. 243 | * @return ManyToManyAttribute attribute definition. 244 | */ 245 | private function getSyncManyToManyAttributeDefinition($key): ManyToManyAttribute 246 | { 247 | if (! isset($this->syncManyToManyAttributeDefinitions[$key])) { 248 | $rawDefinitions = $this->syncManyToManyAttributes(); 249 | if (! isset($rawDefinitions[$key])) { 250 | throw new InvalidArgumentException("Undefined sync many-to-many attribute '{$key}'."); 251 | } 252 | 253 | $this->syncManyToManyAttributeDefinitions[$key] = new ManyToManyAttribute($rawDefinitions[$key]); 254 | } 255 | 256 | return $this->syncManyToManyAttributeDefinitions[$key]; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

Sync Eloquent Many-to-Many via Array Attribute

6 |
7 |

8 | 9 | This extension allows control over Eloquent many-to-many relations via array attributes. 10 | 11 | For license information check the [LICENSE](LICENSE.md)-file. 12 | 13 | [![Latest Stable Version](https://img.shields.io/packagist/v/illuminatech/sync-many-attribute.svg)](https://packagist.org/packages/illuminatech/sync-many-attribute) 14 | [![Total Downloads](https://img.shields.io/packagist/dt/illuminatech/sync-many-attribute.svg)](https://packagist.org/packages/illuminatech/sync-many-attribute) 15 | [![Build Status](https://github.com/illuminatech/sync-many-attribute/workflows/build/badge.svg)](https://github.com/illuminatech/sync-many-attribute/actions) 16 | 17 | 18 | Installation 19 | ------------ 20 | 21 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 22 | 23 | Either run 24 | 25 | ``` 26 | php composer.phar require --prefer-dist illuminatech/sync-many-attribute 27 | ``` 28 | 29 | or add 30 | 31 | ```json 32 | "illuminatech/sync-many-attribute": "*" 33 | ``` 34 | 35 | to the require section of your composer.json. 36 | 37 | 38 | Usage 39 | ----- 40 | 41 | This extension allows control over Eloquent many-to-many relations via array attributes. 42 | Each such attribute matches particular `BelongsToMany` relation and accepts array of related model IDs. 43 | Relations will be automatically synchronized during model saving. 44 | 45 | > Note: in general such approach makes a little sense, since Eloquent already provides fluent interface for many-to-many 46 | relation synchronization. However, this extension make come in handy while working with 3rd party CMS like [Nova](https://nova.laravel.com), 47 | where you have a little control over model saving and post processing. Also it may simplify controller code, removing 48 | relation operations in favor to regular attribute mass assignment. 49 | 50 | In order to use the feature you should add `\Illuminatech\SyncManyAttribute\SyncManyToManyAttribute` trait to your model class 51 | and declare `syncManyToManyAttributes()` method, defining attributes for relation synchronization. This method should return 52 | an array, which each key is the name of the new virtual attribute and value is the name of the relation to be synchronized. 53 | 54 | For example: 55 | 56 | ```php 57 | 'categories', 75 | 'tag_ids' => 'tags', 76 | ]; 77 | } 78 | 79 | public function categories(): BelongsToMany 80 | { 81 | return $this->belongsToMany(Category::class); 82 | } 83 | 84 | public function tags(): BelongsToMany 85 | { 86 | return $this->belongsToMany(Tag::class)->withPivot(['created_at']); 87 | } 88 | 89 | // ... 90 | } 91 | ``` 92 | 93 | Usage example: 94 | 95 | ```php 96 | category_ids = Category::query()->pluck('id')->toArray(); 100 | // ... 101 | $item->save(); // relation `Item::categories()` synchronized automatically 102 | 103 | $item = $item->fresh(); 104 | var_dump($item->category_ids); // outputs array of category IDs like `[1, 3, 8, ...]` 105 | ``` 106 | 107 | You may use sync attributes during HTML form input composition. For example: 108 | 109 | ```blade 110 | ... 111 | 117 | ``` 118 | 119 | Controller code example: 120 | 121 | ```php 122 | validate([ 132 | 'name' => ['required', 'string'], 133 | // ... 134 | 'category_ids' => ['required', 'array'], 135 | 'category_ids.*' => ['int', 'exists:categories,id'], 136 | 'tag_ids' => ['required', 'array'], 137 | 'tag_ids.*' => ['int', 'exists:tags,id'], 138 | ]); 139 | 140 | $item = new Item; 141 | $item->fill($validatedData); // single assignment covers all many-to-many relations 142 | $item->save(); // relation `Item::categories()` synchronized automatically 143 | 144 | // return response 145 | } 146 | } 147 | ``` 148 | 149 | > Note: remember you need to add the names of attribute for many-to-many synchronization to `\Illuminate\Database\Eloquent\Model::$fillable` 150 | in order to make them available for mass assignment. 151 | 152 | 153 | ## Pivot attributes setup 154 | 155 | You may setup the pivot attributes, which should be saved during each relation synchronization. To do so, you should define 156 | the sync attribute as an array, which key defines relation name and value - the pivot attributes. `\Closure` can be used 157 | here for definition of particular pivot attribute value or entire pivot attributes set. 158 | For example: 159 | 160 | ```php 161 | [ 175 | 'categories' => [ 176 | 'type' => 'help-content', 177 | ], 178 | ], 179 | 'tag_ids' => [ 180 | 'tags' => [ 181 | 'attached_at' => function (Item $model) { 182 | return now(); 183 | } 184 | ], 185 | ], 186 | ]; 187 | } 188 | 189 | public function categories(): BelongsToMany 190 | { 191 | return $this->belongsToMany(Category::class)->withPivot(['type']); 192 | } 193 | 194 | public function tags(): BelongsToMany 195 | { 196 | return $this->belongsToMany(Tag::class)->withPivot(['attached_at']); 197 | } 198 | 199 | // ... 200 | } 201 | ``` 202 | 203 | You may use `\Illuminatech\SyncManyAttribute\ManyToManyAttribute` to create sync attribute definition in more OOP style: 204 | 205 | ```php 206 | (new ManyToManyAttribute) 221 | ->relationName('categories') 222 | ->pivotAttributes(['type' => 'help-content']), 223 | 'tag_ids' => (new ManyToManyAttribute) 224 | ->relationName('tags') 225 | ->pivotAttributes([ 226 | 'attached_at' => function (Item $model) { 227 | return now(); 228 | }, 229 | ]), 230 | ]; 231 | } 232 | 233 | public function categories(): BelongsToMany 234 | { 235 | return $this->belongsToMany(Category::class)->withPivot(['type']); 236 | } 237 | 238 | public function tags(): BelongsToMany 239 | { 240 | return $this->belongsToMany(Tag::class)->withPivot(['attached_at']); 241 | } 242 | 243 | // ... 244 | } 245 | ``` 246 | 247 | Defined pivot attributes will be automatically saved during relation synchronization on model saving: 248 | 249 | ```php 250 | category_ids = Category::query()->pluck('id')->toArray(); 254 | // ... 255 | $item->save(); // relation `Item::categories()` synchronized automatically 256 | 257 | $category = $item->categories()->first(); 258 | var_dump($category->pivot->type); // outputs 'help-content' 259 | ``` 260 | 261 | ## Nova Integration 262 | 263 | One of the main benefit of this extension is support of 3rd party CMS like [Nova](https://nova.laravel.com). 264 | You may use sync attributes, allowing user to setup many-to-many relation directly from create/update form, instead of 265 | operating separated listing from details page. 266 | 267 | You can create input for `BelongsToMany` relation as multiple select or checkbox list. 268 | Packages like [fourstacks/nova-checkboxes](https://github.com/fourstacks/nova-checkboxes) might be used for such fields. 269 | The final Nova resource may look like following: 270 | 271 | ```php 272 | sortable(), 286 | 287 | // ... 288 | 289 | // use single checkbox list input instead of `\Laravel\Nova\Fields\BelongsToMany`: 290 | Checkboxes::make(__('Categories'), 'category_ids') 291 | ->options(\App\Models\Category::pluck('name', 'id')), 292 | ]; 293 | } 294 | } 295 | ``` 296 | --------------------------------------------------------------------------------