voyager-admin/voyager

View on GitHub
src/Manager/Settings.php

Summary

Maintainability
B
4 hrs
Test Coverage
B
83%
<?php

namespace Voyager\Admin\Manager;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Voyager\Admin\Classes\Setting;
use Voyager\Admin\Facades\Voyager as VoyagerFacade;

class Settings
{
    protected string $path;
    protected ?Collection $settings = null;

    public function __construct()
    {
        $this->path = Str::finish(storage_path('voyager'), '/').'settings.json';
    }

    /**
     * Sets the path where the settings-file is stored.
     *
     * @param string $path
     *
     * @return string the current path
     */
    public function setPath($path = null)
    {
        if (!is_null($path)) {
            $this->path = $path;
        }

        $this->load(true);

        return $this->path;
    }

    public function get(): ?Collection
    {
        $this->load();
        return $this->settings;
    }

    // Set a settings-value based on the key. When locale is not provided and the setting is translatable, it expects an array of locale-values
    public function set(string $key, mixed $value, ?string $locale = null, bool $save = true): void
    {
        $this->load();
        $setting = $this->getSettingsByKey($key);

        if ($setting->count() == 1) {
            $setting = $setting->first();
            if ($setting->translatable && !is_array($value) && $locale == null) {
                throw new \Exception('Setting `'.$key.'` is translatable but no locale was provided and the value is not an array');
            }
            if ($setting->translatable && is_array($value) && $locale == null) {
                $setting->value = array_merge($setting->value ?? [], $value);
            } else if ($setting->translatable && $locale !== null) {
                $setting->value[$locale] = $value;
            } else {
                $setting->value = $value;
            }

            // Save settings when $save is true. Useful when you want to batch-update settings
            if ($save) {
                $this->save();
            }
        } else {
            throw new \Exception('Setting with key `'.$key.'` does not exist or matches multiple settings');
        }
    }

    public function merge(array $settings): void
    {
        $this->load();
        $this->settings = $this->settings?->merge($settings);
    }

    public function setting(?string $key = null, mixed $default = null, bool $translate = true): mixed
    {
        $this->load();
        $settings = $this->getSettingsByKey($key);

        // Modify collection and only include key/value pairs
        $settings = $settings->mapWithKeys(function ($setting) use ($translate, $default) {
            $key = $setting->key;
            if ($setting->group !== null && $setting->group !== '') {
                $key = implode('.', [$setting->group, $setting->key]);
            }
            if ($translate && ($setting->translatable ?? false)) {
                return [$key => VoyagerFacade::translate($setting->value, app()->getLocale(), config('app.fallback_locale')) ?? $default];
            }

            return [$key => $setting->value ?? $default];
        })->transform(function ($value) {
            if ($value === 'true') {
                return true;
            } elseif ($value === 'false') {
                return false;
            }

            return $value;
        });

        if ($settings->count() == 0) {
            return $default;
        } elseif ($settings->count() == 1) {
            $settings = $settings->first();
        }

        return $settings;
    }

    public function exists(?string $group, string $key): bool
    {
        $this->load();

        return $this->settings?->where('group', $group)->where('key', $key)->count() > 0;
    }

    public function save(mixed $content = null): void
    {
        if (is_null($content)) {
            $content = $this->settings;
        }
        $this->load();

        if (is_string($content)) {
            $content = VoyagerFacade::getJson($content, []);
        }

        // Remove UUID from settings
        foreach ($content as $key => $setting) {
            if (is_array($setting) && array_key_exists('uuid', $setting)) {
                unset($content[$key]['uuid']);
            }
        }

        if (!is_string($content)) {
            $content = json_encode($content, JSON_PRETTY_PRINT);
        }

        VoyagerFacade::writeToFile($this->path, $content);
        $this->load(true);
    }

    public function getSettingsByKey(?string $key): Collection
    {
        $key = $key ?? '';
        $this->load();
        if (is_null($this->settings)) {
            return collect();
        }
        if (Str::contains($key, '.')) {
            // We are looking for a setting in a group
            list($group, $key) = explode('.', $key);

            return $this->settings->where('group', $group)->where('key', $key);
        } elseif (!empty($key)) {
            // We are looking for a setting without a group OR all group-settings
            $group = $this->settings->where('group', null)->where('key', $key);

            if ($group->count() == 0) {
                // All settings in a group
                return $this->settings->where('group', $key);
            } else {
                // Setting without a group but matching key
                return $group;
            }
        }

        return $this->settings;
    }

    public function load(bool $force = false): void
    {
        if (!$this->settings || (is_array($this->settings) && count($this->settings) == 0) || $force) {
            VoyagerFacade::ensureFileExists($this->path, '[]');
            $this->settings = collect(VoyagerFacade::getJson(File::get($this->path), []));
        }
    }

    public function unload(): void
    {
        $this->settings = null;
    }
}