Relationship
Introduction
The model form supports data processing related to multiple models such as one-to-one, one-to-many, and many-to-many. Please use lowercase relation functions in your models for example:
// Structure
pages
id-integer
name-string
body-text
pages_info
id-integer
page_id-integer
user_id-integer
info-text
// Model
class Page extends Model
{
public function pageinfo()
{
return $this->hasOne(PageInfo::class);
}
}
// PageController.php
// Make use the relation function is lower case. CamelCase will converted to snake_case
$form->textarea('pageinfo.info', __('Page info'));
One to one
The users
table and the profiles
table are associated one-to-one with the profiles.user_id
field
users
id-integer
name-string
email-string
profiles
id-integer
user_id-integer
age-integer
gender-string
The corresponding data model is:
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
HasOne
The following code can directly edit the age and gender fields in the profile of the associated model in the form of the User model
$form = new Form(new User);
$form->text('name');
$form->text('email');
// Fields associated with model profile
$form->text('profile.age');
$form->text('profile.gender');
BelongsTo
Similarly, the reverse relationship can also directly edit the data in the attribution model User
in the form of the Profile
model
$form = new Form(new Profile);
$form->text('age');
$form->text('gender');
// Fields associated with model profile
$form->text('user.name');
$form->text('user.email');
MorphOne
The usage method of MorphOne
association is the same as the above HasOne
and BelongsTo
association.
One to many
HasMany
Two of painters
and paintings
establish a one-to-many relationship through painter_id
:
painters
id-integer
username-string
bio-text
paintings
id-integer
painter_id-integer
title-string
body-text
completed_at-timestamp
The model of the table is:
class Painter extends Model
{
public function paintings()
{
return $this->hasMany(Painting::class,'painter_id');
}
}
class Painting extends Model
{
public function painter()
{
return $this->belongsTo(Painter::class,'painter_id');
}
}
Use the following form to build the code to edit the main table and field fields:
// Subtable fields
$form->hasMany('paintings', function (Form\NestedForm $form) {
$form->text('title');
$form->image('body');
$form->datetime('completed_at');
});
// You can use the second parameter to set the label
$form->hasMany('paintings','Painting', function (Form\NestedForm $form) {
});
// You display this relation in three different modes (default, tab & table)
$form->hasMany('paintings', function ($form) {
...
})->mode("table");
Note: At present, HasMany forms are not friendly enough to support complex form components (rich text, etc.).
MorphMany
The use of polymorphic relation is to re-use one table structure that hold relations to multible tables in our case:
posts
and pages
in relation to comments
. So the comments table contains both comments for posts and pages. The data is for this is being stored in commentable_id & commentable_type fields automaticly.
posts
id-integer
content-text
pages
id-integer
content-text
comments
id-integer
body-integer
commentable_id-integer
commentable_type-string
The models are as following:
class Post extends Model
{
public function comments()
{
return $this->morphMany(Comment::class,'commentable');
}
}
class Page extends Model
{
public function comments()
{
return $this->morphMany(Comment::class,'commentable');
}
}
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
Use the following form to build the code to edit the field in either the PageController of PostController:
$form->morphMany('comments', function (Form\NestedForm $form) {
$form->textarea('body');
});
// You can use the second parameter to set the label
$form->morphMany('comments','The Comments', function (Form\NestedForm $form) {
});
In fact, the morphMany method is an alias of the hasMany method, and the distinction is made due to the different associated names. The two methods have the same options and functions
HasMany With Resourceable
HasMany with Resourceable also know as Linked resources can be used to... ADD FURTHER INSTRUCTIONS
Many to many
BelongsToMany
The users
table and the roles
table are related through the middle table role_user
many-to-many, a user can have multiple roles, and a role can also belong to multiple users
users
id-integer
name-string
roles
id-integer
name-integer
role_user
user_id-integer
role_id-integer
The corresponding data model is:
class User extends Model
{
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
class Role extends Model
{
public function user()
{
return $this->belongsToMany(User::class);
}
}
Then there are two components multipleSelect
and checkbox
which support the selection of many to many association relationship
For example, select the role in the user's form:
$form = new Form(new User);
$form->multipleSelect('roles','Role')->options(Role::all()->pluck('name','id'));
Or select the user in the role form:
$form = new Form(new Role);
$form->multipleSelect('users','User')->options(User::all()->pluck('name','id'));
The same usage of checkbox
:
$form = new Form(new User);
$form->checkbox('roles','role')->options(Role::all()->pluck('name','id'));
BelongsTo selection
The attribution relationship selection lets easely select related elements.
Take the following article table and user table as examples, each article belongs to a user (author)
page
id-integer
user_id-integer
title-string
body-text
users
id-integer
name-string
email-string
avatar-string
model:
class Article extends Model
{
public function author()
{
return $this->belongsTo(User::class);
}
}
class User extends Model
{
}
If you want to select the user in the form of the page, you can use the select
component
$form->select('author_id')->options(User::all()->pluck('name','id'));
There are two obvious problems with the select
component. First, if there are too many users, the selected option will be too long. Second, there is no way to display more user information in the selected option, such as email
, The two fields "avatar".
The following two methods BelongsTo
and BelongsToMany
solve this problem very well, using the form of popup list
to select the attribution object.
BelongsTo
Also use the table structure and model in the above example, using the following method
First define the list selection class:
<?php
namespace App\Admin\Selectable;
use App\Models\User;
use OpenAdmin\Admin\Grid\Filter;
use OpenAdmin\Admin\Grid\Selectable;
class UsersSelect extends Selectable
{
public $model = User::class;
public static $display_field = "tag"; // display field when using in grid
public function make()
{
$this->column('id');
$this->column('name');
$this->column('email');
$this->column('avatar','Avatar')->image();
$this->column('created_at');
$this->filter(function (Filter $filter) {
$filter->like('name');
});
}
}
In the list selection class, the $model
attribute is used to specify the model of the list. The default data of the list is 10
. You can use the attribute protected $perPage = 5;
to set the number of each page.
The make
method is used to build the list, please refer to model-grid documentation
Here is how to use it in the form:
use App\Admin\Selectable\UsersSelect;
$form->belongsTo('user_id', UsersSelect::class,'Author');
The effect is as follows:
BelongsToMany
Use belongsToMany
method instead of multipleSelect
to select many-to-many relationship
Take the following project table and user table as examples, each project has a belongs to multiple relation with users (collaborators)
projects
id-integer
title-string
body-text
user_id-integer
users
id-integer
name-string
email-string
avatar-string
project_user
user_id-integer
project_id-integer
model:
class Project extends Model
{
public function collaborators()
{
return $this->belongsToMany(Users::class);
}
}
class User extends Model
{
}
Here is how to use it in the form:
// Select the class using the list defined in the example above
use App\Admin\Selectable\UsersSelect;
$form->belongsToMany('collaborators', UsersSelect::class, __('Collaborators'));
The effect is as follows:
Use in the list page
As long as the attribution selector is used in the form, it can also be used in the list page to achieve in-line editing
// Select the class using the list defined in the example above
use App\Admin\Selectable\UsersSelect;
$grid->column('user_id','Project Owner')
->belongsTo(UsersSelect::class);
$grid->column('collaborators','Collaborators')
->belongsToMany(UsersSelect::class);
Note that when used in a list, its recommanded to add a display
method to the list selection class App\Admin\Selectable\Users
to define the display of these two columns in the list:
class UsersSelect extends Selectable
{
...
public static function display()
{
return function ($value) {
// For belongs to many relationships
if (is_array($value)) {
return implode(', ', array_map(function ($collaborator) {
return "<span class='_{$collaborator['id']}'>{$collaborator['name']}</span>";
}, $value));
}
// For belongsTo relationship
return "<span class='_".$value."'>".optional($this->user)->name."</span>";
};
}
}
BelongsToMany / multiple example
BelongsTo / single relation example:
Unsupported relationship
The following types of relationship do not supported
HasOneThrough
, HasManyThrough
, MorphTo
, MorphToMany