The simplest way to handle this kind of relations is to provide an extra field on your models that will serve as a representation of the relations.
For example, from a user we need to see his companies: expose a new field user.company_ids
.
This field don't have to be in the database, your API or a proxy can list the related companies ID in this field, and for write requests use it to create, change or delete relations.
Once this field is implemented, admin-on-rest is very easy to configure.
I made a fully working snippet: https://codesandbox.io/s/r04y8rn96p
First, write your top-level resources:
<Admin
authClient={authClient}
restClient={restClient}
title="Example Admin"
locale="en"
messages={messages}
>
<Resource
name="companies"
list={CompanyList}
create={CompanyCreate}
edit={CompanyEdit}
show={CompanyShow}
remove={Delete}
icon={CompanyIcon}
/>
<Resource
name="users"
list={UserList}
create={UserCreate}
edit={UserEdit}
remove={Delete}
icon={UserIcon}
show={UserShow}
/>
<Resource
name="company_user"
icon={CompanyUserIcon}
list={CompanyUserList}
create={CompanyUserCreate}
show={CompanyUserShow}
remove={Delete}
/>
</Admin>
Then, you can use ReferenceArrayInput
for the user model on the extra field just like a one-to-many relation.
export const UserEdit = ({ ...props }) => (
<Edit title={<UserTitle />} {...props}>
<TabbedForm>
<FormTab label="User Infos">
<TextInput source="name" />
<DateInput source="published_at" defaultValue={() => new Date()} />
<TextInput source="company_ids" />
</FormTab>
<FormTab label="Companies">
<ReferenceArrayInput source="company_ids" reference="companies">
<SelectArrayInput optionText="title" />
</ReferenceArrayInput>
</FormTab>
</TabbedForm>
</Edit>
);
Since the idea is to mock the relations into an extra field, you can handle this field from your backend (the best choice) or the frontend via the restClient
. Here is an dummy example for read-only relations:
// It's better to implement these rules on the backend
const improvedRestClient = (type, resource, params) => {
return restClient(type, resource, params).then((response) => {
// Inject company_ids into user
if (type === 'GET_ONE' && resource === 'users') {
return restClient('GET_LIST', 'company_user', defaultOptions).then(({ data: companyUsers }) => {
const user = response.data;
return {
data: {
...user,
company_ids: companyUsers
.filter(item => item.user_id === user.id)
.map(item => item.company_id),
},
}
});
}
// Inject user_ids into company
if (type === 'GET_ONE' && resource === 'companies') {
return restClient('GET_LIST', 'company_user', defaultOptions).then(({ data: companyUsers }) => {
const company = response.data;
return {
data: {
...company,
user_ids: companyUsers
.filter(item => item.company_id === company.id)
.map(item => item.user_id),
},
}
});
}
return response;
});
};