• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visavi / rotor / 26723576697

31 May 2026 08:27PM UTC coverage: 14.172% (-0.4%) from 14.618%
26723576697

push

github

visavi
Перенес файлы обновлений бд в отдельную папку

785 of 5539 relevant lines covered (14.17%)

1.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.76
/app/Models/Module.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Models;
6

7
use Exception;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Support\Facades\Artisan;
10
use Illuminate\Support\Facades\Cache;
11
use Illuminate\Support\Facades\DB;
12
use Illuminate\Support\Facades\File;
13
use Illuminate\Support\Str;
14

15
/**
16
 * Class Module
17
 *
18
 * @property int    $id
19
 * @property string $name
20
 * @property string $version
21
 * @property bool   $active
22
 * @property array  $settings
23
 * @property int    $updated_at
24
 * @property int    $created_at
25
 */
26
class Module extends Model
27
{
28
    /**
29
     * Assets modules path
30
     */
31
    protected const ASSETS_PATH = '/assets/modules/';
32

33
    /**
34
     * Indicates if the model should be timestamped.
35
     */
36
    public $timestamps = false;
37

38
    /**
39
     * The attributes that aren't mass assignable.
40
     */
41
    protected $guarded = [];
42

43
    /**
44
     * Get the attributes that should be cast.
45
     */
46
    protected function casts(): array
60✔
47
    {
48
        return [
60✔
49
            'active'   => 'bool',
60✔
50
            'settings' => 'array',
60✔
51
        ];
60✔
52
    }
53

54
    /**
55
     * Выполняет применение миграции
56
     */
57
    public function migrate(): void
×
58
    {
59
        $migrationPath = base_path('modules/' . $this->name . '/database/migrations');
×
60

61
        if (file_exists($migrationPath)) {
×
62
            Artisan::call('migrate', [
×
63
                '--force'    => true,
×
64
                '--realpath' => true,
×
65
                '--path'     => $migrationPath,
×
66
            ]);
×
67
        }
68
    }
69

70
    /**
71
     * Выполняет откат миграций
72
     */
73
    public function rollback(): void
×
74
    {
75
        $migrationPath = base_path('modules/' . $this->name . '/database/migrations');
×
76

77
        if (file_exists($migrationPath)) {
×
78
            $migrator = app('migrator');
×
79
            $nextBatchNumber = $migrator->getRepository()->getNextBatchNumber();
×
80
            $migrationNames = array_keys($migrator->getMigrationFiles($migrationPath));
×
81

82
            DB::table(config('database.migrations.table'))
×
83
                ->whereIn('migration', $migrationNames)
×
84
                ->update(['batch' => $nextBatchNumber]);
×
85

86
            Artisan::call('migrate:rollback', [
×
87
                '--force'    => true,
×
88
                '--realpath' => true,
×
89
                '--path'     => $migrationPath,
×
90
            ]);
×
91
        }
92
    }
93

94
    /**
95
     * Создает симлинки модулей
96
     */
97
    public function createSymlink(): void
×
98
    {
99
        $originPath = public_path($this->getLinkName());
×
100
        if (file_exists($originPath)) {
×
101
            return;
×
102
        }
103

104
        $assetsPath = base_path('modules/' . $this->name . '/resources/assets');
×
105
        if (! file_exists($assetsPath)) {
×
106
            return;
×
107
        }
108

109
        File::link($assetsPath, $originPath);
×
110
    }
111

112
    /**
113
     * Удаляет симлинки модулей
114
     */
115
    public function deleteSymlink(): void
×
116
    {
117
        $originPath = public_path($this->getLinkName());
×
118
        if (! file_exists($originPath)) {
×
119
            return;
×
120
        }
121

122
        File::delete($originPath);
×
123
    }
124

125
    /**
126
     * Получает название директории для симлинка
127
     */
128
    public function getLinkName(): string
×
129
    {
130
        return self::ASSETS_PATH . Str::plural(strtolower($this->name));
×
131
    }
132

133
    /**
134
     * Получает название директории для симлинка из пути
135
     */
136
    public static function getLinkNameByPath(string $modulePath): string
×
137
    {
138
        return self::ASSETS_PATH . Str::plural(strtolower(basename($modulePath)));
×
139
    }
140

141
    /**
142
     * Количество установленных модулей с доступным обновлением
143
     */
144
    public static function updatesCount(): int
×
145
    {
146
        $installed = self::query()->pluck('version', 'name')->all();
×
147
        if (! $installed) {
×
148
            return 0;
×
149
        }
150

151
        $registry = ModuleRegistry::getAvailableModules();
×
152

153
        $count = 0;
×
154
        foreach ($installed as $name => $version) {
×
155
            $configFile = base_path('modules/' . $name . '/module.php');
×
156
            if (! file_exists($configFile)) {
×
157
                continue;
×
158
            }
159

160
            $config = include $configFile;
×
161
            $localVersion = $config['version'] ?? null;
×
162
            $registryVersion = $registry[$name]['version'] ?? null;
×
163

164
            $hasUpdate = ($localVersion && version_compare($localVersion, $version, '>'))
×
165
                || ($registryVersion && version_compare($registryVersion, $version, '>'));
×
166

167
            if ($hasUpdate) {
×
168
                $count++;
×
169
            }
170
        }
171

172
        return $count;
×
173
    }
174

175
    /**
176
     * Get enabled modules
177
     */
178
    public static function getEnabledModules(): array
60✔
179
    {
180
        $cached = Cache::get('modules', []);
60✔
181
        if ($cached !== []) {
60✔
182
            return $cached;
×
183
        }
184

185
        $modules = self::loadEnabledModules();
60✔
186

187
        if ($modules !== []) {
60✔
188
            Cache::forever('modules', $modules);
×
189
        }
190

191
        return $modules;
60✔
192
    }
193

194
    /**
195
     * Загружает активные модули с их файловой структурой
196
     */
197
    private static function loadEnabledModules(): array
60✔
198
    {
199
        try {
200
            $settings = self::query()
60✔
201
                ->where('active', true)
60✔
202
                ->pluck('settings', 'name')
60✔
203
                ->all();
60✔
204
        } catch (Exception) {
×
205
            return [];
×
206
        }
207

208
        $result = [];
60✔
209
        foreach ($settings as $name => $moduleSettings) {
60✔
210
            $result[$name] = [
×
211
                'files'  => self::scanModuleFiles($name),
×
212
                'config' => self::loadModuleConfig($name, $moduleSettings ?? []),
×
213
            ];
×
214
        }
215

216
        return $result;
60✔
217
    }
218

219
    /**
220
     * Загружает merged config модуля (file + settings)
221
     */
222
    private static function loadModuleConfig(string $name, array $settings): ?array
×
223
    {
224
        $configFile = base_path('modules/' . $name . '/config.php');
×
225
        if (! file_exists($configFile)) {
×
226
            return null;
×
227
        }
228

229
        $config = include $configFile;
×
230
        if (! is_array($config)) {
×
231
            return null;
×
232
        }
233

234
        return $settings ? array_replace_recursive($config, $settings) : $config;
×
235
    }
236

237
    /**
238
     * Сканирует наличие файлов модуля
239
     */
240
    private static function scanModuleFiles(string $name): array
×
241
    {
242
        $base = base_path('modules/' . $name . '/');
×
243

244
        return [
×
245
            'views'      => is_dir($base . 'resources/views'),
×
246
            'lang'       => is_dir($base . 'resources/lang'),
×
247
            'helpers'    => file_exists($base . 'helpers.php'),
×
248
            'hooks'      => file_exists($base . 'hooks.php'),
×
249
            'routes'     => file_exists($base . 'routes.php'),
×
250
            'middleware' => file_exists($base . 'middleware.php'),
×
251
            'module'     => file_exists($base . 'module.php'),
×
252
            'commands'   => array_map(
×
253
                static fn ($file) => 'Modules\\' . $name . '\\Console\\' . basename($file, '.php'),
×
254
                glob($base . 'Console/*.php') ?: []
×
255
            ),
×
256
        ];
×
257
    }
258
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc