Passed
Push — master ( 6896fd...530886 )
by Josh
02:31
created

Blender::__construct()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 50
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 50
rs 8.8177
c 0
b 0
f 0
cc 6
nc 32
nop 3
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: jgulledge
5
 * Date: 9/29/2017
6
 * Time: 3:33 PM
7
 */
8
9
namespace LCI\Blend;
10
11
use LCI\Blend\Helpers\Format;
12
use LCI\Blend\Migrations\MigrationsCreator;
13
use LCI\Blend\Model\xPDO\BlendMigrations;
14
use modX;
0 ignored issues
show
Bug introduced by
The type modX 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...
15
use LCI\Blend\Helpers\BlendableLoader;
16
use LCI\MODX\Console\Helpers\UserInteractionHandler;
17
use Exception;
18
19
class Blender
20
{
21
    /** @var string ~ version number of the project */
22
    private $version = '1.0.0 beta';
23
24
    /** @var array a list of valid upgrade migrations */
25
    protected $update_migrations = [
26
        '0.9.7' => 'v0_9_7_update',
27
        '0.9.8' => 'v0_9_8_update',
28
        '0.9.9' => 'v0_9_9_update',
29
        '0.9.10' => 'v0_9_10_update',
30
        '0.9.11' => 'v0_9_11_update',
31
        '1.0.0 beta' => 'v1_0_0_beta_update'
32
    ];
33
34
    /** @var  \modx */
0 ignored issues
show
Bug introduced by
The type modx 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...
35
    protected $modx;
36
37
    /** @var array  */
38
    protected $modx_version_info = [];
39
40
    /** @var \LCI\MODX\Console\Helpers\UserInteractionHandler */
41
    protected $userInteractionHandler;
42
43
    /** @var array  */
44
    protected $config = [];
45
46
    /** @var boolean|array  */
47
    protected $blendMigrations = false;
48
49
    /** @var BlendableLoader */
50
    protected $blendableLoader;
51
52
    /** @var  \Tagger */
0 ignored issues
show
Bug introduced by
The type Tagger 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...
53
    protected $tagger;
54
55
    protected $resource_id_map = [];
56
57
    protected $resource_seek_key_map = [];
58
59
    protected $category_map = [];
60
61
    /** @var string date('Y_m_d_His') */
62
    protected $seeds_dir = '';
63
64
    /** @var int  */
65
    protected $xpdo_version = 3;
66
67
    protected $blend_class_object = 'BlendMigrations';
68
69
    protected $blend_package = 'blend';
70
71
    /**
72
     * Stockpile constructor.
73
     *
74
     * @param \modX $modx
75
     * @param UserInteractionHandler $userInteractionHandler
76
     * @param array $config
77
     */
78
    public function __construct(modX $modx, UserInteractionHandler $userInteractionHandler, $config = [])
79
    {
80
        $this->modx = $modx;
81
82
        $this->modx_version_info = $this->modx->getVersionData();
83
84
        $this->userInteractionHandler = $userInteractionHandler;
85
86
        if (version_compare($this->modx_version_info['full_version'], '3.0') >= 0) {
87
            $this->xpdo_version = 3;
88
            $this->blend_class_object = 'LCI\\Blend\\Model\\xPDO\\BlendMigrations';
89
            $this->blend_package = 'LCI\\Blend\\Model\\xPDO';
90
91
        } else {
92
            $this->xpdo_version = 2;
93
        }
94
95
        $blend_modx_migration_dir = dirname(__DIR__);
96
        if (isset($config['blend_modx_migration_dir'])) {
97
            $blend_modx_migration_dir = $config['blend_modx_migration_dir'];
98
        }
99
100
        $this->config = [
101
            'migration_templates_path' => __DIR__.'/Migrations/templates/',
102
            'migrations_path' => $blend_modx_migration_dir.'database/migrations/',
103
            'seeds_path' => $blend_modx_migration_dir.'database/seeds/',
104
            'model_dir' => __DIR__.($this->xpdo_version >= 3 ? '/' : '/xpdo2/'),
105
            'extras' => [
106
                'tagger' => false
107
            ]
108
        ];
109
        $this->config = array_merge($this->config, $config);
110
111
        $this->seeds_dir = date('Y_m_d_His');
112
113
        $tagger_path = $this->modx->getOption('tagger.core_path', null, $this->modx->getOption('core_path').'components/tagger/').'model/tagger/';
114
        if (is_dir($tagger_path)) {
115
            $this->config['extras']['tagger'] = true;
116
            /** @var \Tagger $tagger */
117
            $this->tagger = $this->modx->getService('tagger', 'Tagger', $tagger_path, []);
118
        }
119
120
        if ($this->xpdo_version >= 3) {
121
            $this->modx->setPackage($this->blend_package, $this->config['model_dir']);
122
123
        } else {
124
            $this->modx->addPackage($this->blend_package, $this->config['model_dir']);
125
        }
126
127
        $this->blendableLoader = new BlendableLoader($this, $this->modx, $this->userInteractionHandler);
128
    }
129
130
    /**
131
     * @deprecated
132
     * @param $name
133
     * @param $arguments
134
     * @return mixed
135
     * @throws Exception
136
     */
137
    public function __call($name, $arguments)
138
    {
139
        // How to mark as deprecated?
140
        if (method_exists($this->blendableLoader, $name)) {
141
142
            $message = get_class($this) . '->'.$name.'() has been deprecated, please use ' . get_class($this) . '->getBlendableLoader()->' . $name;
143
            trigger_error($message, E_USER_WARNING);
144
            $this->modx->log(\modX::LOG_LEVEL_ERROR, $message);
145
146
            return call_user_func_array(array($this->blendableLoader, $name), $arguments);
147
148
        } else {
149
            throw new Exception('Call to undefined Method ' . get_class($this) . '->' . $name);
150
        }
151
    }
152
153
    /**
154
     * @return BlendableLoader
155
     */
156
    public function getBlendableLoader(): BlendableLoader
157
    {
158
        return $this->blendableLoader;
159
    }
160
161
    /**
162
     * @return string
163
     */
164
    public function getBlendClassObject()
165
    {
166
        return $this->blend_class_object;
167
    }
168
169
    /**
170
     * @return UserInteractionHandler
171
     */
172
    public function getUserInteractionHandler()
173
    {
174
        return $this->userInteractionHandler;
175
    }
176
177
    /**
178
     * @return string
179
     */
180
    public function getVersion()
181
    {
182
        return $this->version;
183
    }
184
185
    /**
186
     * @return string
187
     */
188
    public function getSeedsDir()
189
    {
190
        return $this->seeds_dir;
191
    }
192
193
    /**
194
     * @param string $seeds_dir ~ local folder
195
     *
196
     * @return Blender
197
     */
198
    public function setSeedsDir($seeds_dir)
199
    {
200
        $this->seeds_dir = $seeds_dir;
201
        return $this;
202
    }
203
204
    /**
205
     * @param null $directory_key
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $directory_key is correct as it would always require null to be passed?
Loading history...
206
     * @return string
207
     */
208
    public function getSeedsPath($directory_key = null)
209
    {
210
        $seed_path = $this->config['seeds_path'];
211
        if (!empty($directory_key)) {
212
            $seed_path .= trim($directory_key, '/').DIRECTORY_SEPARATOR;
213
        }
214
        return $seed_path;
215
    }
216
217
    /**
218
     * @return string
219
     */
220
    public function getMigrationPath()
221
    {
222
        return $this->config['migrations_path'];
223
    }
224
225
    /**
226
     * @param bool $reload
227
     * @param string $dir
228
     * @param int $count
229
     * @param int $id
230
     * @param string $name
231
     *
232
     * @return array ~ array of \BlendMigrations
233
     */
234
    public function getBlendMigrationCollection($reload = false, $dir = 'ASC', $count = 0, $id = 0, $name = null)
235
    {
236
        if (!$this->blendMigrations || $reload) {
237
            $blendMigrations = [];
238
239
            /** @var \xPDOQuery $query */
240
            $query = $this->modx->newQuery($this->blend_class_object);
241
            if ($id > 0) {
242
                $query->where(['id' => $id]);
243
            } elseif (!empty($name)) {
244
                $query->where(['name' => $name]);
245
            }
246
            // @TODO need a ran sequence column to better order of down
247
            $query->sortBy('name', $dir);
248
            if ($count > 0) {
249
                $query->limit($count);
250
            }
251
            $query->prepare();
252
            //echo 'SQL: '.$query->toSQL();
253
            $migrationCollection = $this->modx->getCollection($this->blend_class_object, $query);
254
255
            /** @var \BlendMigrations $migration */
256
            foreach ($migrationCollection as $migration) {
257
                $blendMigrations[$migration->get('name')] = $migration;
258
            }
259
            $this->blendMigrations = $blendMigrations;
260
        }
261
        return $this->blendMigrations;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->blendMigrations also could return the type true which is incompatible with the documented return type array.
Loading history...
262
    }
263
264
    /**
265
     * @return \Tagger
266
     */
267
    public function getTagger()
268
    {
269
        return $this->tagger;
270
    }
271
272
    /**
273
     * @param bool $refresh
274
     * @return array
275
     */
276
    public function getCategoryMap($refresh = false)
277
    {
278
        if (count($this->category_map) == 0 || $refresh) {
279
            $this->category_map = [
280
                'ids' => [],
281
                'names' => [],
282
                'lineage' => []
283
            ];
284
            $query = $this->modx->newQuery('modCategory');
285
            $query->sortBy('parent');
286
            $query->sortBy('rank');
287
            $categories = $this->modx->getCollection('modCategory', $query);
288
            foreach ($categories as $category) {
289
                $category_data = $category->toArray();
290
291
                $this->category_map['ids'][$category->get('id')] = $category_data;
292
293
                $key = trim($category->get('category'));
294
                // This is not unique!
295
                $this->category_map['names'][$key] = $category_data;
296
297
                // Get the lineage: Parent=>Child=>Grand Child as key
298
                $lineage = $key;
299
                if ($category_data['parent'] > 0 && isset($this->category_map['ids'][$category_data['parent']]) && isset($this->category_map['ids'][$category_data['parent']]['lineage'])) {
300
                    $lineage = $this->category_map['ids'][$category_data['parent']]['lineage'].'=>'.$key;
301
                } elseif ($category_data['parent'] > 0) {
302
                    //$this->out('DID NOT FIND PARENT?? '. print_r($category_data, true), true);
303
                }
304
305
                $this->category_map['ids'][$category->get('id')]['lineage'] = $lineage;
306
307
                $this->category_map['lineage'][$lineage] = $category->toArray();
308
            }
309
        }
310
        return $this->category_map;
311
    }
312
313
    /**
314
     * @param string $message
315
     * @param bool $error
316
     */
317
    public function out($message, $error = false)
318
    {
319
        if ($error) {
320
            $this->userInteractionHandler->tellUser($message, userInteractionHandler::MASSAGE_ERROR);
321
322
        } else {
323
            $this->userInteractionHandler->tellUser($message, userInteractionHandler::MASSAGE_STRING);
324
        }
325
    }
326
327
    /**
328
     * @param string $message
329
     */
330
    public function outSuccess($message)
331
    {
332
        $this->userInteractionHandler->tellUser($message, userInteractionHandler::MASSAGE_SUCCESS);
333
    }
334
335
    /**
336
     * @param string $name
337
     * @param string $server_type
338
     * @param string|null $migration_path
339
     *
340
     * @return bool
341
     */
342
    public function createBlankMigrationClassFile($name, $server_type = 'master', $migration_path = null)
343
    {
344
        $migrationCreator = new MigrationsCreator($this->userInteractionHandler);
345
346
        if (empty($migration_path)) {
347
            $migration_path = $this->getMigrationPath();
348
        }
349
350
        $success = $migrationCreator
351
            ->setPathTimeStamp($this->getSeedsDir())
352
            ->setName($name)
353
            ->setDescription('')
354
            ->setServerType($server_type)
355
            ->setMigrationsPath($migration_path)
356
            ->createBlankMigrationClassFile();
357
358
        $this->logCreatedMigration($migrationCreator->getLogData());
359
        return $success;
360
    }
361
362
    /**
363
     * @return SeedMaker
364
     */
365
    public function getSeedMaker()
366
    {
367
        return new SeedMaker($this->modx, $this);
368
    }
369
370
    /**
371
     * @param string $method
372
     * @param bool $prompt
373
     */
374
    public function install($method = 'up', $prompt = false)
375
    {
376
        $migration_name = 'install_blender';
377
        $custom_migration_dir = __DIR__.'/Migrations/Blend/';
378
379
        $this->runInstallMigration($migration_name, $custom_migration_dir, null, $method, $prompt);
380
    }
381
382
    /**
383
     * @param string $migration_name
384
     * @param string|null $custom_migration_path
385
     * @param string|null $seed_root_path
386
     * @param string $method
387
     * @param bool $prompt
388
     */
389
    protected function runInstallMigration($migration_name, $custom_migration_path = null, $seed_root_path = null, $method = 'up', $prompt = false)
390
    {
391
        // new blender for each instance
392
        $config = $this->config;
393
394
        if (!empty($custom_migration_path)) {
395
            $config['migrations_path'] = $custom_migration_path;
396
        }
397
        if (!empty($seed_root_path)) {
398
            $config['seeds_path'] = $seed_root_path;
399
        }
400
401
        $blender = new Blender($this->modx, $this->getUserInteractionHandler(), $config);
402
403
        /** @var Migrations $migrationProcessClass */
404
        $migrationProcessClass = $blender->loadMigrationClass($migration_name, $blender);
405
406
        if (!$migrationProcessClass instanceof Migrations) {
0 ignored issues
show
introduced by
$migrationProcessClass is always a sub-type of LCI\Blend\Migrations. If $migrationProcessClass can have other possible types, add them to src/Blender.php:403.
Loading history...
407
            $this->out('File is not an instance of LCI\Blend\Migrations: '.$migration_name, true);
408
            $this->out('Did not process, verify it is in the proper directory', true);
409
410
        } elseif ($method == 'up' && !$this->isBlendInstalledInModx()) {
411
412
            $migrationProcessClass->up();
413
414
            /** @var \BlendMigrations $migration */
415
            $migration = $this->modx->newObject($this->blend_class_object);
416
            if ($migration) {
0 ignored issues
show
introduced by
$migration is of type BlendMigrations, thus it always evaluated to true. If $migration can have other possible types, add them to src/Blender.php:414
Loading history...
417
                $migration->set('name', $migration_name);
418
                $migration->set('type', 'master');
419
                $migration->set('description', $migrationProcessClass->getDescription());
420
                $migration->set('version', $migrationProcessClass->getVersion());
421
                $migration->set('status', 'up_complete');
422
                $migration->set('created_at', date('Y-m-d H:i:s'));
423
                $migration->set('processed_at', date('Y-m-d H:i:s'));
424
                if ($migration->save()) {
425
                    $this->outSuccess($migration_name.' ran and logged');
426
                } else {
427
                    $this->out($migration_name.' did not log correctly', true);
428
                }
429
430
                // does the migration directory exist?
431
                if (!file_exists($this->getMigrationPath())) {
432
                    $create = true;
433
                    if ($prompt) {
434
                        $create = $this->userInteractionHandler->promptConfirm('Create the following directory for migration files?' . PHP_EOL
435
                            .$this->getMigrationPath(), true);
436
                    }
437
                    if ($create) {
438
                        mkdir($this->getMigrationPath(), 0700, true);
439
                        $this->outSuccess('Created migration directory: '.$this->getMigrationPath());
440
                    }
441
                }
442
443
            } else {
444
                $this->out($migration_name.' did not log correctly', true);
445
            }
446
447
        } elseif ($method == 'down') {
448
            $migrationProcessClass->down();
449
450
            /** @var \BlendMigrations $migration */
451
            $migration = $this->modx->getObject($this->blend_class_object, ['name' => $migration_name]);
452
            if ($migration) {
0 ignored issues
show
introduced by
$migration is of type BlendMigrations, thus it always evaluated to true. If $migration can have other possible types, add them to src/Blender.php:450
Loading history...
453
                $migration->set('name', $migration_name);
454
                $migration->set('description', $migrationProcessClass->getDescription());
455
                $migration->set('version', $migrationProcessClass->getVersion());
456
                $migration->set('status', 'down_complete');
457
                $migration->set('processed_at', date('Y-m-d H:i:s'));
458
                $migration->save();
459
            }
460
461
        }
462
    }
463
464
    /**
465
     * @param string $method
466
     */
467
    public function update($method = 'up')
468
    {
469
        $current_vesion = $this->modx->getOption('blend.version');
470
471
        // new blender for each instance
472
        $config = $this->config;
473
        $config['migrations_path'] = __DIR__.'/Migrations/Blend/';
474
475
        $blender = new Blender($this->modx, $this->getUserInteractionHandler(), $config);
476
477
        foreach ($this->update_migrations as $v => $migration_name) {
478
            if (version_compare($v, $current_vesion) === 1) {
479
                // can not use as xPDO get queries fill the SELECT with the DB fields and since we are adding one this is a SQL error
480
                //$blender->runMigration($method, 'master', 0, 0, $migration_name);
481
482
                /** @var Migrations $migrationProcessClass */
483
                $migrationProcessClass = $this->loadMigrationClass($migration_name, $blender);
484
485
                if (!$migrationProcessClass instanceof Migrations) {
486
                    $this->out('File is not an instance of LCI\Blend\Migrations: '.$migration_name, true);
487
                    $this->out('Did not process, verify it is in the proper directory', true);
488
489
                } elseif ($method == 'up' && $this->isBlendInstalledInModx()) {
490
491
                    $migrationProcessClass->up();
492
493
                    /** @var \BlendMigrations $migration */
494
                    $migration = $this->modx->newObject($this->blend_class_object);
495
                    if ($migration) {
496
                        $migration->set('name', $migration_name);
497
                        $migration->set('type', 'master');
498
                        $migration->set('description', $migrationProcessClass->getDescription());
499
                        $migration->set('version', $migrationProcessClass->getVersion());
500
                        $migration->set('status', 'up_complete');
501
                        $migration->set('created_at', date('Y-m-d H:i:s'));
502
                        $migration->set('processed_at', date('Y-m-d H:i:s'));
503
                        if ($migration->save()) {
504
                            $this->outSuccess('Blend updated to '.$v);
505
                        } else {
506
                            $this->out('Blend did not update to '.$v, true);
507
                        }
508
509
                    } else {
510
                        $this->out('Blender could not save the update to '.$v, true);
511
                    }
512
513
                } elseif ($method == 'down') {
514
                    $migrationProcessClass->down();
515
516
                    /** @var \BlendMigrations $migration */
517
                    $migration = $this->modx->getObject($this->blend_class_object, ['name' => $migration_name]);
518
                    if ($migration) {
519
                        $migration->set('name', $migration_name);
520
                        $migration->set('description', $migrationProcessClass->getDescription());
521
                        $migration->set('version', $migrationProcessClass->getVersion());
522
                        $migration->set('status', 'down_complete');
523
                        $migration->set('processed_at', date('Y-m-d H:i:s'));
524
                        $migration->save();
525
                    }
526
527
                }
528
529
            }
530
        }
531
532
    }
533
534
    /**
535
     * @return bool
536
     */
537
    public function requireUpdate()
538
    {
539
        $upgrade = false;
540
541
        $current_vesion = $this->modx->getOption('blend.version');
542
        //                                      FILE version,        DB Version
543
        if ($this->isBlendInstalledInModx() && (!$current_vesion || version_compare($this->getVersion(), $current_vesion))) {
544
            $upgrade = true;
545
        }
546
547
        return $upgrade;
548
    }
549
550
    /**
551
     * @return bool
552
     */
553
    public function isBlendInstalledInModx()
554
    {
555
        try {
556
            $table = $this->modx->getTableName($this->blend_class_object);
557
            if ($this->modx->query("SELECT 1 FROM {$table} LIMIT 1") === false) {
558
                return false;
559
            }
560
        } catch (Exception $exception) {
561
            // We got an exception == table not found
562
            return false;
563
        }
564
565
        /** @var \xPDOQuery $query */
566
        $query = $this->modx->newQuery($this->blend_class_object);
567
        $query->select('id');
568
        $query->where([
569
            'name' => 'install_blender',
570
            'status' => 'up_complete'
571
        ]);
572
        $query->sortBy('name');
573
574
        $installMigration = $this->modx->getObject($this->blend_class_object, $query);
575
        if ($installMigration instanceof \BlendMigrations || $installMigration instanceof \LCI\Blend\Model\xPDO\BlendMigrations) {
576
            return true;
577
        }
578
579
        return false;
580
    }
581
    /**
582
     * @param string $method
583
     * @param string $type
584
     * @param int $count
585
     * @param int $id
586
     * @param string $name
587
     */
588
    public function runMigration($method = 'up', $type = 'master', $count = 0, $id = 0, $name = null)
589
    {
590
        $dir = 'ASC';
591
        if ($method == 'down') {
592
            $dir = 'DESC';
593
        } else {
594
            $count = 0;
595
        }
596
        // 1. Get all migrations currently in DB:
597
        $blendMigrations = $this->getBlendMigrationCollection(false, $dir, $count, $id, $name);
598
599
        // 2. Load migration files:
600
        if ($method == 'up') {
601
            if ($this->retrieveMigrationFiles()) {
602
                // this is needed just to insure that the order is correct and any new files
603
                $blendMigrations = $this->getBlendMigrationCollection(true, $dir, $count, $id, $name);
604
            }
605
        }
606
607
        // 3. now run migration if proper
608
        /** @var \BlendMigrations $migration */
609
        foreach ($blendMigrations as $name => $migration) {
610
            if ($id > 0 && $migration->get('id') != $id) {
611
                continue;
612
            }
613
            /** @var string $name */
614
            $name = $migration->get('name');
615
616
            /** @var string $status ~ ready|up_complete|down_complete*/
617
            $status = $migration->get('status');
618
619
            /** @var string $server_type */
620
            $server_type = $migration->get('type');
621
622
            if (($server_type != $type) || ($method == 'up' && $status == 'up_complete') || ($method == 'down' && $status != 'up_complete')) {
623
                continue;
624
            }
625
626
            // new blender for each instance
627
            $blender = new Blender($this->modx, $this->getUserInteractionHandler(), $this->config);
628
629
            /** @var Migrations $migrationProcessClass */
630
            $migrationProcessClass = $this->loadMigrationClass($name, $blender);
631
632
            if ($migrationProcessClass instanceof Migrations) {
633
                $this->out('Load Class: '.$name.' M: '.$method);
634
                if ($method == 'up') {
635
                    $migrationProcessClass->up();
636
                    $this->out('Run up: '.$name);
637
                    $migration->set('status', 'up_complete');
638
                    $migration->set('processed_at', date('Y-m-d H:i:s'));
639
                    $migration->save();
640
641
                } elseif ($method == 'down') {
642
                    $migrationProcessClass->down();
643
                    $migration->set('status', 'down_complete');
644
                    $migration->set('processed_at', date('Y-m-d H:i:s'));
645
                    $migration->save();
646
647
                } else {
648
                    // error
649
                }
650
            } else {
651
                // error
652
            }
653
        }
654
    }
655
656
    /**
657
     * @return bool ~ true if new migrations were found
658
     */
659
    public function retrieveMigrationFiles()
660
    {
661
        // 1. Get all migrations currently in DB:
662
        $migrationCollection = $this->modx->getCollection($this->blend_class_object);
663
664
        $blendMigrations = [];
665
666
        /** @var \BlendMigrations $migration */
667
        foreach ($migrationCollection as $migration) {
668
            $blendMigrations[$migration->get('name')] = $migration;
669
        }
670
671
        $migration_dir = $this->getMigrationPath();
672
        $this->out('Searching '.$migration_dir);
673
674
        $reload = false;
675
        /** @var \DirectoryIterator $file */
676
        foreach (new \DirectoryIterator($this->getMigrationPath()) as $file) {
677
            if ($file->isFile() && $file->getExtension() == 'php') {
678
679
                $name = $file->getBasename('.php');
680
                // @TODO query DB! and test this method
681
                if (!isset($blendMigrations[$name])) {
682
                    $this->out('Create new '.$name);
683
                    /** @var Migrations $migrationProcessClass */
684
                    $migrationProcessClass = $this->loadMigrationClass($name, $this);
685
686
                    /** @var \BlendMigrations $migration */
687
                    $migration = $this->modx->newObject($this->blend_class_object);
688
                    $migration->set('name', $name);
689
                    $migration->set('status', 'ready');
690
                    if ($migrationProcessClass instanceof Migrations) {
691
                        $migration->set('description', $migrationProcessClass->getDescription());
692
                        $migration->set('version', $migrationProcessClass->getVersion());
693
                        $migration->set('author', $migrationProcessClass->getAuthor());
694
                    }
695
                    if (!$migration->save()) {
696
                        exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
697
                    };
698
699
                    $reload = true;
700
                }
701
            }
702
        }
703
        return $reload;
704
    }
705
706
    /**
707
     * @param string $name
708
     * @param Blender $blender
709
     *
710
     * @return bool|Migrations
711
     */
712
    protected function loadMigrationClass($name, Blender $blender)
713
    {
714
        $migrationProcessClass = false;
715
716
        $file = $blender->getMigrationPath().$name.'.php';
717
        if (file_exists($file)) {
718
            require_once $file;
719
720
            if (class_exists($name)) {
721
                /** @var Migrations $migrationProcessClass */
722
                $migrationProcessClass = new $name($this->modx, $blender);
723
            }
724
        }
725
726
        return $migrationProcessClass;
727
    }
728
729
    /**
730
     * @param $type
731
     * @param null $name
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $name is correct as it would always require null to be passed?
Loading history...
732
     * @return string
733
     */
734
    public function getMigrationName($type, $name = null)
735
    {
736
        $format = new Format($this->seeds_dir);
737
        return $format->getMigrationName($type, $name);
738
    }
739
740
    /**
741
     * @param string $name
742
     * @param string $type ~ chunk, plugin, resource, snippet, systemSettings, template, site
743
     *
744
     * @return bool
745
     */
746
    public function removeMigrationFile($name, $type)
747
    {
748
        // @TODO refactor for setting $name
749
        $class_name = $this->getMigrationName($type, $name);
750
751
        $removed = false;
752
        $migration_file = $this->getMigrationPath().$class_name.'.php';
753
        if (file_exists($migration_file)) {
754
            if (unlink($migration_file)) {
755
                $removed = true;
756
                $migration = $this->modx->getObject($this->blend_class_object, ['name' => $class_name]);
757
                if (is_object($migration) && $migration->remove()) {
758
                    $this->out($class_name.' migration has been removed from the blend_migrations table');
759
760
                }
761
            } else {
762
                $this->out($class_name.' migration has not been removed from the blend_migrations table', true);
763
            }
764
765
        } else {
766
            $this->out($this->getMigrationPath().$class_name.'.php migration could not be found to remove', true);
767
        }
768
769
        return $removed;
770
    }
771
    /**
772
     * @param int $id
773
     *
774
     * @return bool|array
775
     */
776
    public function getResourceSeedKeyFromID($id)
777
    {
778
        if (!isset($this->resource_id_map[$id])) {
779
            $seed_key = $context = false;
780
            $resource = $this->modx->getObject('modResource', $id);
781
            if ($resource) {
782
                $context = $resource->get('context_key');
783
                if (!isset($this->resource_seek_key_map[$context])) {
784
                    $this->resource_seek_key_map[$context] = [];
785
                }
786
                $seed_key = $this->getSeedKeyFromAlias($resource->get('alias'));
787
                $this->resource_seek_key_map[$context][$seed_key] = $id;
788
            }
789
            $this->resource_id_map[$id] = [
790
                'context' => $context,
791
                'seed_key' => $seed_key
792
            ];
793
        }
794
795
        return $this->resource_id_map[$id];
796
    }
797
798
    /**
799
     * @param string $seed_key
800
     * @param string $context
801
     *
802
     * @return bool|int
803
     */
804
    public function getResourceIDFromSeedKey($seed_key, $context = 'web')
805
    {
806
        if (!isset($this->resource_seek_key_map[$context])) {
807
            $this->resource_seek_key_map[$context] = [];
808
        }
809
        if (!isset($this->resource_seek_key_map[$context][$seed_key])) {
810
            $id = false;
811
            $alias = $this->getAliasFromSeedKey($seed_key);
812
            $resource = $this->modx->getObject('modResource', ['alias' => $alias, 'context_key' => $context]);
813
            if ($resource) {
814
                $id = $resource->get('id');
815
                $this->resource_seek_key_map[$context][$seed_key] = $id;
816
                $this->resource_id_map[$id] = [
817
                    'context' => $context,
818
                    'seed_key' => $seed_key
819
                ];
820
            }
821
            $this->resource_seek_key_map[$context][$seed_key] = $id;
822
        }
823
824
        return $this->resource_seek_key_map[$context][$seed_key];
825
    }
826
827
    /**
828
     * @param string $alias
829
     *
830
     * @return string
831
     */
832
    public function getSeedKeyFromAlias($alias)
833
    {
834
        return str_replace('/', '#', $alias);
835
    }
836
837
    /**
838
     * @param string $seed_key
839
     *
840
     * @return string
841
     */
842
    public function getAliasFromSeedKey($seed_key)
843
    {
844
        return str_replace('#', '/', $seed_key);
845
    }
846
847
    /**
848
     * @deprecated
849
     * @param string $name
850
     * @param string $type ~ template, template-variable, chunk, snippet or plugin
851
     * @return string
852
     */
853
    public function getElementSeedKeyFromName($name, $type)
854
    {
855
        return $type.'_'.$this->getSeedKeyFromName($name);
856
    }
857
858
    /**
859
     * @param string $name
860
     * @return string
861
     */
862
    public function getSeedKeyFromName($name)
863
    {
864
        // @TODO review
865
        return str_replace('/', '#', $name);
866
    }
867
868
    /**
869
     * @param string $seed_key
870
     * @return string
871
     */
872
    public function getNameFromSeedKey($seed_key)
873
    {
874
        return str_replace('#', '/', $seed_key);
875
    }
876
877
    /**
878
     * @param $data
879
     */
880
    protected function logCreatedMigration($data)
881
    {
882
        try {
883
            /** @var BlendMigrations $migration */
884
            $migration = $this->modx->newObject($this->blend_class_object);
885
            if ($migration) {
0 ignored issues
show
introduced by
$migration is of type LCI\Blend\Model\xPDO\BlendMigrations, thus it always evaluated to true. If $migration can have other possible types, add them to src/Blender.php:883
Loading history...
886
                $migration->fromArray($data);
887
                $migration->save();
888
            }
889
        } catch (Exception $exception) {
890
            $this->out($exception->getMessage(), true);
891
        }
892
    }
893
}
894