|
| 1 | +# Many2Many |
| 2 | + |
| 3 | +This plugin lets you manage many-to-many relationships. |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +Install the plugin: |
| 8 | + |
| 9 | +```bash |
| 10 | +npm i @adminforth/many2many |
| 11 | +``` |
| 12 | + |
| 13 | +## Setting up |
| 14 | + |
| 15 | +Let's say we want to implement a relationship where every apartment can have many realtors and each realtor can have many apartments. |
| 16 | +We'll also need a junction resource to connect realtors and apartments. |
| 17 | + |
| 18 | +First, create the `realtors` table and the junction table `realtorsAparts`: |
| 19 | + |
| 20 | + |
| 21 | +```ts title="./schema.prisma" |
| 22 | +//diff-add |
| 23 | +model realtors { |
| 24 | + //diff-add |
| 25 | + id String @id |
| 26 | + //diff-add |
| 27 | + name String |
| 28 | + //diff-add |
| 29 | + surname String |
| 30 | + //diff-add |
| 31 | +} |
| 32 | +//diff-add |
| 33 | +model realtorsAparts { |
| 34 | + //diff-add |
| 35 | + id String @id |
| 36 | + //diff-add |
| 37 | + realtorId String |
| 38 | + //diff-add |
| 39 | + apartmentId String |
| 40 | + //diff-add |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +Migrate the Prisma schema: |
| 45 | + |
| 46 | +```bash |
| 47 | +npm run makemigration -- --name add-realtors-and-realtorsAparts; npm run migrate:local |
| 48 | +``` |
| 49 | + |
| 50 | +Now create a resource for the realtors: |
| 51 | + |
| 52 | +```ts title="./resources/realtors.ts" |
| 53 | +import { AdminForthDataTypes, AdminForthResourceInput } from 'adminforth'; |
| 54 | + |
| 55 | +export default { |
| 56 | + dataSource: 'maindb', |
| 57 | + table: 'realtors', |
| 58 | + resourceId: 'realtors', |
| 59 | + label: 'Realtors', |
| 60 | + recordLabel: (r) => ` ${r.name}`, |
| 61 | + columns: [ |
| 62 | + { |
| 63 | + name: 'id', |
| 64 | + type: AdminForthDataTypes.STRING, |
| 65 | + label: 'Identifier', |
| 66 | + showIn: { |
| 67 | + list: false, |
| 68 | + edit: false, |
| 69 | + create: false, |
| 70 | + }, |
| 71 | + primaryKey: true, |
| 72 | + fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7), |
| 73 | + }, |
| 74 | + { |
| 75 | + name: 'name', |
| 76 | + required: true, |
| 77 | + showIn: { all: true }, |
| 78 | + type: AdminForthDataTypes.STRING, |
| 79 | + maxLength: 255, |
| 80 | + minLength: 3, |
| 81 | + }, |
| 82 | + { |
| 83 | + name: "surname", |
| 84 | + required: true, |
| 85 | + showIn: { all: true }, |
| 86 | + type: AdminForthDataTypes.STRING, |
| 87 | + maxLength: 100, |
| 88 | + minLength: 3, |
| 89 | + } |
| 90 | + ], |
| 91 | + options: { |
| 92 | + listPageSize: 12, |
| 93 | + allowedActions: { |
| 94 | + edit: true, |
| 95 | + delete: true, |
| 96 | + show: true, |
| 97 | + filter: true, |
| 98 | + }, |
| 99 | + }, |
| 100 | +} as AdminForthResourceInput; |
| 101 | +``` |
| 102 | + |
| 103 | +And one for the junction table `realtorsAparts`: |
| 104 | + |
| 105 | +```ts title="./resources/realtorsAparts.ts" |
| 106 | +import { AdminForthDataTypes, AdminForthResourceInput } from 'adminforth'; |
| 107 | + |
| 108 | +export default { |
| 109 | + dataSource: 'maindb', |
| 110 | + table: 'realtorsAparts', |
| 111 | + resourceId: 'realtorsAparts', |
| 112 | + label: 'Realtors-aparts', |
| 113 | + columns: [ |
| 114 | + { |
| 115 | + name: 'id', |
| 116 | + type: AdminForthDataTypes.STRING, |
| 117 | + label: 'Identifier', |
| 118 | + showIn: { |
| 119 | + list: false, |
| 120 | + edit: false, |
| 121 | + create: false, |
| 122 | + }, |
| 123 | + primaryKey: true, |
| 124 | + fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7), |
| 125 | + }, |
| 126 | + { |
| 127 | + name: 'realtorId', |
| 128 | + foreignResource: { |
| 129 | + resourceId: 'realtors', |
| 130 | + searchableFields: ['name'], |
| 131 | + searchIsCaseSensitive: true |
| 132 | + } |
| 133 | + }, |
| 134 | + { |
| 135 | + name: 'apartmentId', |
| 136 | + foreignResource: { |
| 137 | + resourceId: 'aparts', |
| 138 | + searchableFields: ['title'], |
| 139 | + searchIsCaseSensitive: true |
| 140 | + } |
| 141 | + }, |
| 142 | + ], |
| 143 | + options: { |
| 144 | + listPageSize: 12, |
| 145 | + allowedActions: { |
| 146 | + edit: true, |
| 147 | + delete: true, |
| 148 | + show: true, |
| 149 | + filter: true, |
| 150 | + }, |
| 151 | + }, |
| 152 | +} as AdminForthResourceInput; |
| 153 | +``` |
| 154 | + |
| 155 | +Now add the plugin resources to the main config file: |
| 156 | + |
| 157 | +```ts title="./index.ts" |
| 158 | + |
| 159 | +//diff-add |
| 160 | +import realtorsResource from './resources/realtors.js'; |
| 161 | +//diff-add |
| 162 | +import realtorsApartsResource from './resources/realtorsAparts.js'; |
| 163 | + |
| 164 | + ... |
| 165 | + |
| 166 | + |
| 167 | +dataSources: [ |
| 168 | + { |
| 169 | + id: 'maindb', |
| 170 | + url: `${process.env.DATABASE_URL}` |
| 171 | + }, |
| 172 | + ], |
| 173 | + resources: [ |
| 174 | + ... |
| 175 | + //diff-add |
| 176 | + realtorsResource, |
| 177 | + //diff-add |
| 178 | + realtorsApartsResource |
| 179 | + ] |
| 180 | + |
| 181 | + ... |
| 182 | + |
| 183 | + menu: [ |
| 184 | + |
| 185 | + ... |
| 186 | + //diff-add |
| 187 | + { |
| 188 | + //diff-add |
| 189 | + label: 'Realtors', |
| 190 | + //diff-add |
| 191 | + resourceId: 'realtors' |
| 192 | + //diff-add |
| 193 | + }, |
| 194 | + //diff-add |
| 195 | + { |
| 196 | + //diff-add |
| 197 | + label: 'Realtors-aparts', |
| 198 | + //diff-add |
| 199 | + resourceId: 'realtorsAparts' |
| 200 | + //diff-add |
| 201 | + }, |
| 202 | + |
| 203 | + ... |
| 204 | + |
| 205 | + ] |
| 206 | + |
| 207 | +``` |
| 208 | + |
| 209 | +Finally, add the plugin to the `apartments` resource: |
| 210 | + |
| 211 | + |
| 212 | +```ts title="./resources/apartments.ts" |
| 213 | +//diff-add |
| 214 | +import Many2ManyPlugin from '@adminforth/many2many'; |
| 215 | + |
| 216 | + ... |
| 217 | + |
| 218 | + plugins: [ |
| 219 | + ... |
| 220 | + |
| 221 | + //diff-add |
| 222 | + new Many2ManyPlugin({ |
| 223 | + //diff-add |
| 224 | + linkedResourceId: 'realtors', |
| 225 | + //diff-add |
| 226 | + }) |
| 227 | + |
| 228 | + ... |
| 229 | + ] |
| 230 | + |
| 231 | + ... |
| 232 | + |
| 233 | +``` |
| 234 | + |
| 235 | + |
| 236 | +The plugin is set up. Create some records in the `realtors` table: |
| 237 | + |
| 238 | +Now, when creating an apartment, you can select (link) one or more realtors. |
| 239 | + |
| 240 | +After saving the record, rows in the junction table are created automatically: |
| 241 | + |
| 242 | + |
| 243 | + |
| 244 | +## Disable automatic cleanup of the junction table |
| 245 | +By default, when you delete a realtor or an apartment, all related rows in the junction table are deleted automatically. To avoid this behavior, add: |
| 246 | + |
| 247 | +```ts |
| 248 | + |
| 249 | + ... |
| 250 | + |
| 251 | + new Many2ManyPlugin({ |
| 252 | + linkedResourceId: 'realtors', |
| 253 | + //diff-add |
| 254 | + dontDeleteJunctionRecords: true, // prevents automatic deletion of related junction rows |
| 255 | + }) |
| 256 | + |
| 257 | + ... |
| 258 | + |
| 259 | +``` |
| 260 | + |
| 261 | +## Making editable fields for the both resources |
| 262 | + |
| 263 | +There might be cases, when you want to make fields editable on both resources. For these cases you can just create second instance of plugin on the second resource and it will allow you to use this plugin from the both resources. |
| 264 | + |
| 265 | +```ts title="./resources/apartments.ts" |
| 266 | +//diff-add |
| 267 | +import Many2ManyPlugin from '@adminforth/many2many'; |
| 268 | + |
| 269 | + ... |
| 270 | + |
| 271 | + plugins: [ |
| 272 | + ... |
| 273 | + |
| 274 | + //diff-add |
| 275 | + new Many2ManyPlugin({ |
| 276 | + //diff-add |
| 277 | + linkedResourceId: 'realtors', |
| 278 | + //diff-add |
| 279 | + }) |
| 280 | + |
| 281 | + ... |
| 282 | + ] |
| 283 | + |
| 284 | + ... |
| 285 | + |
| 286 | +``` |
| 287 | + |
| 288 | + |
| 289 | +and |
| 290 | + |
| 291 | + |
| 292 | +```ts title="./resources/realtors.ts" |
| 293 | +//diff-add |
| 294 | +import Many2ManyPlugin from '@adminforth/many2many'; |
| 295 | + |
| 296 | + ... |
| 297 | + |
| 298 | + plugins: [ |
| 299 | + ... |
| 300 | + |
| 301 | + //diff-add |
| 302 | + new Many2ManyPlugin({ |
| 303 | + //diff-add |
| 304 | + linkedResourceId: 'aparts', |
| 305 | + //diff-add |
| 306 | + }) |
| 307 | + |
| 308 | + ... |
| 309 | + ] |
| 310 | + |
| 311 | + ... |
| 312 | + |
| 313 | +``` |
0 commit comments