Completed
Push — d64 ( 4968c1...c23225 )
by Welling
02:10
created

ClientLocal   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 528
Duplicated Lines 11.74 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 62
loc 528
ccs 0
cts 325
cp 0
rs 6.8
c 0
b 0
f 0
wmc 55
lcom 1
cbo 13

37 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getTables() 0 4 1
A getTable() 0 4 1
A getColumns() 0 4 1
A getColumn() 0 4 1
A getEntries() 0 6 1
A getEntry() 0 7 1
A getUsers() 0 5 1
A getUser() 0 4 1
A getGroups() 0 4 1
A getGroup() 0 4 1
A getGroupPrivileges() 0 8 1
A getFiles() 0 4 1
A getFile() 0 4 1
A getSettings() 0 4 1
A getSettingsByCollection() 0 8 1
A getMessages() 0 7 1
A createEntry() 15 15 3
A updateEntry() 15 15 3
A deleteEntry() 0 13 2
A createUser() 0 4 1
A updateUser() 0 4 1
A deleteUser() 0 4 1
A createFile() 0 6 1
A updateFile() 0 4 1
A deleteFile() 0 4 1
A createPreferences() 0 11 2
A createBookmark() 0 20 1
A createColumn() 0 10 1
A createGroup() 0 4 1
B createMessage() 0 52 6
A sendMessage() 0 4 1
A createPrivileges() 16 16 1
B createTable() 0 31 4
B createColumnUIOptions() 0 47 4
A getPreferences() 16 16 1
A getTableGateway() 0 9 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ClientLocal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ClientLocal, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Directus – <http://getdirectus.com>
5
 *
6
 * @link      The canonical repository – <https://github.com/directus/directus>
7
 * @copyright Copyright 2006-2016 RANGER Studio, LLC – <http://rangerstudio.com>
8
 * @license   GNU General Public License (v3) – <http://www.gnu.org/copyleft/gpl.html>
9
 */
10
11
namespace Directus\SDK;
12
13
use Directus\Database\Connection;
14
use Directus\Database\TableGateway\BaseTableGateway;
15
use Directus\Database\TableGateway\DirectusActivityTableGateway;
16
use Directus\Database\TableGateway\DirectusMessagesTableGateway;
17
use Directus\Database\TableGateway\DirectusPreferencesTableGateway;
18
use Directus\Database\TableGateway\DirectusPrivilegesTableGateway;
19
use Directus\Database\TableGateway\DirectusUiTableGateway;
20
use Directus\Database\TableGateway\DirectusUsersTableGateway;
21
use Directus\Database\TableGateway\RelationalTableGateway;
22
use Directus\Database\TableSchema;
23
use Directus\Util\ArrayUtils;
24
use Directus\Util\SchemaUtils;
25
26
/**
27
 * Client Local
28
 *
29
 * Client to Interact with the database directly using Directus Database Component
30
 *
31
 * @author Welling Guzmán <[email protected]>
32
 */
33
class ClientLocal extends AbstractClient
34
{
35
    /**
36
     * @var BaseTableGateway[]
37
     */
38
    protected $tableGateways = [];
39
40
    /**
41
     * @var Connection
42
     */
43
    protected $connection = null;
44
45
    /**
46
     * ClientLocal constructor.
47
     *
48
     * @param $connection
49
     */
50
    public function __construct($connection)
51
    {
52
        $this->connection = $connection;
53
    }
54
55
    /**
56
     * @inheritDoc
57
     */
58
    public function getTables(array $params = [])
59
    {
60
        return $this->createResponseFromData(TableSchema::getTablesSchema($params));
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFro...TablesSchema($params)); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\Entry to the return on line 60 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getTables of type Directus\SDK\Response\EntryCollection.
Loading history...
61
    }
62
63
    /**
64
     * @inheritDoc
65
     */
66
    public function getTable($tableName)
67
    {
68
        return $this->createResponseFromData(TableSchema::getSchemaArray($tableName));
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFro...hemaArray($tableName)); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 68 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getTable of type Directus\SDK\Response\Entry.
Loading history...
69
    }
70
71
    /**
72
     * @inheritDoc
73
     */
74
    public function getColumns($tableName, array $params = [])
75
    {
76
        return $this->createResponseFromData(TableSchema::getColumnSchemaArray($tableName, $params));
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFro...($tableName, $params)); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\Entry to the return on line 76 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getColumns of type Directus\SDK\Response\EntryCollection.
Loading history...
77
    }
