BelongsToMany

# Basics

The BelongsToMany field is designed to work with the relation of the same name in Laravel and includes all the basic methods.

To create this field, use the static make() method.

BelongsToMany::make(
Closure|string $label,
?string $relationName = null,
Closure|string|null $formatted = null,
?ModelResource $resource = null
)
  • $label - label, field header,
  • $relationName - name of the relationship,
  • $formatted - a closure or field in a related table to display values,
  • $resource - the model resource referenced by the relation.

The presence of the model resource referenced by the relation is mandatory!
The resource also needs to be registered with the service provider MoonShineServiceProvider in the method menu() or resources(). Otherwise, there will be a 404 error.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', 'categories', resource: new CategoryResource())
];
}
 
//...

If you do not specify $relationName, then the relation name will be determined automatically based on $label.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
];
}
 
//...

You can omit $resource if the model resource matches the name of the relationship.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', 'categories')
];
}
 
//...

By default, a field in the related table is used to display the value. which is specified by the $column property in the model resource.
The $formatted argument allows you to override this.

namespace App\MoonShine\Resources;
 
use MoonShine\Resources\ModelResource;
 
class CategoryResource extends ModelResource
{
//...
 
public string $column = 'title';
 
//...
}

If you need to specify a more complex value to display, then the $formatted argument can be passed a callback function.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make(
'Categories',
'categories',
fn($item) => "$item->id. $item->title"
)
];
}
 
//...

# Column header

By default, the table column header uses the property $title of the relationship model resource.
The columnLabel() method allows you to override the title.

columnLabel(string $label)
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->columnLabel('Title')
];
}
 
//...

# Pivot

The fields() method is used to implement pivot fields in the BelongsToMany relationship.

fields(Fields|Closure|array $fields)
use MoonShine\Fields\Relationships\BelongsToMany;
use MoonShine\Fields\Text;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Contacts', resource: new ContactResource())
->fields([
Text::make('Contact', 'text'),
])
];
}
 
//...

The relationship must specify which pivot fields are used in the staging table!
More details in the official documentation Laravel .

# Creating a Relationship Object

The creatable() method allows you to create a new relation object through a modal window.

creatable(
Closure|bool|null $condition = null,
?ActionButton $button = null,
)
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->creatable()
];
}
 
//...

You can customize the create button by passing the button parameter to the method.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->creatable(
button: ActionButton::make('Custom button', '')
)
];
}
 
//...

# Select

The BelongsToMany field can be displayed as a drop-down list, To do this, you need to use the selectMode() method.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->selectMode()
];
}
 
//...

# Options

All choices options are available to change via data attributes:

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Countries', resource: new CountryResource())
->selectMode()
->customAttributes([
'data-max-item-count' => 2
])
];
}
 
//...

For more details please contact Choices .

# Placeholder

The placeholder() method allows you to set placeholder attribute on the field.

placeholder(string $value)
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Countries', 'countries')
->nullable()
->placeholder('Countries')
];
}
 
//...

The placeholder() method is only used if the field is displayed as a dropdown list selectMode()!

# Tree

The tree() method allows you to display values as a tree with checkboxes, for example, for categories that have nesting. The method must be passed a column in the database on which the tree will be built.

tree(string $parentColumn)
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->tree('parent_id')
];
}
 
//...

# Preview

By default, preview will display the field as a table.

To change the display in preview you can use the following methods.

onlyCount

The onlyCount() method allows you to display only the number of selected values in preview.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->onlyCount()
];
}
 
//...
inLine

The inLine() method allows you to display field values as a line.

inLine(string $separator = '', Closure|bool $badge = false, ?Closure $link = null)

You can pass optional parameters to the method:

  • separator - separator between elements;
  • badge - closure or boolean value, responsible for displaying elements as badge;
  • $link - a closure that should return url links or components.

When passing the boolean value true to the badge parameter, the color will be used Primary . To change the color displayed by badge, use closure and return the Badge::make() component.

use MoonShine\Components\Link;
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->inLine(
separator: ' ',
badge: fn($model, $value) => Badge::make($value, 'color'),
link: fn(Category $category, $value, $field) => Link::make(
(new CategoryResource())->detailPageUrl($category),
$value
)
)
];
}
 
//...

The onlyLink() method will allow you to display the relationship as a link with the number of elements.

onlyLink(?string $linkRelation = null, Closure|bool|null $condition = null)

You can pass optional parameters to the method:

  • linkRelation - link to the relationship;
  • condition - closure or boolean value, responsible for displaying the relationship as a link.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->onlyLink()
];
}
 
//...
linkRelation

The linkRelation parameter allows you to create a link to a relation with a parent resource binding.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->onlyLink('category')
];
}
 
//...
condition

