diff --git a/admin/advanced-customization.md b/admin/advanced-customization.md new file mode 100644 index 00000000000..f714af21e9e --- /dev/null +++ b/admin/advanced-customization.md @@ -0,0 +1,462 @@ +# Customizing the Admin + +In the previous sections, we have seen how to customize the generated Admin by [updating the schema](./schema.md), and by [customizing the guesser components](./customizing.md). + +But we can go much further in customizing the generated pages by leveraging React Admin components and props. + +In the following sections, we will for instance learn how to: + +- Change the default theme and layout +- Display the number of related records instead of listing them +- Navigate from the list using simple row click +- Make mutations undoable +- Improve the layout of a form +- Switch to a tabbed layout +- Create a custom field component +- Add icons to the menu + +Let's dive in! + +## Changing the Default Theme and Layout + +API Platform comes with its own [layout](https://marmelab.com/react-admin/Admin.html#layout) and [themes](https://marmelab.com/react-admin/Admin.html#theme) by default. + +![Admin with default API Platform theme and layout](./images/api-platform-admin-theme.png) + +However you may not find them to your liking, or you may want to remove the API Platform logo from the top bar. + +To change the top bar logo, you will need to opt out of API Platform's default Layout component, and provide your own. + +You can for instance use the default [Layout](https://marmelab.com/react-admin/Layout.html) provided by `react-admin`. + +```diff +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; ++import { Layout } from 'react-admin'; + +export const App = () => ( +- ++ + + + +); +``` + +To customize the light and dark themes, you will need to use the [`theme`](https://marmelab.com/react-admin/Admin.html#theme) and [`darkTheme`](https://marmelab.com/react-admin/Admin.html#darktheme) props of the `` component. + +Here too, we can use the default themes provided by `react-admin`. + +```diff +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; +-import { Layout } from 'react-admin'; ++import { Layout, defaultDarkTheme, defaultLightTheme } from 'react-admin'; + +export const App = () => ( +- ++ + + + +); +``` + +Here is the result: + +![Admin with default react-admin theme and layout](./images/react-admin-theme.png) + +## Displaying the Number of Related Records + +When dealing with related records, the default behavior of the guessers is to display the list of related records. + +However if there are many related records, it can be more suitable to display the number of related records instead. + +Reusing our example with `books` and `reviews`, here is how you can display the number of reviews for each book in the book list: + +```diff +import { ListGuesser, FieldGuesser } from '@api-platform/admin'; +import { NumberField } from 'react-admin'; + +const BookList = () => ( + + + + + +- ++ + +); +``` + +![Admin with number of related records](./images/admin-reference-record-count.png) + +**Tip:** It is recommended to also set a custom `label` to the column, as the label is otherwise humanized from the `source` prop, which is no longer suitable with a source like `reviews.length`. + +## Hiding the Show And Edit Buttons in the List View + +By default, the list guesser displays a `Show` and `Edit` button for each row. + +However the UX can often be improved by setting a default action when clicking on a row, and removing the `Show` and `Edit` buttons. + +To hide these buttons, we will need to replace the `` by a [``](https://marmelab.com/react-admin/List.html) component, provided by `react-admin`. + +Then, to get the same layout as before, we will choose to render the list items using a [``](https://marmelab.com/react-admin/Datagrid.html) component. + +`` will automatically set the row click action to redirect to the show view if there is one, or to the edit view otherwise. + +```diff +-import { ListGuesser, FieldGuesser } from '@api-platform/admin'; ++import { FieldGuesser } from '@api-platform/admin'; +-import { NumberField } from 'react-admin'; ++import { List, Datagrid, NumberField } from 'react-admin'; + +const BookList = () => ( +- ++ ++ + + + + + ++ ++ +- +); +``` + +The UI is now more concise: + +![Admin with hidden show and edit buttons](./images/admin-datagrid.png) + +If you want, you can use the [`rowClick`](https://marmelab.com/react-admin/Datagrid.html#rowclick) prop to customize the row click action, for instance to redirect to the book edit view instead: + +```diff +const BookList = () => ( + +- ++ + + + + + + + +); +``` + +**Tip:** Check out the [`` documentation](https://marmelab.com/react-admin/Datagrid.html) for more customization options. + +## Enabling Undoable Mutations + +React Admin offers the possibility to make mutations (e.g. updating or deleting a record) undoable. + +When this feature is enabled, a notification will be displayed at the bottom of the page, allowing the user to undo the mutation for a certain amount of time. + +If the user clicks on the UNDO button, the record will be restored to its previous state. Otherwise, the change is persisted to the API. + +Let's, for instance, add the possibility to undo an update to a book. To do that, we will leverage the [`mutationMode`](https://marmelab.com/react-admin/Edit.html#mutationmode) prop provided by React Admin, and set its value to `"undoable"`. + +This is possible because the `` component is a wrapper around the [``](https://marmelab.com/react-admin/Edit.html) component provided by React Admin, and it will forward the `mutationMode` prop to it. + +```diff +import { EditGuesser, InputGuesser } from "@api-platform/admin"; + +export const BookEdit = () => ( +- ++ + + + + + + + +); +``` + +That's enough to display an undoable notification when updating a book: + +![Admin with undoable mutations](./images/admin-undoable-mutation.png) + +**Tip:** The default `mutationMode` set by `` is `"pessimistic"`, however the default `mutationMode` set by React Admin's `` component is `"undoable"`. + +## Warning the User When There Are Unsaved Changes + +Another feature offered by React Admin is the possibility to warn the user when there are unsaved changes in a form. + +When the user tries to navigate away from a form with unsaved changes, a confirmation dialog will be displayed, asking the user if they want to leave the page. This prevents the risk of losing unsaved data. + +To enable this feature, all we need to do is to leverage the [`warnWhenUnsavedChanges`](https://marmelab.com/react-admin/SimpleForm.html#warnwhenunsavedchanges) prop provided by React Admin. + +This is possible because the `` component is also a wrapper around the [``](https://marmelab.com/react-admin/SimpleForm.html) component provided by React Admin, and it will forward the `warnWhenUnsavedChanges` prop to it. + +```diff +import { EditGuesser, InputGuesser } from "@api-platform/admin"; + +export const BookEdit = () => ( +- ++ + + + + + + + +); +``` + +Now, if the user tries to navigate away from the form with unsaved changes, they will be warned: + +![Admin with unsaved changes warning](./images/admin-warnWhenUnsavedChanges.png) + +## Customizing the Form Layout + +As we saw earlier, `` actually renders two (nested) React Admin components: [``](https://marmelab.com/react-admin/Edit.html) and [``](https://marmelab.com/react-admin/SimpleForm.html). +You can pass additional props to `` which will be forwarded to `` or `` accordingly. + +However there are cases where this won't be enough. For instance, if we want to customize the form layout, we will need to specifically target the form component to pass styling props (such as `sx`), or to replace the component altogether (e.g. to use a [``](https://marmelab.com/react-admin/TabbedForm.html) instead). + +So, for our example, let's first replace the `` by an `` and a ``. + +```diff +-import { EditGuesser, InputGuesser } from "@api-platform/admin"; ++import { InputGuesser } from "@api-platform/admin"; ++import { Edit, SimpleForm } from "react-admin"; + +export const BookEdit = () => ( +- ++ ++ + + + + + + ++ ++ +- +); +``` + +**Tip:** This will also enable [undoable mutation mode](./advanced-customization.md#enabling-undoable-mutations). Indeed, the default `mutationMode` set by `` is `"pessimistic"`, however the default `mutationMode` set by React Admin's `` component is `"undoable"`. You can set the `mutationMode` prop back to `"pessimistic"` if you want to keep the same behavior as before. + +By default, `` organizes the inputs in a very simple layout, simply stacking them vertically. +Under the hood, it uses Material UI's [``](https://mui.com/material-ui/react-stack/) component. +This means we can use with `` any prop that `` accepts, and customize the style of the component using [the `sx` prop](https://marmelab.com/react-admin/SX.html). + +For instance, let's limit the width of the inputs to 500px: + +```diff +export const BookEdit = () => ( + +- ++ + + + + + + + + +); +``` + +We can also use `` directly in the `` to customize the layout further: + +```tsx +import { InputGuesser } from '@api-platform/admin'; +import { Edit, SimpleForm } from 'react-admin'; +import { Stack } from '@mui/material'; + +export const BookEdit = () => ( + + + + + + + + + + + +); +``` + +With these simple changes we already get a more appealing form layout: + +![Admin with customized form layout](./images/admin-form-layout.png) + +**Tip:** Feel free to look at the [``](https://marmelab.com/react-admin/Edit.html) and [``](https://marmelab.com/react-admin/SimpleForm.html) documentation pages to learn more about the customization options they offer. + +**Tip:** `` is not the only form layout provided by React Admin. You can also use another layout such as [``](https://marmelab.com/react-admin/TabbedForm.html), [``](https://marmelab.com/react-admin/LongForm.html), +[``](https://marmelab.com/react-admin/AccordionForm.html), [``](https://marmelab.com/react-admin/WizardForm.html) or even [create your own](https://marmelab.com/react-admin/Form.html). + +## Rendering Related Records in a Dedicated Tab + +Speaking of tabbed layout, a common pattern is to display related records in a dedicated tab of the show view of the main record. + +For instance, let's leverage the [``](https://marmelab.com/react-admin/TabbedShowLayout.html) component provided by React Admin to display the reviews of a book in a dedicated tab. + +We will also leverage `` to fetch the related reviews of a book, and `` to display them in a list. + +```tsx +import { Show, TabbedShowLayout, TextField, DateField, ReferenceArrayField, SimpleList } from 'react-admin'; + +const BookShow = () => ( + + + + + + + + + + + + + review.author + .split(' ') + .map((name: string) => name[0]) + .join('') + } + /> + + + + +); +``` + +Here is the result: + +![Admin with tabbed show layout](./images/admin-tabbed-show-layout.png) + +**Tip:** Feel free to look at the [``](https://marmelab.com/react-admin/TabbedShowLayout.html), [``](https://marmelab.com/react-admin/ReferenceArrayField.html) and [``](https://marmelab.com/react-admin/SimpleList.html) documentation pages to learn more about the customization options they offer. + +## Creating A Custom Field Component + +React Admin already provides numerous off-the-shelf [field](https://marmelab.com/react-admin/Fields.html) and [input](https://marmelab.com/react-admin/Inputs.html) components. + +However, you may still need to create your own custom field component to display a specific type of data, or to add a specific behavior. + +Fortunately, React Admin makes it easy to create custom [field](https://marmelab.com/react-admin/Fields.html#writing-your-own-field-component) or [input](https://marmelab.com/react-admin/Inputs.html#writing-your-own-input-component) components, thanks to the many building blocks it provides. + +Let's take a look at a concrete example. Let's say we want to create a custom field component to display a rating as a series of stars. + +We will leverage Material UI's [``](https://mui.com/material-ui/react-rating/) component for the rendering. + +Since the component is fairly simple, we won't create a dedicated React component, but will instead leverage [``](https://marmelab.com/react-admin/WithRecord.html), a React Admin component allowing to build a custom field on-the-fly. + +```tsx +import { ShowGuesser } from '@api-platform/admin'; +import { FieldGuesser, WithRecord, Labeled } from 'react-admin'; +import { Rating } from '@mui/material'; + +const ReviewShow = () => ( + + + + + + ( + + )} + /> + + + +); +``` + +Here is the result: + +![Admin with custom field component](./images/admin-custom-field.png) + +**Tip:** For a more complex field component, the preferred approach would probably be to create a dedicated React component. You can then leverage the [`useRecordContext`](https://marmelab.com/react-admin/useRecordContext.html) hook to achieve the same result. + +**Tip:** Check out the [Writing Your Own Field Component](https://marmelab.com/react-admin/Fields.html#writing-your-own-field-component) documentation to learn more about creating custom field components. + +Now let's create a custom input component, allowing not only to display a rating as a series of stars, but also to edit it. + +Again, we will leverage Material UI's [``](https://mui.com/material-ui/react-rating/) component for the rendering. But this time, we will leverage the [`useInput`](https://marmelab.com/react-admin/useInput.html) hook provided by React Admin, which allows to easily create a custom input component. + +```tsx +import { useInput } from 'react-admin'; +import { Rating } from '@mui/material'; + +const RatingInput = (props: InputProps) => { + const { field } = useInput(props); + return ( + { + field.onChange(value); + }} + /> + ); +}; +``` + +As you see, the `RatingInput` component is really short. It simply needs to call `field.onChange` whenever the rating changes. + +Now let's use this custom input component in the `ReviewEdit` component: + +```tsx +import { Edit, SimpleForm, InputGuesser, Labeled } from 'react-admin'; +import { RatingInput } from './RatingInput'; + +const ReviewEdit = () => ( + + + + + + + + + + + +); +``` + +Here is the result: + +![Admin with custom input component](./images/admin-custom-input.png) + +## React Admin Components + +As you saw from the previous sections and examples, while API Platform Admin aims at providing a complete and ready-to-use admin interface with as little code as possible, it always provides the flexibility to fully customize every aspect of the generated admin, while keeping a pleasant developer experience. + +This is made possible thanks to the numerous **React Admin components**. They are battle-tested, backend agnostic, fully customizable solutions to common Admin requirements. + +Here are some examples, from the simplest to the most complete solutions: + +- [PrevNextButton](https://marmelab.com/react-admin/PrevNextButton.html) +- [MenuLive](https://marmelab.com/react-admin/MenuLive.html) +- [FilterList](https://marmelab.com/react-admin/FilterList.html) +- [RevisionsButton](https://marmelab.com/react-admin/RevisionsButton.html) +- [WizardForm](https://marmelab.com/react-admin/WizardForm.html) +- [Calendar](https://marmelab.com/react-admin/Calendar.html) +- [SmartRichTextInput](https://marmelab.com/react-admin/SmartRichTextInput.html) +- [SolarLayout](https://marmelab.com/react-admin/SolarLayout.html) +- And many more... + +React Admin already includes 230+ hooks and components. And it always allows you to make your own, thanks to the building blocks it provides. Feel free to read through its [All Features](https://marmelab.com/react-admin/Features.html) documentation page to discover them all. diff --git a/admin/authentication-support.md b/admin/authentication-support.md index af5cc650271..024af85a507 100644 --- a/admin/authentication-support.md +++ b/admin/authentication-support.md @@ -1,17 +1,24 @@ # Authentication Support API Platform Admin delegates the authentication support to React Admin. -Refer to [the chapter dedicated to authentication in the React Admin documentation](https://marmelab.com/react-admin/Authentication.html) -for more information. + +Refer to the [Auth Provider Setup](https://marmelab.com/react-admin/Authentication.html) documentation for more information. + +**Tip:** Once you have set up the authentication, you can also configure React Admin to perform client-side Authorization checks. Refer to the [Authorization](https://marmelab.com/react-admin/Permissions.html) documentation for more information. ## HydraAdmin -The authentication layer for [HydraAdmin component](https://api-platform.com/docs/admin/components/#hydra) -consists of a few parts, which need to be integrated together. +Enabling authentication support for [`` component](./components.md#hydra) consists of a few parts, which need to be integrated together. + +In the following steps, we will see how to: + +- Make authenticated requests to the API (i.e. include the `Authorization` header) +- Redirect users to the login page if they are not authenticated +- Clear expired tokens when encountering unauthorized `401` response -### Authentication +### Make Authenticated Requests -Add the Bearer token from `localStorage` to request headers. +First, we need to implement a `getHeaders` function, that will add the Bearer token from `localStorage` (if there is one) to the `Authorization` header. ```typescript const getHeaders = () => @@ -20,9 +27,13 @@ const getHeaders = () => : {}; ``` -Extend the Hydra fetch function with custom headers for authentication. +Then, extend the Hydra `fetch` function to use the `getHeaders` function to add the `Authorization` header to the requests. ```typescript +import { + fetchHydra as baseFetchHydra, +} from "@api-platform/admin"; + const fetchHydra = (url, options = {}) => baseFetchHydra(url, { ...options, @@ -31,11 +42,14 @@ const fetchHydra = (url, options = {}) => ``` -### Login Redirection +### Redirect To Login Page -Redirect users to a `/login` path, if no token is available in the `localStorage`. +Then, we'll create a `` component, that will redirect users to the `/login` route if no token is available in the `localStorage`, and call the dataProvider's `introspect` function otherwise. + +```tsx +import { Navigate } from "react-router-dom"; +import { useIntrospection } from "@api-platform/admin"; -```typescript const RedirectToLogin = () => { const introspect = useIntrospection(); @@ -47,13 +61,16 @@ const RedirectToLogin = () => { }; ``` -### API Documentation Parsing +### Clear Expired Tokens -Extend the `parseHydraDocumentaion` function from the [API Doc Parser library](https://github.com/api-platform/api-doc-parser) -to handle the documentation parsing. Customize it to clear -expired tokens when encountering unauthorized `401` response. +Now, we will extend the `parseHydraDocumentaion` function (imported from the [@api-platform/api-doc-parser](https://github.com/api-platform/api-doc-parser) library). + +We will customize it to clear expired tokens when encountering unauthorized `401` response. ```typescript +import { parseHydraDocumentation } from "@api-platform/api-doc-parser"; +import { ENTRYPOINT } from "config/entrypoint"; + const apiDocumentationParser = (setRedirectToLogin) => async () => { try { setRedirectToLogin(false); @@ -72,11 +89,16 @@ const apiDocumentationParser = (setRedirectToLogin) => async () => { }; ``` -### Data Provider +### Extend The Data Provider -Initialize the hydra data provider with custom headers and the documentation parser. +Now, we can initialize the Hydra data provider with the custom `fetchHydra` (with custom headers) and `apiDocumentationParser` functions created earlier. ```typescript +import { + hydraDataProvider as baseHydraDataProvider, +} from "@api-platform/admin"; +import { ENTRYPOINT } from "config/entrypoint"; + const dataProvider = (setRedirectToLogin) => baseHydraDataProvider({ entrypoint: ENTRYPOINT, @@ -85,12 +107,12 @@ const dataProvider = (setRedirectToLogin) => }); ``` -### Export Admin Component +### Update The Admin Component -Export the Hydra admin component, and track the users' authentication status. +Lastly, we can stitch everything together in the `Admin` component. -```typescript -// components/admin/Admin.tsx +```tsx +// src/Admin.tsx import Head from "next/head"; import { useState } from "react"; @@ -106,14 +128,14 @@ import { parseHydraDocumentation } from "@api-platform/api-doc-parser"; import authProvider from "utils/authProvider"; import { ENTRYPOINT } from "config/entrypoint"; -// Auth, Parser, Provider calls +// Functions and components created in the previous steps: const getHeaders = () => {...}; const fetchHydra = (url, options = {}) => {...}; const RedirectToLogin = () => {...}; const apiDocumentationParser = (setRedirectToLogin) => async () => {...}; const dataProvider = (setRedirectToLogin) => {...}; -const Admin = () => { +export const Admin = () => { const [redirectToLogin, setRedirectToLogin] = useState(false); return ( @@ -142,29 +164,32 @@ const Admin = () => { ); }; -export default Admin; ``` -### Additional Notes +### Example Implementation For the implementation of the admin component, you can find a working example in the [API Platform's demo application](https://github.com/api-platform/demo/blob/4.0/pwa/components/admin/Admin.tsx). ## OpenApiAdmin -This section explains how to set up and customize the [OpenApiAdmin component](https://api-platform.com/docs/admin/components/#openapi) authentication layer. -It covers: -* Creating a custom HTTP Client -* Data and rest data provider configuration -* Implementation of an auth provider +This section explains how to set up and customize the [`` component](./components.md/#openapi) to enable authentication. -### Data Provider & HTTP Client +In the following steps, we will see how to: -Create a custom HTTP client to add authentication tokens to request headers. -Configure the `openApiDataProvider`, and -inject the custom HTTP client into the [Simple REST Data Provider for React-Admin](https://github.com/Serind/ra-data-simple-rest). +- Make authenticated requests to the API (i.e. include the `Authorization` header) +- Implement an authProvider to redirect users to the login page if they are not authenticated, and clear expired tokens when encountering unauthorized `401` response + +### Making Authenticated Requests + +First, we need to create a custom `httpClient` to add authentication tokens (via the the `Authorization` HTTP header) to requests. + +We will then configure `openApiDataProvider` to use [`ra-data-simple-rest`](https://github.com/marmelab/react-admin/blob/master/packages/ra-data-simple-rest/README.md), a simple REST dataProvider for React Admin, and make it use the `httpClient` we created earlier. -**File:** `src/components/jsonDataProvider.tsx` ```typescript +// src/dataProvider.ts + +const getAccessToken = () => localStorage.getItem("token"); + const httpClient = async (url: string, options: fetchUtils.Options = {}) => { options.headers = new Headers({ ...options.headers, @@ -177,32 +202,31 @@ const httpClient = async (url: string, options: fetchUtils.Options = {}) => { return await fetchUtils.fetchJson(url, options); }; -const jsonDataProvider = openApiDataProvider({ +const dataProvider = openApiDataProvider({ dataProvider: simpleRestProvider(API_ENTRYPOINT_PATH, httpClient), entrypoint: API_ENTRYPOINT_PATH, docEntrypoint: API_DOCS_PATH, }); ``` -> [!NOTE] -> The `simpleRestProvider` provider expect the API to include a `Content-Range` header in the response. -> You can find more about the header syntax in the [Mozilla’s MDN documentation: Content-Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range). -> -> The `getAccessToken` function retrieves the JWT token stored in the browser. +**Note:** The `simpleRestProvider` provider expect the API to include a `Content-Range` header in the response. You can find more about the header syntax in the [Mozilla’s MDN documentation: Content-Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range). + +**Note:** The `getAccessToken` function retrieves the JWT token stored in the browser's localStorage. Replace it with your own logic in case you don't store the token that way. -### Authentication and Authorization +### Creating The AuthProvider -Create and export an `authProvider` object that handles authentication and authorization logic. +Now let's create and export an `authProvider` object that handles authentication and authorization logic. -**File:** `src/components/authProvider.tsx` ```typescript +// src/authProvider.ts + interface JwtPayload { - exp?: number; - iat?: number; - roles: string[]; + sub: string; username: string; } +const getAccessToken = () => localStorage.getItem("token"); + const authProvider = { login: async ({username, password}: { username: string; password: string }) => { const request = new Request(API_AUTH_PATH, { @@ -242,7 +266,7 @@ const authProvider = { const decoded = jwtDecode(token); return Promise.resolve({ - id: "", + id: decoded.sub, fullName: decoded.username, avatar: "", }); @@ -253,20 +277,23 @@ const authProvider = { export default authProvider; ``` -### Export OpenApiAdmin Component +### Updating The Admin Component -**File:** `src/App.tsx` -```typescript -import {OpenApiAdmin} from '@api-platform/admin'; -import authProvider from "./components/authProvider"; -import jsonDataProvider from "./components/jsonDataProvider"; -import {API_DOCS_PATH, API_ENTRYPOINT_PATH} from "./config/api"; +Finally, we can update the `Admin` component to use the `authProvider` and `dataProvider` we created earlier. + +```tsx +// src/Admin.tsx + +import { OpenApiAdmin } from '@api-platform/admin'; +import authProvider from "./authProvider"; +import dataProvider from "./dataProvider"; +import { API_DOCS_PATH, API_ENTRYPOINT_PATH } from "./config/api"; export default () => ( ); diff --git a/admin/components.md b/admin/components.md index ec3d634465e..5aca722b8cf 100644 --- a/admin/components.md +++ b/admin/components.md @@ -1,55 +1,137 @@ -# Components +# Components Reference -## Resource Components +## HydraAdmin -### AdminGuesser +Creates a complete Admin, using [``](./components.md#adminguesser), but configured specially for [Hydra](https://www.hydra-cg.com/). -`` renders automatically an [Admin component](https://marmelab.com/react-admin/Admin.html) for resources exposed by a web API documented with any format supported by `@api-platform/api-doc-parser` -(for Hydra documented APIs, use the [HydraAdmin component](components.md#hydraadmin) instead, -for OpenAPI documented APIs, use the [OpenApiAdmin component](components.md#openapiadmin) instead). -It also creates a [schema analyzer](components.md#schema-analyzer) context, where the `schemaAnalyzer` service (for getting information about the provided API documentation) is stored. +**Tip:** For OpenAPI documented APIs, use the [`` component](./components.md#openapiadmin) instead. -`` renders all exposed resources by default, but you can choose what resource you want to render by passing [ResourceGuesser components](components.md#resourceguesser) as children. -Deprecated resources are hidden by default, but you can add them back using an explicit `` component. +**Tip:** If you want to use other formats (see supported formats: `@api-platform/api-doc-parser`) use [``](./components.md#adminguesser) instead. -```javascript -// App.js -import { AdminGuesser, ResourceGuesser } from '@api-platform/admin'; +```tsx +// App.tsx +import { HydraAdmin, ResourceGuesser } from '@api-platform/admin'; const App = () => ( - - - - + + + {/* ... */} + ); export default App; ``` -#### Props +### HydraAdmin Props + +| Name | Type | Value | required | Description | +| ------------ | ------------------- | ------------ | -------- | ---------------------------- | +| entrypoint | string | - | yes | entrypoint of the API | +| mercure | object|boolean | \* | no | configuration to use Mercure | +| dataProvider | object | dataProvider | no | hydra data provider to use | + +\* `false` to explicitly disable, `true` to enable with default parameters or an object with the following properties: + +- `hub`: the URL to your Mercure hub +- `jwt`: a subscriber JWT to access your Mercure hub +- `topicUrl`: the topic URL of your resources + +### Hydra Data Provider + +An implementation for the React Admin [dataProvider methods](https://marmelab.com/react-admin/DataProviderWriting.html): `create`, `delete`, `getList`, `getManyReference`, `getOne` and `update`. + +The `dataProvider` is used by API Platform Admin to communicate with the API. + +In addition, the specific `introspect` method parses your API documentation. + +Note that the `dataProvider` can be overridden to fit your API needs. + +### Hydra Schema Analyzer + +Analyses your resources and retrieves their types according to the [Schema.org](https://schema.org) vocabulary. + +## OpenApiAdmin -| Name | Type | Value | required | Description | -| ----------------- | ------- | -------------- | -------- | -------------------------------------------------------------------------------- | -| dataProvider | object | dataProvider | yes | communicates with your API | -| schemaAnalyzer | object | schemaAnalyzer | yes | retrieves resource type according to [Schema.org](https://schema.org) vocabulary | -| theme | object | theme | no | theme of your Admin App | -| includeDeprecated | boolean | true or false | no | displays or not deprecated resources | +Creates a complete Admin, as [``](./components.md#adminguesser), but configured specially for [OpenAPI](https://www.openapis.org/). + +**Tip:** If you want to use other formats (see supported formats: `@api-platform/api-doc-parser`) use [``](./components.md#adminguesser) instead. + +```tsx +// App.tsx +import { OpenApiAdmin, ResourceGuesser } from '@api-platform/admin'; -### ResourceGuesser +const App = () => ( + + + {/* ... */} + +); -Based on React Admin [Resource component](https://marmelab.com/react-admin/Resource.html), `` provides default props [CreateGuesser](components.md#createguesser), [ListGuesser](components.md#listguesser), [EditGuesser](components.md#editguesser) and [ShowGuesser](components.md#showguesser). +export default App; +``` -Otherwise, you can pass it your own CRUD components using `create`, `list`, `edit`, `show` props. +### OpenApiAdmin Props -```javascript -// App.js +| Name | Type | Value | required | Description | +| ------------- | ------------------- | ----- | -------- | ---------------------------- | +| docEntrypoint | string | - | yes | doc entrypoint of the API | +| entrypoint | string | - | yes | entrypoint of the API | +| dataProvider | dataProvider | - | no | data provider to use | +| mercure | object|boolean | \* | no | configuration to use Mercure | + +\* `false` to explicitly disable, `true` to enable with default parameters or an object with the following properties: + +- `hub`: the URL to your Mercure hub +- `jwt`: a subscriber JWT to access your Mercure hub +- `topicUrl`: the topic URL of your resources + +### Open API Data Provider + +An implementation for the React Admin [dataProvider methods](https://marmelab.com/react-admin/DataProviderWriting.html): `create`, `delete`, `getList`, `getManyReference`, `getOne` and `update`. + +The `dataProvider` is used by API Platform Admin to communicate with the API. + +In addition, the specific `introspect` method parses your API documentation. + +Note that the `dataProvider` can be overridden to fit your API needs. + +### Open API Schema Analyzer + +Analyses your resources and retrieves their types according to the [Schema.org](https://schema.org) vocabulary. + +## AdminGuesser + +`` automatically renders an [`` component](https://marmelab.com/react-admin/Admin.html) for resources exposed by a web API documented with any format supported by `@api-platform/api-doc-parser`. + +```tsx +// App.tsx +import { AdminGuesser } from '@api-platform/admin'; +import dataProvider from './dataProvider'; +import schemaAnalyzer from './schemaAnalyzer'; + +const App = () => ( + +); + +export default App; +``` + +Use it if your API is neither documented with Hydra nor OpenAPI, but in a format supported by `@api-platform/api-doc-parser`. + +**Tip:** For Hydra documented APIs, use the [`` component](./components.md#hydraadmin) instead. + +**Tip:** For OpenAPI documented APIs, use the [`` component](./components.md#openapiadmin) instead. + +`` renders all exposed resources by default, but you can choose what resource you want to render by passing [`` components](./components.md#resourceguesser) as children. + +```tsx +// App.tsx import { AdminGuesser, ResourceGuesser } from '@api-platform/admin'; +import dataProvider from './dataProvider'; +import schemaAnalyzer from './schemaAnalyzer'; const App = () => ( @@ -57,266 +139,310 @@ const App = () => ( name="books" list={BooksList} show={BooksShow} - create={BooksCreate} edit={BooksEdit} + create={BooksCreate} /> - + ); export default App; ``` -#### ResourceGuesser Props +**Tip:** Deprecated resources are hidden by default, but you can add them back using an explicit `` component. -| Name | Type | Value | required | Description | -| ---- | ------ | ----- | -------- | ------------------------ | -| name | string | - | yes | endpoint of the resource | +### AdminGuesser Props -You can also use props accepted by React Admin [Resource component](https://marmelab.com/react-admin/Resource.html). For example, the props `list`, `show`, `create` or `edit`. +| Name | Type | Value | required | Description | +| ----------------- | --------------- | -------------- | -------- | -------------------------------------------------------------------------------- | +| dataProvider | object | dataProvider | yes | the dataProvider to use to communicate with your API | +| schemaAnalyzer | object | schemaAnalyzer | yes | retrieves resource type according to [Schema.org](https://schema.org) vocabulary | +| authProvider | object | authProvider | no | the authProvider to use to manage authentication | +| admin | React component | - | no | React component to use to render the Admin | +| includeDeprecated | boolean | true or false | no | displays or not deprecated resources | -## Page Components +`` also accepts all props accepted by React Admin's [`` component](https://marmelab.com/react-admin/Admin.html), such as [`theme`](https://marmelab.com/react-admin/Admin.html#theme), [`darkTheme`](https://marmelab.com/react-admin/Admin.html#darktheme), [`layout`](https://marmelab.com/react-admin/Admin.html#layout) and many others. -### ListGuesser +## ResourceGuesser -Based on React Admin [List](https://marmelab.com/react-admin/List.html), `` displays a list of resources in a [Datagrid](https://marmelab.com/react-admin/List.html#the-datagrid-component), according to children passed to it (usually [FieldGuesser](components.md#fieldguesser) or any [field component](https://marmelab.com/react-admin/Fields.html#basic-fields) -available in React Admin). +Based on React Admin [`` component](https://marmelab.com/react-admin/Resource.html), `` provides the default component to render for each view: [``](./components.md#createguesser), [``](./components.md#listguesser), [``](./components.md#editguesser) and [``](./components.md#showguesser). -Use `hasShow` and `hasEdit` props if you want to display `show` and `edit` buttons (both set to `true` by default). +You can also pass your own component to use for any view, using the `create`, `list`, `edit` or `show` props. -By default, `` comes with [Pagination](components.md#pagination). - -```javascript -// BooksList.js -import { FieldGuesser, ListGuesser } from '@api-platform/admin'; -import { ReferenceField, TextField } from 'react-admin'; +```tsx +// App.tsx +import { AdminGuesser, ResourceGuesser } from '@api-platform/admin'; -export const BooksList = (props) => ( - - - - - - - +const App = () => ( + + {/* Uses the default guesser components for each CRUD view */} + + {/* Overrides only the list view */} + + ); + +export default App; ``` -#### ListGuesser Props +### ResourceGuesser Props -| Name | Type | Value | required | Description | -| ------- | ------- | ----- | -------- | --------------------------------------- | -| filters | element | - | no | filters that can be applied to the list | +| Name | Type | Value | required | Description | +| ------ | ------------------- | ----- | -------- | ------------------------------------------- | +| name | string | - | yes | endpoint of the resource | +| list | React ComponentType | - | no | the component to render for the list view | +| create | React ComponentType | - | no | the component to render for the create view | +| edit | React ComponentType | - | no | the component to render for the edit view | +| show | React ComponentType | - | no | the component to render for the show view | -You can also use props accepted by React Admin [List](https://marmelab.com/react-admin/List.html). +`` also accepts all props accepted by React Admin's [`` component](https://marmelab.com/react-admin/Resource.html), such as [`recordRepresentation`](https://marmelab.com/react-admin/Resource.html#recordrepresentation), [`icon`](https://marmelab.com/react-admin/Resource.html#icon) or [`options`](https://marmelab.com/react-admin/Resource.html#options). -### CreateGuesser +## ListGuesser -Displays a creation page for a single item. Uses React Admin [Create](https://marmelab.com/react-admin/CreateEdit.html) and [SimpleForm](https://marmelab.com/react-admin/CreateEdit.html#the-simpleform-component) components. -For simple inputs, you can pass as children API Platform Admin [InputGuesser](components.md#inputguesser), or any React Admin [Input components](https://marmelab.com/react-admin/Inputs.html#input-components) for more complex inputs. +Based on React Admin [``](https://marmelab.com/react-admin/List.html), `` displays a list of records in a [``](https://marmelab.com/react-admin/Datagrid.html). -```javascript -// BooksCreate.js -import { CreateGuesser, InputGuesser } from '@api-platform/admin'; +If no children are passed, it will display fields guessed from the schema. -export const BooksCreate = (props) => ( - - - - - - - +```tsx +// BooksList.tsx +import { ListGuesser } from '@api-platform/admin'; + +export const BooksList = () => ( + /* Will display fields guessed from the schema */ + ); ``` -#### CreateGuesser Props +It also accepts a list of fields as children. They can be either [``](./components.md#fieldguesser) elements, or any [field component](https://marmelab.com/react-admin/Fields.html) +available in React Admin, such as [``](https://marmelab.com/react-admin/TextField.html), [``](https://marmelab.com/react-admin/DateField.html) or [``](https://marmelab.com/react-admin/ReferenceField.html) for instance. -You can use props accepted by React Admin [Create](https://marmelab.com/react-admin/CreateEdit.html). +```tsx +// BooksList.tsx +import { FieldGuesser, ListGuesser } from '@api-platform/admin'; +import { DateField, NumberField } from 'react-admin'; + +export const BooksList = () => ( + + {/* FieldGuesser comes from API Platform Admin */} + + + + + {/* DateField and NumberField come from React Admin */} + + + +); +``` -### EditGuesser +### ListGuesser Props -Displays an edition page for a single item. Uses React Admin [Edit](https://marmelab.com/react-admin/CreateEdit.html) and [SimpleForm](https://marmelab.com/react-admin/CreateEdit.html#the-simpleform-component) components. -For simple inputs, you can use API Platform Admin [InputGuesser](components.md#inputguesser), or any React Admin [Input components](https://marmelab.com/react-admin/Inputs.html#input-components) for more complex inputs. +`` accepts all props accepted by both React Admin [`` component](https://marmelab.com/react-admin/List.html) and [`` component](https://marmelab.com/react-admin/Datagrid.html). -```javascript -// BooksEdit.js -import { EditGuesser, InputGuesser } from '@api-platform/admin'; +For instance you can pass props such as [`filters`](https://marmelab.com/react-admin/List.html#filters-filter-inputs), [`sort`](https://marmelab.com/react-admin/List.html#sort) or [`pagination`](https://marmelab.com/react-admin/List.html#pagination). -export const BooksEdit = (props) => ( - - - - - - - -); -``` +## CreateGuesser -#### EditGuesser Props +Displays a creation page for a single item. Uses React Admin [``](https://marmelab.com/react-admin/Create.html) and [``](https://marmelab.com/react-admin/SimpleForm.html) components. -You can use props accepted by React Admin [Edit](https://marmelab.com/react-admin/CreateEdit.html). +If no children are passed, it will display inputs guessed from the schema. -### ShowGuesser +```tsx +// BooksCreate.tsx +import { CreateGuesser } from '@api-platform/admin'; -Displays a detailed page for one item. Based on React Admin [Show component](https://marmelab.com/react-admin/Show.html). You can pass [FieldGuesser](components.md#fieldguesser) as children for simple fields, or use any of React Admin [basic fields](https://marmelab.com/react-admin/Fields.html#basic-fields) for more complex fields. +export const BooksCreate = () => ( + /* Will display inputs guessed from the schema */ + +); +``` -```javascript -// BooksShow.js -import { FieldGuesser, ShowGuesser } from '@api-platform/admin'; +It also accepts a list of inputs as children, which can be either [``](./components.md#inputguesser) elements, or any [input component](https://marmelab.com/react-admin/Inputs.html) available in React Admin, +such as [``](https://marmelab.com/react-admin/TextInput.html), [``](https://marmelab.com/react-admin/DateInput.html) or [``](https://marmelab.com/react-admin/ReferenceInput.html) for instance. -export const BooksShow = (props) => ( - - - - - - - +```tsx +// BooksCreate.tsx +import { CreateGuesser, InputGuesser } from '@api-platform/admin'; +import { DateInput, TextInput, required } from 'react-admin'; + +export const BooksCreate = () => ( + + {/* InputGuesser comes from API Platform Admin */} + + + + + {/* DateInput and TextInput come from React Admin */} + + + ); ``` -#### ShowGuesser Props +### CreateGuesser Props -You can use props accepted by React Admin [Show component](https://marmelab.com/react-admin/Show.html). +`` accepts all props accepted by both React Admin [`` component](https://marmelab.com/react-admin/Create.html) and [`` component](https://marmelab.com/react-admin/SimpleForm.html). -## Hydra +For instance you can pass props such as [`redirect`](https://marmelab.com/react-admin/Create.html#redirect), [`defaultValues`](https://marmelab.com/react-admin/SimpleForm.html#defaultvalues) or [`warnWhenUnsavedChanges`](https://marmelab.com/react-admin/SimpleForm.html#warnwhenunsavedchanges). -### HydraAdmin +## EditGuesser -Creates a complete Admin, as [AdminGuesser](components.md#adminguesser), but configured specially for [Hydra](https://www.hydra-cg.com/). -If you want to use other formats (see supported formats: `@api-platform/api-doc-parser`) use [AdminGuesser](components.md#adminguesser) instead. +Displays an edition page for a single item. Uses React Admin [``](https://marmelab.com/react-admin/Edit.html) and [``](https://marmelab.com/react-admin/SimpleForm.html) components. -```javascript -// App.js -import { HydraAdmin, ResourceGuesser } from '@api-platform/admin'; +If no children are passed, it will display inputs guessed from the schema. -const App = () => ( - - - {/* ... */} - -); +```tsx +// BooksEdit.tsx +import { EditGuesser } from '@api-platform/admin'; -export default App; +export const BooksEdit = () => ( + /* Will display inputs guessed from the schema */ + +); ``` -#### HydraAdmin Props +It also accepts a list of inputs as children, which can be either [``](./components.md#inputguesser) elements, or any [input component](https://marmelab.com/react-admin/Inputs.html) available in React Admin, +such as [``](https://marmelab.com/react-admin/TextInput.html), [``](https://marmelab.com/react-admin/DateInput.html) or [``](https://marmelab.com/react-admin/ReferenceInput.html) for instance. -| Name | Type | Value | required | Description | -| ------------ | ------------------- | ------------ | -------- | ---------------------------- | -| entrypoint | string | - | yes | entrypoint of the API | -| mercure | object|boolean | \* | no | configuration to use Mercure | -| dataProvider | object | dataProvider | no | hydra data provider to use | - -\* `false` to explicitly disable, `true` to enable with default parameters or an object with the following properties: +```tsx +// BooksEdit.tsx +import { EditGuesser, InputGuesser } from '@api-platform/admin'; +import { DateInput, TextInput, required } from 'react-admin'; -- `hub`: the URL to your Mercure hub -- `jwt`: a subscriber JWT to access your Mercure hub -- `topicUrl`: the topic URL of your resources +export const BooksEdit = () => ( + + {/* InputGuesser comes from API Platform Admin */} + + + -### Hydra Data Provider + {/* DateInput and TextInput come from React Admin */} + + + +); +``` -Based on React Admin `create`, `delete`, `getList`, `getManyReference`, `getOne`, `update` methods, the `dataProvider` is used by API Platform Admin to communicate with the API. -In addition, the specific `introspect` method parses your API documentation. -Note that the `dataProvider` can be overridden to fit your API needs. +### EditGuesser Props -### Hydra Schema Analyzer +`` accepts all props accepted by both React Admin [`` component](https://marmelab.com/react-admin/Edit.html) and [`` component](https://marmelab.com/react-admin/SimpleForm.html). -Analyses your resources and retrieves their types according to the [Schema.org](https://schema.org) vocabulary. +For instance you can pass props such as [`redirect`](https://marmelab.com/react-admin/Edit.html#redirect), [`mutationMode`](https://marmelab.com/react-admin/Edit.html#mutationmode), [`defaultValues`](https://marmelab.com/react-admin/SimpleForm.html#defaultvalues) or [`warnWhenUnsavedChanges`](https://marmelab.com/react-admin/SimpleForm.html#warnwhenunsavedchanges). -## OpenAPI +## ShowGuesser -### OpenApiAdmin +Displays a detailed page for one item. Based on React Admin [``](https://marmelab.com/react-admin/Show.html) ans [``](https://marmelab.com/react-admin/SimpleShowLayout.html) components. -Creates a complete Admin, as [AdminGuesser](components.md#adminguesser), but configured specially for [OpenAPI](https://www.openapis.org/). -If you want to use other formats (see supported formats: `@api-platform/api-doc-parser`) use [AdminGuesser](components.md#adminguesser) instead. +If you pass no children, it will display fields guessed from the schema. -```javascript -// App.js -import { OpenApiAdmin, ResourceGuesser } from '@api-platform/admin'; +```tsx +// BooksShow.tsx +import { ShowGuesser } from '@api-platform/admin'; -const App = () => ( - - - {/* ... */} - +export const BooksShow = () => ( + /* Will display fields guessed from the schema */ + ); - -export default App; ``` -#### OpenApiAdmin Props - -| Name | Type | Value | required | Description | -| ------------- | ------------------- | ----- | -------- | ---------------------------- | -| dataProvider | dataProvider | - | yes | data provider to use | -| docEntrypoint | string | - | yes | doc entrypoint of the API | -| entrypoint | string | - | yes | entrypoint of the API | -| mercure | object|boolean | \* | no | configuration to use Mercure | +It also accepts a list of fields as children, which can be either [``](./components.md#fieldguesser) elements, or any [field component](https://marmelab.com/react-admin/Fields.html) available in React Admin, +such as [``](https://marmelab.com/react-admin/TextField.html), [``](https://marmelab.com/react-admin/DateField.html) or [``](https://marmelab.com/react-admin/ReferenceField.html) for instance. -\* `false` to explicitly disable, `true` to enable with default parameters or an object with the following properties: +```tsx +// BooksShow.tsx +import { FieldGuesser, ShowGuesser } from '@api-platform/admin'; +import { DateField, NumberField } from 'react-admin'; -- `hub`: the URL to your Mercure hub -- `jwt`: a subscriber JWT to access your Mercure hub -- `topicUrl`: the topic URL of your resources +export const BooksShow = () => ( + + {/* FieldGuesser comes from API Platform Admin */} + + + -### Open API Data Provider + {/* DateField and NumberField come from React Admin */} + + + +); +``` -Based on React Admin `create`, `delete`, `getList`, `getManyReference`, `getOne`, `update` methods, the `dataProvider` is used by API Platform Admin to communicate with the API. -In addition, the specific `introspect` method parses your API documentation. -Note that the `dataProvider` can be overridden to fit your API needs. +### ShowGuesser Props -### Open API Schema Analyzer +`` accepts all props accepted by both React Admin [`` component](https://marmelab.com/react-admin/Show.html) and [`` component](https://marmelab.com/react-admin/SimpleShowLayout.html). -Analyses your resources and retrieves their types according to the [Schema.org](https://schema.org) vocabulary. -## Other Components +## FieldGuesser -### FieldGuesser +Renders a field according to its type, using the [schema analyzer](./components.md#hydra-schema-analyzer). -Renders fields according to their types, using the [schema analyzer](components.md#schemaanalyzer). -Based on React Admin [field components](https://marmelab.com/react-admin/Fields.html). +Based on React Admin [field components](https://marmelab.com/react-admin/Fields.html), such as [``](https://marmelab.com/react-admin/TextField.html), [``](https://marmelab.com/react-admin/DateField.html) or [``](https://marmelab.com/react-admin/ReferenceField.html). -```javascript -// BooksShow.js +```tsx +// BooksShow.tsx import { FieldGuesser, ShowGuesser } from '@api-platform/admin'; -export const BooksShow = (props) => ( - - +export const BooksShow = () => ( + + {/* Renders a TextField */} + {/* Renders a NumberField */} - + {/* Renders a DateField */} ); ``` -#### FieldGuesser Props +### FieldGuesser Props | Name | Type | Value | required | Description | | ------ | ------ | ----- | -------- | ------------------------------------ | | source | string | - | yes | name of the property of the resource | -You can also use props accepted by React Admin [basic fields](https://marmelab.com/react-admin/Fields.html#basic-fields). +`` also accepts any [common field prop](https://marmelab.com/react-admin/Fields.html#common-field-props) supported by React Admin, such as [`label`](https://marmelab.com/react-admin/Fields.html#label) for instance. + +## InputGuesser -### InputGuesser +Renders an input according to its type, using the [schema analyzer](./components.md#hydra-schema-analyzer). -Uses React Admin [input components](https://marmelab.com/react-admin/Inputs.html) to generate inputs according to your API documentation (e.g. number HTML input for numbers, checkbox for booleans, selectbox for relationships...). +Uses React Admin [input components](https://marmelab.com/react-admin/Inputs.html), such as [``](https://marmelab.com/react-admin/TextInput.html), [``](https://marmelab.com/react-admin/DateInput.html) or [``](https://marmelab.com/react-admin/ReferenceInput.html). -#### InputGuesser Props +```tsx +// BooksCreate.tsx +import { CreateGuesser, InputGuesser } from '@api-platform/admin'; + +export const BooksCreate = () => ( + + {/* Renders a TextInput */} + + {/* Renders a NumberInput */} + + {/* Renders a DateInput */} + + +); +``` + +### InputGuesser Props | Name | Type | Value | required | Description | | ------ | ------ | ----- | -------- | ------------------------------------ | | source | string | - | yes | name of the property of the resource | + +`` also accepts any [common input prop](https://marmelab.com/react-admin/Inputs.html#common-input-props) supported by React Admin, such as [`defaultValue`](https://marmelab.com/react-admin/Inputs.html#defaultvalue), [`readOnly`](https://marmelab.com/react-admin/Inputs.html#readonly), [`helperText`](https://marmelab.com/react-admin/Inputs.html#helpertext) or [`label`](https://marmelab.com/react-admin/Inputs.html#label). + +You can also pass props that are specific to a certain input component. For example, if you know an `` will render a `` and you would like that input to be multiline, you can set the [`multiline`](https://marmelab.com/react-admin/TextInput.html#multiline) prop. + +```tsx + +``` + diff --git a/admin/customizing.md b/admin/customizing.md index eac82936902..83001b3540e 100644 --- a/admin/customizing.md +++ b/admin/customizing.md @@ -1,75 +1,192 @@ -# Customizing the Admin +# Customizing the Guessers -Customizing API Platform Admin is easy and idiomatic. The tool gives you the ability to customize everything, from the list of resource types that must be administrable to every single input or button. +Using `` or `` directly is a great way to quickly get started with API Platform Admin. They will introspect your API schema (using `@api-platform/api-doc-parser`) and automatically generate CRUD pages for all the resources it exposes. They will even [configure filtering, sorting, and real-time updates with Mercure](./schema.md) if your API supports it. -To do so, you can use the React components provided by API Platform Admin itself, [React Admin](https://marmelab.com/react-admin/), [Material UI](https://material-ui.com/), [community libraries](https://github.com/brillout/awesome-react-components), or [write your own](https://reactjs.org/tutorial/tutorial.html). +For some this may be enough, but you will often find yourself wanting to customize the generated pages further. For instance, you may want to: -## Customizing the Admin's Main Page and the Resource List +- Hide or reorder resources in the menu +- Hide or reorder columns in the list view +- Hide or reorder fields in the show, create and edit views +- Customize the generated list, e.g. add a default sort order +- Customize the generated create and edit views, e.g. to add a warning when there are unsaved changes +- Customize the generated inputs, e.g. set a custom label or make a text input multiline -By default, API Platform Admin automatically builds a tailored [Resource component](https://marmelab.com/react-admin/Resource.html) -(and all its appropriate children) for each resource type exposed by a web API. -Under the hood it uses the `@api-platform/api-doc-parser` library to parse the API documentation. -The API documentation can use Hydra, OpenAPI and any other format supported by the library. -Resources are listed in the order they appear in the machine-readable documentation. +Such changes can't be achieved by modifying the Schema, they require customizing the React components generated by API Platform Admin. -However, it's also possible to display only specific resources, and to order them, while still benefiting from all discovery features provided by API Platform Admin. -To cherry-pick the resources to make available through the admin, pass a list of `` components as children of the root component: +Fortunately, API Platform Admin has you covered! -```javascript -import { HydraAdmin, ResourceGuesser } from '@api-platform/admin'; +## From `` To `` -export default () => ( - - - +If you are using `` or `` directly, there is a simple way to start customizing the generated pages. - {/* While deprecated resources are hidden by default, using an explicit ResourceGuesser component allows to add them back. */} - - +Simply open your browser's developer tools and look at the console. You will see messages like this: + +```txt +If you want to override at least one resource, paste this content in the component of your app: + + + + +``` + +This message tells you which resources are exposed by your API and how to customize the generated pages for each of them. + +Let's say we'd like to hide the `greetings` resource from the menu. We can do this by replacing the `` component (`` in our case) children with a list of ``: + +```diff +-import { HydraAdmin } from "@api-platform/admin"; ++import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; + +const App = () => ( +- ++ ++ ++ ++ ); ``` -Instead of using the `` component provided by API Platform Admin, you can also pass custom React Admin's [Resource components](https://marmelab.com/react-admin/Resource.html), or any other React components that are supported by React Admin's [Admin](https://marmelab.com/react-admin/Admin.html). +Now the `greetings` resource will no longer be displayed in the menu. -## Customizing the List View +![Customized Admin menu](./images/admin-menu.png) -The list view can be customized following the same pattern: +`` also accepts all props react-admin's [``](https://marmelab.com/react-admin/Resource.html) component accepts. This means that, for instance, you can use the `list` prop to use your own list component, but keep using the create, edit and show components introspected by ``: -```javascript -import { - HydraAdmin, - ResourceGuesser, - ListGuesser, - FieldGuesser, -} from '@api-platform/admin'; +```diff +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; ++import { BookList } from "./BookList"; -const ReviewsList = (props) => ( - - - +const App = () => ( + +- ++ + + +); +``` - {/* While deprecated fields are hidden by default, using an explicit FieldGuesser component allows to add them back. */} - - +Likewise, you can use the `icon` prop to customize the icon displayed in the menu: + +```diff ++import AutoStoriesIcon from '@mui/icons-material/AutoStories'; ++import ReviewsIcon from '@mui/icons-material/Reviews'; +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; + +const App = () => ( + +- ++ +- ++ + ); +``` -export default () => ( - - - {/* ... */} - +Here is the result: + +![Admin menu with custom icons](./images/admin-menu-icons.png) + +## Customizing the `` + +By default, `` will render a `` component as the list view for a resource. + +This component will automatically introspect the API schema and generate a list view with all the fields of the resource. + +![Admin default generated list view](./images/admin-default-list.png) + +This is already usable, but may not provide the best user experience yet. + +To start customizing the list view, you can look at the DevTools console. You will see messages like this: + +```txt +If you want to override at least one field, create a BookList component with this content: + +import { ListGuesser, FieldGuesser } from "@api-platform/admin"; + +export const BookList = () => ( + + + + + + + + +); + +Then, update your main admin component: + +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; +import { BookList } from './BookList'; + +const App = () => ( + + + {/* ... */} + +); +``` + +If you follow these instructions, you will end up with the same view as before, but now you can start customizing it. + +For instance, we'll hide the 'Description' column as it takes too much space (we'll reserve that to the show view). And we will also add a default sort order to show the most recent books first. + +Here's how to achieve this: + +```diff +export const BookList = () => ( +- ++ + + +- + + + + ); ``` -In this example, only the fields `author`, `book` and `letter` (that is hidden by default because it is deprecated) will be displayed. The defined order will be respected. +And here is the result: + +![Admin with customized list guesser](./images/admin-custom-list-guesser.png) + +That's already better isn't it? 🙂 + +## Customizing the `` + +Removing or reordering `` components is not the only thing we can do. We can also customize them. -In addition to the `` component, [all React Admin Fields components](https://marmelab.com/react-admin/Fields.html) can be passed as children of ``. +Indeed, `` will forward additional props to the underlying React Admin [Field component](https://marmelab.com/react-admin/Fields.html). -## Customizing the Show View +This means we can use any [common field prop](https://marmelab.com/react-admin/Fields.html#common-field-props) on them. -For the show view: +For instance, let's add a `label` prop to customize the label of the ISBN column to be all uppercase: -```javascript +```diff +export const BookList = () => ( + +- ++ + + + + + +); +``` + +And here is the result: + +![Admin with customized list guesser and field guesser](./images/admin-custom-list-field-guesser.png) + +## Customizing the `` + +Following the same principles as the `` (including looking at the DevTools console) we can customize the show view. + +In the following example, the show view for the `books` resource was customized to make the label of the `isbn` field uppercase: + +```tsx import { HydraAdmin, ResourceGuesser, @@ -77,109 +194,150 @@ import { FieldGuesser, } from '@api-platform/admin'; -const ReviewsShow = (props) => ( - +const BookShow = () => ( + + + + - - - - {/* While deprecated fields are hidden by default, using an explicit FieldGuesser component allows to add them back. */} - - - + ); export default () => ( - - {/* ... */} + + ); ``` -In addition to the `` component, [all React Admin Fields components](https://marmelab.com/react-admin/Fields.html) can be passed as children of ``. +Here is the result: -## Customizing the Create Form +![Admin with customized show guesser](./images/admin-custom-show-guesser.png) -Again, the same logic applies to forms. Here is how to customize the create form: +## From `` To React Admin Fields -```javascript -import { - HydraAdmin, - ResourceGuesser, - CreateGuesser, - InputGuesser, -} from '@api-platform/admin'; +As mentioned in the [Customizing the ``](./customizing.md#customizing-the-fieldguesser) section, we can use any [common field prop](https://marmelab.com/react-admin/Fields.html#common-field-props) from React Admin to customize the `` elements. -const ReviewsCreate = (props) => ( - - - - +However in some cases you may want to go further and use a React Admin [field components](https://marmelab.com/react-admin/Fields.html), such as [``](https://marmelab.com/react-admin/TextField.html), [``](https://marmelab.com/react-admin/DateField.html) or [``](https://marmelab.com/react-admin/ReferenceField.html) directly, to access more advanced features. - {/* While deprecated fields are hidden by default, using an explicit InputGuesser component allows to add them back. */} - +For instance, you can replace a `` with a [``](https://marmelab.com/react-admin/DateField.html) to control more precisely how the publication date is displayed, leveraging the [`showTime`](https://marmelab.com/react-admin/DateField.html#showtime) prop: - - - -); +```diff +import { ShowGuesser, FieldGuesser } from '@api-platform/admin'; ++import { DateField } from 'react-admin'; -export default () => ( - - - {/* ... */} - +const ReviewShow = () => ( + +- ++ + ); ``` -In addition to the `` component, [all React Admin Input components](https://marmelab.com/react-admin/Inputs.html) can be passed as children of ``. +## Customizing the `` and `` -For instance, using an autocomplete input is straightforward, [check out the dedicated documentation entry](handling-relations.md#using-an-autocomplete-input-for-relations)! +Customizing the `` and `` is very similar to customizing the ``. -## Customizing the Edit Form +We can start by looking at the DevTools console to get the initial code of the components. -Finally, you can customize the edit form the same way: +```txt +If you want to override at least one input, create a ReviewEdit component with this content: -```javascript -import { - HydraAdmin, - ResourceGuesser, - EditGuesser, - InputGuesser, -} from '@api-platform/admin'; +import { EditGuesser, InputGuesser } from "@api-platform/admin"; -const ReviewsEdit = (props) => ( - - - - +export const ReviewEdit = () => ( + + + + + + + +); - {/* While deprecated fields are hidden by default, using an explicit InputGuesser component allows to add them back. */} - +Then, update your main admin component: - - - +import { HydraAdmin, ResourceGuesser } from "@api-platform/admin"; +import { ReviewEdit } from './ReviewEdit'; + +const App = () => ( + + + {/* ... */} + +); +``` + +Let's customize this `ReviewEdit` component to: + +- reorder the inputs +- make the `body` input multiline +- mark the `publicationDate` input as read-only + + +```diff +export const ReviewEdit = () => ( + +- +- +- +- +- ++ ++ ++ ++ ++ + ); +``` -export default () => ( - - - {/* ... */} - +Here is the result: + +![Admin with customized edit guesser](./images/admin-custom-edit-guesser.png) + +**Tip:** Here, we leveraged the `multiline` and `readOnly` props of the `` component. But you can use any [common input prop](https://marmelab.com/react-admin/Inputs.html#common-input-props) supported by React Admin [Inputs](https://marmelab.com/react-admin/Inputs.html) on them. + +## From `` To React Admin Inputs + +As mentioned in the previous section, we can use any [common input prop](https://marmelab.com/react-admin/Inputs.html#common-input-props) from React Admin to customize the `` elements. + +However in some cases you may want to go further and use a React Admin [input components](https://marmelab.com/react-admin/Inputs.html), such as [``](https://marmelab.com/react-admin/TextInput.html), [``](https://marmelab.com/react-admin/DateInput.html) or [``](https://marmelab.com/react-admin/ReferenceInput.html) directly, to access more advanced features. + +A good example is to use an [Autocomplete Input to edit a relation](./handling-relations.md#using-an-autocomplete-input-for-relations). + +This leverages both [``](https://marmelab.com/react-admin/ReferenceInput.html) and [``](https://marmelab.com/react-admin/AutocompleteInput.html) to offer a better user experience when editing the relation: + +```diff +import { EditGuesser, InputGuesser } from '@api-platform/admin'; ++import { ReferenceInput, AutocompleteInput } from 'react-admin'; + +const ReviewsEdit = () => ( + + +- ++ ++ ({ title: searchText })} ++ optionText="title" ++ /> ++ + ); ``` -In addition to the `` component, [all React Admin Input components](https://marmelab.com/react-admin/Inputs.html) can be passed as children of ``. +![Admin With AutocompleteInput](./images/AutocompleteInput.png) + +> [!WARNING] +> When replacing `` with a React Admin input component, the validation rules are not automatically applied. You will need to manually add them back. Fortunately, this is very easy to do. Read the [Validation With React Admin Inputs](./validation.md#validation-with-react-admin-inputs) section to learn more. -For instance, using an autocomplete input is straightforward, [checkout the dedicated documentation entry](handling-relations.md#using-an-autocomplete-input-for-relations)! +## Next Step -## Going Further +The above examples are limited to customizing the various API Platform Admin Guessers, but this is just the tip of the iceberg. -API Platform is built on top of [React Admin](https://marmelab.com/react-admin/). -You can use all the features provided by the underlying library with API Platform Admin, including support for [authentication](https://marmelab.com/react-admin/Authentication.html), [authorization](https://marmelab.com/react-admin/Authorization.html) and deeper customization. +By leveraging React Admin components and props, you can go much further in customizing the generated pages. -To learn more about these capabilities, refer to [the React Admin documentation](https://marmelab.com/react-admin/documentation.html). +Head to the next section, [Customizing the Admin](./advanced-customization.md), for step-by-step examples. diff --git a/admin/file-upload.md b/admin/file-upload.md index 55af6df595b..4de9702a3a2 100644 --- a/admin/file-upload.md +++ b/admin/file-upload.md @@ -4,9 +4,9 @@ If you need to handle the file upload in the server part, please follow [the rel This documentation assumes you have a `/media_objects` endpoint accepting `multipart/form-data`-encoded data. -To manage the upload in the admin part, you need to customize [the create form](customizing.md#customizing-the-create-form) or [the edit form](customizing.md#customizing-the-edit-form). +To manage the upload in the admin part, you need to [customize the guessed create or edit form](./customizing.md#from-inputguesser-to-react-admin-inputs). -Add a [FileInput](https://marmelab.com/react-admin/Inputs.html#fileinput) as a child of the guesser. For example, for the create form: +Add a [``](https://marmelab.com/react-admin/FileInput.html) as a child of the guesser. For example, for the create form: ```js import { @@ -16,15 +16,15 @@ import { } from '@api-platform/admin'; import { FileField, FileInput } from 'react-admin'; -const MediaObjectsCreate = (props) => ( - +const MediaObjectsCreate = () => ( + ); -export default () => ( +export const App = () => ( {/* ... */} @@ -35,4 +35,5 @@ export default () => ( And that's it! The guessers are able to detect that you have used a `FileInput` and are passing this information to the data provider, through a `hasFileField` field in the `extraInformation` object, itself in the data. If you are using the Hydra data provider, it uses a `multipart/form-data` request instead of a JSON-LD one. -In the case of the `EditGuesser`, the HTTP method used also becomes a `POST` instead of a `PUT`, to prevent a [PHP bug](https://bugs.php.net/bug.php?id=55815). + +**Note:** In the case of the `EditGuesser`, the HTTP method used becomes a `POST` instead of a `PUT`, to prevent a [PHP bug](https://bugs.php.net/bug.php?id=55815). diff --git a/admin/getting-started.md b/admin/getting-started.md index 9378cb5f98f..cbd2c535fe7 100644 --- a/admin/getting-started.md +++ b/admin/getting-started.md @@ -1,37 +1,99 @@ # Getting Started -## Installation +## API Platform Symfony variant -If you use the [API Platform Symfony variant](../symfony/), API Platform Admin is already installed, you can skip this installation guide. +If you use the [API Platform Symfony variant](../symfony/), good news, API Platform Admin is already installed! 🎉 -Otherwise, follow this guide. +You can access it by visiting `/admin` on your API Platform application. -If you don't have an existing React Application, create one using [Create React App](https://create-react-app.dev/): +When running locally, you can also click on the "Admin" button of the welcome page at [https://localhost](https://localhost). -```console -npm init react-app my-admin +![API Platform welcome page](./images/api-platform-welcome-page.png) + +Here is what it looks like with a simple API exposing a `Greetings` resource: + +![Basic admin with the Greetings resource](./images/basic-admin-greetings.png) + +## Manual Installation + +If you did not use the Symfony variant of API Platform and need to install API Platform Admin manually, follow this guide. + +First, let's scaffold a React Admin Application by using the [Create React Admin](https://marmelab.com/react-admin/CreateReactAdmin.html) tool: + +```bash +npx create-react-admin@latest my-admin cd my-admin ``` Then, install the `@api-platform/admin` library: -```console +```bash npm install @api-platform/admin ``` -## Creating the Admin +Now you can use either: -To initialize API Platform Admin, register it in your application. -For instance, if you used Create React App, replace the content of `src/App.js` by: +- [``](./getting-started.md#using-hydraadmin) to connect your app to an API exposing a Hydra documentation +- [``](./getting-started.md#using-openapiadmin) to connect your app to an API exposing an OpenAPI documentation -```javascript -import { HydraAdmin } from '@api-platform/admin'; +## Using `HydraAdmin` + +You can use the [``](./components.md#hydraadmin) component exported by `@api-platform/admin` to connect your app to an API exposing a Hydra documentation. + +If you used Create React Admin, you can replace the content of `src/App.tsx` by: + +```tsx +import { HydraAdmin } from "@api-platform/admin"; // Replace with your own API entrypoint // For instance if https://example.com/api/books is the path to the collection of book resources, then the entrypoint is https://example.com/api -export default () => ; +export const App = () => ; ``` +**Tip:** if you don't want to hardcode the API URL, you can [use an environment variable](https://vite.dev/guide/env-and-mode). + +Your new administration interface is ready! `HydraAdmin` will automatically fetch the Hydra documentation of your API and generate CRUD pages for all the resources it exposes. + +Type `npm run dev` to try it! + +![Basic admin with the Greetings resource](./images/basic-admin-greetings.png) + +**Tip:** There are more props you can pass to the `HydraAdmin` component to customize the dataProvider or the connection to Mercure. Check the [API documentation](./components.md#hydraadmin) for more information. + +**Tip:** You may also need to configure your API to set the correct CORS headers. Refer to the [Configuring CORS](./getting-started.md#configuring-cors) section below to learn more. + +## Using `OpenApiAdmin` + +You can use the [``](./components.md#openapiadmin) component exported by `@api-platform/admin` to connect your app to an API exposing an OpenAPI documentation. + +If you used Create React Admin, you can replace the content of `src/App.tsx` by: + +```tsx +import { OpenApiAdmin } from "@api-platform/admin"; + +// Replace with your own API entrypoint +export const App = () => ( + +); +``` + +**Tip:** If you don't want to hardcode the API URL, you can use an environment variable (see [Vite.js](https://vite.dev/guide/env-and-mode) or [Next.js](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables) docs). + +Your new administration interface is ready! `OpenApiAdmin` will automatically fetch the Hydra documentation of your API and generate CRUD pages for all the resources it exposes. + +Type `npm run dev` to try it! + +![Basic admin with the Greetings resource](./images/basic-admin-greetings.png) + +**Tip:** There are more props you can pass to the `OpenApiAdmin` component to customize the dataProvider or the connection to Mercure. Check the [API documentation](./components.md#openapiadmin) for more information. + +**Tip:** You may also need to configure your API to set the correct CORS headers. Refer to the [Configuring CORS](./getting-started.md#configuring-cors) section below to learn more. + +## Configuring CORS + Be sure to make your API send proper [CORS HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) to allow the admin's domain to access it. @@ -61,6 +123,6 @@ Clear the cache to apply this change: bin/console cache:clear --env=prod ``` -Your new administration interface is ready! Type `npm start` to try it! +## Next Step -Note: if you don't want to hardcode the API URL, you can [use an environment variable](https://create-react-app.dev/docs/adding-custom-environment-variables). +Learn how to add more features to your generated Admin by [Customizing the Schema](./schema.md). \ No newline at end of file diff --git a/admin/handling-relations.md b/admin/handling-relations.md index c98641f6a8f..7eebda4f133 100644 --- a/admin/handling-relations.md +++ b/admin/handling-relations.md @@ -1,48 +1,54 @@ # Handling Relations -API Platform Admin handles `to-one` and `to-many` relations automatically. +API Platform Admin handles `one-to-one`, `many-to-one` and `one-to-many` relations automatically. -Thanks to [the Schema.org support](schema.org.md), you can easily display the name of a related resource instead of its IRI. +However, in some cases, dependeing on whether or not you chose to embed the relation in the serialized data, you may need to customize the way the relation is displayed and/or can be edited. -## Embedded Relations +## Working With Embedded Relations -If a relation is an array of [embeddeds or an embedded](../core/serialization.md#embedding-relations) resource, the admin will keep them by default. +You can configure your API to [embed the related data](../core/serialization.md#embedding-relations) in the serialized response. -The embedded data will be displayed as text field and editable as text input: the admin cannot determine the fields present in it. -To display the fields you want, see [this section](handling-relations.md#display-a-field-of-an-embedded-relation). - -You can also ask the admin to automatically replace the embedded resources' data by their IRI, -by setting the `useEmbedded` parameter of the Hydra data provider to `false`. -Embedded data is inserted to a local cache: it will not be necessary to make more requests if you reference some fields of the embedded resource later on. +```js +// Without Embedded Book Data +{ + "@id": "/reviews/15", + id: 15, + rating: 5, + body: "A must-read for any software developer. Martin's insights are invaluable.", + author: "Alice Smith", + book: "/books/7" +} -```javascript -// admin/src/App.js +// With Embedded Book Data +{ + "@id": "/reviews/15", + id: 15, + rating: 5, + body: "A must-read for any software developer. Martin's insights are invaluable.", + author: "Alice Smith", + book: { + "@id": "/books/7", + id: 7, + title: "Clean Code", + author: "Robert C. Martin", + } +} +``` -import { HydraAdmin, fetchHydra, hydraDataProvider } from '@api-platform/admin'; -import { parseHydraDocumentation } from '@api-platform/api-doc-parser'; +If you do so, by default the admin will render the full object as text field and text input, which is not very user-friendly. -const entrypoint = process.env.REACT_APP_API_ENTRYPOINT; +![Embedded Relation With Full Object](images/embedded-relation-full-object.png) -const dataProvider = hydraDataProvider({ - entrypoint, - httpClient: fetchHydra, - apiDocumentationParser: parseHydraDocumentation, - mercure: true, - useEmbedded: false, -}); +There are two ways you can handle this situation: -export default () => ( - -); -``` +1. Change the Field and Input components to [display the fields you want](./handling-relations.md#displaying-a-field-of-an-embedded-relation) +2. Ask the admin to [return the embedded resources' IRI instead of the full record](./handling-relations.md#return-the-embedded-resources-iri-instead-of-the-full-record), by leveraging the `useEmbedded` parameter -## Display a Field of an Embedded Relation +### Displaying a Field of an Embedded Relation -If you have an [embedded relation](../core/serialization.md#embedding-relations) and need to display a nested field, the code you need to write depends of the value of `useEmbedded` of the Hydra data provider. +React Admin fields allow to use the dot notation (e.g. `book.title`) to target a field from an embedded relation. -If `true` (default behavior), you need to use the dot notation to display a field: - -```javascript +```jsx import { HydraAdmin, FieldGuesser, @@ -51,54 +57,130 @@ import { } from '@api-platform/admin'; import { TextField } from 'react-admin'; -const BooksList = (props) => ( - - +const ReviewList = () => ( + + + + {/* Use react-admin components directly when you want complex fields. */} - + ); -export default () => ( - - +export const App = () => ( + + ); ``` -If `useEmbedded` is explicitly set to `false`, make sure you write the code as if the relation needs to be fetched as a reference. +![Embedded Relation With Dot Notation](images/embedded-relation-dot-notation.png) -In this case, you _cannot_ use the dot separator to do so. +Allowing to edit the relation, on the other hand, is a little trickier, as it requires transforming the record to replace the nested object by its IRI. -Note that you cannot edit the embedded data directly with this behavior. +Fortunately, this can be done by leveraging the `transform` prop of the `` component. -For instance, if your API returns: +We can edit the relation by leveraging either [``](https://marmelab.com/react-admin/ReferenceInput.html) for a `to-one` relation or [``](https://marmelab.com/react-admin/ReferenceArrayInput.html) for a `to-many` relation. -```json -{ - "@context": "/contexts/Book", - "@id": "/books", - "@type": "Collection", - "member": [ - { - "@id": "/books/07b90597-542e-480b-a6bf-5db223c761aa", - "@type": "https://schema.org/Book", - "title": "War and Peace", - "author": { - "@id": "/authors/d7a133c1-689f-4083-8cfc-afa6d867f37d", - "@type": "https://schema.org/Author", - "firstName": "Leo", - "lastName": "Tolstoi" - } - } - ], - "totalItems": 1 +```jsx +import { + HydraAdmin, + InputGuesser, + EditGuesser, + ResourceGuesser, +} from '@api-platform/admin'; +import { ReferenceInput, AutocompleteInput } from 'react-admin'; + +const reviewEditTransform = (values) => ({ + ...values, + book: values.book['@id'], +}); + +const ReviewEdit = () => ( + + + + + + ({ title: searchText })} + /> + + +); + +export const App = () => ( + + + +); +``` + +This offers a nice and convenient way to edit the relation. + +![Embedded Relation With ReferenceInput](images/embedded-relation-ReferenceInput.png) + +**Tip:** We also had to customize ``'s child [``](https://marmelab.com/react-admin/AutocompleteInput.html) component to override its `label` and `filterToQuery` props. You can learn more about why that's necessary in the [Using an AutoComplete Input for Relations](./handling-relations.md#using-an-autocomplete-input-for-relations) section. + +### Return the Embedded Resources' IRI Instead of the Full Record + +You can also ask the admin to return the embedded resources' IRI instead of the full record, by setting the `useEmbedded` parameter of the Hydra data provider to `false`. + +```jsx +// admin/src/App.jsx + +import { HydraAdmin, dataProvider } from '@api-platform/admin'; + +const entrypoint = process.env.ENTRYPOINT; + +export const App = () => ( + +); +``` + +This tells the dataProvider to return only the IRI in the record, discarding the embedded data. + +```js +// With useEmbedded=true (default) +const record = { + "@id": "/reviews/15", + id: 15, + rating: 5, + body: "A must-read for any software developer. Martin's insights are invaluable.", + author: "Alice Smith", + book: { + "@id": "/books/7", + id: 7, + title: "Clean Code", + author: "Robert C. Martin", + } +} + +// With useEmbedded=false +const record = { + "@id": "/reviews/15", + id: 15, + rating: 5, + body: "A must-read for any software developer. Martin's insights are invaluable.", + author: "Alice Smith", + book: "/books/7" } ``` -If you want to display the author first name in the list, you need to write the following code: +This way, the related record's IRI is returned and can be displayed. -```javascript +![Embedded Relation With useEmbedded To False](images/embedded-relation-useEmbedded-false.png) + +We can improve the UI further by leveraging React Admin's [``](https://marmelab.com/react-admin/ReferenceField.html) component: + +```jsx import { HydraAdmin, FieldGuesser, @@ -107,31 +189,72 @@ import { } from '@api-platform/admin'; import { ReferenceField, TextField } from 'react-admin'; -const BooksList = (props) => ( - - - {/* Use react-admin components directly when you want complex fields. */} - - +const ReviewList = () => ( + + + + + + ); -export default () => ( - - - +export const App = () => ( + + + +); +``` + +This allows to display the title of the related book instead of its IRI. + +![Embedded Relation With ReferenceField](images/embedded-relation-ReferenceField.png) + +Lastly, this also allows to easily edit the relation by leveraging either [``](https://marmelab.com/react-admin/ReferenceInput.html) for a `to-one` relation or [``](https://marmelab.com/react-admin/ReferenceArrayInput.html) for a `to-many` relation. + +```jsx +import { + HydraAdmin, + InputGuesser, + EditGuesser, + ResourceGuesser, +} from '@api-platform/admin'; +import { ReferenceInput, AutocompleteInput } from 'react-admin'; + +const ReviewEdit = () => ( + + + + + + ({ title: searchText })} + /> + + +); + +export const App = () => ( + + ); ``` +This offers a nice and convenient way to edit the relation. + +![Embedded Relation With ReferenceInput](images/embedded-relation-ReferenceInput.png) + +**Tip:** We also had to customize ``'s child [``](https://marmelab.com/react-admin/AutocompleteInput.html) component to override its `filterToQuery` props. You can learn more about why that's necessary in the [Using an AutoComplete Input for Relations](./handling-relations.md#using-an-autocomplete-input-for-relations) section. + ## Using an Autocomplete Input for Relations -Let's go one step further thanks to the [customization capabilities](customizing.md) of API Platform Admin by adding autocompletion support to form inputs for relations. +By default, `` will render a [``](https://marmelab.com/react-admin/SelectInput.html) when it detects a relation. + +We can improve the UX further by rendering an [``](https://marmelab.com/react-admin/AutocompleteInput.html) instead. + +`` allows to search for a related record by typing its name in an input field. This is much more convenient when there are many records to choose from. Let's consider an API exposing `Review` and `Book` resources linked by a `many-to-one` relation (through the `book` property). @@ -192,9 +315,9 @@ class Book Notice the "partial search" [filter](../core/filters.md) on the `title` property of the `Book` resource class. -Now, let's configure API Platform Admin to enable autocompletion for the relation selector: +Now, let's configure API Platform Admin to enable autocompletion for the book selector. We will leverage the [``](https://marmelab.com/react-admin/ReferenceInput.html) and [``](https://marmelab.com/react-admin/AutocompleteInput.html) components from React Admin: -```javascript +```jsx import { HydraAdmin, ResourceGuesser, @@ -204,32 +327,14 @@ import { } from '@api-platform/admin'; import { ReferenceInput, AutocompleteInput } from 'react-admin'; -const ReviewsCreate = (props) => ( - - - - ({ title: searchText })} - optionText="title" - label="Books" - /> - - - - - - -); - -const ReviewsEdit = (props) => ( - +const ReviewsEdit = () => ( + ({ title: searchText })} optionText="title" - label="Books" /> @@ -239,67 +344,30 @@ const ReviewsEdit = (props) => ( ); -export default () => ( - - +export const App = () => ( + + ); ``` -If the book is embedded into a review and if the `useEmbedded` parameter is `true` (default behavior), -you need to change the `ReferenceInput` for the edit component: +The important things to note are: -```javascript -import { - HydraAdmin, - ResourceGuesser, - CreateGuesser, - EditGuesser, - InputGuesser, -} from '@api-platform/admin'; -import { ReferenceInput, AutocompleteInput } from 'react-admin'; +- the `filterToQuery` prop, which allows to search for books by title (leveraging the "partial search" filter mentioned above) +- the `optionText` prop, which tells the `` component to render books using their `title` property -const ReviewsCreate = (props) => ( - - - - ({ title: searchText })} - optionText="title" - label="Books" - /> - +You can now search for books by title in the book selector of the review form. - - - - -); +![Admin With AutocompleteInput](./images/AutocompleteInput.png) -const ReviewsEdit = (props) => ( - - +## Displaying Related Record Name Instead of Their IRI - - ({ title: searchText })} - format={(v) => v['@id'] || v} - optionText="title" - label="Books" - /> - +Thanks to the [Schema.org](./schema.md) support, you can easily display the name of a related resource instead of its IRI. - - - - -); +Follow the [Displaying Related Resource's Name Instead of its IRI](./schema.md#displaying-related-resources-name-instead-of-its-iri) section of the Schema.org documentation to implement this feature. -export default () => ( - - - -); -``` +## Going Further + +React Admin can handle many types of relations, even `many-to-many`. You can learn more about them in the [Fields For Relationships](https://marmelab.com/react-admin/FieldsForRelationships.html) documentation. -The autocomplete field should now work properly! +You can also read the [Handling Relationships in React Admin](https://marmelab.com/blog/2025/02/06/handling-relationships-in-react-admin.html) post from the React Admin blog for concrete examples and source code. diff --git a/admin/images/AutocompleteInput.png b/admin/images/AutocompleteInput.png new file mode 100644 index 00000000000..fff66e529e8 Binary files /dev/null and b/admin/images/AutocompleteInput.png differ diff --git a/admin/images/admin-custom-edit-guesser.png b/admin/images/admin-custom-edit-guesser.png new file mode 100644 index 00000000000..78e12a6cab7 Binary files /dev/null and b/admin/images/admin-custom-edit-guesser.png differ diff --git a/admin/images/admin-custom-field.png b/admin/images/admin-custom-field.png new file mode 100644 index 00000000000..fe5921e185d Binary files /dev/null and b/admin/images/admin-custom-field.png differ diff --git a/admin/images/admin-custom-input.png b/admin/images/admin-custom-input.png new file mode 100644 index 00000000000..552713c88a3 Binary files /dev/null and b/admin/images/admin-custom-input.png differ diff --git a/admin/images/admin-custom-list-field-guesser.png b/admin/images/admin-custom-list-field-guesser.png new file mode 100644 index 00000000000..b50bed61040 Binary files /dev/null and b/admin/images/admin-custom-list-field-guesser.png differ diff --git a/admin/images/admin-custom-list-guesser.png b/admin/images/admin-custom-list-guesser.png new file mode 100644 index 00000000000..755330a34d6 Binary files /dev/null and b/admin/images/admin-custom-list-guesser.png differ diff --git a/admin/images/admin-custom-show-guesser.png b/admin/images/admin-custom-show-guesser.png new file mode 100644 index 00000000000..26412203ace Binary files /dev/null and b/admin/images/admin-custom-show-guesser.png differ diff --git a/admin/images/admin-datagrid.png b/admin/images/admin-datagrid.png new file mode 100644 index 00000000000..0e4b4527d43 Binary files /dev/null and b/admin/images/admin-datagrid.png differ diff --git a/admin/images/admin-default-list.png b/admin/images/admin-default-list.png new file mode 100644 index 00000000000..8d0bb4fa83e Binary files /dev/null and b/admin/images/admin-default-list.png differ diff --git a/admin/images/admin-demo.mp4 b/admin/images/admin-demo.mp4 new file mode 100644 index 00000000000..9681502ec7e Binary files /dev/null and b/admin/images/admin-demo.mp4 differ diff --git a/admin/images/admin-demo.webm b/admin/images/admin-demo.webm new file mode 100644 index 00000000000..c213fe67b87 Binary files /dev/null and b/admin/images/admin-demo.webm differ diff --git a/admin/images/admin-filter.png b/admin/images/admin-filter.png new file mode 100644 index 00000000000..705c039453c Binary files /dev/null and b/admin/images/admin-filter.png differ diff --git a/admin/images/admin-form-layout.png b/admin/images/admin-form-layout.png new file mode 100644 index 00000000000..872e3a0894b Binary files /dev/null and b/admin/images/admin-form-layout.png differ diff --git a/admin/images/admin-menu-icons.png b/admin/images/admin-menu-icons.png new file mode 100644 index 00000000000..a4ee014e502 Binary files /dev/null and b/admin/images/admin-menu-icons.png differ diff --git a/admin/images/admin-menu.png b/admin/images/admin-menu.png new file mode 100644 index 00000000000..e0cdcb49804 Binary files /dev/null and b/admin/images/admin-menu.png differ diff --git a/admin/images/admin-reference-record-count.png b/admin/images/admin-reference-record-count.png new file mode 100644 index 00000000000..005fed2125b Binary files /dev/null and b/admin/images/admin-reference-record-count.png differ diff --git a/admin/images/admin-sort.png b/admin/images/admin-sort.png new file mode 100644 index 00000000000..1b651647df5 Binary files /dev/null and b/admin/images/admin-sort.png differ diff --git a/admin/images/admin-tabbed-show-layout.png b/admin/images/admin-tabbed-show-layout.png new file mode 100644 index 00000000000..ad8e4334b6a Binary files /dev/null and b/admin/images/admin-tabbed-show-layout.png differ diff --git a/admin/images/admin-undoable-mutation.png b/admin/images/admin-undoable-mutation.png new file mode 100644 index 00000000000..2505b86c496 Binary files /dev/null and b/admin/images/admin-undoable-mutation.png differ diff --git a/admin/images/admin-warnWhenUnsavedChanges.png b/admin/images/admin-warnWhenUnsavedChanges.png new file mode 100644 index 00000000000..924c91aeed9 Binary files /dev/null and b/admin/images/admin-warnWhenUnsavedChanges.png differ diff --git a/admin/images/api-platform-admin-theme.png b/admin/images/api-platform-admin-theme.png new file mode 100644 index 00000000000..08a94780173 Binary files /dev/null and b/admin/images/api-platform-admin-theme.png differ diff --git a/admin/images/api-platform-welcome-page.png b/admin/images/api-platform-welcome-page.png new file mode 100644 index 00000000000..11fa3b88a18 Binary files /dev/null and b/admin/images/api-platform-welcome-page.png differ diff --git a/admin/images/basic-admin-greetings.png b/admin/images/basic-admin-greetings.png new file mode 100644 index 00000000000..3c115f4fc2a Binary files /dev/null and b/admin/images/basic-admin-greetings.png differ diff --git a/admin/images/embedded-relation-ReferenceField.png b/admin/images/embedded-relation-ReferenceField.png new file mode 100644 index 00000000000..a687ade02ec Binary files /dev/null and b/admin/images/embedded-relation-ReferenceField.png differ diff --git a/admin/images/embedded-relation-ReferenceInput.png b/admin/images/embedded-relation-ReferenceInput.png new file mode 100644 index 00000000000..ffeea852f0c Binary files /dev/null and b/admin/images/embedded-relation-ReferenceInput.png differ diff --git a/admin/images/embedded-relation-dot-notation.png b/admin/images/embedded-relation-dot-notation.png new file mode 100644 index 00000000000..aef056a2fd4 Binary files /dev/null and b/admin/images/embedded-relation-dot-notation.png differ diff --git a/admin/images/embedded-relation-full-object.png b/admin/images/embedded-relation-full-object.png new file mode 100644 index 00000000000..0eecdb36a31 Binary files /dev/null and b/admin/images/embedded-relation-full-object.png differ diff --git a/admin/images/embedded-relation-useEmbedded-false.png b/admin/images/embedded-relation-useEmbedded-false.png new file mode 100644 index 00000000000..8405a95c2f9 Binary files /dev/null and b/admin/images/embedded-relation-useEmbedded-false.png differ diff --git a/admin/images/react-admin-theme.png b/admin/images/react-admin-theme.png new file mode 100644 index 00000000000..5e6191e8dd2 Binary files /dev/null and b/admin/images/react-admin-theme.png differ diff --git a/admin/images/related-record-with-iri.png b/admin/images/related-record-with-iri.png new file mode 100644 index 00000000000..eebdf49687e Binary files /dev/null and b/admin/images/related-record-with-iri.png differ diff --git a/admin/images/related-record-with-name.png b/admin/images/related-record-with-name.png new file mode 100644 index 00000000000..b8a2417868f Binary files /dev/null and b/admin/images/related-record-with-name.png differ diff --git a/admin/index.md b/admin/index.md index 0349b705bcf..6330913770d 100644 --- a/admin/index.md +++ b/admin/index.md @@ -1,36 +1,59 @@ # The API Platform Admin -![Screencast](images/admin-demo.gif) + -API Platform Admin is a tool to automatically create a beautiful (Material Design) and fully-featured administration interface -for any API supporting [the Hydra Core Vocabulary](https://www.hydra-cg.com/), exposing an [OpenAPI documentation](https://www.openapis.org/) -or other API specification formats supported by [`@api-platform/api-doc-parser`](https://github.com/api-platform/api-doc-parser). +API Platform **Admin** is a tool to automatically create a beautiful (Material Design) and fully-featured administration interface +for any API implementing specification formats supported by [`@api-platform/api-doc-parser`](https://github.com/api-platform/api-doc-parser). -API Platform Admin is the perfect companion of APIs created -using [the API Platform framework](https://api-platform.com), but also supports APIs written with any other programming language or framework as long as they expose a standard Hydra API documentation or an OpenAPI documentation. +In particular, that includes: -API Platform Admin is a 100% standalone Single-Page-Application written in TypeScript with no coupling to the server part, -according to the API-first paradigm. +- APIs using [the Hydra Core Vocabulary](https://www.hydra-cg.com/) +- APIs exposing an [OpenAPI documentation](https://www.openapis.org/) -API Platform Admin parses the API documentation then uses the awesome [React Admin](https://marmelab.com/react-admin/) -library to expose a nice, responsive, management interface (Create-Retrieve-Update-Delete) for all documented resource types. +Of course, API Platform Admin is the perfect companion of APIs created +using [the API Platform framework](https://api-platform.com). But it also supports APIs written with any other programming language or framework as long as they expose a standard Hydra or OpenAPI documentation. -You can **customize everything** by using provided React Admin and [MUI](https://mui.com/) components, or by writing your custom [React](https://reactjs.org/) components. +## Based On React Admin + +API Platform Admin is a Single Page Application (SPA), based on [React Admin](https://marmelab.com/react-admin/), a powerful frontend framework for building B2B applications on top of REST/GraphQL APIs, written in TypeScript and React. + +Thanks to its built-in **guessers**, API Platform Admin parses the API documentation then uses React Admin to expose a nice, responsive management interface (Create-Retrieve-Update-Delete, i.e. CRUD) for all documented resource types. + +Afterwards, you can **customize everything** by using the numerous components provided by [React Admin](https://marmelab.com/react-admin/documentation.html) and [MUI](https://mui.com/), or even writing your own [React](https://reactjs.org/) components. + +

