Passed
Branch develop (56c45f)
by Jens
02:42
created

ContentRepository::deleteDocumentByPath()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
eloc 14
nc 4
nop 2
dl 0
loc 20
rs 9.2
c 2
b 1
f 0
1
<?php
2
3
namespace CloudControl\Cms\storage\repository {
4
5
    use CloudControl\Cms\storage\entities\Document;
6
    use CloudControl\Cms\storage\Repository;
7
    use CloudControl\Cms\storage\storage\DocumentStorage;
8
9
    /**
10
     * Created by jensk on 4-9-2017.
11
     */
12
    class ContentRepository
13
    {
14
        protected $storagePath;
15
        protected $contentDbHandle;
16
17
        /**
18
         * ContentRepository constructor.
19
         * @param $storagePath
20
         */
21
        public function __construct($storagePath)
22
        {
23
            $this->storagePath = $storagePath;
24
        }
25
26
        /**
27
         * @return \PDO
28
         */
29
        public function getContentDbHandle()
30
        {
31
            if ($this->contentDbHandle === null) {
32
                $this->contentDbHandle = new \PDO('sqlite:' . $this->storagePath . DIRECTORY_SEPARATOR . 'content.db');
0 ignored issues
show
Bug introduced by
The call to PDO::__construct() has too few arguments starting with username. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

32
                $this->contentDbHandle = /** @scrutinizer ignore-call */ new \PDO('sqlite:' . $this->storagePath . DIRECTORY_SEPARATOR . 'content.db');

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
33
            }
34
            return $this->contentDbHandle;
35
        }
36
37
        /**
38
         * Prepare the sql statement
39
         * @param $sql
40
         * @return \PDOStatement
41
         * @throws \Exception
42
         */
43
        protected function getDbStatement($sql)
44
        {
45
            $db = $this->getContentDbHandle();
46
            $stmt = $db->query($sql);
47
            if ($stmt === false) {
48
                $errorInfo = $db->errorInfo();
49
                $errorMsg = $errorInfo[2];
50
                throw new \RuntimeException('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>');
51
            }
52
            return $stmt;
53
        }
54
55
        /**
56
         * @param Repository $repository
57
         * @param $document
58
         * @param $db
59
         * @param $documents
60
         * @param $key
61
         * @return mixed
62
         */
63
        private function setAssetsToDocumentFolders(Repository $repository, $document, $db, $documents, $key)
64
        {
65
            if ($document->type === 'folder') {
66
                $document->dbHandle = $db;
67
                $document->documentStorage = new DocumentStorage($repository);
68
                $documents[$key] = $document;
69
            }
70
71
            return $documents;
72
        }
73
74
        /**
75
         * @param Repository $repository
76
         * @param string $folderPath
77
         * @return array
78
         * @throws \Exception
79
         */
80
        public function getDocumentsWithState(Repository $repository, $folderPath = '/')
81
        {
82
            $db = $this->getContentDbHandle();
83
            $folderPathWithWildcard = $folderPath . '%';
84
85
            $ifRootIndex = 1;
86
            if ($folderPath === '/') {
87
                $ifRootIndex = 0;
88
            }
89
90
            $sql = '
91
            SELECT documents_unpublished.*,
92
            	   IFNULL(documents_published.state,"unpublished") as state,
93
            	   IFNULL(documents_published.publicationDate,NULL) as publicationDate,
94
            	   (documents_published.lastModificationDate != documents_unpublished.lastModificationDate) as unpublishedChanges 
95
              FROM documents_unpublished
96
		 LEFT JOIN documents_published
97
         		ON documents_published.path = documents_unpublished.path
98
             WHERE documents_unpublished.`path` LIKE ' . $db->quote($folderPathWithWildcard) . '
99
               AND substr(documents_unpublished.`path`, ' . (strlen($folderPath) + $ifRootIndex + 1) . ') NOT LIKE "%/%"
100
               AND length(documents_unpublished.`path`) > ' . (strlen($folderPath) + $ifRootIndex) . '
101
               AND documents_unpublished.path != ' . $db->quote($folderPath) . '
102
          ORDER BY documents_unpublished.`type` DESC, documents_unpublished.`path` ASC
103
        ';
104
            $stmt = $this->getDbStatement($sql);
105
106
107
            $documents = $stmt->fetchAll(\PDO::FETCH_CLASS, Document::class);
108
            foreach ($documents as $key => $document) {
109
                $documents = $this->setAssetsToDocumentFolders($repository, $document, $db, $documents, $key);
110
            }
111
            //dump($documents);
112
            return $documents;
113
        }
114
115
        /**
116
         * Get all documents and folders in a certain path
117
         *
118
         * @param Repository $repository
119
         * @param        $folderPath
120
         * @param string $state
121
         * @return array
122
         * @throws \Exception
123
         */
124
        public function getDocumentsByPath(Repository $repository, $folderPath, $state = 'published')
125
        {
126
            if (!in_array($state, Document::$DOCUMENT_STATES, true)) {
127
                throw new \RuntimeException('Unsupported document state: ' . $state);
128
            }
129
            $db = $this->getContentDbHandle();
130
            $folderPathWithWildcard = $folderPath . '%';
131
132
            $sql = 'SELECT *
133
              FROM documents_' . $state . '
134
             WHERE `path` LIKE ' . $db->quote($folderPathWithWildcard) . '
135
               AND substr(`path`, ' . (strlen($folderPath) + 1) . ') NOT LIKE "%/%"
136
               AND path != ' . $db->quote($folderPath) . '
137
          ORDER BY `type` DESC, `path` ASC';
138
            $stmt = $this->getDbStatement($sql);
139
140
            $documents = $stmt->fetchAll(\PDO::FETCH_CLASS, Document::class);
141
            foreach ($documents as $key => $document) {
142
                $documents = $this->setAssetsToDocumentFolders($repository, $document, $db, $documents, $key);
143
            }
144
            return $documents;
145
        }
146
147
        /**
148
         * @param \CloudControl\Cms\storage\Repository $repository
149
         * @param        $path
150
         * @param string $state
151
         * @return bool|Document
152
         * @throws \Exception
153
         */
154
        public function getDocumentByPath(Repository $repository, $path, $state = 'published')
155
        {
156
            if (!in_array($state, Document::$DOCUMENT_STATES, true)) {
157
                throw new \RuntimeException('Unsupported document state: ' . $state);
158
            }
159
            $db = $this->getContentDbHandle();
160
            $document = $this->fetchDocument('
161
            SELECT *
162
              FROM documents_' . $state . '
163
             WHERE path = ' . $db->quote($path) . '
164
        ');
165
            if ($document instanceof Document && $document->type === 'folder') {
166
                $document->dbHandle = $db;
167
                $document->documentStorage = new DocumentStorage($repository);
168
            }
169
            return $document;
170
        }
171
172
        /**
173
         * Return the result of the query as Document
174
         * @param $sql
175
         * @return mixed
176
         * @throws \Exception
177
         */
178
        protected function fetchDocument($sql)
179
        {
180
            $stmt = $this->getDbStatement($sql);
181
            return $stmt->fetchObject(Document::class);
182
        }
183
184
        /**
185
         * @param Repository $repository
186
         * @param $path
187
         * @return bool|Document
188
         * @throws \Exception
189
         */
190
        public function getDocumentContainerByPath(Repository $repository, $path)
191
        {
192
            $document = $this->getDocumentByPath($repository, $path, 'unpublished');
193
            if ($document === false) {
194
                return false;
195
            }
196
            $slugLength = strlen($document->slug);
197
            $containerPath = substr($path, 0, -$slugLength);
198
            if ($containerPath === '/') {
199
                return $this->getRootFolder();
200
            }
201
            if (substr($containerPath, -1) === '/') {
202
                $containerPath = substr($containerPath, 0, -1);
203
            }
204
            $containerFolder = $this->getDocumentByPath($repository, $containerPath, 'unpublished');
205
            return $containerFolder;
206
        }
207
208
        /**
209
         * Create a (non-existent) root folder Document and return it
210
         * @return Document
211
         */
212
        protected function getRootFolder()
213
        {
214
            $rootFolder = new Document();
215
            $rootFolder->path = '/';
216
            $rootFolder->type = 'folder';
217
            return $rootFolder;
218
        }
219
220
        /**
221
         * Returns the count of all documents stored in the db
222
         *
223
         * @param string $state
224
         *
225
         * @return int
226
         * @throws \Exception
227
         */
228
        public function getTotalDocumentCount($state = 'published')
229
        {
230
            if (!in_array($state, Document::$DOCUMENT_STATES, true)) {
231
                throw new \RuntimeException('Unsupported document state: ' . $state);
232
            }
233
            $db = $this->getContentDbHandle();
234
            $stmt = $db->query('
235
			SELECT count(*)
236
			  FROM documents_' . $state . '
237
			 WHERE `type` != "folder"
238
		');
239
            $result = $stmt->fetch(\PDO::FETCH_ASSOC);
240
            if (!is_array($result)) {
241
                return 0;
242
            }
243
            return (int)current($result);
244
        }
245
246
        /**
247
         * @return array
248
         * @throws \Exception
249
         */
250
        public function getPublishedDocumentsNoFolders()
251
        {
252
            $db = $this->getContentDbHandle();
253
            $sql = '
254
			SELECT *
255
			  FROM documents_published
256
			 WHERE `type` != "folder"
257
		';
258
            $stmt = $db->query($sql);
259
            $result = $stmt->fetchAll(\PDO::FETCH_CLASS, Document::class);
260
            if ($stmt === false || !$stmt->execute()) {
261
                $errorInfo = $db->errorInfo();
262
                $errorMsg = $errorInfo[2];
263
                throw new \RuntimeException('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>');
264
            }
265
            return $result;
266
        }
267
268
        /**
269
         * @param $path
270
         * @throws \Exception
271
         */
272
        public function publishDocumentByPath($path)
273
        {
274
            $this->publishOrUnpublishDocumentByPath($path);
275
        }
276
277
        /**
278
         * @param $path
279
         * @throws \Exception
280
         */
281
        public function unpublishDocumentByPath($path)
282
        {
283
            $this->publishOrUnpublishDocumentByPath($path, false);
284
        }
285
286
        /**
287
         * @param $path
288
         * @param bool $publish
289
         * @throws \Exception
290
         */
291
        public function publishOrUnpublishDocumentByPath($path, $publish = true)
292
        {
293
            $sql = 'DELETE FROM documents_published
294
					  WHERE `path` = :path';
295
            if ($publish) {
296
                $sql = '
297
				INSERT OR REPLACE INTO documents_published 
298
					  (`id`,`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,`state`,`lastModificationDate`,`creationDate`,`publicationDate`,`lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`)
299
				SELECT `id`,`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,"published" as state,`lastModificationDate`,`creationDate`,' . time() . ' as publicationDate, `lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`
300
				  FROM documents_unpublished
301
				 WHERE `path` = :path
302
			';
303
            }
304
            $db = $this->getContentDbHandle();
305
            $stmt = $db->prepare($sql);
306
            if ($stmt === false) {
307
                $errorInfo = $db->errorInfo();
308
                $errorMsg = $errorInfo[2];
309
                throw new \RuntimeException('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>');
310
            }
311
            $stmt->bindValue(':path', $path);
312
            $stmt->execute();
313
        }
314
315
        public function cleanPublishedDeletedDocuments()
316
        {
317
            $sql = '   DELETE FROM documents_published
318
						 WHERE documents_published.path IN (
319
						SELECT documents_published.path
320
						  FROM documents_published
321
					 LEFT JOIN documents_unpublished
322
							ON documents_unpublished.path = documents_published.path
323
						 WHERE documents_unpublished.path IS NULL
324
		)';
325
            $stmt = $this->getDbStatement($sql);
326
            $stmt->execute();
327
        }
328
329
        /**
330
         * Save the document to the database
331
         *
332
         * @param Document $documentObject
333
         * @param string $state
334
         *
335
         * @return bool
336
         * @throws \Exception
337
         */
338
        public function saveDocument($documentObject, $state = 'published')
339
        {
340
            if (!in_array($state, Document::$DOCUMENT_STATES, true)) {
341
                throw new \RuntimeException('Unsupported document state: ' . $state);
342
            }
343
            $db = $this->getContentDbHandle();
344
            $stmt = $this->getDbStatement('
345
            INSERT OR REPLACE INTO documents_' . $state . ' (`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,`state`,`lastModificationDate`,`creationDate`,`lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`)
346
            VALUES(
347
              ' . $db->quote($documentObject->path) . ',
348
              ' . $db->quote($documentObject->title) . ',
349
              ' . $db->quote($documentObject->slug) . ',
350
              ' . $db->quote($documentObject->type) . ',
351
              ' . $db->quote($documentObject->documentType) . ',
352
              ' . $db->quote($documentObject->documentTypeSlug) . ',
353
              ' . $db->quote($documentObject->state) . ',
354
              ' . $db->quote($documentObject->lastModificationDate) . ',
355
              ' . $db->quote($documentObject->creationDate) . ',
356
              ' . $db->quote($documentObject->lastModifiedBy) . ',
357
              ' . $db->quote(json_encode($documentObject->fields)) . ',
358
              ' . $db->quote(json_encode($documentObject->bricks)) . ',
359
              ' . $db->quote(json_encode($documentObject->dynamicBricks)) . '
360
            )
361
        ');
362
            return $stmt->execute();
363
        }
364
365
        /**
366
         * Delete the document from the database
367
         * If it's a folder, also delete it's contents
368
         *
369
         * @param Repository $repository
370
         * @param        $path
371
         * @throws \Exception
372
         */
373
        public function deleteDocumentByPath(Repository $repository, $path)
374
        {
375
            $db = $this->getContentDbHandle();
376
            $documentToDelete = $this->getDocumentByPath($repository, $path, 'unpublished');
377
            if ($documentToDelete instanceof Document) {
378
                if ($documentToDelete->type === 'document') {
379
                    $stmt = $this->getDbStatement('
380
                    DELETE FROM documents_unpublished
381
                          WHERE path = ' . $db->quote($path) . '
382
                ');
383
                    $stmt->execute();
384
                } elseif ($documentToDelete->type === 'folder') {
385
                    $folderPathWithWildcard = $path . '%';
386
                    $stmt = $this->getDbStatement('
387
                    DELETE FROM documents_unpublished
388
                          WHERE (path LIKE ' . $db->quote($folderPathWithWildcard) . '
389
                            AND substr(`path`, ' . (strlen($path) + 1) . ', 1) = "/")
390
                            OR path = ' . $db->quote($path) . '
391
                ');
392
                    $stmt->execute();
393
                }
394
            }
395
        }
396
    }
397
398
399
}