The condition parameter via a closure will allow you to change the display method depending on the conditions.

use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->onlyLink(condition: function (int $count, Field $field): bool {
return $count > 10;
})
];
}
 
//...

# Query for values

The valuesQuery() method allows you to change the query for obtaining values.

valuesQuery(Closure $callback)
use Illuminate\Contracts\Database\Eloquent\Builder;
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', 'categories', resource: new CategoryResource())
->valuesQuery(fn(Builder $query, Field $field) => $query->where('active', true))
];
}
 
//...

To implement asynchronous search for values, use the asyncSearch() method.

asyncSearch(
string $asyncSearchColumn = null,
int $asyncSearchCount = 15,
?Closure $asyncSearchQuery = null,
?Closure $asyncSearchValueCallback = null,
?string $associatedWith = null,
?string $url = null,
bool $replaceQuery = false,
)
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Countries', 'countries', resource: new CountryResource())
->asyncSearch()
];
}
 
//...

The search will be carried out using the resource relationship field column. By default column=id.

You can pass parameters to the asyncSearch() method:

  • $asyncSearchColumn - the field in which the search takes place;
  • $asyncSearchCount - number of elements in the search results;
  • $asyncSearchQuery - callback-function for filtering values;
  • $asyncSearchValueCallback - callback-function for customizing output;
  • $associatedWith - the field with which to establish a connection;
  • $url - url to process the asynchronous request,
  • $replaceQuery - replace query.
use Illuminate\Contracts\Database\Eloquent\Builder;
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Countries', 'countries', resource: new CountryResource())
->asyncSearch(
'title',
10,
asyncSearchQuery: function (Builder $query, Request $request, Field $field) {
return $query->where('id', '!=', 2);
},
asyncSearchValueCallback: function ($country, Field $field) {
return $country->id . ' | ' . $country->title;
},
'https://moonshine-laravel.com/async'
)
];
}
 
//...

When building a query in asyncSearchQuery(), you can use the current form values. To do this, you need to pass Request to the callback function.

use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use MoonShine\Fields\Relationships\BelongsToMany;
use MoonShine\Fields\Select;
 
//...
 
public function fields(): array
{
return [
Select::make('Country', 'country_id'),
BelongsToMany::make('Cities', 'cities', resource: new CityResource())
->asyncSearch(
'title',
asyncSearchQuery: function (Builder $query, Request $request, Field $field): Builder {
return $query->where('country_id', $request->get('country_id'));
}
)
];
}
 
//...

When building a query in asyncSearchQuery(), the original state of the builder is preserved.
If you need to replace it with your builder, then use the replaceQuery flag.

use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use MoonShine\Fields\Relationships\BelongsToMany;
use MoonShine\Fields\Select;
 
//...
 
public function fields(): array
{
return [
Select::make('Country', 'country_id'),
BelongsToMany::make('Cities', 'cities', resource: new CityResource())
->asyncSearch(
'title',
asyncSearchQuery: function (Builder $query, Request $request, Field $field): Builder {
return $query->where('country_id', $request->get('country_id'));
},
replaceQuery: true
)
];
}
 
//...

Requests must be customized using the asyncSearch() method. Don't use valuesQuery()!

# Related fields

To associate select values between fields, you can use the associatedWith() method.

associatedWith(string $column, ?Closure $asyncSearchQuery = null)
  • $column - the field with which the connection is established;
  • $asyncSearchQuery - callback function for filtering values.
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Cities', 'cities', resource: new CityResource())
->associatedWith('country_id')
];
}
 
//...

For more complex setup, you can use asyncSearch().

# Values with picture

The withImage() method allows you to add an image to a value.

withImage(
string $column,
string $disk = 'public',
string $dir = ''
)
  • $column - field with an image;
  • $disk - file system disk;
  • $dir - directory relative to the root of the disk.
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make(Countries, resource: new CountryResource())
->withImage('thumb', 'public', 'countries')->selectMode()
];
}
 
//...

# Buttons

The buttons() method allows you to add additional buttons to the BelongsToMany field.

buttons(array $buttons)
use MoonShine\ActionButtons\ActionButton;
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->buttons([
ActionButton::make('Check all', '')
->onClick(fn() => 'checkAll', 'prevent'),
 
ActionButton::make('Uncheck all', '')
->onClick(fn() => 'uncheckAll', 'prevent')
])
];
}
 
//...
withCheckAll

The withCheckAll() method allows you to add checkAll/uncheckAll buttons to the BelongsToMany field similar to the previous example.

use MoonShine\ActionButtons\ActionButton;
use MoonShine\Fields\Relationships\BelongsToMany;
 
//...
 
public function fields(): array
{
return [
BelongsToMany::make('Categories', resource: new CategoryResource())
->withCheckAll()
];
}
 
//...