Passed
Branch master (0828fa)
by Gabor
03:19
created

getFilesystemSetByApplicationAndDirectory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 7
cp 0
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
crap 2
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Data\Storage\Filesystem;
15
16
use WebHemi\DateTime;
17
use WebHemi\Data\ConnectorInterface;
18
use WebHemi\Data\EntityInterface;
19
use WebHemi\Data\Storage\AbstractStorage;
20
use WebHemi\Data\Entity\Filesystem\FilesystemEntity;
21
22
/**
23
 * Class FilesystemStorage.
24
 *
25
 * @SuppressWarnings(PHPMD.TooManyFields)
26
 */
27
class FilesystemStorage extends AbstractStorage
28
{
29
    public const TYPE_DOCUMENT = FilesystemEntity::TYPE_DOCUMENT;
30
    public const TYPE_BINARY = FilesystemEntity::TYPE_BINARY;
31
    public const TYPE_DIRECTORY = FilesystemEntity::TYPE_DIRECTORY;
32
    public const TYPE_SYMLINK = FilesystemEntity::TYPE_SYMLINK;
33
34
    /** @var string */
35
    protected $dataGroup = 'webhemi_filesystem';
36
    /** @var string */
37
    protected $idKey = 'id_filesystem';
38
    /** @var string */
39
    private $idApplication = 'fk_application';
40
    /** @var string */
41
    private $idCategory = 'fk_category';
42
    /** @var string */
43
    private $idParent = 'fk_parent_node';
44
    /** @var string */
45
    private $idDocument = 'fk_filesystem_document';
46
    /** @var string */
47
    private $idFile = 'fk_filesystem_file';
48
    /** @var string */
49
    private $idDirectory = 'fk_filesystem_directory';
50
    /** @var string */
51
    private $idLink = 'fk_filesystem_link';
52
    /** @var string */
53
    private $path = 'path';
54
    /** @var string */
55
    private $baseName = 'basename';
56
    /** @var string */
57
    private $title = 'title';
58
    /** @var string */
59
    private $description = 'description';
60
    /** @var string */
61
    private $isHidden = 'is_hidden';
62
    /** @var string */
63
    private $isReadOnly = 'is_read_only';
64
    /** @var string */
65
    private $isDeleted = 'is_deleted';
66
    /** @var string */
67
    private $dateCreated = 'date_created';
68
    /** @var string */
69
    private $dateModified = 'date_modified';
70
    /** @var string */
71
    private $datePublished = 'date_published';
72
73
    /**
74
     * Populates an entity with storage data.
75
     *
76
     * @param EntityInterface $dataEntity
77
     * @param array           $data
78
     * @return void
79
     *
80
     * @SuppressWarnings(PHPMD.NPathComplexity) - sorry, this will remain like this. It's a complex object, period.
81
     */
82
    protected function populateEntity(EntityInterface&$dataEntity, array $data) : void
83
    {
84
        /* @var FilesystemEntity $dataEntity */
85
        $dataEntity->setFilesystemId((int) $data[$this->idKey])
86
            ->setApplicationId((int) $data[$this->idApplication])
87
            ->setCategoryId(isset($data[$this->idCategory]) ? (int)$data[$this->idCategory] : null)
88
            ->setParentId(isset($data[$this->idParent]) ? (int)$data[$this->idParent] : null)
89
            ->setDocumentId(isset($data[$this->idDocument]) ? (int)$data[$this->idDocument] : null)
90
            ->setFileId(isset($data[$this->idFile]) ? (int)$data[$this->idFile] : null)
91
            ->setDirectoryId(isset($data[$this->idDirectory]) ? (int)$data[$this->idDirectory] : null)
92
            ->setLinkId(isset($data[$this->idLink]) ? (int)$data[$this->idLink] : null)
93
            ->setPath($data[$this->path])
94
            ->setBaseName($data[$this->baseName])
95
            ->setTitle($data[$this->title])
96
            ->setDescription($data[$this->description])
97
            ->setHidden((bool) $data[$this->isHidden])
98
            ->setReadOnly((bool) $data[$this->isReadOnly])
99
            ->setDeleted((bool) $data[$this->isDeleted])
100
            ->setDateCreated(new DateTime($data[$this->dateCreated] ?? 'now'))
101
            ->setDateModified(!empty($data[$this->dateModified]) ? new DateTime($data[$this->dateModified]) : null)
0 ignored issues
show
Bug introduced by
It seems like !empty($data[$this->date...->dateModified]) : null can be null; however, setDateModified() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
102
            ->setDatePublished(!empty($data[$this->datePublished]) ? new DateTime($data[$this->datePublished]) : null);
0 ignored issues
show
Bug introduced by
It seems like !empty($data[$this->date...>datePublished]) : null can be null; however, setDatePublished() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
103
    }
104
105
    /**
106
     * Get data from an entity.
107
     *
108
     * @param EntityInterface $dataEntity
109
     * @return array
110
     */
111
    protected function getEntityData(EntityInterface $dataEntity) : array
112
    {
113
        /** @var FilesystemEntity $dataEntity */
114
        $dateCreated = $dataEntity->getDateCreated();
115
        $dateModified = $dataEntity->getDateModified();
116
        $datePublished = $dataEntity->getDatePublished();
117
118
        return [
119
            $this->idKey => $dataEntity->getKeyData(),
120
            $this->idApplication = $dataEntity->getApplicationId(),
0 ignored issues
show
Documentation Bug introduced by
It seems like $dataEntity->getApplicationId() can also be of type integer. However, the property $idApplication is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
121
            $this->idCategory = $dataEntity->getCategoryId(),
0 ignored issues
show
Documentation Bug introduced by
It seems like $dataEntity->getCategoryId() can also be of type integer. However, the property $idCategory is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
122
            $this->idParent = $dataEntity->getParentId(),
0 ignored issues
show
Documentation Bug introduced by
It seems like $dataEntity->getParentId() can also be of type integer. However, the property $idParent is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
123
            $this->documentId = $dataEntity->getDocumentId(),
0 ignored issues
show
Bug introduced by
The property documentId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
124
            $this->fileId = $dataEntity->getFileId(),
0 ignored issues
show
Bug introduced by
The property fileId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
125
            $this->directoryId = $dataEntity->getDirectoryId(),
0 ignored issues
show
Bug introduced by
The property directoryId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
126
            $this->linkId = $dataEntity->getLinkId(),
0 ignored issues
show
Bug introduced by
The property linkId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
127
            $this->path => $dataEntity->getPath(),
128
            $this->baseName => $dataEntity->getBaseName(),
129
            $this->title => $dataEntity->getTitle(),
130
            $this->description => $dataEntity->getDescription(),
131
            $this->isHidden => (int) $dataEntity->getHidden(),
132
            $this->isReadOnly => (int) $dataEntity->getReadOnly(),
133
            $this->isDeleted => (int) $dataEntity->getDeleted(),
134
            $this->dateCreated => $dateCreated instanceof DateTime ? $dateCreated->format('Y-m-d H:i:s') : null,
135
            $this->dateModified => $dateModified instanceof DateTime ? $dateModified->format('Y-m-d H:i:s') : null,
136
            $this->datePublished => $datePublished instanceof DateTime ? $datePublished->format('Y-m-d H:i:s') : null
137
        ];
138
    }
139
140
    /**
141
     * Gets the filesystem entity by the identifier.
142
     *
143
     * @param int $identifier
144
     * @return null|FilesystemEntity
145
     */
146
    public function getFilesystemById(int $identifier) : ? FilesystemEntity
147
    {
148
        /** @var null|FilesystemEntity $dataEntity */
149
        $dataEntity = $this->getDataEntity([$this->idKey => $identifier]);
150
151
        return $dataEntity;
152
    }
153
154
    /**
155
     * Gets the filesystem entity set by application and directory.
156
     *
157
     * @param int $applicationId
158
     * @param int $directoryId
159
     * @return FilesystemEntity[]
160
     */
161
    public function getFilesystemSetByApplicationAndDirectory(int $applicationId, int $directoryId) : ? array
162
    {
163
        /** @var FilesystemEntity[] $dataEntitySet */
164
        $dataEntitySet = $this->getDataEntitySet(
165
            [$this->idApplication => $applicationId, $this->idDirectory => $directoryId]
166
        );
167
168
        return $dataEntitySet;
169
    }
170
171
    /**
172
     * Gets the filesystem entity set by application and tag.
173
     *
174
     * @param int $applicationId
175
     * @param int $tagId
176
     * @return FilesystemEntity[]
177
     */
178
    public function getFilesystemSetByApplicationAndTag(int $applicationId, int $tagId) : ? array
179
    {
180
        /** @var ConnectorInterface $connector */
181
        $connector = $this->getConnector();
182
183
        // Switch to another data group (DO NOT FORGET TO SET IT BACK!!)
184
        $connector->setDataGroup('webhemi_filesystem_to_filesystem_tag')
185
            ->setIdKey('id_filesystem_to_filesystem_tag');
186
187
        $dataSet = $connector->getDataSet(['fk_filesystem_tag' => $tagId]);
188
        $filesystemIds = [];
189
190
        foreach ($dataSet as $data) {
191
            $filesystemIds[] = $data['fk_filesystem'];
192
        }
193
194
        // switch back to the original data group
195
        $connector->setDataGroup($this->dataGroup)
196
            ->setIdKey($this->idKey);
197
198
        return empty($filesystemIds)
199
            ? []
200
            : $this->getPublishedDocuments(
201
                $applicationId,
202
                [$this->idKey => $filesystemIds]
203
            );
204
    }
205
206
    /**
207
     * Gets the filesystem entity by application and path.
208
     *
209
     * @param int $applicationId
210
     * @param string $path
211
     * @param string $baseName
212
     * @return null|FilesystemEntity
213
     */
214
    public function getFilesystemByApplicationAndPath(
215
        int $applicationId,
216
        string $path,
217
        string $baseName
218
    ) : ? FilesystemEntity {
219
        /** @var null|FilesystemEntity $dataEntity */
220
        $dataEntity = $this->getDataEntity(
221
            [
222
                $this->idApplication => $applicationId,
223
                $this->path => $path,
224
                $this->baseName => $baseName
225
            ]
226
        );
227
228
        return $dataEntity;
229
    }
230
231
    /**
232
     * Gets the published documents
233
     *
234
     * @param int $applicationId
235
     * @param array $additionalExpressions
236
     * @param string|null $order
237
     * @param int|null $limit
238
     * @param int|null $offset
239
     * @param string|null $groupBy
240
     * @param string|null $having
241
     * @return FilesystemEntity[]
242
     */
243
    public function getPublishedDocuments(
244
        int $applicationId,
245
        array $additionalExpressions = [],
246
        string $order = null,
247
        int $limit = null,
248
        int $offset = null,
249
        string $groupBy = null,
250
        string $having = null
251
    ) : array {
252
        $defaultExpressions = [
253
            $this->idApplication => $applicationId,
254
            $this->isHidden => 0,
255
            $this->isDeleted => 0,
256
            $this->datePublished => true, // >> IS NOT NULL
257
            $this->idDocument => true // >> will get documents only
258
        ];
259
260
        // This way the default ones can be overwritten.
261
        $expressions = array_merge($defaultExpressions, $additionalExpressions);
262
263
        $options = [
264
            ConnectorInterface::OPTION_ORDER => ($order ?? $this->datePublished.' DESC')
265
        ];
266
267
        if (is_numeric($limit)) {
268
            $options[ConnectorInterface::OPTION_LIMIT] = (int) $limit;
269
270
            if (is_numeric($offset)) {
271
                $options[ConnectorInterface::OPTION_OFFSET] = (int) $offset;
272
            }
273
        }
274
275
        if (!empty($groupBy)) {
276
            $options[ConnectorInterface::OPTION_GROUP] = $groupBy;
277
278
            if (!empty($having)) {
279
                $options[ConnectorInterface::OPTION_HAVING] = $having;
280
            }
281
        }
282
283
        /** @var FilesystemEntity[] $dataEntitySet */
284
        $dataEntitySet = $this->getDataEntitySet($expressions, $options);
285
286
        return $dataEntitySet;
287
    }
288
289
    /**
290
     * Gets simple structured meta information for a filesystem record.
291
     *
292
     * @param int $filesystemId
293
     * @return array
294
     */
295
    public function getPublicationMeta(int $filesystemId) : array
296
    {
297
        $filesystemMetaSet = [];
298
299
        /** @var ConnectorInterface $connector */
300
        $connector = $this->getConnector();
301
302
        // Switch to another data group (DO NOT FORGET TO SET IT BACK!!)
303
        $connector->setDataGroup('webhemi_filesystem_meta')
304
            ->setIdKey('id_filesystem_meta');
305
306
        $filesystemRecord = $connector->getDataSet(['fk_filesystem' => $filesystemId]);
307
308
        // switch back to the original data group
309
        $connector->setDataGroup($this->dataGroup)
310
            ->setIdKey($this->idKey);
311
312
        foreach ($filesystemRecord as $data) {
313
            $filesystemMetaSet[$data['meta_key']] = $data['meta_data'];
314
        }
315
316
        return $filesystemMetaSet;
317
    }
318
}
319