1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright 2016, Cake Development Corporation (http://cakedc.com) |
4
|
|
|
* |
5
|
|
|
* Licensed under The MIT License |
6
|
|
|
* Redistributions of files must retain the above copyright notice. |
7
|
|
|
* |
8
|
|
|
* @copyright Copyright 2016, Cake Development Corporation (http://cakedc.com) |
9
|
|
|
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace CakeDC\Api\Service\Action\Extension; |
13
|
|
|
|
14
|
|
|
use CakeDC\Api\Service\Action\Action; |
15
|
|
|
use CakeDC\Api\Service\Action\CrudAction; |
16
|
|
|
use CakeDC\Api\Service\Action\ExtensionRegistry; |
17
|
|
|
use CakeDC\Api\Service\Utility\ReverseRouting; |
18
|
|
|
use Cake\Event\Event; |
19
|
|
|
use Cake\Event\EventListenerInterface; |
20
|
|
|
use Cake\Utility\Inflector; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Class CrudHateoasExtension |
24
|
|
|
* |
25
|
|
|
* @package CakeDC\Api\Service\Action\Extension |
26
|
|
|
*/ |
27
|
|
|
class CrudHateoasExtension extends Extension implements EventListenerInterface |
28
|
|
|
{ |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var ReverseRouting |
32
|
|
|
*/ |
33
|
|
|
protected $_reverseRouter; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* CrudHateous Extension constructor. |
37
|
|
|
* |
38
|
|
|
* @param ExtensionRegistry $registry An ExtensionRegistry instance. |
39
|
|
|
* @param array $config Configuration. |
40
|
|
|
*/ |
41
|
4 |
|
public function __construct(ExtensionRegistry $registry, array $config = []) |
42
|
|
|
{ |
43
|
4 |
|
parent::__construct($registry, $config); |
44
|
4 |
|
$this->_reverseRouter = new ReverseRouting(); |
45
|
4 |
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Returns a list of events this object is implementing. When the class is registered |
49
|
|
|
* in an event manager, each individual method will be associated with the respective event. |
50
|
|
|
* |
51
|
|
|
* @return array |
52
|
|
|
*/ |
53
|
4 |
|
public function implementedEvents() |
54
|
|
|
{ |
55
|
|
|
return [ |
56
|
4 |
|
'Action.afterProcess' => 'afterAction', |
57
|
4 |
|
]; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* After action callback. |
62
|
|
|
* |
63
|
|
|
* @param Event $event An Event instance. |
64
|
|
|
* @return void |
65
|
|
|
*/ |
66
|
4 |
|
public function afterAction(Event $event) |
67
|
|
|
{ |
68
|
4 |
|
$action = $event->subject(); |
69
|
4 |
|
$result = $action->service()->result(); |
70
|
4 |
|
$actionName = $action->name(); |
71
|
4 |
|
$links = []; |
72
|
|
|
//$route = $action->route(); |
|
|
|
|
73
|
4 |
|
if ($actionName == 'view') { |
74
|
2 |
|
$links = $this->_buildViewLinks($action); |
75
|
2 |
|
} |
76
|
4 |
|
if ($actionName == 'index') { |
77
|
2 |
|
$links = $this->_buildIndexLinks($action); |
78
|
2 |
|
} |
79
|
|
|
|
80
|
4 |
|
$parent = $action->service()->parent(); |
81
|
|
|
|
82
|
4 |
|
if ($parent !== null) { |
83
|
2 |
|
$result = $parent->result(); |
84
|
2 |
|
} |
85
|
4 |
|
$result->setPayload('links', $links); |
86
|
4 |
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Builds index action links. |
90
|
|
|
* |
91
|
|
|
* @param Action $action An Action instance. |
92
|
|
|
* @return array |
93
|
|
|
*/ |
94
|
2 |
|
protected function _buildIndexLinks(Action $action) |
95
|
|
|
{ |
96
|
2 |
|
$links = []; |
97
|
2 |
|
$indexRoute = $action->route(); |
98
|
2 |
|
$parent = $action->service()->parent(); |
99
|
2 |
|
$path = $this->_reverseRouter->indexPath($action); |
100
|
|
|
|
101
|
2 |
|
$links[] = $this->_reverseRouter->link('self', $path, $indexRoute['_method']); |
102
|
2 |
|
$links[] = $this->_reverseRouter->link($action->service()->name() . ':add', $path, 'POST'); |
103
|
|
|
|
104
|
2 |
|
if ($parent !== null) { |
105
|
1 |
|
$parentName = $parent->name() . ':view'; |
106
|
1 |
|
$path = $this->_reverseRouter->parentViewPath($parentName, $action, 'view'); |
107
|
1 |
|
$links[] = $this->_reverseRouter->link($parentName, $path, 'GET'); |
108
|
1 |
|
} |
109
|
|
|
|
110
|
2 |
|
return $links; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Builds view action links. |
115
|
|
|
* |
116
|
|
|
* @param Action $action An Action instance. |
117
|
|
|
* @return array |
118
|
|
|
*/ |
119
|
2 |
|
protected function _buildViewLinks(Action $action) |
120
|
|
|
{ |
121
|
2 |
|
$links = []; |
122
|
2 |
|
$viewRoute = $action->route(); |
123
|
2 |
|
$service = $action->service(); |
124
|
2 |
|
$parent = $action->service()->parent(); |
125
|
2 |
|
$path = null; |
126
|
2 |
|
if ($parent !== null) { |
127
|
1 |
|
$parentRoutes = $parent->routes(); |
128
|
1 |
|
$currentRoute = $this->_reverseRouter->findRoute($viewRoute, $parentRoutes); |
|
|
|
|
129
|
1 |
|
if ($currentRoute !== null) { |
130
|
1 |
|
unset($viewRoute['id']); |
131
|
1 |
|
$path = $parent->routeReverse($viewRoute); |
132
|
1 |
|
array_pop($viewRoute['pass']); |
133
|
|
|
|
134
|
1 |
|
$indexName = $service->name() . ':index'; |
135
|
1 |
|
$indexPath = $this->_reverseRouter->parentViewPath($indexName, $action, 'index'); |
136
|
1 |
|
} |
137
|
1 |
|
} else { |
138
|
1 |
|
unset($viewRoute['id']); |
139
|
1 |
|
$path = $service->routeReverse($viewRoute); |
140
|
1 |
|
array_pop($viewRoute['pass']); |
141
|
|
|
|
142
|
1 |
|
$indexName = $service->name() . ':index'; |
143
|
1 |
|
$route = collection($service->routes()) |
144
|
|
|
->filter(function ($item) use ($indexName) { |
145
|
1 |
|
return $item->getName() == $indexName; |
146
|
1 |
|
}) |
147
|
1 |
|
->first(); |
148
|
1 |
|
$indexPath = $service->routeReverse($route->defaults); |
149
|
|
|
} |
150
|
|
|
|
151
|
2 |
|
$links[] = $this->_reverseRouter->link('self', $path, $viewRoute['_method']); |
152
|
2 |
|
$links[] = $this->_reverseRouter->link($action->service()->name() . ':edit', $path, 'PUT'); |
153
|
2 |
|
$links[] = $this->_reverseRouter->link($action->service()->name() . ':delete', $path, 'DELETE'); |
154
|
2 |
|
if (!empty($indexPath)) { |
155
|
2 |
|
$links[] = $this->_reverseRouter->link($action->service()->name() . ':index', $indexPath, 'GET'); |
156
|
2 |
|
} |
157
|
|
|
|
158
|
2 |
|
if ($parent === null && $action instanceof CrudAction) { |
159
|
1 |
|
$table = $action->table(); |
160
|
1 |
|
$hasMany = $table->associations()->type('HasMany'); |
161
|
1 |
|
foreach ($hasMany as $assoc) { |
162
|
|
|
$target = $assoc->target(); |
163
|
|
|
$alias = $target->alias(); |
164
|
|
|
|
165
|
|
|
$targetClass = get_class($target); |
166
|
|
|
list(, $className) = namespaceSplit($targetClass); |
167
|
|
|
$className = preg_replace('/(.*)Table$/', '\1', $className); |
168
|
|
|
if ($className === '') { |
169
|
|
|
$className = $alias; |
170
|
|
|
} |
171
|
|
|
$serviceName = Inflector::underscore($className); |
172
|
|
|
|
173
|
|
|
$indexName = $serviceName . ':index'; |
174
|
|
|
$route = collection($service->routes()) |
175
|
|
|
->filter(function ($item) use ($indexName) { |
176
|
|
|
return $item->getName() == $indexName; |
177
|
|
|
}) |
178
|
|
|
->first(); |
179
|
|
|
|
180
|
|
|
$currentId = Inflector::singularize(Inflector::underscore($service->name())) . '_id'; |
|
|
|
|
181
|
|
|
$defaults = $route->defaults; |
182
|
|
|
$viewRoute = $action->route(); |
183
|
|
|
$defaults[$currentId] = $viewRoute['id']; |
184
|
|
|
$indexPath = $service->routeReverse($defaults); |
185
|
|
|
|
186
|
|
|
$links[] = $this->_reverseRouter->link($serviceName . ':index', $indexPath, 'GET'); |
187
|
1 |
|
} |
188
|
1 |
|
} |
189
|
|
|
|
190
|
2 |
|
if ($parent !== null) { |
191
|
1 |
|
$parentName = $parent->name() . ':view'; |
192
|
1 |
|
$path = $this->_reverseRouter->parentViewPath($parentName, $action, 'view'); |
193
|
1 |
|
$links[] = $this->_reverseRouter->link($parentName, $path, 'GET'); |
194
|
1 |
|
} |
195
|
|
|
|
196
|
2 |
|
return $links; |
197
|
|
|
} |
198
|
|
|
} |
199
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.