Passed
Push — master ( 51ac1b...cef3f3 )
by Thomas
09:07
created

src/Extensions/DevBuildExtension.php (3 issues)

1
<?php
2
3
namespace LeKoala\DevToolkit\Extensions;
4
5
use Exception;
6
use SilverStripe\ORM\DB;
7
use RecursiveIteratorIterator;
8
use RecursiveDirectoryIterator;
9
use SilverStripe\Core\ClassInfo;
10
use SilverStripe\Core\Extension;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\Control\Director;
13
use LeKoala\DevToolkit\Helpers\FileHelper;
14
use LeKoala\DevToolkit\Helpers\SubsiteHelper;
15
16
/**
17
 * Allow the following functions before dev build
18
 * - renameColumns
19
 * - truncateSiteTree
20
 *
21
 * Allow the following functions after dev build:
22
 * - generateQueryTraits
23
 * - clearCache
24
 * - clearEmptyFolders
25
 * - provisionLocales
26
 *
27
 * Preserve current subsite
28
 *
29
 * @property \SilverStripe\Dev\DevBuildController|\LeKoala\DevToolkit\Extensions\DevBuildExtension $owner
30
 */
31
class DevBuildExtension extends Extension
32
{
33
    protected $currentSubsite;
34
35
    public function beforeCallActionHandler()
36
    {
37
        $this->currentSubsite = SubsiteHelper::currentSubsiteID();
38
39
        $renameColumns = $this->owner->getRequest()->getVar('fixTableCase');
0 ignored issues
show
The method getRequest() does not exist on LeKoala\DevToolkit\Extensions\DevBuildExtension. ( Ignorable by Annotation )

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

39
        $renameColumns = $this->owner->/** @scrutinizer ignore-call */ getRequest()->getVar('fixTableCase');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
40
        if ($renameColumns) {
41
            $this->displayMessage("<div class='build'><p><b>Fixing tables case</b></p><ul>\n\n");
42
            $this->fixTableCase();
43
            $this->displayMessage("</ul>\n<p><b>Tables fixed!</b></p></div>");
44
        }
45
46
        $renameColumns = $this->owner->getRequest()->getVar('renameColumns');
47
        if ($renameColumns) {
48
            $this->displayMessage("<div class='build'><p><b>Renaming columns</b></p><ul>\n\n");
49
            $this->renameColumns();
50
            $this->displayMessage("</ul>\n<p><b>Columns renamed!</b></p></div>");
51
        }
52
53
        $truncateSiteTree = $this->owner->getRequest()->getVar('truncateSiteTree');
54
        if ($truncateSiteTree) {
55
            $this->displayMessage("<div class='build'><p><b>Truncating SiteTree</b></p><ul>\n\n");
56
            $this->truncateSiteTree();
57
            $this->displayMessage("</ul>\n<p><b>SiteTree truncated!</b></p></div>");
58
        }
59
    }
60
61
    protected function fixTableCase()
62
    {
63
        if (!Director::isDev()) {
64
            throw new Exception("Only available in dev mode");
65
        }
66
67
        $conn = DB::get_conn();
68
        $dbName = $conn->getSelectedDatabase();
69
70
        $tablesSql = "SELECT table_name FROM information_schema.tables WHERE table_schema = '$dbName';";
71
72
        $result = DB::query($tablesSql);
0 ignored issues
show
The assignment to $result is dead and can be removed.
Loading history...
73
74
        //TODO: check list of tables name and match any lowercased one to the right one from the db schema
75
    }
76
77
    protected function truncateSiteTree()
78
    {
79
        if (!Director::isDev()) {
80
            throw new Exception("Only available in dev mode");
81
        }
82
83
        $sql = <<<SQL
84
        TRUNCATE TABLE ErrorPage;
85
        TRUNCATE TABLE ErrorPage_Live;
86
        TRUNCATE TABLE ErrorPage_Versions;
87
        TRUNCATE TABLE SiteTree;
88
        TRUNCATE TABLE SiteTree_CrossSubsiteLinkTracking;
89
        TRUNCATE TABLE SiteTree_EditorGroups;
90
        TRUNCATE TABLE SiteTree_ImageTracking;
91
        TRUNCATE TABLE SiteTree_LinkTracking;
92
        TRUNCATE TABLE SiteTree_Live;
93
        TRUNCATE TABLE SiteTree_Versions;
94
        TRUNCATE TABLE SiteTree_ViewerGroups;
95
SQL;
96
        DB::query($sql);
97
        $this->displayMessage($sql);
98
    }
99
100
    /**
101
     * Loop on all DataObjects and look for rename_columns property
102
     *
103
     * It will rename old columns from old_value => new_value
104
     *
105
     * @return void
106
     */
107
    protected function renameColumns()
108
    {
109
        $classes = $this->getDataObjects();
110
111
        foreach ($classes as $class) {
112
            if (!property_exists($class, 'rename_columns')) {
113
                continue;
114
            }
115
116
            $fields = $class::$rename_columns;
117
118
            $schema = DataObject::getSchema();
119
            $tableName = $schema->baseDataTable($class);
120
121
            $dbSchema = DB::get_schema();
122
            foreach ($fields as $oldName => $newName) {
123
                if ($dbSchema->hasField($tableName, $oldName)) {
124
                    if ($dbSchema->hasField($tableName, $newName)) {
125
                        $this->displayMessage("<li>$oldName still exists in $tableName. Data will be migrated to $newName and old column $oldName will be dropped.</li>");
126
                        // Migrate data
127
                        DB::query("UPDATE $tableName SET $newName = $oldName WHERE $newName IS NULL");
128
                        // Remove column
129
                        DB::query("ALTER TABLE $tableName DROP COLUMN $oldName");
130
                    } else {
131
                        $this->displayMessage("<li>Renaming $oldName to $newName in $tableName</li>");
132
                        $dbSchema->renameField($tableName, $oldName, $newName);
133
                    }
134
                } else {
135
                    $this->displayMessage("<li>$oldName does not exist anymore in $tableName</li>");
136
                }
137
138
                // Look for fluent
139
                $fluentTable = $tableName . '_Localised';
140
                if ($dbSchema->hasTable($fluentTable)) {
141
                    if ($dbSchema->hasField($fluentTable, $oldName)) {
142
                        if ($dbSchema->hasField($fluentTable, $newName)) {
143
                            $this->displayMessage("<li>$oldName still exists in $fluentTable. Data will be migrated to $newName and old column $oldName will be dropped.</li>");
144
                            // Migrate data
145
                            DB::query("UPDATE $fluentTable SET $newName = $oldName WHERE $newName IS NULL");
146
                            // Remove column
147
                            DB::query("ALTER TABLE $fluentTable DROP COLUMN $oldName");
148
                        } else {
149
                            $this->displayMessage("<li>Renaming $oldName to $newName in $fluentTable</li>");
150
                            $dbSchema->renameField($fluentTable, $oldName, $newName);
151
                        }
152
                    } else {
153
                        $this->displayMessage("<li>$oldName does not exist anymore in $fluentTable</li>");
154
                    }
155
                }
156
            }
157
        }
158
    }
159
160
    public function afterCallActionHandler()
161
    {
162
        // Other helpers
163
        $clearCache = $this->owner->getRequest()->getVar('clearCache');
164
        $clearEmptyFolders = $this->owner->getRequest()->getVar('clearEmptyFolders');
165
166
        $this->displayMessage("<div class='build'>");
167
        if ($clearCache) {
168
            $this->clearCache();
169
        }
170
        if ($clearEmptyFolders) {
171
            $this->clearEmptyFolders();
172
        }
173
        $this->displayMessage("</div>");
174
175
        // Restore subsite
176
        if ($this->currentSubsite) {
177
            SubsiteHelper::changeSubsite($this->currentSubsite);
178
        }
179
180
        $provisionLocales = $this->owner->getRequest()->getVar('provisionLocales');
181
        if ($provisionLocales) {
182
            $this->displayMessage("<div class='build'><p><b>Provisioning locales</b></p><ul>\n\n");
183
            try {
184
                \LeKoala\Multilingual\LangHelper::provisionLocales();
0 ignored issues
show
The type LeKoala\Multilingual\LangHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
185
                $this->displayMessage("</ul>\n<p><b>Locales provisioned!</b></p></div>");
186
            } catch (Exception $ex) {
187
                $this->displayMessage($ex->getMessage() . '<br/>');
188
            }
189
        }
190
    }
191
192
    protected function clearCache()
193
    {
194
        $this->displayMessage("<strong>Clearing cache folder</strong>");
195
        $folder = Director::baseFolder() . '/silverstripe-cache';
196
        if (!is_dir($folder)) {
197
            $this->displayMessage("silverstripe-cache does not exist in base folder\n");
198
            return;
199
        }
200
        FileHelper::rmDir($folder);
201
        mkdir($folder, 0755);
202
        $this->displayMessage("Cleared silverstripe-cache folder\n");
203
    }
204
205
    protected function clearEmptyFolders()
206
    {
207
        $this->displayMessage("<strong>Clearing empty folders in assets</strong>");
208
        $folder = Director::publicFolder() . '/assets';
209
        if (!is_dir($folder)) {
210
            $this->displayMessage("assets folder does not exist in public folder\n");
211
            return;
212
        }
213
214
        $objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder), RecursiveIteratorIterator::SELF_FIRST);
215
        foreach ($objects as $name => $object) {
216
            if ($object->isDir()) {
217
                $path = $object->getPath();
218
                if (!is_readable($path)) {
219
                    $this->displayMessage("$path is not readable\n");
220
                    continue;
221
                }
222
                if (!FileHelper::dirContainsChildren($path)) {
223
                    rmdir($path);
224
                    $this->displayMessage("Removed $path\n");
225
                }
226
            }
227
        }
228
    }
229
230
    /**
231
     * @return array
232
     */
233
    protected function getDataObjects()
234
    {
235
        $classes = ClassInfo::subclassesFor(DataObject::class);
236
        array_shift($classes); // remove dataobject
237
        return $classes;
238
    }
239
240
    /**
241
     * @param $message
242
     */
243
    protected function displayMessage($message)
244
    {
245
        echo Director::is_cli() ? strip_tags($message) : nl2br($message);
246
    }
247
}
248