|
1 | 1 | # Handling Relations to Collections
|
2 | 2 |
|
3 |
| -Currently, API Platform Admin doesn't handle `to-many` relations. The core library [is being patched](https://github.com/api-platform/core/pull/1189) |
4 |
| -to document relations to collections through OWL. |
5 |
| - |
6 |
| -Meanwhile, it is possible to manually configure API Platform to handle relations to collections. |
7 |
| - |
8 |
| -We will create the admin for an API exposing `Person` and `Book` resources linked with a `many-to-many` |
| 3 | +Considering an API exposing `Person` and `Book` resources linked with a `many-to-many` |
9 | 4 | relation between them (through the `authors` property).
|
10 | 5 |
|
11 |
| -This API can be created using the following PHP code: |
| 6 | +This API using the following PHP code: |
12 | 7 |
|
13 | 8 | ```php
|
14 | 9 | <?php
|
@@ -74,54 +69,59 @@ class Book
|
74 | 69 | }
|
75 | 70 | ```
|
76 | 71 |
|
77 |
| -Let's customize the components used for the `authors` property: |
| 72 | +The admin handles this `to-many` relation automatically! |
| 73 | + |
| 74 | +But we can go further: |
| 75 | + |
| 76 | +## Customizing a Property |
| 77 | + |
| 78 | +Let's customize the components used for the `authors` property, to display them by their 'name' instead 'id' (the default behavior). |
78 | 79 |
|
79 | 80 | ```javascript
|
80 | 81 | import React, { Component } from 'react';
|
81 | 82 | import { ReferenceArrayField, SingleFieldList, ChipField, ReferenceArrayInput, SelectArrayInput } from 'react-admin';
|
82 | 83 | import { AdminBuilder, hydraClient } from '@api-platform/admin';
|
83 |
| -import parseHydraDocumentation from 'api-doc-parser/lib/hydra/parseHydraDocumentation'; |
| 84 | +import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation'; |
84 | 85 |
|
85 | 86 | const entrypoint = 'https://demo.api-platform.com';
|
86 | 87 |
|
87 | 88 | export default class extends Component {
|
88 |
| - state = {api: null, resources: null}; |
| 89 | + state = { api: null } |
89 | 90 |
|
90 | 91 | componentDidMount() {
|
91 |
| - parseHydraDocumentation(entrypoint).then({api, resources} => { |
92 |
| - const books = r.find(r => 'books' === r.name); |
| 92 | + parseHydraDocumentation(entrypoint).then(({api}) => { |
| 93 | + const books = api.resources.find(({ name }) => 'books' === name) |
| 94 | + const authors = books.fields.find(({ name }) => 'authors' === name) |
93 | 95 |
|
94 | 96 | // Set the field in the list and the show views
|
95 |
| - books.readableFields.find(f => 'authors' === f.name).fieldComponent = |
96 |
| - <ReferenceArrayField label="Authors" reference="people" source="authors" key="authors"> |
| 97 | + authors.field = props => ( |
| 98 | + <ReferenceArrayField source={authors.name} reference={authors.reference.name} key={authors.name} {...props}> |
97 | 99 | <SingleFieldList>
|
98 | 100 | <ChipField source="name" key="name"/>
|
99 | 101 | </SingleFieldList>
|
100 | 102 | </ReferenceArrayField>
|
101 |
| - ; |
| 103 | + ); |
102 | 104 |
|
103 | 105 | // Set the input in the edit and create views
|
104 |
| - books.writableFields.find(f => 'authors' === f.name).inputComponent = |
105 |
| - <ReferenceArrayInput label="Authors" reference="people" source="authors" key="authors"> |
| 106 | + authors.input = props => ( |
| 107 | + <ReferenceArrayInput source={authors.name} reference={authors.reference.name} label="Authors" key={authors.name} {...props} allowEmpty> |
106 | 108 | <SelectArrayInput optionText="name"/>
|
107 | 109 | </ReferenceArrayInput>
|
108 |
| - ; |
| 110 | + ); |
109 | 111 |
|
110 |
| - this.setState({api, resources}); |
| 112 | + this.setState({ api }); |
111 | 113 | }
|
112 | 114 | )
|
113 | 115 | }
|
114 | 116 |
|
115 | 117 | render() {
|
116 | 118 | if (null === this.state.api) return <div>Loading...</div>;
|
117 | 119 |
|
118 |
| - return <AdminBuilder api={this.state.api} dataProvider={hydraClient({entrypoint: entrypoint, resources: this.state.resources})}/> |
| 120 | + return <AdminBuilder api={ this.state.api } dataProvider={ hydraClient(this.state.api) }/> |
119 | 121 | }
|
120 | 122 | }
|
121 | 123 | ```
|
122 | 124 |
|
123 |
| -The admin now properly handles this `to-many` relation! |
124 |
| - |
125 | 125 | ## Using an Autocomplete Input for Relations
|
126 | 126 |
|
127 | 127 | We'll make one last improvement to our admin: transforming the relation selector we just created to use autocompletion.
|
@@ -161,11 +161,11 @@ Then edit the configuration of API Platform Admin to pass a `filterToQuery` prop
|
161 | 161 | // ...
|
162 | 162 |
|
163 | 163 | // Set the input in the edit and create views
|
164 |
| - books.writableFields.find(f => 'authors' === f.name).inputComponent = |
165 |
| - <ReferenceArrayInput label="Authors" reference="people" source="authors" key="authors" filterToQuery={searchText => ({ name: searchText })}> |
166 |
| - <SelectArrayInput optionText="name"/> |
167 |
| - </ReferenceArrayInput> |
168 |
| - ; |
| 164 | + authors.input = props => ( |
| 165 | + <ReferenceArrayInput source={authors.name} reference={authors.reference.name} label="Authors" key={authors.name} filterToQuery={searchText => ({ name: searchText })} {...props} allowEmpty> |
| 166 | + <SelectArrayInput optionText="name"/> |
| 167 | + </ReferenceArrayInput> |
| 168 | + ); |
169 | 169 |
|
170 | 170 | // ...
|
171 | 171 | }
|
|
0 commit comments