78
79
    /**
80
     * @inheritDoc
81
     */
82
    public function getColumn($tableName, $columnName)
83
    {
84
        return $this->createResponseFromData(TableSchema::getColumnSchema($tableName, $columnName)->toArray());
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFro...olumnName)->toArray()); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 84 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getColumn of type Directus\SDK\Response\Entry.
Loading history...
85
    }
86
87
    /**
88
     * @inheritDoc
89
     */
90
    public function getEntries($tableName, array $params = [])
91
    {
92
        $tableGateway = $this->getTableGateway($tableName);
93
94
        return $this->createResponseFromData($tableGateway->getEntries($params));
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFro...->getEntries($params)); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\Entry to the return on line 94 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getEntries of type Directus\SDK\Response\EntryCollection.
Loading history...
95
    }
96
97
    /**
98
     * @inheritDoc
99
     */
100
    public function getEntry($tableName, $id, array $params = [])
101
    {
102
        // @TODO: Dynamic ID
103
        return $this->getEntries($tableName, array_merge($params, [
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getEntries..., array('id' => $id))); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getEntry of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
104
            'id' => $id
105
        ]));
106
    }
107
108
    /**
109
     * @inheritDoc
110
     */
111
    public function getUsers(array $params = [])
112
    {
113
        // @TODO: store the directus tables somewhere (SchemaManager?)
114
        return $this->getEntries('directus_users', $params);
115
    }
116
117
    /**
118
     * @inheritDoc
119
     */
120
    public function getUser($id, array $params = [])
121
    {
122
        return $this->getEntry('directus_users', $id, $params);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getEntry('..._users', $id, $params); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getUser of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
123
    }
124
125
    /**
126
     * @inheritDoc
127
     */
128
    public function getGroups(array $params = [])
129
    {
130
        return $this->getEntries('directus_groups', $params);
131
    }
132
133
    /**
134
     * @inheritDoc
135
     */
136
    public function getGroup($id, array $params = [])
137
    {
138
        return $this->getEntry('directus_groups', $id, $params);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getEntry('...groups', $id, $params); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getGroup of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
139
    }
140
141
    /**
142
     * @inheritDoc
143
     */
144
    public function getGroupPrivileges($groupID)
145
    {
146
        $this->getEntries('directus_privileges', [
147
            'filter' => [
148
                'group_id' => ['eq' => $groupID]
149
            ]
150
        ]);
151
    }
152
153
    /**
154
     * @inheritDoc
155
     */
156
    public function getFiles(array $params = [])
157
    {
158
        return $this->getEntries('directus_files', $params);
159
    }
160
161
    /**
162
     * @inheritDoc
163
     */
164
    public function getFile($id, array $params = [])
165
    {
166
        return $this->getEntry('directus_files', $id, $params);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getEntry('..._files', $id, $params); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getFile of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
167
    }
168
169
    /**
170
     * @inheritDoc
171
     */
172
    public function getSettings()
173
    {
174
        return $this->getEntries('directus_settings');
175
    }
176
177
    /**
178
     * @inheritDoc
179
     */
180
    public function getSettingsByCollection($collectionName)
181
    {
182
        return $this->getEntries('directus_settings', [
183
            'filter' => [
184
                'collection' => ['eq' => $collectionName]
185
            ]
186
        ]);
187
    }
188
189
    /**
190
     * @inheritDoc
191
     */
192
    public function getMessages($userId)