React Admin Screencast
Watch the React Admin screencast

## Features -- Automatically generates an admin interface for all the resources of the API thanks to the hypermedia features of Hydra or to the OpenAPI documentation -- Generates 'list', 'create', 'show', and 'edit' screens, as well as a delete button -- Generates suitable inputs and fields according to the API doc (e.g. number HTML input for numbers, checkbox for booleans, selectbox for relationships...) -- Generates suitable inputs and fields according to Schema.org types if available (e.g. email field for `https://schema.org/email`) -- Handles relationships -- Supports pagination -- Supports filters and ordering -- Automatically validates whether a field is mandatory client-side according to the API description -- Sends proper HTTP requests to the API and decodes them using Hydra and JSON-LD formats if available +Simply by reading your API documentation, API Platform Admin provides the following features: + +- Generate 'list', 'create', 'show', and 'edit' views for all resources +- Automatically detect the type for inputs and fields +- Client-side [validation](./validation.md) on required inputs +- Pagination +- Filtering and ordering +- Easily view and edit [related records](./handling-relations.md) +- Display the related resource’s name instead of its IRI ([using the Schema.org vocabulary](./schema.md#displaying-related-resources-name-instead-of-its-iri)) - Nicely displays server-side errors (e.g. advanced validation) -- Supports real-time updates with [Mercure](https://mercure.rocks) -- All the [features provided by React-admin](https://marmelab.com/react-admin/Tutorial.html) can also be used -- **100% customizable** +- Real-time updates with [Mercure](https://mercure.rocks) + +By [leveraging React Admin components](./advanced-customization.md), you can further customize the generated interface and get access to many more features: + +- Powerful Datagrid components +- Search and filtering +- Advanced form validation +- Undoable mutations +- Authentication +- Access Control +- Internationalization +- [And many more](https://marmelab.com/react-admin/Features.html) + +## Next Step + +Get your Admin up and running by following the [Getting Started guide](./getting-started.md). \ No newline at end of file diff --git a/admin/openapi.md b/admin/openapi.md deleted file mode 100644 index cfba6580cfd..00000000000 --- a/admin/openapi.md +++ /dev/null @@ -1,46 +0,0 @@ -# OpenAPI - -API Platform Admin has native support for API exposing an [OpenAPI documentation](https://www.openapis.org/). - -To use it, use the `OpenApiAdmin` component, with the entry point of the API and the entry point of the OpenAPI documentation in JSON: - -```javascript -import { OpenApiAdmin } from '@api-platform/admin'; - -export default () => ( - -); -``` - -> [!NOTE] -> -> The OpenAPI documentation needs to follow some assumptions to be understood correctly by the underlying `api-doc-parser`. -> See the [dedicated part in the `api-doc-parser` library README](https://github.com/api-platform/api-doc-parser#openapi-support). - -## Data Provider - -By default, the component will use a basic data provider, without pagination support. - -If you want to use [another data provider](https://marmelab.com/react-admin/DataProviderList.html), pass the `dataProvider` prop to the component: - -```javascript -import { OpenApiAdmin } from '@api-platform/admin'; -import drfProvider from 'ra-data-django-rest-framework'; - -export default () => ( - -); -``` - -## Mercure Support - -Mercure support can be enabled manually by giving the `mercure` prop to the `OpenApiAdmin` component. - -See also [the dedicated section](real-time-mercure.md). diff --git a/admin/performance.md b/admin/performance.md index d769f17ec8c..b856ab16ae4 100644 --- a/admin/performance.md +++ b/admin/performance.md @@ -1,4 +1,4 @@ -# Performance +# Performance Tips To make the admin faster and greener, you can make some changes to your API. @@ -9,9 +9,9 @@ the admin will fetch the relations one by one. In this case, it can be improved by doing only one request for all the related resources instead. -To do so, you need to make sure the [search filter](../core/filters.md#search-filter) is enabled for the identifier of the related resource. +To do so, you need to make sure the [search filter](../core/doctrine-filters.md#search-filter) is enabled for the identifier of the related resource. -For instance, if you have a book resource having a relation to author resources and you display the author names on your book list, +For instance, if you have a `book` resource having a relation to `author` resources and you display the author names on your book list, you can make sure the authors are retrieved in one go by writing: ```php @@ -36,3 +36,16 @@ class Author public string $name; } ``` + +Instead of issuing a separate request for each author, the admin will now fetch all the authors in one go, with a request similar to the following: + +```txt +https://localhost/authors? + page=1 + &itemsPerPage=5 + &id[0]=/authors/7 + &id[1]=/authors/8 + &id[2]=/authors/9 + &id[3]=/authors/10 + &id[4]=/authors/11 +``` diff --git a/admin/schema.md b/admin/schema.md new file mode 100644 index 00000000000..18618db1bbe --- /dev/null +++ b/admin/schema.md @@ -0,0 +1,147 @@ +# Customizing the Schema + +Both [`HydraAdmin`](./components.md#hydraadmin) and [`OpenApiAdmin`](./components.md#openapiadmin) leverage introspection of the API schema to discover its capabilities, like **filtering** and **sorting**. + +They also detect wether the API has real-time capabilities using [Mercure](./real-time-mercure.md), and automatically enable it if it does. + +Lastly, API Platform Admin has native support for the popular [Schema.org](./schema.md#about-schemaorg) vocabulary, which enables it to automatically use the field type matching your data, or display a related resource's name instead of its IRI. + +## Adding Filtering Capabilities + +You can add the [`ApiFilter` attribute](../core/filters.md#apifilter-attribute) to an API Platform resource to configure a filter on a property. + +For instance, here is how configure filtering on the `id`, `title` and `author` properties of a `Book` resource: + +```php + 'exact', + 'title' => 'ipartial', + 'author' => 'ipartial' +])] +class Book +{ + // ... +} +``` + +If you are using the guessers, the Admin will automatically update the Book list view to include a filter on the selected properties. + +![Filtering on the title property](./images/admin-filter.png) + +**Tip:** Learn more about the [`ApiFilter` attribute](../core/filters.md#apifilter-attribute) in the core documentation. + +## Adding Sorting Capabilities + +You can also use the [`ApiFilter` attribute](../core/filters.md#apifilter-attribute) on an API Plaform resource to configure sorting. + +For instance, here is how to configure sorting on the `id`, `isbn`, `title`, `author` and `publicationDate` properties of a `Book` resource: + +```php + 'ASC', + 'isbn' => 'ASC', + 'title' => 'ASC', + 'author' => 'ASC', + 'publicationDate' => 'DESC' +])] +class Book +{ + // ... +} +``` + +If you are using the guessers, the Admin will automatically update the Book list view to make the selected columns sortable. + +![Sorting by the title property](./images/admin-sort.png) + +**Tip:** Learn more about the [`ApiFilter` attribute](../core/filters.md#apifilter-attribute) in the core documentation. + +## Enabling Real-Time Updates + +You can use the `mercure` attribute to hint API Platform that it must dispatch the updates regarding the given resources to the Mercure hub: + +```php + Schema.org is a collaborative, community activity with a mission to create, maintain, and promote schemas for structured data on the Internet, on web pages, in email messages, and beyond. + +To leverage this capability, your API must use the JSON-LD format and the appropriate Schema.org types. +The following examples will use [API Platform Core](../core/) to create such API, but keep in mind that this feature will work with any JSON-LD API using the Schema.org vocabulary, regardless of the used web framework or programming language. + +## Displaying Related Resource's Name Instead of its IRI + +By default, IRIs of related objects are displayed in lists and forms. +However, it is often more user-friendly to display a string representation of the resource (such as its name) instead of its ID. + +To configure which property should be shown to represent your entity, map the property containing the name of the object with the `https://schema.org/name` type: + +```php +// api/src/Entity/Person.php +... + +#[ApiProperty(iris: ["https://schema.org/name"])] +private $name; + +... +``` + +| With IRI | With Resource Name | +| ---------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| ![Related Record With IRI](./images/related-record-with-iri.png) | ![Related Record With Resource Name](./images/related-record-with-name.png) | + +## Emails, URLs and Identifiers + +Besides, it is also possible to use the documentation to customize some fields automatically while configuring the semantics of your data. + +The following Schema.org types are currently supported by API Platform Admin: + +- `https://schema.org/email`: the field will be rendered using the [``](https://marmelab.com/react-admin/EmailField.html) React Admin component +- `https://schema.org/url`: the field will be rendered using the [``](https://marmelab.com/react-admin/UrlField.html) React Admin component +- `https://schema.org/identifier`: the field will be formatted properly in inputs + +Note: if you already use validation on your properties, the semantics are already configured correctly (see [the correspondence table](../core/validation.md#open-vocabulary-generated-from-validation-metadata))! + +## Next Step + +Learn how to tweak the generated Admin by [Customizing the Guessers](./customizing.md). diff --git a/admin/schema.org.md b/admin/schema.org.md deleted file mode 100644 index f8ecc7b7c75..00000000000 --- a/admin/schema.org.md +++ /dev/null @@ -1,37 +0,0 @@ -# Using the Schema.org Vocabulary - -API Platform Admin has native support for the popular [Schema.org](https://schema.org) vocabulary. - -> Schema.org is a collaborative, community activity with a mission to create, maintain, and promote schemas for structured data on the Internet, on web pages, in email messages, and beyond. - -To leverage this capability, your API must use the JSON-LD format and the appropriate Schema.org types. -The following examples will use [API Platform Core](../core/) to create such API, but keep in mind that this feature will work with any JSON-LD API using the Schema.org vocabulary, regardless of the used web framework or programming language. - -## Displaying Related Resource's Name Instead of its IRI - -By default, IRIs of related objects are displayed in lists and forms. -However, it is often more user-friendly to display a string representation of the resource (such as its name) instead of its ID. - -To configure which property should be shown to represent your entity, map the property containing the name of the object with the `https://schema.org/name` type: - -```php -// api/src/Entity/Person.php -... - -#[ApiProperty(iris: ["https://schema.org/name"])] -private $name; - -... -``` - -## Emails, URLs and Identifiers - -Besides, it is also possible to use the documentation to customize some fields automatically while configuring the semantics of your data. - -The following Schema.org types are currently supported by API Platform Admin: - -- `https://schema.org/email`: the field will be rendered using the `` React Admin component -- `https://schema.org/url`: the field will be rendered using the `` React Admin component -- `https://schema.org/identifier`: the field will be formatted properly in inputs - -Note: if you already use validation on your properties, the semantics are already configured correctly (see [the correspondence table](../core/validation.md#open-vocabulary-generated-from-validation-metadata))! diff --git a/admin/validation.md b/admin/validation.md index efc67c707de..777ecdd318d 100644 --- a/admin/validation.md +++ b/admin/validation.md @@ -5,7 +5,7 @@ API Platform Admin manages automatically two types of validation: client-side va ## Client-side Validation If the API documentation indicates that a field is mandatory, -API Platform Admin will automatically add a [required client-side validation](https://marmelab.com/react-admin/CreateEdit.html#per-input-validation-built-in-field-validators). +API Platform Admin will automatically add a [required client-side validation](https://marmelab.com/react-admin/Validation.html#per-input-validation-built-in-field-validators). For instance, with API Platform as backend, if you write the following: @@ -25,7 +25,7 @@ class Book } ``` -If you create a new book and touch the "Title" field without typing, you will see: +If you create a new book and try to submit without filling the "Title" field, you will see: ![Required title field](images/required-field.png) @@ -34,8 +34,7 @@ If you create a new book and touch the "Title" field without typing, you will se When the form is submitted and if submission errors are received, API Platform Admin will automatically show the errors for the corresponding fields. -To do so, it uses the [submission validation](https://marmelab.com/react-admin/CreateEdit.html#submission-validation) feature of React Admin, -and the mapping between the response and the fields is done by the [schema analyzer](components.md#schemaanalyzer) with its method `getSubmissionErrors`. +To do so, it uses the [Server-Side Validation](https://marmelab.com/react-admin/Validation.html#server-side-validation) feature of React Admin, and the mapping between the response and the fields is done by the [schema analyzer](components.md#hydra-schema-analyzer) with its method `getSubmissionErrors`. API Platform is supported by default, but if you use another backend, you will need to override the `getSubmissionErrors` method. @@ -60,3 +59,39 @@ class Book If you submit the form with an invalid ISBN, you will see: ![Submission error field](images/submission-error-field.png) + +## Validation With React Admin Inputs + +If you replace an `` with a React Admin [input component](https://marmelab.com/react-admin/Inputs.html), such as [``](https://marmelab.com/react-admin/TextInput.html), [``](https://marmelab.com/react-admin/DateInput.html) or [``](https://marmelab.com/react-admin/ReferenceInput.html), you will need to **manually add the validation rules back**. + +Fortunately, this is very easy to do, thanks to the [`validate`](https://marmelab.com/react-admin/Inputs.html#validate) prop of the input components. + +For instance, here is how to replace the input for the required `title` field: + +```diff +import { EditGuesser, InputGuesser } from '@api-platform/admin'; ++import { TextInput, required } from 'react-admin'; + +export const BookEdit = () => ( + +- ++ + +); +``` + +React Admin already comes with several [built-in validators](https://marmelab.com/react-admin/Validation.html#per-input-validation-built-in-field-validators), such as: + +* `required(message)` if the field is mandatory, +* `minValue(min, message)` to specify a minimum value for integers, +* `maxValue(max, message)` to specify a maximum value for integers, +* `minLength(min, message)` to specify a minimum length for strings, +* `maxLength(max, message)` to specify a maximum length for strings, +* `number(message)` to check that the input is a valid number, +* `email(message)` to check that the input is a valid email address, +* `regex(pattern, message)` to validate that the input matches a regular expression, +* `choices(list, message)` to validate that the input is within a given list + +React Admin also supports [Global Validation](https://marmelab.com/react-admin/Validation.html#global-validation) (at the form level). + +Check out the [Form Validation](https://marmelab.com/react-admin/Validation.html) documentation to learn more. diff --git a/extra/releases.md b/extra/releases.md index 50655968081..37f68ddaadd 100644 --- a/extra/releases.md +++ b/extra/releases.md @@ -10,8 +10,8 @@ For example: - version 3.2 has been released on 12 October 2023; - version 3.3 has been released on 9 April 2024 (we were a little late, it should have been published in March); - versions 3.4 has been released on 18 September 2024; -- versions 4.0 has been released on 27 September 2024; -- versions 4.1 has been released on 28 February 2025; +- versions 4.0 has been released on 27 September 2024; +- versions 4.1 has been released on 28 February 2025; ## Maintenance diff --git a/outline.yaml b/outline.yaml index 0e50c8e99ae..61052a26d0d 100644 --- a/outline.yaml +++ b/outline.yaml @@ -82,15 +82,15 @@ chapters: items: - index - getting-started + - schema-org + - customizing + - advanced-customization - handling-relations - - openapi - - schema.org - validation - real-time-mercure - authentication-support - file-upload - performance - - customizing - components - title: Create Client path: create-client