Completed
Push — master ( fa21f3...8fd760 )
by Evgeny
45s
created

CrudHateoasExtension   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 79.41%

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 172
ccs 81
cts 102
cp 0.7941
rs 10
wmc 17
lcom 1
cbo 11

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A implementedEvents() 0 6 1
A _buildIndexLinks() 0 18 2
C _buildViewLinks() 0 79 9
A afterAction() 0 21 4
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();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $currentRoute is correct as $this->_reverseRouter->f...ewRoute, $parentRoutes) (which targets CakeDC\Api\Service\Utili...rseRouting::findRoute()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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';
0 ignored issues
show
Bug introduced by
It seems like \Cake\Utility\Inflector:...score($service->name()) targeting Cake\Utility\Inflector::underscore() can also be of type boolean; however, Cake\Utility\Inflector::singularize() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
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