193
    {
194
        $messagesTableGateway = new DirectusMessagesTableGateway($this->connection, null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<Directus\Permissions\Acl>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
195
        $result = $messagesTableGateway->fetchMessagesInboxWithHeaders($userId);
196
197
        return $this->createResponseFromData($result);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFromData($result); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\Entry to the return on line 197 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getMessages of type Directus\SDK\Response\EntryCollection.
Loading history...
198
    }
199
200
    /**
201
     * @inheritDoc
202
     */
203 View Code Duplication
    public function createEntry($tableName, array $data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
204
    {
205
        $tableGateway = $this->getTableGateway($tableName);
206
        $data = $this->processData($tableName, $data);
207
208
        foreach($data as $key => $value) {
209
            if ($value instanceof File) {
210
                $data[$key] = $this->processFile($value);
211
            }
212
        }
213
214
        $newRecord = $tableGateway->manageRecordUpdate($tableName, $data);
215
216
        return $this->getEntry($tableName, $newRecord[$tableGateway->primaryKeyFieldName]);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getEntry($...>primaryKeyFieldName]); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createEntry of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
217
    }
218
219
    /**
220
     * @inheritDoc
221
     */
222 View Code Duplication
    public function updateEntry($tableName, $id, array $data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
223
    {
224
        $tableGateway = $this->getTableGateway($tableName);
225
        $data = $this->processData($tableName, $data);
226
227
        foreach($data as $key => $value) {
228
            if ($value instanceof File) {
229
                $data[$key] = $this->processFile($value);
230
            }
231
        }
232
233
        $updatedRecord = $tableGateway->manageRecordUpdate($tableName, array_merge($data, ['id' => $id]));
234
235
        return $this->getEntry($tableName, $updatedRecord[$tableGateway->primaryKeyFieldName]);
236
    }
237
238
    /**
239
     * @inheritDoc
240
     */
241
    public function deleteEntry($tableName, $ids)
242
    {
243
        // @TODO: Accept EntryCollection and Entry
244
        $tableGateway = $this->getTableGateway($tableName);
245
246
        if (!is_array($ids)) {
247
            $ids = [$ids];
248
        }
249
250
        return $tableGateway->delete(function($delete) use ($ids) {
251
            return $delete->where->in('id', $ids);
252
        });
253
    }
254
255
    /**
256
     * @inheritDoc
257
     */
258
    public function createUser(array $data)
259
    {
260
        return $this->createEntry('directus_users', $data);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createEntr...irectus_users', $data); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createUser of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
261
    }
262
263
    /**
264
     * @inheritDoc
265
     */
266
    public function updateUser($id, array $data)
267
    {
268
        return $this->updateEntry('directus_users', $id, $data);
269
    }
270
271
    /**
272
     * @inheritDoc
273
     */
274
    public function deleteUser($ids)
275
    {
276
        return $this->deleteEntry('directus_users', $ids);
277
    }
278
279
    /**
280
     * @inheritDoc
281
     */
282
    public function createFile(File $file)
283
    {
284
        $data = $this->processFile($file);
285
286
        return $this->createEntry('directus_files', $data);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createEntr...irectus_files', $data); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createFile of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
287
    }
288
289
    /**
290
     * @inheritDoc
291
     */
292
    public function updateFile($id, array $data)
293
    {
294
        return $this->updateEntry('directus_files', $id, $data);
295
    }
296
297
    /**
298
     * @inheritDoc
299
     */
300
    public function deleteFile($ids)
301
    {
302
        return $this->deleteEntry('directus_files', $ids);
303
    }
304
305
    public function createPreferences($data)
306
    {
307
        if (!ArrayUtils::contains($data, ['title', 'table_name'])) {
308
            throw new \Exception('title and table_name are required');
309
        }
310
311
        $acl = $this->container->get('acl');
312
        $data['user'] = $acl->getUserId();
313
314
        return $this->createEntry('directus_preferences', $data);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createEntr...s_preferences', $data); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createPreferences of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
315
    }
316
317
    /**
318
     * @inheritdoc
319
     */
320
    public function createBookmark($data)
321
    {
322
        $acl = $this->container->get('acl');
323
        $data['user'] = $acl->getUserId();
324
325
        $preferences = $this->createPreferences(ArrayUtils::pick($data, [
326
            'title', 'table_name', 'sort', 'status', 'search_string', 'sort_order', 'columns_visible', 'user'
327
        ]));
328
329
        $title = $preferences->title;
0 ignored issues
show
Bug introduced by
The property title does not seem to exist in Directus\SDK\Response\EntryCollection.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
330
        $tableName = $preferences->table_name;
0 ignored issues
show
Bug introduced by
The property table_name does not seem to exist in Directus\SDK\Response\EntryCollection.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
331
        $bookmarkData = [
332
            'section' => 'search',
333
            'title' => $title,
334
            'url' => 'tables/' . $tableName . '/pref/' . $title,
335
            'user' => $data['user']
336
        ];
337
338
        return $this->createEntry('directus_bookmarks', $bookmarkData);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createEntr...marks', $bookmarkData); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createBookmark of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
339
    }
340
341
    /**
342
     * @inheritdoc
343
     */
344
    public function createColumn($data)
345
    {
346
        $data = $this->parseColumnData($data);
347
348
        $tableGateway = $this->getTableGateway($data['table_name']);
349
350
        $tableGateway->addColumn($data['table_name'], ArrayUtils::omit($data, ['table_name']));
351
352
        return $this->getColumn($data['table_name'], $data['column_name']);
353
    }
354
355
    /**
356
     * @inheritdoc
357
     */
358
    public function createGroup(array $data)
359
    {
360
        return $this->createEntry('directus_groups', $data);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createEntr...rectus_groups', $data); (Directus\SDK\Response\EntryCollection) is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createGroup of type Directus\SDK\Response\Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
361
    }
362
363
    /**
364
     * @inheritdoc
365
     */
366
    public function createMessage(array $data)
367
    {
368
        $this->requiredAttributes(['from', 'message', 'subject'], $data);
369
        $this->requiredOneAttribute(['to', 'toGroup'], $data);
370
371
        $recipients = $this->getMessagesTo($data);
372
        $recipients = explode(',', $recipients);
373
        ArrayUtils::remove($data, ['to', 'toGroup']);
374
375
        $groupRecipients = [];
376
        $userRecipients = [];
377
        foreach ($recipients as $recipient) {
378
            $typeAndId = explode('_', $recipient);
379
            if ($typeAndId[0] == 0) {
380
                $userRecipients[] = $typeAndId[1];
381
            } else {
382
                $groupRecipients[] = $typeAndId[1];
383
            }
384
        }
385
386
        $ZendDb = $this->container->get('connection');
387
        $acl = $this->container->get('acl');
388
        if (count($groupRecipients) > 0) {
389
            $usersTableGateway = new DirectusUsersTableGateway($ZendDb, $acl);
390
            $result = $usersTableGateway->findActiveUserIdsByGroupIds($groupRecipients);
391
            foreach ($result as $item) {
392
                $userRecipients[] = $item['id'];
393
            }
394
        }
395
396
        $userRecipients[] = $acl->getUserId();
397
398
        $messagesTableGateway = new DirectusMessagesTableGateway($ZendDb, $acl);
399
        $id = $messagesTableGateway->sendMessage($data, array_unique($userRecipients), $acl->getUserId());
400
401
        if ($id) {
402
            $Activity = new DirectusActivityTableGateway($ZendDb, $acl);
403
            $data['id'] = $id;
404
            $Activity->recordMessage($data, $acl->getUserId());
405
        }
406
407
        $message = $messagesTableGateway->fetchMessageWithRecipients($id, $acl->getUserId());
408
        $response = [
409
            'meta' => [
410
                'type' => 'entry',
411
                'table' => 'directus_messages'
412
            ],
413
            'data' => $message
414
        ];
415
416
        return $this->createResponseFromData($response);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFromData($response); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 416 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createMessage of type Directus\SDK\Response\Entry.
Loading history...
417
    }
418
419
    /**
420
     * @inheritdoc
421
     */
422
    public function sendMessage(array $data)
423
    {
424
        return $this->createMessage($data);
425
    }
426
427 View Code Duplication
    public function createPrivileges(array $data)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
428
    {
429
        $connection = $this->container->get('connection');
430
        $acl = $this->container->get('acl');
431
        $privileges = new DirectusPrivilegesTableGateway($connection, $acl);
432
433
        $response = [
434
            'meta' => [
435
                'type' => 'entry',
436
                'table' => 'directus_privileges'
437
            ],
438
            'data' => $privileges->insertPrivilege($data)
439
        ];
440
441
        return $this->createResponseFromData($response);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFromData($response); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 441 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createPrivileges of type Directus\SDK\Response\Entry.
Loading history...
442
    }
443
444
    public function createTable($name, array $data = [])
445
    {
446
        $isTableNameAlphanumeric = preg_match("/[a-z0-9]+/i", $name);
447
        $zeroOrMoreUnderscoresDashes = preg_match("/[_-]*/i", $name);
448
449
        if (!($isTableNameAlphanumeric && $zeroOrMoreUnderscoresDashes)) {
450
            return $this->createResponseFromData(['error' => ['message' => 'invalid_table_name']]);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFro...invalid_table_name'))); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 450 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createTable of type Directus\SDK\Response\Entry.
Loading history...
451
        }
452
453
        $schema = $this->container->get('schemaManager');
454
        $emitter = $this->container->get('emitter');
455
        if (!$schema->tableExists($name)) {
456
            $emitter->run('table.create:before', $name);
457
            // Through API:
458
            // Remove spaces and symbols from table name
459
            // And in lowercase
460
            $name = SchemaUtils::cleanTableName($name);
461
            $schema->createTable($name);
462
            $emitter->run('table.create', $name);
463
            $emitter->run('table.create:after', $name);
464
        }
465
466
        $connection = $this->container->get('connection');
467
        $acl = $this->container->get('acl');
468
        $privileges = new DirectusPrivilegesTableGateway($connection, $acl);
469
470
        return $this->createResponseFromData($privileges->insertPrivilege([
1 ignored issue
show
Bug Compatibility introduced by
The expression $this->createResponseFro...able_name' => $name))); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 470 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::createTable of type Directus\SDK\Response\Entry.
Loading history...
471
            'group_id' => 1,
472
            'table_name' => $name
473
        ]));
474
    }
475
476
    /**
477
     * @inheritdoc
478
     */
479
    public function createColumnUIOptions(array $data)
480
    {
481
        $this->requiredAttributes(['table', 'column', 'ui', 'options'], $data);
482
        $tableGateway = $this->getTableGateway('directus_ui');
483
484
        $table = $data['table'];
485
        $column = $data['column'];
486
        $ui = $data['ui'];
487
488
        $data = $data['options'];
489
        $keys = ['table_name' => $table, 'column_name' => $column, 'ui_name' => $ui];
490
        $uis = to_name_value($data, $keys);
491
492
        $column_settings = [];
493
        foreach ($uis as $col) {
494
            $existing = $tableGateway->select(['table_name' => $table, 'column_name' => $column, 'ui_name' => $ui, 'name' => $col['name']])->toArray();
495
            if (count($existing) > 0) {
496
                $col['id'] = $existing[0]['id'];
497
            }
498
            array_push($column_settings, $col);
499
        }
500
        $tableGateway->updateCollection($column_settings);
501
502
        $connection = $this->container->get('connection');
503
        $acl = $this->container->get('acl');
504
        $UiOptions = new DirectusUiTableGateway($connection, $acl);
505
        $response = $UiOptions->fetchOptions($table, $column, $ui);
506
507
        if (!$response) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $response of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
508
            $response = [
509
                'error' => [
510
                    'message' => sprintf('unable_to_find_column_%s_options_for_%s', ['column' => $column, 'ui' => $ui])
511
                ],
512
                'success' => false
513
            ];
514
        } else {
515
            $response = [
516
                'meta' => [
517
                    'type' => 'entry',
518
                    'table' => 'directus_ui'
519
                ],
520
                'data' => $response
521
            ];
522
        }
523
524
        return $this->createResponseFromData($response);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFromData($response); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 524 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInt...::createColumnUIOptions of type Directus\SDK\Response\Entry.
Loading history...
525
    }
526
527 View Code Duplication
    public function getPreferences($table, $user)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
528
    {
529
        $acl = $this->container->get('acl');
530
        $connection = $this->container->get('connection');
531
        $preferencesTableGateway = new DirectusPreferencesTableGateway($connection, $acl);
532
533
        $response = [
534
            'meta' => [
535
                'type' => 'entry',
536
                'table' => 'directus_preferences'
537
            ],
538
            'data' => $preferencesTableGateway->fetchByUserAndTableAndTitle($user, $table)
539
        ];
540
541
        return $this->createResponseFromData($response);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->createResponseFromData($response); of type Directus\SDK\Response\En...ctus\SDK\Response\Entry adds the type Directus\SDK\Response\EntryCollection to the return on line 541 which is incompatible with the return type declared by the interface Directus\SDK\RequestsInterface::getPreferences of type Directus\SDK\Response\Entry.
Loading history...
542
    }
543
544
    /**
545
     * Get a table gateway for the given table name
546
     *
547
     * @param $tableName
548
     *
549
     * @return RelationalTableGateway
550
     */
551
    protected function getTableGateway($tableName)
552
    {
553
        if (!array_key_exists($tableName, $this->tableGateways)) {
554
            $acl = TableSchema::getAclInstance();
555
            $this->tableGateways[$tableName] = new RelationalTableGateway($tableName, $this->connection, $acl);
556
        }
557
558
        return $this->tableGateways[$tableName];
559
    }
560
}
561