Skip to content

Commit da79e1b

Browse files
committed
Merge branch 'crontroller-inheritance' of https://github.com/ivanproskuryakov/routing-controllers into ivanproskuryakov-crontroller-inheritance
2 parents 70a3465 + 3618cf8 commit da79e1b

File tree

14 files changed

+350
-82
lines changed

14 files changed

+350
-82
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ You can use routing-controllers with [express.js][1] or [koa.js][2].
5555
+ [Interceptor classes](#interceptor-classes)
5656
+ [Global interceptors](#global-interceptors)
5757
* [Creating instances of classes from action params](#creating-instances-of-classes-from-action-params)
58+
* [Controller inheritance](#controller-inheritance)
5859
* [Auto validating action params](#auto-validating-action-params)
5960
* [Using authorization features](#using-authorization-features)
6061
- [@Authorized decorator](#authorized-decorator)
@@ -1236,6 +1237,33 @@ Learn more about class-transformer and how to handle more complex object constru
12361237
This behaviour is enabled by default.
12371238
If you want to disable it simply pass `classTransformer: false` to createExpressServer method. Alternatively you can disable transforming for [individual controllers or routes](#selectively-disable-requestresponse-transforming).
12381239

1240+
## Controller Inheritance
1241+
Often your application may need to have an option to inherit controller from another to reuse code and void duplication.
1242+
A good example of the use is the CRUD operations which can be hidden inside `AbstractBaseController` with the possibility to add new and overload methods, the template method pattern.
1243+
1244+
```typescript
1245+
@Controller(`/product`)
1246+
class ProductController extends AbstractControllerTemplate {}
1247+
@Controller(`/category`)
1248+
class CategoryController extends AbstractControllerTemplate {}
1249+
abstract class AbstractControllerTemplate {
1250+
@Post()
1251+
public create() {}
1252+
1253+
@Read()
1254+
public read() {}
1255+
1256+
@Put()
1257+
public update() {}
1258+
1259+
@Delete()
1260+
public delete() {}
1261+
}
1262+
1263+
```
1264+
https://en.wikipedia.org/wiki/Template_method_pattern
1265+
1266+
12391267
## Auto validating action params
12401268

12411269
Sometimes parsing a json object into instance of some class is not enough.

sample/sample17-controllers-inheritance/app.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,12 @@ useExpressServer(app, {
88
});
99
app.listen(3001); // run express app
1010

11-
console.log("Express server is running on port 3001. Open http://localhost:3001/blogs/ or http://localhost:3002/posts/");
11+
console.log(
12+
"Possible GET endpoints you may see from a browser",
13+
"http://localhost:3001/article",
14+
"http://localhost:3001/article/1000",
15+
"http://localhost:3001/product",
16+
"http://localhost:3001/product/1000",
17+
"http://localhost:3001/category",
18+
"http://localhost:3001/category/1000",
19+
);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {Res} from "../../../src/decorator/Res";
2+
import {Put} from "../../../src/decorator/Put";
3+
import {Post} from "../../../src/decorator/Post";
4+
import {Param} from "../../../src/decorator/Param";
5+
import {Get} from "../../../src/decorator/Get";
6+
import {Delete} from "../../../src/decorator/Delete";
7+
import {Body} from "../../../src/decorator/Body";
8+
9+
import {MockedRepository} from "../repository/MockedRepository";
10+
import {IInstance} from "../interface/IInstance";
11+
12+
/**
13+
* @description the base controller class used by derivatives
14+
*/
15+
export abstract class AbstractControllerTemplate {
16+
/**
17+
* @description domain part of a system, also called object|entity|model
18+
*/
19+
protected domain: string;
20+
protected repository: MockedRepository;
21+
22+
@Post()
23+
public async create(
24+
@Body() payload: any,
25+
@Res() res: any
26+
): Promise<{}> {
27+
const item = await this.repository.create(payload);
28+
29+
res.status(201);
30+
res.location(`/${this.domain}/${item.id}`);
31+
32+
return {};
33+
}
34+
35+
@Put("/:id")
36+
public async updated(
37+
@Param("id") id: number,
38+
@Body() payload: any,
39+
@Res() res: any
40+
): Promise<{}> {
41+
await this.repository.update(id, payload);
42+
res.status(204);
43+
44+
return {};
45+
}
46+
47+
@Get("/:id")
48+
public read(
49+
@Param("id") id: number,
50+
@Res() res: any
51+
): Promise<IInstance> {
52+
return this.repository.find(id);
53+
}
54+
55+
@Get()
56+
public readCollection(
57+
@Res() res: any
58+
): Promise<IInstance[]> {
59+
return this.repository.getCollection();
60+
}
61+
62+
@Delete("/:id")
63+
public async delete(
64+
@Param("id") id: number,
65+
@Res() res: any
66+
): Promise<{}> {
67+
await this.repository.delete(id);
68+
69+
return {};
70+
}
71+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {Controller} from "../../../src/decorator/Controller";
2+
import {AbstractControllerTemplate} from "./AbstractContollerTemplate";
3+
import {MockedRepository} from "../repository/MockedRepository";
4+
5+
const domain = "article";
6+
7+
@Controller(`/${domain}`)
8+
export class ArticleController extends AbstractControllerTemplate {
9+
protected constructor() {
10+
super();
11+
12+
this.domain = domain;
13+
this.repository = new MockedRepository(domain);
14+
}
15+
}

sample/sample17-controllers-inheritance/controllers/BaseController.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.

sample/sample17-controllers-inheritance/controllers/BlogController.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {Controller} from "../../../src/decorator/Controller";
2+
import {AbstractControllerTemplate} from "./AbstractContollerTemplate";
3+
import {MockedRepository} from "../repository/MockedRepository";
4+
5+
const domain = "category";
6+
7+
@Controller(`/${domain}`)
8+
export class CategoryController extends AbstractControllerTemplate {
9+
protected constructor() {
10+
super();
11+
12+
this.domain = domain;
13+
this.repository = new MockedRepository(domain);
14+
}
15+
}

sample/sample17-controllers-inheritance/controllers/PostController.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {Controller} from "../../../src/decorator/Controller";
2+
import {AbstractControllerTemplate} from "./AbstractContollerTemplate";
3+
import {MockedRepository} from "../repository/MockedRepository";
4+
5+
const domain = "product";
6+
7+
@Controller(`/${domain}`)
8+
export class ProductController extends AbstractControllerTemplate {
9+
protected constructor() {
10+
super();
11+
12+
this.domain = domain;
13+
this.repository = new MockedRepository(domain);
14+
}
15+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface IInstance {
2+
id: number;
3+
type: string;
4+
}

0 commit comments

Comments
 (0)