vendor and env first commit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable / Disable auto save
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Auto-save every time the application shuts down
|
||||
|
|
||||
*/
|
||||
'auto_save' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Options for caching. Set whether to enable cache, its key, time to live
|
||||
| in seconds and whether to auto clear after save.
|
||||
|
|
||||
*/
|
||||
'cache' => [
|
||||
'enabled' => false,
|
||||
'key' => 'setting',
|
||||
'ttl' => 3600,
|
||||
'auto_clear' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Setting driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Select where to store the settings.
|
||||
|
|
||||
| Supported: "database", "json", "memory"
|
||||
|
|
||||
*/
|
||||
'driver' => 'database',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Options for database driver. Enter which connection to use, null means
|
||||
| the default connection. Set the table and column names.
|
||||
|
|
||||
*/
|
||||
'database' => [
|
||||
'connection' => null,
|
||||
'table' => 'settings',
|
||||
'key' => 'key',
|
||||
'value' => 'value',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JSON driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Options for json driver. Enter the full path to the .json file.
|
||||
|
|
||||
*/
|
||||
'json' => [
|
||||
'path' => storage_path() . '/settings.json',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Override application config values
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If defined, settings package will override these config values.
|
||||
|
|
||||
| Sample:
|
||||
| "app.locale" => "settings.locale",
|
||||
|
|
||||
*/
|
||||
'override' => [
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fallback
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define fallback settings to be used in case the default is null
|
||||
|
|
||||
| Sample:
|
||||
| "currency" => "USD",
|
||||
|
|
||||
*/
|
||||
'fallback' => [
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Required Extra Columns
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The list of columns required to be set up
|
||||
|
|
||||
| Sample:
|
||||
| "user_id",
|
||||
| "tenant_id",
|
||||
|
|
||||
*/
|
||||
'required_extra_columns' => [
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define the keys which should be crypt automatically.
|
||||
|
|
||||
| Sample:
|
||||
| "payment.key"
|
||||
|
|
||||
*/
|
||||
'encrypted_keys' => [
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting\Contracts;
|
||||
|
||||
use Akaunting\Setting\Support\Arr;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
abstract class Driver
|
||||
{
|
||||
/**
|
||||
* The settings data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* Whether the store has changed since it was last loaded.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $unsaved = false;
|
||||
|
||||
/**
|
||||
* Whether the settings data are loaded.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $loaded = false;
|
||||
|
||||
/**
|
||||
* Include and merge with fallbacks
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $with_fallback = true;
|
||||
|
||||
/**
|
||||
* Excludes fallback data
|
||||
*/
|
||||
public function withoutFallback()
|
||||
{
|
||||
$this->with_fallback = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific key from the settings data.
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed $default Optional default value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->load();
|
||||
|
||||
return Arr::get($this->data, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fallback value if default is null.
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFallback($key, $default = null)
|
||||
{
|
||||
if (($default !== null) || is_array($key)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return Arr::get((array) config('setting.fallback'), $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given value is same as fallback.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEqualToFallback($key, $value)
|
||||
{
|
||||
return (string) $this->getFallback($key) == (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a key exists in the settings data.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->load();
|
||||
|
||||
return Arr::has($this->data, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific key to a value in the settings data.
|
||||
*
|
||||
* @param string|array $key Key string or associative array of key => value
|
||||
* @param mixed $value Optional only if the first argument is an array
|
||||
*/
|
||||
public function set($key, $value = null)
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->load();
|
||||
$this->unsaved = true;
|
||||
|
||||
if (is_array($key)) {
|
||||
foreach ($key as $k => $v) {
|
||||
Arr::set($this->data, $k, $v);
|
||||
}
|
||||
} else {
|
||||
Arr::set($this->data, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset a key in the settings data.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function forget($key)
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->unsaved = true;
|
||||
|
||||
if ($this->has($key)) {
|
||||
Arr::forget($this->data, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset all keys in the settings data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function forgetAll()
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config('setting.cache.enabled')) {
|
||||
Cache::forget($this->getCacheKey());
|
||||
}
|
||||
|
||||
$this->unsaved = true;
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all settings data.
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->load();
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any changes done to the settings data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->unsaved) {
|
||||
// either nothing has been changed, or data has not been loaded, so
|
||||
// do nothing by returning early
|
||||
return;
|
||||
}
|
||||
|
||||
if (config('setting.cache.enabled') && config('setting.cache.auto_clear')) {
|
||||
Cache::forget($this->getCacheKey());
|
||||
}
|
||||
|
||||
$this->write($this->data);
|
||||
$this->unsaved = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure data is loaded.
|
||||
*
|
||||
* @param $force Force a reload of data. Default false.
|
||||
*/
|
||||
public function load($force = false)
|
||||
{
|
||||
if (!$this->checkExtraColumns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->loaded && !$force) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fallback_data = $this->with_fallback ? config('setting.fallback') : [];
|
||||
$driver_data = $this->readData();
|
||||
|
||||
$this->data = Arr::merge((array) $fallback_data, (array) $driver_data);
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from driver or cache
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function readData()
|
||||
{
|
||||
if (config('setting.cache.enabled')) {
|
||||
return $this->readDataFromCache();
|
||||
}
|
||||
|
||||
return $this->read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from cache
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function readDataFromCache()
|
||||
{
|
||||
return Cache::remember($this->getCacheKey(), config('setting.cache.ttl'), function () {
|
||||
return $this->read();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if extra columns are set up.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function checkExtraColumns()
|
||||
{
|
||||
if (!$required_extra_columns = config('setting.required_extra_columns')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array_keys_exists($required_extra_columns, $this->getExtraColumns())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key based on extra columns.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCacheKey()
|
||||
{
|
||||
$key = config('setting.cache.key');
|
||||
|
||||
foreach ($this->getExtraColumns() as $name => $value) {
|
||||
$key .= '_' . $name . '_' . $value;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra columns added to the rows.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getExtraColumns();
|
||||
|
||||
/**
|
||||
* Read data from driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function read();
|
||||
|
||||
/**
|
||||
* Write data to driver.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function write(array $data);
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting\Drivers;
|
||||
|
||||
use Akaunting\Setting\Contracts\Driver;
|
||||
use Akaunting\Setting\Support\Arr;
|
||||
use Closure;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Support\Arr as LaravelArr;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class Database extends Driver
|
||||
{
|
||||
/**
|
||||
* The database connection instance.
|
||||
*
|
||||
* @var \Illuminate\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The table to query from.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* The key column name to query from.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* The value column name to query from.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Keys which should be encrypt automatically.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $encrypted_keys;
|
||||
|
||||
/**
|
||||
* Any query constraints that should be applied.
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
protected $query_constraint;
|
||||
|
||||
/**
|
||||
* Any extra columns that should be added to the rows.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extra_columns = [];
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Database\Connection $connection
|
||||
* @param string $table
|
||||
*/
|
||||
public function __construct(Connection $connection, $table = null, $key = null, $value = null, array $encrypted_keys = [])
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->table = $table ?: 'settings';
|
||||
$this->key = $key ?: 'key';
|
||||
$this->value = $value ?: 'value';
|
||||
$this->encrypted_keys = $encrypted_keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table to query from.
|
||||
*
|
||||
* @param string $table
|
||||
*/
|
||||
public function setTable($table)
|
||||
{
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key column name to query from.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function setKey($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value column name to query from.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query constraint.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function setConstraint(Closure $callback)
|
||||
{
|
||||
$this->data = [];
|
||||
$this->loaded = false;
|
||||
$this->query_constraint = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extra columns to be added to the rows.
|
||||
*
|
||||
* @param array $columns
|
||||
*/
|
||||
public function setExtraColumns(array $columns)
|
||||
{
|
||||
$this->extra_columns = $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra columns added to the rows.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtraColumns()
|
||||
{
|
||||
return $this->extra_columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function forget($key)
|
||||
{
|
||||
parent::forget($key);
|
||||
|
||||
// because the database driver cannot store empty arrays, remove empty
|
||||
// arrays to keep data consistent before and after saving
|
||||
$segments = explode('.', $key);
|
||||
array_pop($segments);
|
||||
|
||||
while ($segments) {
|
||||
$segment = implode('.', $segments);
|
||||
|
||||
// non-empty array - exit out of the loop
|
||||
if ($this->get($segment)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// remove the empty array and move on to the next segment
|
||||
$this->forget($segment);
|
||||
array_pop($segments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function write(array $data)
|
||||
{
|
||||
// Get current data
|
||||
$db_data = $this->newQuery()->get([$this->key, $this->value])->toArray();
|
||||
|
||||
$insert_data = LaravelArr::dot($data);
|
||||
$update_data = [];
|
||||
$delete_keys = [];
|
||||
|
||||
foreach ($db_data as $db_row) {
|
||||
$key = $db_row->{$this->key};
|
||||
$value = $db_row->{$this->value};
|
||||
|
||||
$is_in_insert = $is_different_in_db = $is_same_as_fallback = false;
|
||||
|
||||
if (isset($insert_data[$key])) {
|
||||
$is_in_insert = true;
|
||||
$is_different_in_db = (string) $insert_data[$key] != (string) $value;
|
||||
$is_same_as_fallback = $this->isEqualToFallback($key, $insert_data[$key]);
|
||||
}
|
||||
|
||||
if ($is_in_insert) {
|
||||
if ($is_same_as_fallback) {
|
||||
// Delete if new data is same as fallback
|
||||
$delete_keys[] = $key;
|
||||
} elseif ($is_different_in_db) {
|
||||
// Update if new data is different from db
|
||||
$update_data[$key] = $insert_data[$key];
|
||||
}
|
||||
} else {
|
||||
// Delete if current db not available in new data
|
||||
$delete_keys[] = $key;
|
||||
}
|
||||
|
||||
unset($insert_data[$key]);
|
||||
}
|
||||
|
||||
foreach ($update_data as $key => $value) {
|
||||
$value = $this->prepareValue($key, $value);
|
||||
|
||||
$this->newQuery()
|
||||
->where($this->key, '=', $key)
|
||||
->update([$this->value => $value]);
|
||||
}
|
||||
|
||||
if ($insert_data) {
|
||||
$this->newQuery(true)
|
||||
->insert($this->prepareInsertData($insert_data));
|
||||
}
|
||||
|
||||
if ($delete_keys) {
|
||||
$this->newQuery()
|
||||
->whereIn($this->key, $delete_keys)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms settings data into an array ready to be insterted into the
|
||||
* database. Call array_dot on a multidimensional array before passing it
|
||||
* into this method!
|
||||
*
|
||||
* @param array $data Call array_dot on a multidimensional array before passing it into this method!
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareInsertData(array $data)
|
||||
{
|
||||
$db_data = [];
|
||||
|
||||
if ($this->getExtraColumns()) {
|
||||
foreach ($data as $key => $value) {
|
||||
$value = $this->prepareValue($key, $value);
|
||||
|
||||
// Don't insert if same as fallback
|
||||
if ($this->isEqualToFallback($key, $value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$db_data[] = array_merge(
|
||||
$this->getExtraColumns(),
|
||||
[$this->key => $key, $this->value => $value]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
foreach ($data as $key => $value) {
|
||||
$value = $this->prepareValue($key, $value);
|
||||
|
||||
// Don't insert if same as fallback
|
||||
if ($this->isEqualToFallback($key, $value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$db_data[] = [$this->key => $key, $this->value => $value];
|
||||
}
|
||||
}
|
||||
|
||||
return $db_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided key should be encrypted or not.
|
||||
* Also type casts the given value to a string so errors with booleans or integers are handeled.
|
||||
* Otherwise it returns the original value.
|
||||
*
|
||||
* @param string $key Key to check if it's inside the encryptedValues variable.
|
||||
* @param mixed $value Info: Encryption only supports strings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareValue(string $key, $value)
|
||||
{
|
||||
// Check if key should be encrypted
|
||||
if (in_array($key, $this->encrypted_keys)) {
|
||||
// Cast to string to avoid error when a user passes a boolean value
|
||||
return Crypt::encryptString((string) $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided key should be decrypted or not.
|
||||
* Otherwise it returns the original value.
|
||||
*
|
||||
* @param string $key Key to check if it's inside the encryptedValues variable.
|
||||
* @param mixed $value Info: Encryption only supports strings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function unpackValue(string $key, $value)
|
||||
{
|
||||
// Check if key should be encrypted
|
||||
if (in_array($key, $this->encrypted_keys)) {
|
||||
// Cast to string to avoid error when a user passes a boolean value
|
||||
return Crypt::decryptString((string) $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function read()
|
||||
{
|
||||
return $this->parseReadData($this->newQuery()->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse data coming from the database.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parseReadData($data)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($data as $row) {
|
||||
if (is_array($row)) {
|
||||
$key = $row[$this->key];
|
||||
$value = $row[$this->value];
|
||||
} elseif (is_object($row)) {
|
||||
$key = $row->{$this->key};
|
||||
$value = $row->{$this->value};
|
||||
} else {
|
||||
$msg = 'Expected array or object, got ' . gettype($row);
|
||||
throw new \UnexpectedValueException($msg);
|
||||
}
|
||||
|
||||
// Encryption
|
||||
$value = $this->unpackValue($key, $value);
|
||||
|
||||
Arr::set($results, $key, $value);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new query builder instance.
|
||||
*
|
||||
* @param bool $insert
|
||||
*
|
||||
* @return \Illuminate\Database\Query\Builder
|
||||
*/
|
||||
protected function newQuery($insert = false)
|
||||
{
|
||||
$query = $this->connection->table($this->table);
|
||||
|
||||
if (!$insert) {
|
||||
foreach ($this->getExtraColumns() as $key => $value) {
|
||||
$query->where($key, '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->query_constraint !== null) {
|
||||
$callback = $this->query_constraint;
|
||||
$callback($query, $insert);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting\Drivers;
|
||||
|
||||
use Akaunting\Setting\Contracts\Driver;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
class Json extends Driver
|
||||
{
|
||||
/**
|
||||
* @param \Illuminate\Filesystem\Filesystem $files
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct(Filesystem $files, $path = null)
|
||||
{
|
||||
$this->files = $files;
|
||||
|
||||
$this->setPath($path ?: storage_path() . '/settings.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path for the JSON file.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
// If the file does not already exist, we will attempt to create it.
|
||||
if (!$this->files->exists($path)) {
|
||||
$result = $this->files->put($path, '{}');
|
||||
if ($result === false) {
|
||||
throw new \InvalidArgumentException("Could not write to $path.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->files->isWritable($path)) {
|
||||
throw new \InvalidArgumentException("$path is not writable.");
|
||||
}
|
||||
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExtraColumns()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function read()
|
||||
{
|
||||
$contents = $this->files->get($this->path);
|
||||
|
||||
$data = json_decode($contents, true);
|
||||
|
||||
if ($data === null) {
|
||||
throw new \RuntimeException("Invalid JSON in {$this->path}");
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function write(array $data)
|
||||
{
|
||||
if ($data) {
|
||||
$contents = json_encode($data);
|
||||
} else {
|
||||
$contents = '{}';
|
||||
}
|
||||
|
||||
$this->files->put($this->path, $contents);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting\Drivers;
|
||||
|
||||
use Akaunting\Setting\Contracts\Driver;
|
||||
|
||||
class Memory extends Driver
|
||||
{
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data = null)
|
||||
{
|
||||
if ($data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExtraColumns()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function read()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function write(array $data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting;
|
||||
|
||||
use Illuminate\Support\Facades\Facade as BaseFacade;
|
||||
|
||||
class Facade extends BaseFacade
|
||||
{
|
||||
/**
|
||||
* Get the registered name of the component.
|
||||
*/
|
||||
public static function getFacadeAccessor()
|
||||
{
|
||||
return 'setting';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting;
|
||||
|
||||
use Akaunting\Setting\Drivers\Database;
|
||||
use Akaunting\Setting\Drivers\Json;
|
||||
use Akaunting\Setting\Drivers\Memory;
|
||||
use Illuminate\Support\Manager as BaseManager;
|
||||
|
||||
class Manager extends BaseManager
|
||||
{
|
||||
/**
|
||||
* The container instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Container\Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* The application instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
*/
|
||||
public function __construct($app = null)
|
||||
{
|
||||
$this->container = $app ?? app();
|
||||
|
||||
parent::__construct($this->container);
|
||||
}
|
||||
|
||||
public function getDefaultDriver()
|
||||
{
|
||||
return config('setting.driver');
|
||||
}
|
||||
|
||||
public function createJsonDriver()
|
||||
{
|
||||
$path = config('setting.json.path');
|
||||
|
||||
return new Json($this->container['files'], $path);
|
||||
}
|
||||
|
||||
public function createDatabaseDriver()
|
||||
{
|
||||
$connection = $this->container['db']->connection(config('setting.database.connection'));
|
||||
$table = config('setting.database.table');
|
||||
$key = config('setting.database.key');
|
||||
$value = config('setting.database.value');
|
||||
$encryptedKeys = config('setting.encrypted_keys');
|
||||
|
||||
return new Database($connection, $table, $key, $value, $encryptedKeys);
|
||||
}
|
||||
|
||||
public function createMemoryDriver()
|
||||
{
|
||||
return new Memory();
|
||||
}
|
||||
|
||||
public function createArrayDriver()
|
||||
{
|
||||
return $this->createMemoryDriver();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting\Middleware;
|
||||
|
||||
use Closure;
|
||||
|
||||
class AutoSaveSetting
|
||||
{
|
||||
/**
|
||||
* Create a new save settings middleware.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setting = app('setting');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
$this->setting->save();
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Set up the options.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->table = config('setting.database.table');
|
||||
$this->key = config('setting.database.key');
|
||||
$this->value = config('setting.database.value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create($this->table, function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string($this->key)->index();
|
||||
$table->text($this->value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop($this->table);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting;
|
||||
|
||||
use Akaunting\Setting\Middleware\AutoSaveSetting;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\View\Compilers\BladeCompiler;
|
||||
|
||||
class Provider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->publishes([
|
||||
__DIR__ . '/Config/setting.php' => config_path('setting.php'),
|
||||
__DIR__ . '/Migrations/2017_08_24_000000_create_settings_table.php' => database_path('migrations/2017_08_24_000000_create_settings_table.php'),
|
||||
], 'setting');
|
||||
|
||||
// Auto save setting
|
||||
if (config('setting.auto_save')) {
|
||||
$kernel = $this->app['Illuminate\Contracts\Http\Kernel'];
|
||||
$kernel->pushMiddleware(AutoSaveSetting::class);
|
||||
}
|
||||
|
||||
$this->override();
|
||||
|
||||
// Register blade directive
|
||||
$this->callAfterResolving('blade.compiler', function (BladeCompiler $compiler) {
|
||||
$compiler->directive('setting', function ($expression) {
|
||||
return "<?php echo setting($expression); ?>";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('setting.manager', function ($app) {
|
||||
return new Manager($app);
|
||||
});
|
||||
|
||||
$this->app->singleton('setting', function ($app) {
|
||||
return $app['setting.manager']->driver();
|
||||
});
|
||||
|
||||
$this->mergeConfigFrom(__DIR__ . '/Config/setting.php', 'setting');
|
||||
}
|
||||
|
||||
private function override()
|
||||
{
|
||||
$override = config('setting.override', []);
|
||||
|
||||
foreach (Arr::dot($override) as $config_key => $setting_key) {
|
||||
$config_key = is_string($config_key) ? $config_key : $setting_key;
|
||||
|
||||
try {
|
||||
if (! is_null($value = setting($setting_key))) {
|
||||
config([$config_key => $value]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace Akaunting\Setting\Support;
|
||||
|
||||
class Arr
|
||||
{
|
||||
/**
|
||||
* This class is a static class and should not be instantiated.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element from an array.
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $key Specify a nested element by separating keys with full stops.
|
||||
* @param mixed $default If the element is not found, return this.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get(array $data, $key, $default = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if (is_array($key)) {
|
||||
return static::getArray($data, $key, $default);
|
||||
}
|
||||
|
||||
foreach (explode('.', $key) as $segment) {
|
||||
if (!is_array($data)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (!array_key_exists($segment, $data)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$data = $data[$segment];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected static function getArray(array $input, $keys, $default = null)
|
||||
{
|
||||
$output = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
static::set($output, $key, static::get($input, $key, $default));
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an array has a given key.
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has(array $data, $key)
|
||||
{
|
||||
foreach (explode('.', $key) as $segment) {
|
||||
if (!is_array($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!array_key_exists($segment, $data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $data[$segment];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an element of an array.
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $key Specify a nested element by separating keys with full stops.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function set(array &$data, $key, $value)
|
||||
{
|
||||
$segments = explode('.', $key);
|
||||
|
||||
$key = array_pop($segments);
|
||||
|
||||
// iterate through all of $segments except the last one
|
||||
foreach ($segments as $segment) {
|
||||
if (!array_key_exists($segment, $data)) {
|
||||
$data[$segment] = array();
|
||||
} elseif (!is_array($data[$segment])) {
|
||||
throw new \UnexpectedValueException('Non-array segment encountered');
|
||||
}
|
||||
|
||||
$data = &$data[$segment];
|
||||
}
|
||||
|
||||
$data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset an element from an array.
|
||||
*
|
||||
* @param array &$data
|
||||
* @param string $key Specify a nested element by separating keys with full stops.
|
||||
*/
|
||||
public static function forget(array &$data, $key)
|
||||
{
|
||||
$segments = explode('.', $key);
|
||||
|
||||
$key = array_pop($segments);
|
||||
|
||||
// iterate through all of $segments except the last one
|
||||
foreach ($segments as $segment) {
|
||||
if (!array_key_exists($segment, $data)) {
|
||||
return;
|
||||
} elseif (!is_array($data[$segment])) {
|
||||
throw new \UnexpectedValueException('Non-array segment encountered');
|
||||
}
|
||||
|
||||
$data = &$data[$segment];
|
||||
}
|
||||
|
||||
unset($data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two multidimensional arrays recursive
|
||||
*
|
||||
* @param array $array_1
|
||||
* @param array $array_2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function merge(array $array_1, array $array_2)
|
||||
{
|
||||
$merged = $array_1;
|
||||
|
||||
foreach ($array_2 as $key => $value) {
|
||||
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
|
||||
$merged[$key] = static::merge($merged[$key], $value);
|
||||
} elseif (is_numeric($key)) {
|
||||
if (!in_array($value, $merged)) {
|
||||
$merged[] = $value;
|
||||
}
|
||||
} else {
|
||||
$merged[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $merged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('array_keys_exists')) {
|
||||
/**
|
||||
* Easily check if multiple array keys exist.
|
||||
*
|
||||
* @param array $keys
|
||||
* @param array $arr
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function array_keys_exists(array $keys, array $arr)
|
||||
{
|
||||
return !array_diff_key(array_flip($keys), $arr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('setting')) {
|
||||
/**
|
||||
* Get / set the specified setting value.
|
||||
*
|
||||
* If an array is passed as the key, we will assume you want to set an array of values.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function setting($key = null, $default = null)
|
||||
{
|
||||
$setting = app('setting');
|
||||
|
||||
if (is_null($key)) {
|
||||
return $setting;
|
||||
}
|
||||
|
||||
if (is_array($key)) {
|
||||
$setting->set($key);
|
||||
|
||||
return $setting;
|
||||
}
|
||||
|
||||
return $setting->get($key, $default);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user