-
Notifications
You must be signed in to change notification settings - Fork 87
Add join type awareness to models and data stores #982
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
Add join type awareness to models and data stores #982
Conversation
For example's sake, here's an example of the kind of thing necessary now to do efficient queries (code @tn3rb has been using in current development): export default createHigherOrderComponent(
compose( [
withDatesListFilterState,
withTicketsListFilterState,
withSelect( ( select, ownProps ) => {
let eventDates = [];
let eventDateTickets = [];
let eventDateIds = [];
let eventDateTicketMap = {};
const coreStore = select( 'eventespresso/core' );
const listStore = select( 'eventespresso/lists' );
const event = coreStore.getEventById( ownProps.eventId );
if ( isModelEntityOfModel( event, 'event' ) ) {
eventDates = coreStore.getRelatedEntities( event, 'datetimes' );
eventDates = condenseArray( eventDates );
if ( ! isEmpty( eventDates ) ) {
eventDateIds = getDatetimeEntityIds( eventDates );
const queryString = 'where[Datetime.DTT_ID][IN]=[' +
eventDateIds.join( ',' ) + ']';
const ticketQueryString = queryString +
'&default_where_conditions=minimum';
eventDateTickets = listStore.getEntities(
'ticket',
ticketQueryString
);
eventDateTicketMap = buildEventDateTicketRelationsMap(
eventDateIds,
eventDateTickets,
listStore.getEntities( 'datetime_ticket', queryString )
);
}
}
return { event, eventDates, eventDateTickets, eventDateTicketMap };
} ),
] ),
'withDatesAndTicketsFilterState'
)( DatesAndTicketsFilterState ); |
f7e86c3
to
cee6e50
Compare
66ab4e0
to
6637af0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks great but had a couple of really minor questions/suggestions
modelName, | ||
relationName, | ||
); | ||
const relationSchema = yield resolveSelect( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we do a hasJoinTable
check before proceeding any further?
ie: what happens if the following function calls run (resolveSelect()
, getPrimaryKey()
, etc) when no join exists?
EDIT
nevermind... i see now that this is checking for a many to many intersection table and not just any table join
! isArray( relationRecords ) ? | ||
[ relationRecords ] : | ||
relationRecords; | ||
while ( relationRecords.length > 0 ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks to me like relationRecords
could still potentially be null
at this point,
so might want to add an Array.isArray()
check or similar protection to avoid "Cannot read property 'length' of null" errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great catch! Ya I'll just check it isn't null before running the while.
} | ||
} | ||
} | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the blocks of logic in this if else
statement are large enough that they could benefit from being extracted into their own function which would make this more readable.
ie:
if ( hasJoinTable ) {
getManyRelations();
} else {
getSingularRelations()
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue here is that this is a generator so your examples would have to be generators also (which increases the testing maintenance load as well). With this only occurring in this method for now (and the components not being reused anywhere yet ) I'd prefer to wait on extracting things until future potential use demonstrates a pattern. For instance the pattern that might get extracted is the actual while
loops. I think its premature to extract this yet.
Worth noting, all the logic from the resolveRelationRecordForRelation
action used to be in this resolver as well! So I already did do some extraction :)
- fixes a bug with state not updating correctly for entity map.
- returns values of object usable for cloning via a factory.createNew call.
- tests needed updates due to changes in defaults and fixtures for other tests.
ab1fe3e
to
b4c542b
Compare
…type awareness to models and data stores (#982) ## Problem this Pull Request solves As @tn3rb has been working on implementing the CRUD work from the models/data-stores into the REM work he is doing, he's encountering difficulties with: - efficient network queries (so getting all the tickets for all the datetimes attached to an event in as few queries as possible - as opposed to doing a getRelatedTickets request for each datetime). - using the existing selectors for the `eventespress/core` in an expected manner despite the efficient queries. The difficulty lies in the fact that retrieving and setting up all the relations in the store state for entities retrieved through efficient queries is a extensive process that's easy to get "wrong" because of the opinionated way relation data is kept in the state. The purpose of this pull then is to introduce some changes so that: - `BaseEntity` models currently _are aware_ of the possible relations, but are _not_ aware of the relation types. We need to expose this in the models so efficient queries can be built. - Alternatively (and what might be the better option) is `eventespresso/schema` can be used for deriving the relation types and relation models from the schema stored in its state. Then there could simply be selectors that get the needed data for building the appropriate queries and knowing what dispatches to execute to get the correct relations setup in `eventespresso/core`. - `eventespresso/core` will have a selector (and resolver) for getting all the related entities for a collection of entity ids. So for example `getRelatedEntitiesForIds( datetimeIds, 'ticket' )`. This would handle all the necessary dispatches etc for correctly adding the relations to the store state as well as using the new selectors in `eventespresso/schema` to know if relations have to be joined via a join table query. - I'd like to also explore if the schema (and the REST API behaviour) provides enough information to programmatically derive the most efficient endpoint to do the query on. For instance, since the `include` parameter allows for requesting related model entities to be returned in the response, it might be possible to get all the entities and their relations in one go via the join table (or base model). If that is the case, then the number of requests can be reduced more and it might even make the relation dispatches easier. ## Changes in this pull Along with the purpose outlined in the original post, I created a test app in #988 based off of this branch to do some implementation testing of things to ensure it worked as expected. While testing, there were a number of other fixes that had to be done. Along with all the below, unit-tests were updated, new tests added, and additional developer documentation was created as well. ### New features * added `resolveRelationRecordForRelation` action generator (`eventespresso/core`) * added `getRelatedEntitiesForIds` selector and resolver. (`eventespresso/core`) * added new `receiveRelationSchema` action and related reducer to `eventespresso/schema` store. * added new `hasJoinTableRelation` selector and resolver (`eventespresso/schema`). * added new `getRelationType` selector and resolver (`eventespresso/schema`). * added new `getRelationSchema` selector and resolver (`eventespresso/schema`). * added a new `clone` getter to the `BaseEntity` class. Can be used to clone a given entity instance. * added a new `forClone` property to the `BaseEntity` class. Can be used to export a plain object representing all the fields and values on a given entity instance that can immediately be used in instantiating a new entity instance. ### Fixes * Fix incorrect argument list in the `receiveEntityAndResolve` dispatch control invoked in the `createRelation` action. ( `eventespresso/core`) * Add normalization for model and relation names in the `createRelations` action. (`eventespresso/core`) * Ensure the the `getEntityById` and `getRelationEntities` selectors are resolved for the new relations created in the `createRelations` action. (`eventespresso/core`) * add some fixes to the `eventespresso/core` reducer for relations: make sure id is always normalized. * make sure model name is normalized for `getSchemaForModel` resolver in `eventespresso/schema` "
Note: given a high priority because this is a blocker for REM work.
Problem this Pull Request solves
As @tn3rb has been working on implementing the CRUD work from the models/data-stores into the REM work he is doing, he's encountering difficulties with:
eventespress/core
in an expected manner despite the efficient queries.The difficulty lies in the fact that retrieving and setting up all the relations in the store state for entities retrieved through efficient queries is a extensive process that's easy to get "wrong" because of the opinionated way relation data is kept in the state.
The purpose of this pull then is to introduce some changes so that:
BaseEntity
models currently are aware of the possible relations, but are not aware of the relation types. We need to expose this in the models so efficient queries can be built.eventespresso/schema
can be used for deriving the relation types and relation models from the schema stored in its state. Then there could simply be selectors that get the needed data for building the appropriate queries and knowing what dispatches to execute to get the correct relations setup ineventespresso/core
.eventespresso/core
will have a selector (and resolver) for getting all the related entities for a collection of entity ids. So for examplegetRelatedEntitiesForIds( datetimeIds, 'ticket' )
. This would handle all the necessary dispatches etc for correctly adding the relations to the store state as well as using the new selectors ineventespresso/schema
to know if relations have to be joined via a join table query.include
parameter allows for requesting related model entities to be returned in the response, it might be possible to get all the entities and their relations in one go via the join table (or base model). If that is the case, then the number of requests can be reduced more and it might even make the relation dispatches easier.Changes in this pull
Along with the purpose outlined in the original post, I created a test app in #988 based off of this branch to do some implementation testing of things to ensure it worked as expected. While testing, there were a number of other fixes that had to be done.
Along with all the below, unit-tests were updated, new tests added, and additional developer documentation was created as well.
New features
resolveRelationRecordForRelation
action generator (eventespresso/core
)getRelatedEntitiesForIds
selector and resolver. (eventespresso/core
)receiveRelationSchema
action and related reducer toeventespresso/schema
store.hasJoinTableRelation
selector and resolver (eventespresso/schema
).getRelationType
selector and resolver (eventespresso/schema
).getRelationSchema
selector and resolver (eventespresso/schema
).clone
getter to theBaseEntity
class. Can be used to clone a given entity instance.forClone
property to theBaseEntity
class. Can be used to export a plain object representing all the fields and values on a given entity instance that can immediately be used in instantiating a new entity instance.Fixes
receiveEntityAndResolve
dispatch control invoked in thecreateRelation
action. (eventespresso/core
)createRelations
action. (eventespresso/core
)getEntityById
andgetRelationEntities
selectors are resolved for the new relations created in thecreateRelations
action. (eventespresso/core
)eventespresso/core
reducer for relations: make sure id is always normalized.getSchemaForModel
resolver ineventespresso/schema
How has this been tested
Checklist
esc_html__()
, see https://codex.wordpress.org/I18n_for_WordPress_Developers)