Fields

BelongsToMany

Basics

The BelongsToMany field is designed to work with the relationship of the same name in Laravel and includes all Basic Methods.

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

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

The model resource that the relationship references is required! The resource must also be registered in the service provider MoonShineServiceProvider in the $core->resources() method. Otherwise, a 500 error will occur (Resource is required for MoonShine\Laravel\Fields\Relationships\BelongsToMany...).

use MoonShine\Laravel\Fields\Relationships\BelongsToMany;
 
BelongsToMany::make('Categories', 'categories', resource: CategoryResource::class)

belongs_to_many belongs_to_many_dark

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

BelongsToMany::make('Categories', resource: CategoryResource::class)

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

class CategoryResource extends ModelResource
{
//...
}
//...
BelongsToMany::make('Categories', 'categories')

If $relationName is not specified, then the relationship name will be determined automatically based on $label (following camelCase rules).

class CategoryResource extends ModelResource
{
//...
}
//...
BelongsToMany::make('Categories')

By default, the value is displayed using the field in the related table specified by the $column property in the model resource. The $formatted argument allows overriding this.

namespace App\MoonShine\Resources;
 
use MoonShine\Resources\ModelResource;
 
class CategoryResource extends ModelResource
{
public string $column = 'title';
}
//..
BelongsToMany::make(
'Categories',
'Categories',
formatted: 'name'
)

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

BelongsToMany::make(
'Categories',
'categories',
fn($item) => "$item->id. $item->title"
)

Column Label

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

columnLabel(string $label)
BelongsToMany::make('Categories', resource: CategoryResource::class)
->columnLabel('Title')

Pivot

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

fields(FieldsContract|Closure|iterable $fields)
use MoonShine\Laravel\Fields\Relationships\BelongsToMany;
use MoonShine\UI\Fields\Text;
 
BelongsToMany::make('Contacts', resource: ContactResource::class)
->fields([
Text::make('Contact', 'text'),
])

belongs_to_many_pivot

belongs_to_many_pivot_dark

In the relationship, you must specify which pivot fields are used in the intermediate table! For more details, see the official documentation Laravel.

Creating Relationship Object

The creatable() method allows you to create a new relationship object via a modal window.

creatable(
Closure|bool|null $condition = null,
?ActionButton $button = null,
)
BelongsToMany::make('Categories', resource: CategoryResource::class)
->creatable()

belongs_to_many_creatable

belongs_to_many_creatable_dark

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

BelongsToMany::make('Categories', resource: CategoryResource::class)
->creatable(
button: ActionButton::make('Custom button', '')
)

Select

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

BelongsToMany::make('Categories', resource: CategoryResource::class)
->selectMode()

belongs_to_many_select

belongs_to_many_select_dark

Options

All select options are available for modification via data attributes:

BelongsToMany::make('Countries', resource: ContactResource::class)
->selectMode()
->customAttributes([
'data-max-item-count' => 2
])

For more detailed information, please refer to Choices.

Placeholder

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

placeholder(string $value)
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 values to be displayed in a tree format with checkboxes, for example, for categories that have nesting. The method requires you to pass the column in the database by which the tree will be built.

tree(string $parentColumn)
BelongsToMany::make('Categories', resource: CategoryResource::class)
->tree('parent_id')

belongs_to_many_tree

belongs_to_many_tree_dark

Preview

By default, in preview, the field will be displayed in a table.

belongs_to_many_preview

belongs_to_many_preview_dark

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.

BelongsToMany::make('Categories', resource: CategoryResource::class)
->onlyCount()

belongs_to_many_preview_count

belongs_to_many_preview_count_dark

inLine

The inLine() method allows displaying the field values in a single line.

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

You can pass optional parameters to the method:

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

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

use MoonShine\UI\Components\Link;
use MoonShine\Laravel\Fields\Relationships\BelongsToMany;
 
BelongsToMany::make('Categories', resource: CategoryResource::class)
->inLine(
separator: ' ',
badge: fn($model, $value) => Badge::make((string) $value, 'primary'),
link: fn(Property $property, $value, $field) => (string) Link::make(
app(CategoryResource::class)->getDetailPageUrl($property->id),
$value
)
)

