-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SPIKE Passing data into a React GridField #537
Comments
RFC is here: #556 |
You still have to have an omniscient view of what all these queries are at boot time to put all of them into HOCs. |
the team feels this is done @ingo? |
Pulled this back into "In progress", Aaron's looking into an alternative solution - I don't think we're there yet. Probably more than five points by now, but this is important to get over the line properly. |
Alternative solutionBearing in mind that we're trying to make the 80% use case as simple and elegant as possible, there is probably a better way to do this. SummaryRefactor to use a more content API friendly approach. Instead of relying on registries to create magic one-off operations per Because the queries are predictable and standardised, we can pass the string representation of the query down the Components are fetched from Injector at render time rather than composed via transforms on boot. This has the biggest risk for a performance impact, as far as I can tell. But doing this through transforms requires action at boot time, which would require a registry, as outlined above. Advantages
Disadvantages
API Change ProposalHave chatted about this with @tractorcow, who has vast knowledge of the ORM and his recommendation was to avoid the pitfalls of introspecting a $field = ReactGridField::create('MyField', 'Hey this is my field')
->setSourceType(Member::class)
->setSourceFilters(['Title:PartialMatch', '%damian%']); This has a similar feel to the old Again, keeping in mind we're only going for the 80% use case, this seems pretty palatable. The power users will have access to a Thoughts? |
Another open question is how to handle computed fields, which are common in things like If We'll need some way of declaring what getters each dataobject wants to put into its GraphQL type. $scaffolder->addFields($allDBFields);
$scaffolder->addFields(array_keys($summary_fields)); But that feels like a lot of coupling. What if each GridFieldComponent had access to the scaffolder? That way you could have |
A new POC is up using the approach outlined above, where graphql, in literal form, is passed down through the form schema into a Thinking about it further, however, this bifurcates the path to query customisation. The new POC builds the query server side, which would ostensibly allow module developers to hook into those query building APIs and influence what gets sent to the react component. However, we also have a client side API, using injector, that allows query customisation. These two approaches are completely incompatible with each other. The frontend injector is not designed to make updates to a query that is already expressed as a string. This means developers would have to know the difference in implementation -- that To make the two approaches compatible, I recommend we use abstraction on both sides. [
'data' => [
'graphql' => [
'name' => 'ReadFiles',
'params' => [
// ...
]
'type' => 'QUERY',
'operation' => 'readFiles',
'fields' => [
'pageInfo' => [
'totalCount',
],
'edges' => [
'node' => [
'Filename',
'Thumbnail',
]
]
],
'fragments' => [
'FileInterface' // No guarantee this exists. Assumes registered with client?
]
]
]
] It starts to look an awful lot like an AST, and perhaps for a more standard approach, we could just use pure AST, but well-supported client side libraries that mutate AST aren't easy to find at first glance. Otherwise, an opinionated, declarative structure like this has its merits as well, namely that it would require less configuration and be more adaptable to a set of components that are mostly relying on the same 4-5 types of operations for 90% of use cases. This raises questions about how we're handling query customisation client side, as well. Right now, while injectable queries are expressed in the abstract, it's a configuration-first API that can get quite cumbersome. Have a look at the POC for injectable asset-admin. There's a lot to consider, and perhaps something more procedural is in order. const graphql = new QueryBuilder('ReadFiles');
graphql.setType(QUERY)
.setOperation('readFiles')
.setParams(...)
.addFields([
'Filename',
'Title',
new FieldSet('Owners', [
'FirstName',
'Surname',
]),
])
const query = {
apolloConfig,
query: graphql
} This is similar to the PHP query builder that is currently in the React GridField POC
Injector.query.transform('my-transform', updater => {
updater.query.alterQuery('ReadFiles', graphql => {
graphql.getFieldList('Root.Owners').addField('Created');
graphql.getField('Root.Filename').addArg('Relative', 'Boolean!')
});
}); This is very similar to what we use for form validation. A less configuration-heavy API would make queries easier to test and debug, but also offer more consistency between module authors and end users. Further, it would be entirely compatible with a form schema approach: const graphql = new QueryBuilder().fromJSON(formSchema.data.graphql); |
Parent issue: #463
RFC: #556
POC: https://github.com/open-sausages/react-gridfield-poc
Description
In 3.x, data is passed into a
GridField
through aDataList
orArrayList
. The underlying APIs are the same between fetching data for CMS editing and exposing them in your own SilverStripe templates.In 5.x, we want tabular view components to be powered by GraphQL. This presents a major shift of how data constraints are defined, impacting a large part of the SilverStripe developer experience.
Options
Option 1: Ad-hoc GraphQL endpoints
Each endpoint represent a particular
GridField
with a particularDataList
. Even though they might return the same data as a generic GraphQL endpoint ("Content API"), they are accessed under a different path.Code example:
Creates a
getCommentablePageApprovedComments
query, which needs to be known at GraphQL schema compile time (requiring Aaron's GridField Registry).Option 2: Form-based GraphQL endpoint
Same as above, but uses a generic
getGridFieldData()
query, reconstructing the Form with its GridField to query its DataList. Does not require all GridFields to be known at GraphQL schema compile time.Option 3: Generic GraphQL endpoints
There is only one GraphQL endpoint for a particular model. Any modifications to the list of this model (filtering, sorting, paging, custom fields) is expressed through GraphQL resolvers. While developers handle
DataList
in those custom resolvers, they are writing a generic querying interface with enough capabilities to handle the needs of many GridField implementations.Code example (not thought out, just to demonstrate the complexities involved here):
Option 4: GridField providers and registry
See Aaron's RFC
Option 5: DataList auto-converts to GraphQL query
Hard to achieve since we have to cover the whole API surface of
DataList
, or risk creating a leaky abstraction. For example, you can query relation fields inDataList->filter()
through a dot notation (Team::get()->filter('Players.Count():GreaterThan', 10)
).The text was updated successfully, but these errors were encountered: