phpvms/app/Services/Installer/SeederService.php
2022-03-24 16:55:21 -04:00

312 lines
8.9 KiB
PHP

<?php
namespace App\Services\Installer;
use App\Contracts\Service;
use App\Models\Setting;
use App\Services\DatabaseService;
use Carbon\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Yaml\Yaml;
use function trim;
class SeederService extends Service
{
private DatabaseService $databaseSvc;
private array $counters = [];
private array $offsets = [];
// Map an environment to a seeder directory, if we want to share
public static $seed_mapper = [
'production' => 'prod',
];
public function __construct(DatabaseService $databaseSvc)
{
$this->databaseSvc = $databaseSvc;
}
/**
* See if there are any seeds that are out of sync
*
* @return bool
*/
public function seedsPending(): bool
{
if ($this->settingsSeedsPending()) {
return true;
}
if ($this->permissionsSeedsPending()) {
return true;
}
return false;
}
/**
* Syncronize all of the seed files, run this after the migrations
* and on first install.
*/
public function syncAllSeeds(): void
{
$this->syncAllSettings();
$this->syncAllPermissions();
$this->syncAllModules();
$this->syncAllYamlFileSeeds();
}
/**
* Read all of the YAML files from disk and seed them
*/
public function syncAllYamlFileSeeds(): void
{
Log::info('Running seeder');
$env = App::environment();
if (array_key_exists($env, self::$seed_mapper)) {
$env = self::$seed_mapper[$env];
}
// Gather all of the files to seed
collect()
->concat(Storage::disk('seeds')->files($env))
->map(function ($file) {
return database_path('seeds/'.$file);
})
->filter(function ($file) {
$info = pathinfo($file);
return $info['extension'] === 'yml';
})
->each(function ($file) {
Log::info('Seeding .'.$file);
$this->databaseSvc->seed_from_yaml_file($file);
});
}
public function syncAllModules(): void
{
$data = file_get_contents(database_path('/seeds/modules.yml'));
$yml = Yaml::parse($data);
foreach ($yml as $module) {
$module['updated_at'] = Carbon::now('UTC');
$count = DB::table('modules')->where('name', $module['name'])->count('name');
if ($count === 0) {
$module['created_at'] = Carbon::now('UTC');
DB::table('modules')->insert($module);
} else {
DB::table('modules')
->where('name', $module['name'])
->update($module);
}
}
}
public function syncAllSettings(): void
{
$data = file_get_contents(database_path('/seeds/settings.yml'));
$yml = Yaml::parse($data);
foreach ($yml as $setting) {
if (trim($setting['key']) === '') {
continue;
}
$this->addSetting($setting['key'], $setting);
}
}
public function syncAllPermissions(): void
{
$data = file_get_contents(database_path('/seeds/permissions.yml'));
$yml = Yaml::parse($data);
foreach ($yml as $perm) {
$count = DB::table('permissions')->where('name', $perm['name'])->count('name');
if ($count === 0) {
DB::table('permissions')->insert($perm);
} else {
DB::table('permissions')
->where('name', $perm['name'])
->update($perm);
}
}
}
/**
* @param $key
* @param $attrs
*/
public function addSetting($key, $attrs): void
{
$id = Setting::formatKey($key);
$group = $attrs['group'];
$order = $this->getNextOrderNumber($group);
$attrs = array_merge(
[
'id' => $id,
'key' => $key,
'offset' => $this->offsets[$group],
'order' => $order,
'name' => '',
'group' => $group,
'value' => $attrs['value'],
'default' => $attrs['value'],
'options' => '',
'type' => 'hidden',
'description' => '',
],
$attrs
);
$count = DB::table('settings')->where('id', $id)->count('id');
if ($count === 0) {
DB::table('settings')->insert($attrs);
} else {
unset($attrs['value']); // Don't overwrite this
DB::table('settings')
->where('id', $id)
->update($attrs);
}
}
/**
* Dynamically figure out the offset and the start number for a group.
* This way we don't need to mess with how to order things
* When calling getNextOrderNumber(users) 31, will be returned, then 32, and so on
*
* @param $name
* @param null $offset
* @param int $start_offset
*/
private function addCounterGroup($name, $offset = null, $start_offset = 0): void
{
if ($offset === null) {
$group = DB::table('settings')
->where('group', $name)
->first();
if ($group === null) {
$offset = (int) DB::table('settings')->max('offset');
if ($offset === null) {
$offset = 0;
$start_offset = 1;
} else {
$offset += 100;
$start_offset = $offset + 1;
}
} else {
// Now find the number to start from
$start_offset = (int) DB::table('settings')->where('group', $name)->max('order');
if ($start_offset === null) {
$start_offset = $offset + 1;
} else {
$start_offset++;
}
$offset = $group->offset;
}
}
$this->counters[$name] = $start_offset;
$this->offsets[$name] = $offset;
}
/**
* Get the next increment number from a group
*
* @param $group
*
* @return int
*/
private function getNextOrderNumber($group): int
{
if (!\in_array($group, $this->counters, true)) {
$this->addCounterGroup($group);
}
$idx = $this->counters[$group];
$this->counters[$group]++;
return $idx;
}
/**
* See if there are seeds pending for the settings
*
* @return bool
*/
private function settingsSeedsPending(): bool
{
$all_settings = DB::table('settings')->get();
$data = file_get_contents(database_path('/seeds/settings.yml'));
$yml = Yaml::parse($data);
// See if any are missing from the DB
foreach ($yml as $setting) {
if (trim($setting['key']) === '') {
continue;
}
$id = Setting::formatKey($setting['key']);
$row = $all_settings->firstWhere('id', $id);
// Doesn't exist in the table, quit early and say there is stuff pending
if (!$row) {
Log::info('Setting '.$id.' missing, update available');
return true;
}
// See if any of these column values have changed
foreach (['name', 'description'] as $column) {
$currVal = $row->{$column};
$newVal = $setting[$column];
if ($currVal !== $newVal) {
return true;
}
}
// See if any of the options have changed
if ($row->type === 'select') {
if (!empty($row->options) && $row->options !== $setting['options']) {
Log::info('Options for '.$id.' changed, update available');
return true;
}
}
}
return false;
}
/**
* See if there are seeds pending for the permissions
*
* @return bool
*/
private function permissionsSeedsPending(): bool
{
$all_permissions = DB::table('permissions')->get();
$data = file_get_contents(database_path('/seeds/permissions.yml'));
$yml = Yaml::parse($data);
foreach ($yml as $perm) {
$row = $all_permissions->firstWhere('name', $perm['name']);
if (!$row) {
return true;
}
// See if any of these column values have changed
foreach (['display_name', 'description'] as $column) {
if ($row->{$column} !== $perm[$column]) {
return true;
}
}
}
return false;
}
}