@@ -43,30 +43,29 @@ These act as de-normalized 'views' of the underlying normalized data and make it
4343easy for the application to render 'virtual' rows comprised of Cell values from
4444multiple joined Table objects in the Store.
4545
46- Some of these, like the main ` movies ` query, are set up for the lifetime of the
47- application:
48-
49- | Query | From  ; Tables | Cell Ids |
50- | ----------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
51- | ` movies ` | ` movies ` , ` genres ` , ` people ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` , ` overview ` , ` directorId ` , ` directorName ` , ` directorImage ` , ` castId1 ` , ` castName1 ` , ` castImage1 ` , ` castId2 ` , ` castName2 ` , ` castImage2 ` , ` castId3 ` , ` castName3 ` , ` castImage3 ` |
52- | ` years ` | ` movies ` | ` year ` , ` movieCount ` |
53- | ` genres ` | ` movies ` | ` genreId ` , ` genreName ` , ` movieCount ` |
54- | ` directors ` | ` movies ` , ` people ` | ` directorId ` , ` directorName ` , ` directorImage ` , ` gender ` , ` popularity ` , ` movieCount ` |
55- | ` cast ` | ` cast ` , ` people ` | ` castId ` , ` castName ` , ` castImage ` , ` gender ` , ` popularity ` , ` movieCount ` |
56-
57- Others, like the ` moviesInYear ` query, are set up when a specific page is being
58- viewed (in that case, the detail page for a particular year):
59-
60- | Query | From  ; Tables | Cell Ids |
61- | -------------------- | -------------------------- | ------------------------------------------------------------------------------ |
62- | ` moviesInYear ` | ` movies ` , ` genres ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` |
63- | ` moviesInGenre ` | ` movies ` , ` genres ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` |
64- | ` moviesWithDirector ` | ` movies ` , ` genres ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` |
65- | ` moviesWithCast ` | ` cast ` , ` movies ` , ` genres ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` |
66-
67- You might notice that many of these queries share the same Cell Ids. You'll
68- discover that TinyBase lets you compose queries programmatically, so we'll be
69- able to build these queries without much repetition: the common
46+ The following queries are set up for the lifetime of the application:
47+
48+ | Query | From  ; Tables | Cell Ids |
49+ | ----------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50+ | ` movies ` | ` movies ` , ` genres ` , ` people ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` , ` overview ` , ` directorId ` , ` directorName ` , ` directorImage ` , ` castId1 ` , ` castName1 ` , ` castImage1 ` , ` castId2 ` , ` castName2 ` , ` castImage2 ` , ` castId3 ` , ` castName3 ` , ` castImage3 ` |
51+ | ` years ` | ` movies ` | ` year ` , ` movieCount ` |
52+ | ` genres ` | ` movies ` | ` genreId ` , ` genreName ` , ` movieCount ` |
53+ | ` directors ` | ` movies ` , ` people ` | ` directorId ` , ` directorName ` , ` directorImage ` , ` gender ` , ` popularity ` , ` movieCount ` |
54+ | ` cast ` | ` cast ` , ` people ` | ` castId ` , ` castName ` , ` castImage ` , ` gender ` , ` popularity ` , ` movieCount ` |
55+ | ` yearGenreMovies ` | ` movies ` , ` genres ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` |
56+ | ` directedMovies ` | ` movies ` , ` genres ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` |
57+ | ` appearedMovies ` | ` cast ` , ` movies ` , ` genres ` | ` movieId ` , ` movieName ` , ` movieImage ` , ` year ` , ` rating ` , ` genreId ` , ` genreName ` |
58+
59+ The final three queries are interesting since they are parameterized.
60+ ` yearGenreMovies ` query accepts optional ` year ` and ` genreId ` params to filter
61+ movies dynamically. Similarly, the ` directedMovies ` and ` appearedMovies ` queries
62+ accept a ` personId ` param. This means we can set up these queries once, but
63+ update them with different params to get different results as the user drills
64+ into different pages.
65+
66+ You might notice that many of these queries share the same selected Cell Ids.
67+ You'll discover that TinyBase lets you compose queries programmatically, so
68+ we'll be able to build these queries without much repetition: the common
7069` queryMovieBasics ` function is used to select the same Cell Ids into most of
7170these query views.
7271
@@ -523,51 +522,56 @@ We also create query definitions for the other persistent queries. These use the
523522` group` function to count the number of movies per year, genre, and so on, used
524523in the overview components of each of the main sections of the app.
525524
526- The ` movies` query definition is re-used across the app, so note that it takes
527- optional params for ` year` and ` genreId` so that different views can show movies
528- by year or genre if required.
529-
530525` ` ` js
531- // ...
532- queries .setQueryDefinition (' years' , ' movies' , ({select, group}) => {
533- select (' year' );
534- select ((_ , rowId ) => rowId).as (' movieId' );
535- group (' movieId' , ' count' ).as (' movieCount' );
536- });
526+ // ...
527+ queries .setQueryDefinition (' years' , ' movies' , ({select, group}) => {
528+ select (' year' );
529+ select ((_ , rowId ) => rowId).as (' movieId' );
530+ group (' movieId' , ' count' ).as (' movieCount' );
531+ });
537532
538- queries .setQueryDefinition (' genres' , ' movies' , ({select, join, group}) => {
539- select (' genreId' );
540- select ((_ , rowId ) => rowId).as (' movieId' );
541- join (' genres' , ' genreId' );
542- select (' genres' , ' name' ).as (' genreName' );
543- group (' movieId' , ' count' ).as (' movieCount' );
544- });
533+ queries .setQueryDefinition (' genres' , ' movies' , ({select, join, group}) => {
534+ select (' genreId' );
535+ select ((_ , rowId ) => rowId).as (' movieId' );
536+ join (' genres' , ' genreId' );
537+ select (' genres' , ' name' ).as (' genreName' );
538+ group (' movieId' , ' count' ).as (' movieCount' );
539+ });
545540
546- queries .setQueryDefinition (' directors' , ' movies' , ({select, join, group}) => {
547- select (' directorId' );
548- select ((_ , rowId ) => rowId).as (' movieId' );
549- select (' people' , ' name' ).as (' directorName' );
550- select (' people' , ' image' ).as (' directorImage' );
551- select (' people' , ' gender' );
552- select (' people' , ' popularity' );
553- join (' people' , ' directorId' );
554- group (' movieId' , ' count' ).as (' movieCount' );
555- });
541+ queries .setQueryDefinition (' directors' , ' movies' , ({select, join, group}) => {
542+ select (' directorId' );
543+ select ((_ , rowId ) => rowId).as (' movieId' );
544+ select (' people' , ' name' ).as (' directorName' );
545+ select (' people' , ' image' ).as (' directorImage' );
546+ select (' people' , ' gender' );
547+ select (' people' , ' popularity' );
548+ join (' people' , ' directorId' );
549+ group (' movieId' , ' count' ).as (' movieCount' );
550+ });
556551
557- queries .setQueryDefinition (' cast' , ' cast' , ({select, join, group}) => {
558- select (' castId' );
559- select (' movieId' );
560- select (' people' , ' name' ).as (' castName' );
561- select (' people' , ' image' ).as (' castImage' );
562- select (' people' , ' gender' );
563- select (' people' , ' popularity' );
552+ queries .setQueryDefinition (' cast' , ' cast' , ({select, join, group}) => {
553+ select (' castId' );
554+ select (' movieId' );
555+ select (' people' , ' name' ).as (' castName' );
556+ select (' people' , ' image' ).as (' castImage' );
557+ select (' people' , ' gender' );
558+ select (' people' , ' popularity' );
559+ join (' people' , ' castId' );
560+ group (' movieId' , ' count' ).as (' movieCount' );
561+ });
562+ // ...
563+ ` ` `
564564
565- join (' people' , ' castId' );
566- group (' movieId' , ' count' ).as (' movieCount' );
567- });
565+ And finally the parameterized queries. The ` yearGenreMovies` query definition is
566+ re-used across the app, so note that it takes optional params for ` year` and
567+ ` genreId` so that different views can show movies by year or genre if required.
568+ Similarly, the ` directedMovies` and ` appearedMovies` queries take a ` personId`
569+ param to show movies for a specific director or actor.
568570
571+ ` ` ` js
572+ // ...
569573 queries .setQueryDefinition (
570- ' movies ' ,
574+ ' yearGenreMovies ' ,
571575 ' movies' ,
572576 ({select, join, where, param}) => {
573577 queryMovieBasics ({select, join});
@@ -581,14 +585,59 @@ by year or genre if required.
581585 {year: null , genreId: null },
582586 );
583587
588+ queries .setQueryDefinition (
589+ ' directedMovies' ,
590+ ' movies' ,
591+ ({select, join, where, param}) => {
592+ queryMovieBasics ({select, join});
593+ where (' directorId' , param (' personId' ));
594+ },
595+ {personId: null },
596+ );
597+
598+ queries .setQueryDefinition (
599+ ' appearedMovies' ,
600+ ' cast' ,
601+ ({select, join, where, param}) => {
602+ select (' movieId' );
603+ select (' movies' , ' name' ).as (' movieName' );
604+ select (' movies' , ' image' ).as (' movieImage' );
605+ select (' movies' , ' year' );
606+ select (' movies' , ' rating' );
607+ select (' movies' , ' genreId' );
608+ select (' genres' , ' name' ).as (' genreName' );
609+ join (' movies' , ' movieId' );
610+ join (' genres' , ' movies' , ' genreId' );
611+ where (' castId' , param (' personId' ));
612+ },
613+ {personId: null },
614+ );
615+
584616 return queries;
585617}
586618```
587619
588- That's it for the main persistent queries that power most of the major views of
620+ That's it for the main persistent queries that power all the different views of
589621the app. We'll refer to these by their query Id when we actually bind them to
590622components.
591623
624+ Finally, let's create a convenient hook that will allow us to parameterize the
625+ ` yearGenreMovies ` query, and one to parameterize both the ` directedMovies ` and
626+ ` appearedMovies ` queries for a given person:
627+
628+ ``` js
629+ const useSetYearGenre = ({year, genreId}) =>
630+ useSetParamValuesCallback (
631+ ' yearGenreMovies' ,
632+ (yearGenre ) => yearGenre,
633+ )({year, genreId});
634+
635+ const useSetPersonMovies = (personId ) => {
636+ useSetParamValuesCallback (' directedMovies' , (person ) => person)({personId});
637+ useSetParamValuesCallback (' appearedMovies' , (person ) => person)({personId});
638+ };
639+ ```
640+
592641## The ResultSortedTableInHtmlTable Component
593642
594643Most of the movies app is built from tabular data views, and it's nice to have a
@@ -824,7 +873,7 @@ components to create the major views of the application.
824873
825874First, the overview of all the rated movies in the database (which displays on
826875the 'Movies' tab when the app first loads), comprising a
827- ` ResultSortedTableInHtmlTable ` that renders the ` movies ` query with four
876+ ResultSortedTableInHtmlTable component that renders the ` movies ` query with four
828877columns, sorted by rating.
829878
830879``` jsx
@@ -1010,19 +1059,18 @@ Moving on, the detail for a specific year is just a sorted table of the movies
10101059from that year.
10111060
10121061Here is a case where we can use a parameterized query, introduced in TinyBase
1013- v7.2. The ` movies ` query was set up once in the app initialization, so we use
1014- the ` useSetParamValuesCallback ` hook to update its parameters whenever the
1062+ v7.2. The ` yearGenreMovies ` query was set up once in the app initialization, so
1063+ we use the useSetParamValuesCallback hook to update its params whenever the
10151064` year ` prop changes. This is more efficient and ergonomic than rebuilding the
10161065query definition every time.
10171066
10181067``` jsx
10191068const YearDetail = ({year}) => {
1020- useSetParamValuesCallback (' movies' , () => ({year}), [year])();
1021-
1069+ useSetYearGenre ({year});
10221070 return (
10231071 < Page title= {` Movies from ${ year} ` }>
10241072 < ResultSortedTableInHtmlTable
1025- queryId= " movies "
1073+ queryId= " yearGenreMovies "
10261074 customCells= {customCellsForMoviesInYear}
10271075 cellId= " rating"
10281076 descending= {true }
@@ -1043,16 +1091,16 @@ const customCellsForMoviesInYear = {
10431091```
10441092
10451093The genre detail page is very similar, using the same parameterized query but
1046- setting the ` genreId ` parameter instead:
1094+ setting the ` genreId ` param instead:
10471095
10481096``` jsx
10491097const GenreDetail = ({genreId}) => {
1050- useSetParamValuesCallback ( ' movies ' , () => ( {genreId}), [genreId])( );
1098+ useSetYearGenre ( {genreId});
10511099 const name = useCell (' genres' , genreId, ' name' );
10521100 return name == null ? null : (
10531101 < Page title= {` ${ name} movies` }>
10541102 < ResultSortedTableInHtmlTable
1055- queryId= " movies "
1103+ queryId= " yearGenreMovies "
10561104 customCells= {customCellsForMoviesInGenre}
10571105 cellId= " rating"
10581106 descending= {true }
@@ -1072,59 +1120,24 @@ const customCellsForMoviesInGenre = {
10721120};
10731121```
10741122
1075- Finally, we build the detail page for a person. We create two queries on the fly
1076- here, one for those movies for which the person is the director, and one for
1077- those in which they are cast .
1123+ Finally, we build the detail page for a person. Again, we use parameterized
1124+ queries to get the movies they have directed and the movies in which they have
1125+ appeared without having to re-define the whole query each time .
10781126
1079- The latter is slightly more complex since it needs to use the many-to-many
1080- ` cast ` Table as its root, from where it joins to the ` movies ` Table and ` genres `
1081- Table in turn. Nevertheless, the result Cell Ids are named to be consistent with
1082- the other queries, so that we can use the same custom components to render each
1083- part of the HTML table.
1084-
1085- This component is also slightly more complex that the others because it is also
1127+ This component is slightly more complex than the others because it is also
10861128rendering some parts of its content directly from the ` people ` Table (rather
10871129than via a query) - hence the use of the basic ` useCell ` hook and ` CellView `
10881130component, for example.
10891131
10901132``` jsx
10911133const PersonDetail = ({personId}) => {
1092- const queries = useQueries ();
1093- useMemo (
1094- () =>
1095- queries
1096- .setQueryDefinition (
1097- ' moviesWithDirector' ,
1098- ' movies' ,
1099- ({select, join, where}) => {
1100- queryMovieBasics ({select, join});
1101- where (' directorId' , personId);
1102- },
1103- )
1104- .setQueryDefinition (
1105- ' moviesWithCast' ,
1106- ' cast' ,
1107- ({select, join, where}) => {
1108- select (' movieId' );
1109- select (' movies' , ' name' ).as (' movieName' );
1110- select (' movies' , ' image' ).as (' movieImage' );
1111- select (' movies' , ' year' );
1112- select (' movies' , ' rating' );
1113- select (' movies' , ' genreId' );
1114- select (' genres' , ' name' ).as (' genreName' );
1115- join (' movies' , ' movieId' );
1116- join (' genres' , ' movies' , ' genreId' );
1117- where (' castId' , personId);
1118- },
1119- ),
1120- [personId],
1121- );
1134+ useSetPersonMovies (personId);
11221135
11231136 const props = {tableId: ' people' , rowId: personId};
11241137 const name = useCell (' people' , personId, ' name' );
11251138 const died = useCell (' people' , personId, ' died' );
1126- const moviesWithDirector = useResultRowIds (' moviesWithDirector ' );
1127- const moviesWithCast = useResultRowIds (' moviesWithCast ' );
1139+ const directedMovies = useResultRowIds (' directedMovies ' );
1140+ const appearedMovies = useResultRowIds (' appearedMovies ' );
11281141
11291142 return name == null ? null : (
11301143 < Page title= {name}>
@@ -1149,11 +1162,11 @@ const PersonDetail = ({personId}) => {
11491162 < CellView {... props} cellId= " biography" / >
11501163 < / p>
11511164
1152- {moviesWithDirector .length == 0 ? null : (
1165+ {directedMovies .length == 0 ? null : (
11531166 <>
11541167 < h2> As director: < / h2>
11551168 < ResultSortedTableInHtmlTable
1156- queryId= " moviesWithDirector "
1169+ queryId= " directedMovies "
11571170 customCells= {customCellsForMoviesWithPeople}
11581171 cellId= " rating"
11591172 descending= {true }
@@ -1165,11 +1178,11 @@ const PersonDetail = ({personId}) => {
11651178 < / >
11661179 )}
11671180
1168- {moviesWithCast .length == 0 ? null : (
1181+ {appearedMovies .length == 0 ? null : (
11691182 <>
11701183 < h2> As cast: < / h2>
11711184 < ResultSortedTableInHtmlTable
1172- queryId= " moviesWithCast "
1185+ queryId= " appearedMovies "
11731186 customCells= {customCellsForMoviesWithPeople}
11741187 cellId= " rating"
11751188 descending= {true }
0 commit comments