belongs_to_many_preview_in_line

belongs_to_many_preview_in_line_dark

The relatedLink() method will display the relationship as a link with a count of elements. The link will lead to the IndexPage of the child resource from the HasMany relationship, which will show only those data elements.

relatedLink(?string $linkRelation = null, Closure|bool $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.

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

BelongsToMany::make('Categories', resource: CategoryResource::class)
->relatedLink('category')

The condition parameter through a closure will allow changing the display method depending on conditions.

BelongsToMany::make('Categories', resource: CategoryResource::class)
->relatedLink(condition: function (int $count, Field $field): bool {
return $count > 10;
})

Values Query

The valuesQuery() method allows you to modify the query for retrieving values.

valuesQuery(Closure $callback)
use Illuminate\Contracts\Database\Eloquent\Builder;
use MoonShine\Laravel\Fields\Relationships\BelongsToMany;
 
BelongsToMany::make('Countries', 'countries', resource: ContactResource::class)
->valuesQuery(fn(Builder $query, Field $field) => $query->where('active', true))

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

asyncSearch(
string $column = null,
?Closure $searchQuery = null,
?Closure $formatted = null,
?string $associatedWith = null,
int $limit = 15,
?string $url = null,
)
BelongsToMany::make('Countries', 'countries', resource: ContactResource::class)
->asyncSearch()

The search will be performed by the relationship resource column. By default, column=id

You can pass parameters to the asyncSearch() method:

  • $column - the field to search by,
  • $searchQuery - callback function for filtering values,
  • $formatted - callback function for customizing the output,
  • $associatedWith - field to establish a relationship,
  • $limit - number of elements in the search results,
  • $url - url for processing the asynchronous request,
use Illuminate\Contracts\Database\Eloquent\Builder;
use MoonShine\Laravel\Fields\Relationships\BelongsToMany;
 
BelongsToMany::make('Countries', 'countries', resource: ContactResource::class)
->asyncSearch(
'title',
10,
searchQuery: function (Builder $query, Request $request, Field $field) {
return $query->where('id', '!=', 2);
},
formatted: function ($country, Field $field) {
return $country->id . ' | ' . $country->title;
},
'https://moonshine-laravel.com/async'
)

When constructing the query in searchQuery(), you can use the current form values. To do this, pass Request to the callback function.

use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use MoonShine\Laravel\Fields\Relationships\BelongsToMany;
use MoonShine\UI\Fields\Select;
 
Select::make('Country', 'country_id'),
BelongsToMany::make('Cities', 'cities', resource: CityResource::class)
->asyncSearch(
'title',
searchQuery: function (Builder $query, Request $request, Field $field): Builder {
return $query->where('country_id', $request->get('country_id'));
}
)

Queries should be set up using the asyncSearch() method. Do not use valuesQuery()!

Associated Fields

To establish a relationship of select values between fields, you can use the associatedWith() method.

associatedWith(string $column, ?Closure $searchQuery = null)
  • $column - the field with which the relationship is established,
  • searchQuery - callback function for filtering values.
BelongsToMany::make('Cities', 'cities', resource: CityResource::class)
->associatedWith('country_id')

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

Values with Image

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

withImage(
string $column,
string $disk = 'public',
string $dir = ''
)
  • $column - field with the image,
  • $disk - filesystem disk,
  • $dir - directory relative to the root of the disk.
BelongsToMany::make('Countries', resource: ContactResource::class)
->withImage('thumb', 'public', 'countries')->selectMode()

with_image

belongs_to_image_dark

Buttons

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

buttons(array $buttons)
use MoonShine\ActionButtons\ActionButton;
use MoonShine\Laravel\Fields\Relationships\BelongsToMany;
 
BelongsToMany::make('Categories', resource: CategoryResource::class)
->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 a checkAll button to the BelongsToMany field, similar to the previous example.

BelongsToMany::make('Categories', resource: CategoryResource::class)
->withCheckAll()